"""
Implement grid used to store results of a batch fit.
This is in Guiframe rather than fitting which is probably where it should be.
Actually could be a generic framework implemented in fit gui module. At this
point however there this grid behaves independently of the fitting panel and
only knows about information sent to it but not about the fits or fit panel and
thus cannot feed back to the fitting panel. This could change in the future.
The organization of the classes goes as:
.. note:: Path to this is: /sasview/src/sas/sasgui/guiframe/data_processor.py
.. note:: Path to image is: /sasview/src/sas/sasgui/guiframe/media/BatchGridClassLayout.png
.. image:: ../../user/sasgui/guiframe/BatchGridClassLayout.png
:align: center
"""
from __future__ import print_function
import os
import sys
import copy
import math
import re
import wx
import numpy as np
import wx.aui
from wx.aui import AuiNotebook as nb
import wx.lib.sheet as sheet
from wx.lib.scrolledpanel import ScrolledPanel
from sas.sasgui.guiframe.panel_base import PanelBase
from sas.sasgui.guiframe.events import NewPlotEvent
from sas.sasgui.guiframe.events import StatusEvent
from sas.sasgui.plottools import plottables
from sas.sasgui.guiframe.dataFitting import Data1D
FUNC_DICT = {"sqrt": "math.sqrt",
"pow": "math.sqrt"}
[docs]class BatchCell(object):
"""
Object describing a cell in the grid.
"""
def __init__(self):
"""
Initialize attributes of class (label, value, col, row, object)
"""
self.label = ""
self.value = None
self.col = -1
self.row = -1
self.object = []
[docs]def parse_string(sentence, list):
"""
Return a dictionary of column label and index or row selected
:param sentence: String to parse
:param list: list of columns label
:returns: col_dict
"""
p2 = re.compile(r'\d+')
p = re.compile(r'[\+\-\*\%\/]')
labels = p.split(sentence)
col_dict = {}
for elt in labels:
rang = None
temp_arr = []
for label in list:
label_pos = elt.find(label)
separator_pos = label_pos + len(label)
if label_pos != -1 and len(elt) >= separator_pos and\
elt[separator_pos] == "[":
# the label contain , meaning the range selected is not
# continuous
if elt.count(',') > 0:
new_temp = []
temp = elt.split(label)
for item in temp:
range_pos = item.find(":")
if range_pos != -1:
rang = p2.findall(item)
for i in xrange(int(rang[0]), int(rang[1]) + 1):
new_temp.append(i)
temp_arr += new_temp
else:
# continuous range
temp = elt.split(label)
for item in temp:
if item.strip() != "":
range_pos = item.find(":")
if range_pos != -1:
rang = p2.findall(item)
for i in xrange(int(rang[0]), int(rang[1]) + 1):
temp_arr.append(i)
col_dict[elt] = (label, temp_arr)
return col_dict
[docs]class SPanel(ScrolledPanel):
"""
ensure proper scrolling of GridPanel
Adds a SetupScrolling call to the normal ScrolledPanel init.
GridPanel then subclasses this class
"""
def __init__(self, parent, *args, **kwds):
"""
initialize ScrolledPanel then force a call to SetupScrolling
"""
ScrolledPanel.__init__(self, parent, *args, **kwds)
self.SetupScrolling()
[docs]class GridCellEditor(sheet.CCellEditor):
"""
Custom cell editor
This subclasses the sheet.CCellEditor (itself a subclass of
grid.GridCellEditor) in order to override two of its methods:
PaintBackrgound and EndEdit.
This is necessary as the sheet module is broken in wx 3.0.2 and
improperly subclasses grid.GridCellEditor
"""
def __init__(self, grid):
"""
Override of CCellEditor init. Runs the grid.GridCellEditor init code
"""
super(GridCellEditor, self).__init__(grid)
[docs] def PaintBackground(self, dc, rect, attr):
"""
Overrides wx.sheet.CCellEditor.PaintBackground which incorrectly calls
the base class method.
In wx3.0 all paint objects must explicitly
have a wxPaintDC (Device Context) object. Thus the paint event which
generates a call to this method provides such a DC object and the
base class in grid expects to receive that object. sheet was apparently
not updated to reflect this and hence fails. This could thus
become obsolete in a future bug fix of wxPython.
Apart from adding a dc variable in the list of arguments in the def
and in the call to the base class the rest of this method is copied
as is from sheet.CCellEditor.PaintBackground
**From original GridCellEditor docs:**
Draws the part of the cell not occupied by the edit control. The
base class version just fills it with background colour from the
attribute.
.. note:: There is no need to override this if you don't need
to do something out of the ordinary.
:param dc: the wxDC object for the paint
"""
# Call base class method.
DC = dc
super(sheet.CCellEditor,self).PaintBackground(DC, rect, attr)
[docs] def EndEdit(self, row, col, grid, previous):
"""
Commit editing the current cell. Returns True if the value has changed.
:param previous: previous value in the cell
"""
changed = False # Assume value not changed
val = self._tc.GetValue() # Get value in edit control
if val != self._startValue: # Compare
changed = True # If different then changed is True
grid.GetTable().SetValue(row, col, val) # Update the table
self._startValue = '' # Clear the class' start value
self._tc.SetValue('') # Clear contents of the edit control
return changed
[docs]class GridPage(sheet.CSheet):
"""
Class that receives the results of a batch fit.
GridPage displays the received results in a wx.grid using sheet. This is
then used by GridPanel and GridFrame to present the full GUI.
"""
def __init__(self, parent, panel=None):
"""
Initialize
Initialize all the attributes of GridPage, and the events. include
the init stuff from sheet.CSheet as well.
"""
#sheet.CSheet.__init__(self, parent)
# The following is the __init__ from CSheet. ##########################
# We re-write it here because the class is broken in wx 3.0,
# such that the cell editor is not able to receive the right
# number of parameters when it is called. The only way to
# pick a different cell editor is apparently to re-write the __init__.
wx.grid.Grid.__init__(self, parent, -1)
# Init variables
self._lastCol = -1 # Init last cell column clicked
self._lastRow = -1 # Init last cell row clicked
self._selected = None # Init range currently selected
# Map string datatype to default renderer/editor
self.RegisterDataType(wx.grid.GRID_VALUE_STRING,
wx.grid.GridCellStringRenderer(),
GridCellEditor(self))
self.CreateGrid(4, 3) # By default start with a 4 x 3 grid
self.SetColLabelSize(18) # Default sizes and alignment
self.SetRowLabelSize(50)
self.SetRowLabelAlignment(wx.ALIGN_RIGHT, wx.ALIGN_BOTTOM)
self.SetColSize(0, 75) # Default column sizes
self.SetColSize(1, 75)
self.SetColSize(2, 75)
# Sink events
self.Bind(wx.grid.EVT_GRID_RANGE_SELECT, self.OnRangeSelect)
self.Bind(wx.grid.EVT_GRID_ROW_SIZE, self.OnRowSize)
self.Bind(wx.grid.EVT_GRID_COL_SIZE, self.OnColSize)
self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnGridSelectCell)
# NOTE: the following bind to standard sheet methods that are
# overriden in this subclassn - actually we have currently
# disabled the on_context_menu that would override the OnRightClick
self.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnCellChange)
self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnLeftClick)
self.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.OnRightClick)
#self.Bind(wx.grid.EVT_GRID_CELL_LEFT_DCLICK, self.OnLeftDoubleClick)
# This ends the __init__ section for CSheet. ##########################
# The following events must be bound even if CSheet is working
# properly and does not need the above re-implementation of the
# CSheet init method. Basically these override any intrinsic binding
self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self.on_right_click)
self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_left_click)
self.AdjustScrollbars()
#self.SetLabelBackgroundColour('#DBD4D4')
self.uid = wx.NewId()
self.parent = parent
self.panel = panel
self.col_names = []
self.data_inputs = {}
self.data_outputs = {}
self.data = None
self.details = ""
self.file_name = None
self._cols = 50
self._rows = 3001
self.last_selected_row = -1
self.last_selected_col = -1
self.col_width = 30
self.row_height = 20
self.max_row_touse = 0
self.axis_value = []
self.axis_label = ""
self.selected_cells = []
self.selected_cols = []
self.selected_rows = []
self.plottable_cells = []
self.plottable_flag = False
self.SetColMinimalAcceptableWidth(self.col_width)
self.SetRowMinimalAcceptableHeight(self.row_height)
self.SetNumberRows(self._rows)
self.SetNumberCols(self._cols)
color = self.parent.GetBackgroundColour()
for col in range(self._cols):
self.SetCellBackgroundColour(0, col, color)
self.AutoSize()
self.list_plot_panels = {}
self.default_col_width = 75
self.EnableEditing(True)
if self.GetNumberCols() > 0:
self.default_col_width = self.GetColSize(0)
# We have moved these to the top of the init section with the
# rest of the grid event bindings from the sheet init when
# appropriate
#self.Bind(wx.grid.EVT_GRID_LABEL_LEFT_CLICK, self.on_left_click)
#self.Bind(wx.grid.EVT_GRID_LABEL_RIGHT_CLICK, self.on_right_click)
#self.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.on_selected_cell)
#self.Bind(wx.grid.EVT_GRID_CMD_CELL_CHANGE, self.on_edit_cell)
#self.Bind(wx.grid.EVT_GRID_CELL_RIGHT_CLICK, self.onContextMenu)
[docs] def OnLeftClick(self, event):
"""
Overrides sheet.CSheet.OnLefClick.
Processes when a cell is selected by left clicking on that cell. First
process the base Sheet method then the current class specific method
"""
sheet.CSheet.OnLeftClick(self, event)
self.on_selected_cell(event)
[docs] def OnCellChange(self, event):
"""
Overrides sheet.CSheet.OnCellChange.
Processes when a cell has been edited by a cell editor. Checks for the
edited row being outside the max row to use attribute and if so updates
the last row. Then calls the base handler using skip.
"""
row, _ = event.GetRow(), event.GetCol()
if row > self.max_row_touse:
self.max_row_touse = row
if self.data is None:
self.data = {}
event.Skip()
[docs] def on_selected_cell(self, event):
"""
Handler catching cell selection.
Called after calling base 'on left click' method.
"""
flag = event.CmdDown() or event.ControlDown()
flag_shift = event.ShiftDown()
row, col = event.GetRow(), event.GetCol()
cell = (row, col)
if not flag and not flag_shift:
self.selected_cols = []
self.selected_rows = []
self.selected_cells = []
self.axis_label = ""
self.axis_value = []
self.plottable_list = []
self.plottable_cells = []
self.plottable_flag = False
self.last_selected_col = col
self.last_selected_row = row
if col >= 0:
if flag:
label_row = row
else:
label_row = 0
self.axis_label = self.GetCellValue(label_row, col)
self.selected_cols.append(col)
if flag_shift:
if not self.selected_rows:
min_r = 1
else:
min_r = min(self.selected_rows)
for row_s in range(min_r, row + 1):
cel = (row_s, col)
if cel not in self.selected_cells:
if row > 0:
self.selected_cells.append(cel)
self.selected_rows.append(row)
for row_s in self.selected_rows:
cel = (row_s, col)
if row_s > row:
try:
self.selected_cells.remove(cel)
except:
pass
try:
self.selected_rows.remove(row_s)
except:
pass
elif flag:
if cell not in self.selected_cells:
if row > 0:
self.selected_cells.append(cell)
self.selected_rows.append(row)
else:
try:
self.selected_cells.remove(cell)
except:
pass
try:
self.selected_rows.remove(row)
except:
pass
else:
self.selected_cells.append(cell)
self.selected_rows.append(row)
self.axis_value = []
for cell_row, cell_col in self.selected_cells:
if cell_row > 0 and cell_row < self.max_row_touse:
self.axis_value.append(self.GetCellValue(cell_row, cell_col))
event.Skip()
[docs] def on_left_click(self, event):
"""
Is triggered when the left mouse button is clicked while the mouse
is hovering over the column 'label.'
This processes the information on the selected column: the column name
(in row 0 of column) and the range of cells with a valid value to be
used by the GridPanel set_axis methods.
"""
flag = event.CmdDown() or event.ControlDown()
col = event.GetCol()
row = event.GetRow()
if not flag:
self.selected_cols = []
self.selected_rows = []
self.selected_cells = []
self.axis_label = ""
self.axis_value = []
self.plottable_list = []
self.plottable_cells = []
self.plottable_flag = False
self.last_selected_col = col
self.last_selected_row = row
if row != -1 and row not in self.selected_rows:
self.selected_rows.append(row)
if col != -1:
for row in range(1, self.GetNumberRows() + 1):
cell = (row, col)
if row > 0 and row < self.max_row_touse:
if cell not in self.selected_cells:
self.selected_cells.append(cell)
else:
if flag:
self.selected_cells.remove(cell)
self.selected_cols.append(col)
self.axis_value = []
for cell_row, cell_col in self.selected_cells:
val = self.GetCellValue(cell_row, cell_col)
if not val:
self.axis_value.append(self.GetCellValue(cell_row, cell_col))
self.axis_label = self.GetCellValue(0, col)
if not self.axis_label:
self.axis_label = " "
event.Skip()
[docs] def on_right_click(self, event):
"""
Is triggered when the right mouse button is clicked while the mouse
is hovering over the column 'label.'
This brings up a context menu that allows the deletion of the column,
or the insertion of a new column either to the right or left of the
current column. If inserting a new column can insert a blank column or
choose a number of hidden columns. By default all the error parameters
are in hidden columns so as to save space on the grid. Also any other
intrinsic variables stored with the data such as Temperature, pressure,
time etc can be used to populate this menu.
"""
col = event.GetCol()
row = event.GetRow()
# Ignore the index column
if col < 0 or row != -1:
return
self.selected_cols = []
self.selected_cols.append(col)
# Slicer plot popup menu
slicerpop = wx.Menu()
col_label_menu = wx.Menu()
c_name = self.GetCellValue(0, col)
label = "Insert column before %s " % str(c_name)
slicerpop.AppendSubMenu(col_label_menu, '&%s' % str(label), str(label))
row = 0
label = self.GetCellValue(row, col)
self.insert_col_menu(col_label_menu, label, self)
col_after_menu = wx.Menu()
label = "Insert column after %s " % str(c_name)
slicerpop.AppendSubMenu(col_after_menu, '&%s' % str(label), str(label))
self.insert_after_col_menu(col_after_menu, label, self)
wx_id = wx.NewId()
hint = 'Remove selected column %s'
slicerpop.Append(wx_id, '&Remove Column', hint)
wx.EVT_MENU(self, wx_id, self.on_remove_column)
pos = wx.GetMousePosition()
pos = self.ScreenToClient(pos)
self.PopupMenu(slicerpop, pos)
event.Skip()
[docs] def on_remove_column(self, event=None):
"""
Called when user chooses remove from the column right click menu
Checks the columnn exists then calls the remove_column method
"""
if self.selected_cols is not None or len(self.selected_cols) > 0:
col = self.selected_cols[0]
self.remove_column(col=col, numCols=1)
[docs] def remove_column(self, col, numCols=1):
"""
Remove the col column from the current grid
"""
# add data to the grid
row = 0
col_name = self.GetCellValue(row, col)
self.data[col_name] = []
for row in range(1, self.GetNumberRows() + 1):
if row < self.max_row_touse:
value = self.GetCellValue(row, col)
self.data[col_name].append(value)
for k, value_list in self.data.iteritems():
if k != col_name:
length = len(value_list)
if length < self.max_row_touse:
diff = self.max_row_touse - length
for i in range(diff):
self.data[k].append("")
self.DeleteCols(pos=col, numCols=numCols, updateLabels=True)
[docs] def on_insert_column(self, event):
"""
Called when user chooses insert 'column before' submenu
of the column context menu obtained when right clicking on a given
column header.
Sets up to insert column into the current grid before the current
highlighted column location and sets up what to populate that column
with. Then calls insert_column method to actually do the insertion.
"""
if self.selected_cols is not None or len(self.selected_cols) > 0:
col = self.selected_cols[0]
# add data to the grid
wx_id = event.GetId()
col_name = event.GetEventObject().GetLabelText(wx_id)
self.insert_column(col=col, col_name=col_name)
if not issubclass(event.GetEventObject().__class__, wx.Menu):
col += 1
self.selected_cols[0] += 1
[docs] def on_insert_after_column(self, event):
"""
Called when user chooses insert 'column after' submenu
of the column context menu obtained when right clicking on a given
column header.
Sets up to insert column into the current grid after the current
highlighted column location and sets up what to populate that column
with. Then calls insert_column method to actually do the insertion.
"""
if self.selected_cols is not None or len(self.selected_cols) > 0:
col = self.selected_cols[0] + 1
# add data to the grid
wx_id = event.GetId()
col_name = event.GetEventObject().GetLabelText(wx_id)
self.insert_column(col=col, col_name=col_name)
if not issubclass(event.GetEventObject().__class__, wx.Menu):
self.selected_cols[0] += 1
[docs] def insert_column(self, col, col_name):
"""
Insert column at position col with data[col_name] into the current
grid.
"""
row = 0
self.InsertCols(pos=col, numCols=1, updateLabels=True)
if col_name.strip() != "Empty":
self.SetCellValue(row, col, str(col_name.strip()))
if col_name in self.data.keys():
value_list = self.data[col_name]
cell_row = 1
for value in value_list:
label = value#format_number(value, high=True)
self.SetCellValue(cell_row, col, str(label))
cell_row += 1
self.AutoSizeColumn(col, True)
width = self.GetColSize(col)
if width < self.default_col_width:
self.SetColSize(col, self.default_col_width)
color = self.parent.GetBackgroundColour()
self.SetCellBackgroundColour(0, col, color)
self.ForceRefresh()
[docs] def on_set_x_axis(self, event):
"""
Just calls the panel version of the method
"""
self.panel.set_xaxis(x=self.axis_value, label=self.axis_label)
[docs] def on_set_y_axis(self, event):
"""
Just calls the panel version of the method
"""
self.panel.set_yaxis(y=self.axis_value, label=self.axis_label)
[docs] def set_data(self, data_inputs, data_outputs, details, file_name):
"""
Add data to the grid
:param data_inputs: data to use from the context menu of the grid
:param data_ouputs: default columns displayed
"""
self.file_name = file_name
self.details = details
if data_outputs is None:
data_outputs = {}
self.data_outputs = data_outputs
if data_inputs is None:
data_inputs = {}
self.data_inputs = data_inputs
self.data = {}
for item in (self.data_outputs, self.data_inputs):
self.data.update(item)
if len(self.data_outputs) > 0:
self._cols = self.GetNumberCols()
self._rows = self.GetNumberRows()
self.col_names = self.data_outputs.keys()
self.col_names.sort()
nbr_user_cols = len(self.col_names)
#Add more columns to the grid if necessary
if nbr_user_cols > self._cols:
new_col_nbr = nbr_user_cols - self._cols + 1
self.AppendCols(new_col_nbr, True)
#Add more rows to the grid if necessary
nbr_user_row = len(self.data_outputs.values()[0])
if nbr_user_row > self._rows + 1:
new_row_nbr = nbr_user_row - self._rows + 1
self.AppendRows(new_row_nbr, True)
# add data to the grid
wx.CallAfter(self.set_grid_values)
self.ForceRefresh()
[docs] def set_grid_values(self):
"""
Set the values in grids
"""
# add data to the grid
row = 0
col = 0
cell_col = 0
for col_name in self.col_names:
# use the first row of the grid to add user defined labels
self.SetCellValue(row, col, str(col_name))
col += 1
cell_row = 1
value_list = self.data_outputs[col_name]
for value in value_list:
label = value
if issubclass(value.__class__, BatchCell):
label = value.label
try:
float(label)
label = str(label)#format_number(label, high=True)
except:
label = str(label)
self.SetCellValue(cell_row, cell_col, label)
self.AutoSizeColumn(cell_col, True)
width = self.GetColSize(cell_col)
if width < self.default_col_width:
self.SetColSize(cell_col, self.default_col_width)
cell_row += 1
cell_col += 1
if cell_row > self.max_row_touse:
self.max_row_touse = cell_row
[docs] def get_grid_view(self):
"""
Return value contained in the grid
"""
grid_view = {}
for col in xrange(self.GetNumberCols()):
label = self.GetCellValue(row=0, col=col)
label = label.strip()
if label != "":
grid_view[label] = []
for row in range(1, self.max_row_touse):
value = self.GetCellValue(row=row, col=col)
if value != "":
grid_view[label].append(value)
else:
grid_view[label].append(None)
return grid_view
[docs] def get_nofrows(self):
"""
Return number of total rows
"""
return self._rows
[docs] def on_copy(self, event):
"""
Called when copy is chosen from cell right click context menu
THIS METHOD IS NOT CURRENTLY USED. it is part of right click cell
context menu which is being removed. This will probably be removed in
future versions and is being superseded by more traditional cut and
paste options
"""
self.Copy()
[docs] def on_paste(self, event):
"""
Called when paste is chosen from cell right click context menu
THIS METHOD IS NOT CURRENTLY USED. it is part of right click cell
context menu which is being removed. This will probably be removed in
future versions and is being superseded by more traditional cut and
paste options
"""
if self.data is None:
self.data = {}
if self.file_name is None:
self.file_name = 'copied_data'
self.Paste()
[docs] def on_clear(self, event):
"""
Called when clear cell is chosen from cell right click context menu
THIS METHOD IS NOT CURRENTLY USED. it is part of right click cell
context menu which is being removed. This will probably be removed in
future versions and is being superseded by more traditional cut and
paste options
"""
self.Clear()
[docs]class Notebook(nb, PanelBase):
"""
## Internal name for the AUI manager
window_name = "Fit panel"
## Title to appear on top of the window
"""
window_caption = "Notebook "
def __init__(self, parent, manager=None, data=None, *args, **kwargs):
"""
"""
nb.__init__(self, parent, -1,
style=wx.aui.AUI_NB_WINDOWLIST_BUTTON |
wx.aui.AUI_BUTTON_DOWN |
wx.aui.AUI_NB_DEFAULT_STYLE |
wx.CLIP_CHILDREN)
PanelBase.__init__(self, parent)
self.gpage_num = 1
self.enable_close_button()
self.parent = parent
self.manager = manager
self.data = data
#add empty page
self.add_empty_page()
self.pageClosedEvent = wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE
self.Bind(self.pageClosedEvent, self.on_close_page)
[docs] def add_empty_page(self):
"""
"""
grid = GridPage(self, panel=self.parent)
self.AddPage(grid, "", True)
pos = self.GetPageIndex(grid)
title = "Table" + str(self.gpage_num)
self.SetPageText(pos, title)
self.SetSelection(pos)
self.enable_close_button()
self.gpage_num += 1
return grid, pos
[docs] def on_edit_axis(self):
"""
Return the select cell range from a given selected column. Checks that
all cells are from the same column
"""
pos = self.GetSelection()
grid = self.GetPage(pos)
#grid.selected_cols = [grid.GetSelectedRows()]#
if len(grid.selected_cols) >= 1:
col = grid.selected_cols[0]
for c in grid.selected_cols:
if c != col:
msg = "Edit axis doesn't understand this selection.\n"
msg += "Please select only one column"
raise ValueError, msg
for (_, cell_col) in grid.selected_cells:
if cell_col != col:
msg = "Cannot use cells from different columns for "
msg += "this operation.\n"
msg += "Please select elements of the same col.\n"
raise ValueError, msg
# Finally check the highlighted cell if any cells missing
self.get_highlighted_row(True)
else:
msg = "No item selected.\n"
msg += "Please select only one column or one cell"
raise ValueError, msg
return grid.selected_cells
[docs] def get_highlighted_row(self, is_number=True):
"""
Add highlight rows
"""
pos = self.GetSelection()
grid = self.GetPage(pos)
col = grid.selected_cols[0]
# Finally check the highlighted cell if any cells missing
for row in range(grid.get_nofrows()):
if grid.IsInSelection(row, col):
cel = (row, col)
if row < 1 and not is_number:
continue
# empty cell
if not grid.GetCellValue(row, col).lstrip().rstrip():
if cel in grid.selected_cells:
grid.selected_cells.remove(cel)
continue
if is_number:
try:
float(grid.GetCellValue(row, col))
except:
# non numeric cell
if cel in grid.selected_cells:
grid.selected_cells.remove(cel)
continue
if cel not in grid.selected_cells:
grid.selected_cells.append(cel)
[docs] def get_column_labels(self):
"""
return dictionary of columns labels on the current page
"""
pos = self.GetSelection()
grid = self.GetPage(pos)
labels = {}
for col in range(grid.GetNumberCols()):
label = grid.GetColLabelValue(int(col))
if label.strip() != "":
labels[label.strip()] = col
return labels
[docs] def create_axis_label(self, cell_list):
"""
Receive a list of cells and create a string presenting the selected
cells that can be used as data for one axis of a plot.
:param cell_list: list of tuple
"""
pos = self.GetSelection()
grid = self.GetPage(pos)
label = ""
col_name = ""
def create_label(col_name, row_min=None, row_max=None):
"""
"""
result = " "
if row_min is not None or row_max is not None:
if row_min is None:
result = str(row_max) + "]"
elif row_max is None:
result = str(col_name) + "[" + str(row_min) + ":"
else:
result = str(col_name) + "[" + str(row_min) + ":"
result += str(row_max) + "]"
return str(result)
if len(cell_list) > 0:
if len(cell_list) == 1:
row_min, col = cell_list[0]
col_name = grid.GetColLabelValue(int(col))
col_title = grid.GetCellValue(0, col)
label = create_label(col_name, row_min + 1, row_min + 1)
return label, col_title
else:
temp_list = copy.deepcopy(cell_list)
temp_list.sort()
length = len(temp_list)
row_min, col = temp_list[0]
row_max, _ = temp_list[length - 1]
col_name = grid.GetColLabelValue(int(col))
col_title = grid.GetCellValue(0, col)
index = 0
for row in xrange(row_min, row_max + 1):
if index > 0 and index < len(temp_list):
new_row, _ = temp_list[index]
if row != new_row:
temp_list.insert(index, (None, None))
if index - 1 >= 0:
new_row, _ = temp_list[index - 1]
if new_row is not None and new_row != ' ':
label += create_label(col_name, None,
int(new_row) + 1)
else:
label += "]"
label += ","
if index + 1 < len(temp_list):
new_row, _ = temp_list[index + 1]
if new_row is not None:
label += create_label(col_name,
int(new_row) + 1, None)
if row_min is not None and row_max is not None:
if index == 0:
label += create_label(col_name,
int(row_min) + 1, None)
elif index == len(temp_list) - 1:
label += create_label(col_name, None,
int(row_max) + 1)
index += 1
# clean up the list
label_out = ''
for item in label.split(','):
if item.split(":")[1] == "]":
continue
else:
label_out += item + ","
return label_out, col_title
[docs] def on_close_page(self, event):
"""
close the page
"""
if self.GetPageCount() == 1:
event.Veto()
wx.CallAfter(self.enable_close_button)
[docs] def set_data(self, data_inputs, data_outputs, details="", file_name=None):
"""
"""
if data_outputs is None or data_outputs == {}:
return
inputs, outputs = self.get_odered_results(data_inputs, data_outputs)
for pos in range(self.GetPageCount()):
grid = self.GetPage(pos)
if grid.data is None:
#Found empty page
grid.set_data(data_inputs=inputs,
data_outputs=outputs,
details=details,
file_name=file_name)
self.SetSelection(pos)
return
grid, pos = self.add_empty_page()
grid.set_data(data_inputs=inputs,
data_outputs=outputs,
file_name=file_name,
details=details)
[docs] def get_odered_results(self, inputs, outputs=None):
"""
Order a list of 'inputs.' Used to sort rows and columns to present
in batch results grid.
"""
# Let's re-order the data from the keys in 'Data' name.
if outputs is None:
return
try:
# For outputs from batch
to_be_sort = [str(item.label) for item in outputs['Data']]
except:
# When inputs are from an external file
return inputs, outputs
inds = np.lexsort((to_be_sort, to_be_sort))
for key in outputs.keys():
key_list = outputs[key]
temp_key = [item for item in key_list]
for ind in inds:
temp_key[ind] = key_list[inds[ind]]
outputs[key] = temp_key
for key in inputs.keys():
key_list = inputs[key]
if len(key_list) == len(inds):
temp_key = [item for item in key_list]
for ind in inds:
temp_key[ind] = key_list[inds[ind]]
inputs[key] = temp_key
else:
inputs[key] = []
return inputs, outputs
[docs] def add_column(self):
"""
Append a new column to the grid
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
pos = self.GetSelection()
grid = self.GetPage(pos)
grid.AppendCols(1, True)
[docs] def on_remove_column(self):
"""
Remove the selected column from the grid
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
pos = self.GetSelection()
grid = self.GetPage(pos)
grid.on_remove_column(event=None)
[docs]class GridPanel(SPanel):
"""
A ScrolledPanel class that contains the grid sheet as well as a number of
widgets to create interesting plots and buttons for help etc.
"""
def __init__(self, parent, data_inputs=None,
data_outputs=None, *args, **kwds):
"""
Initialize the GridPanel
"""
SPanel.__init__(self, parent, *args, **kwds)
self.vbox = wx.BoxSizer(wx.VERTICAL)
self.plotting_sizer = wx.FlexGridSizer(3, 7, 10, 5)
self.button_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.grid_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.vbox.AddMany([(self.grid_sizer, 1, wx.EXPAND, 0),
(wx.StaticLine(self, -1), 0, wx.EXPAND, 0),
(self.plotting_sizer),
(self.button_sizer, 0, wx.BOTTOM, 10)])
self.parent = parent
self._data_inputs = data_inputs
self._data_outputs = data_outputs
self.x = []
self.y = []
self.dy = []
self.x_axis_label = None
self.y_axis_label = None
self.dy_axis_label = None
self.x_axis_title = None
self.y_axis_title = None
self.x_axis_unit = None
self.y_axis_unit = None
self.view_button = None
self.plot_button = None
self.notebook = None
self.plot_num = 1
self.layout_grid()
self.layout_plotting_area()
self.SetSizer(self.vbox)
[docs] def set_xaxis(self, label="", x=None):
"""
"""
if x is None:
x = []
self.x = x
self.x_axis_label.SetValue("%s[:]" % str(label))
self.x_axis_title.SetValue(str(label))
[docs] def set_yaxis(self, label="", y=None):
"""
"""
if y is None:
y = []
self.y = y
self.y_axis_label.SetValue("%s[:]" % str(label))
self.y_axis_title.SetValue(str(label))
[docs] def set_dyaxis(self, label="", dy=None):
"""
"""
if dy is None:
dy = []
self.dy = dy
self.dy_axis_label.SetValue("%s[:]" % str(label))
[docs] def get_plot_axis(self, col, list):
"""
"""
axis = []
pos = self.notebook.GetSelection()
grid = self.notebook.GetPage(pos)
for row in list:
label = grid.GetCellValue(0, col)
value = grid.GetCellValue(row - 1, col).strip()
if value != "":
if label.lower().strip() == "data":
axis.append(float(row - 1))
else:
try:
axis.append(float(value))
except:
msg = "Invalid data in row %s column %s" % (str(row), str(col))
wx.PostEvent(self.parent.parent,
StatusEvent(status=msg, info="error"))
return None
else:
axis.append(None)
return axis
[docs] def on_view(self, event):
"""
Get object represented by the given cells and plot them. Basically
plot the column in y vs the column in x.
"""
pos = self.notebook.GetSelection()
grid = self.notebook.GetPage(pos)
title = self.notebook.GetPageText(pos)
self.notebook.get_highlighted_row(False)
if len(grid.selected_cells) == 0:
msg = "Highlight a Data or Chi2 column first..."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
elif len(grid.selected_cells) > 20:
msg = "Too many data (> 20) to plot..."
msg += "\n Please select no more than 20 data."
wx.MessageDialog(self, msg, 'Plotting', wx.OK)
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
for cell in grid.selected_cells:
row, col = cell
label_row = 0
label = grid.GetCellValue(label_row, col)
if label in grid.data:
values = grid.data[label]
if row > len(values) or row < 1:
msg = "Invalid cell was chosen."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
continue
else:
value = values[row - 1]
if issubclass(value.__class__, BatchCell):
if value.object is None or len(value.object) == 0:
msg = "Row %s , " % str(row)
msg += "Column %s is NOT " % str(label)
msg += "the results of fits to view..."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
for new_plot in value.object:
if new_plot is None or \
not issubclass(new_plot.__class__,
plottables.Plottable):
msg = "Row %s , " % str(row)
msg += "Column %s is NOT " % str(label)
msg += "the results of fits to view..."
wx.PostEvent(self.parent.parent,
StatusEvent(status=msg, info="error"))
return
if issubclass(new_plot.__class__, Data1D):
if label in grid.list_plot_panels.keys():
group_id = grid.list_plot_panels[label]
else:
group_id = str(new_plot.group_id) + str(grid.uid)
grid.list_plot_panels[label] = group_id
if group_id not in new_plot.list_group_id:
new_plot.group_id = group_id
new_plot.list_group_id.append(group_id)
else:
if label.lower() in ["data", "chi2"]:
if len(grid.selected_cells) != 1:
msg = "2D View: Please select one data set"
msg += " at a time for View Fit Results."
wx.PostEvent(self.parent.parent,
StatusEvent(status=msg, info="error"))
return
wx.PostEvent(self.parent.parent,
NewPlotEvent(plot=new_plot,
group_id=str(new_plot.group_id),
title=title))
msg = "Plotting the View Fit Results completed!"
wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
else:
msg = "Row %s , " % str(row)
msg += "Column %s is NOT " % str(label)
msg += "the results of fits to view..."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
[docs] def on_plot(self, event):
"""
Evaluate the contains of textcrtl and plot result
"""
pos = self.notebook.GetSelection()
grid = self.notebook.GetPage(pos)
column_names = {}
if grid is not None:
column_names = self.notebook.get_column_labels()
#evaluate x
sentence = self.x_axis_label.GetValue()
try:
if sentence.strip() == "":
msg = "Select column values for x axis"
raise ValueError, msg
except:
msg = "X axis value error."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
dict = parse_string(sentence, column_names.keys())
try:
sentence = self.get_sentence(dict, sentence, column_names)
x = eval(sentence)
except:
msg = "Need a proper x-range."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
#evaluate y
sentence = self.y_axis_label.GetValue()
try:
if sentence.strip() == "":
msg = "select value for y axis"
raise ValueError, msg
except:
msg = "Y axis value error."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
dict = parse_string(sentence, column_names.keys())
try:
sentence = self.get_sentence(dict, sentence, column_names)
y = eval(sentence)
except:
msg = "Need a proper y-range."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
#evaluate y
sentence = self.dy_axis_label.GetValue()
dy = None
if sentence.strip() != "":
dict = parse_string(sentence, column_names.keys())
sentence = self.get_sentence(dict, sentence, column_names)
try:
dy = eval(sentence)
except:
msg = "Need a proper dy-range."
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
if len(x) != len(y) or (len(x) == 0 or len(y) == 0):
msg = "Need same length for X and Y axis and both greater than 0"
msg += " to plot.\n"
msg += "Got X length = %s, Y length = %s" % (str(len(x)), str(len(y)))
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
if dy is not None and (len(y) != len(dy)):
msg = "Need same length for Y and dY axis and both greater than 0"
msg += " to plot.\n"
msg += "Got Y length = %s, dY length = %s" % (str(len(y)), str(len(dy)))
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
if dy is None:
dy = np.zeros(len(y))
#plotting
new_plot = Data1D(x=x, y=y, dy=dy)
new_plot.id = wx.NewId()
new_plot.is_data = False
new_plot.group_id = wx.NewId()
y_title = self.y_axis_title.GetValue()
x_title = self.x_axis_title.GetValue()
title = "%s_vs_%s" % (y_title, x_title)
new_plot.xaxis(x_title, self.x_axis_unit.GetValue())
new_plot.yaxis(y_title, self.y_axis_unit.GetValue())
try:
title = y_title.strip()
title += "_" + self.notebook.GetPageText(pos)
title += "_" + str(self.plot_num)
self.plot_num += 1
new_plot.name = title
new_plot.xtransform = "x"
new_plot.ytransform = "y"
wx.PostEvent(self.parent.parent,
NewPlotEvent(plot=new_plot,
group_id=str(new_plot.group_id), title=title))
msg = "Plotting completed!"
wx.PostEvent(self.parent.parent, StatusEvent(status=msg))
self.parent.parent.update_theory(data_id=new_plot.id, theory=new_plot)
except:
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
[docs] def on_help(self, event):
"""
Bring up the Batch Grid Panel Usage Documentation whenever
the HELP button is clicked.
Calls DocumentationWindow with the path of the location within the
documentation tree (after /doc/ ....". Note that when using old
versions of Wx (before 2.9) and thus not the release version of
installers, the help comes up at the top level of the file as
webbrowser does not pass anything past the # to the browser when it is
running "file:///...."
:param evt: Triggers on clicking the help button
"""
#import documentation window here to avoid circular imports
#if put at top of file with rest of imports.
from documentation_window import DocumentationWindow
_TreeLocation = "user/sasgui/perspectives/fitting/fitting_help.html"
_PageAnchor = "#batch-fit-mode"
_doc_viewer = DocumentationWindow(self, -1, _TreeLocation, _PageAnchor,
"Batch Mode Help")
[docs] def get_sentence(self, dict, sentence, column_names):
"""
Get sentence from dict
"""
for tok, (col_name, list) in dict.iteritems():
col = column_names[col_name]
axis = self.get_plot_axis(col, list)
if axis is None:
return None
sentence = sentence.replace(tok, "numpy.array(%s)" % str(axis))
for key, value in FUNC_DICT.iteritems():
sentence = sentence.replace(key.lower(), value)
return sentence
[docs] def layout_grid(self):
"""
Draw the area related to the grid by adding it as the first element
in the panel's grid_sizer
"""
self.notebook = Notebook(parent=self)
self.notebook.set_data(self._data_inputs, self._data_outputs)
self.grid_sizer.Add(self.notebook, 1, wx.EXPAND, 0)
[docs] def layout_plotting_area(self):
"""
Add the area containing all the plot options, buttons etc to a plotting
area sizer to later be added to the top level grid_sizer
"""
view_description = wx.StaticBox(self, -1, 'Plot Fits/Residuals')
note = "To plot the fits (or residuals), click the 'View Fits' button"
note += "\n after highlighting the Data names (or Chi2 values)."
note_text = wx.StaticText(self, -1, note)
boxsizer1 = wx.StaticBoxSizer(view_description, wx.HORIZONTAL)
self.x_axis_title = wx.TextCtrl(self, -1)
self.y_axis_title = wx.TextCtrl(self, -1)
self.x_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
self.y_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
self.dy_axis_label = wx.TextCtrl(self, -1, size=(200, -1))
self.x_axis_add = wx.Button(self, -1, "Add")
self.x_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis,
id=self.x_axis_add.GetId())
self.y_axis_add = wx.Button(self, -1, "Add")
self.y_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis,
id=self.y_axis_add.GetId())
self.dy_axis_add = wx.Button(self, -1, "Add")
self.dy_axis_add.Bind(event=wx.EVT_BUTTON, handler=self.on_edit_axis,
id=self.dy_axis_add.GetId())
self.x_axis_unit = wx.TextCtrl(self, -1)
self.y_axis_unit = wx.TextCtrl(self, -1)
self.view_button = wx.Button(self, -1, "View Fits")
view_tip = "Highlight the data set or the Chi2 column first."
self.view_button.SetToolTipString(view_tip)
wx.EVT_BUTTON(self, self.view_button.GetId(), self.on_view)
self.plot_button = wx.Button(self, -1, "Plot")
plot_tip = "Highlight a column for each axis and \n"
plot_tip += "click the Add buttons first."
self.plot_button.SetToolTipString(plot_tip)
self.help_button = wx.Button(self, -1, "HELP")
self.help_button.SetToolTipString("Get Help for Batch Mode")
self.help_button.Bind(wx.EVT_BUTTON, self.on_help)
boxsizer1.AddMany([(note_text, 0, wx.LEFT, 10),
(self.view_button, 0, wx.LEFT | wx.RIGHT, 10)])
self.button_sizer.AddMany([(boxsizer1, 0,
wx.LEFT | wx.RIGHT | wx.BOTTOM, 10),
(self.plot_button, 0,
wx.LEFT | wx.TOP | wx.BOTTOM, 12),
(self.help_button,0,
wx.LEFT | wx.TOP | wx.BOTTOM, 12)])
wx.EVT_BUTTON(self, self.plot_button.GetId(), self.on_plot)
self.plotting_sizer.AddMany(\
[(wx.StaticText(self, -1, "X-axis Label\nSelection Range"), 1,
wx.TOP | wx.BOTTOM | wx.LEFT, 10),
(self.x_axis_label, 1, wx.TOP | wx.BOTTOM, 10),
(self.x_axis_add, 1, wx.TOP | wx.BOTTOM | wx.RIGHT, 10),
(wx.StaticText(self, -1, "X-axis Label"), 1, wx.TOP | wx.BOTTOM | wx.LEFT, 10),
(self.x_axis_title, 1, wx.TOP | wx.BOTTOM, 10),
(wx.StaticText(self, -1, "X-axis Unit"), 1, wx.TOP | wx.BOTTOM, 10),
(self.x_axis_unit, 1, wx.TOP | wx.BOTTOM, 10),
(wx.StaticText(self, -1, "Y-axis Label\nSelection Range"), 1,
wx.BOTTOM | wx.LEFT, 10),
(self.y_axis_label, wx.BOTTOM, 10),
(self.y_axis_add, 1, wx.BOTTOM | wx.RIGHT, 10),
(wx.StaticText(self, -1, "Y-axis Label"), 1,
wx.BOTTOM | wx.LEFT, 10),
(self.y_axis_title, wx.BOTTOM, 10),
(wx.StaticText(self, -1, "Y-axis Unit"), 1, wx.BOTTOM, 10),
(self.y_axis_unit, 1, wx.BOTTOM, 10),
(wx.StaticText(self, -1, "dY-Bar (Optional)\nSelection Range"),
1, wx.BOTTOM | wx.LEFT, 10),
(self.dy_axis_label, wx.BOTTOM, 10),
(self.dy_axis_add, 1, wx.BOTTOM | wx.RIGHT, 10),
(-1, -1),
(-1, -1),
(-1, -1),
(-1, 1)])
[docs] def on_edit_axis(self, event):
"""
Get the selected column on the visible grid and set values for axis
"""
try:
cell_list = self.notebook.on_edit_axis()
label, title = self.create_axis_label(cell_list)
except:
msg = str(sys.exc_value)
wx.PostEvent(self.parent.parent, StatusEvent(status=msg, info="error"))
return
tcrtl = event.GetEventObject()
if tcrtl == self.x_axis_add:
self.edit_axis_helper(self.x_axis_label, self.x_axis_title, label, title)
elif tcrtl == self.y_axis_add:
self.edit_axis_helper(self.y_axis_label, self.y_axis_title, label, title)
elif tcrtl == self.dy_axis_add:
self.edit_axis_helper(self.dy_axis_label, None, label, None)
[docs] def create_axis_label(self, cell_list):
"""
Receive a list of cells and create a string presenting the selected
cells.
:param cell_list: list of tuple
"""
if self.notebook is not None:
return self.notebook.create_axis_label(cell_list)
[docs] def edit_axis_helper(self, tcrtl_label, tcrtl_title, label, title):
"""
get controls to modify
"""
if label is not None:
tcrtl_label.SetValue(str(label))
if title is not None:
tcrtl_title.SetValue(str(title))
[docs] def add_column(self):
"""
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
if self.notebook is not None:
self.notebook.add_column()
[docs] def on_remove_column(self):
"""
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
if self.notebook is not None:
self.notebook.on_remove_column()
[docs]class GridFrame(wx.Frame):
"""
The main wx.Frame for the batch results grid
"""
def __init__(self, parent=None, data_inputs=None, data_outputs=None, id=-1,
title="Batch Fitting Results Panel", size=(800, 500)):
"""
Initialize the Frame
"""
wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size)
self.parent = parent
self.panel = GridPanel(self, data_inputs, data_outputs)
menubar = wx.MenuBar()
self.SetMenuBar(menubar)
self.curr_col = None
self.curr_grid = None
self.curr_col_name = ""
self.file = wx.Menu()
menubar.Append(self.file, "&File")
hint = "Open file containing batch results"
open_menu = self.file.Append(wx.NewId(), 'Open ', hint)
wx.EVT_MENU(self, open_menu.GetId(), self.on_open)
hint = "Open the the current grid into excel"
self.open_excel_menu = self.file.Append(wx.NewId(), 'Open with Excel', hint)
wx.EVT_MENU(self, self.open_excel_menu.GetId(), self.open_with_excel)
self.file.AppendSeparator()
self.save_menu = self.file.Append(wx.NewId(), 'Save As', 'Save into File')
wx.EVT_MENU(self, self.save_menu.GetId(), self.on_save_page)
# We need to grab a WxMenu handle here, otherwise the next one to grab
# the handle will be treated as the Edit Menu handle when checking in
# on_menu_open event handler and thus raise an exception when it hits an
# unitialized object. Alternative is to comment out that whole section
# in on_menu_open, but that would make it more difficult to undo the
# hidding of the menu. PDB July 12, 2015.
#
# To enable the Edit menubar comment out next line and uncomment the
# following line.
self.edit = wx.Menu()
#self.add_edit_menu()
self.Bind(wx.EVT_MENU_OPEN, self.on_menu_open)
self.Bind(wx.EVT_CLOSE, self.on_close)
[docs] def on_copy(self, event):
"""
On Copy from the Edit menu item on the menubar
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
if event is not None:
event.Skip()
pos = self.panel.notebook.GetSelection()
grid = self.panel.notebook.GetPage(pos)
grid.Copy()
[docs] def on_paste(self, event):
"""
On Paste from the Edit menu item on the menubar
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
if event is not None:
event.Skip()
pos = self.panel.notebook.GetSelection()
grid = self.panel.notebook.GetPage(pos)
grid.on_paste(None)
[docs] def on_clear(self, event):
"""
On Clear from the Edit menu item on the menubar
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
pos = self.panel.notebook.GetSelection()
grid = self.panel.notebook.GetPage(pos)
grid.Clear()
[docs] def GetLabelText(self, id):
"""
Get Label Text
"""
for item in self.insert_before_menu.GetMenuItems():
m_id = item.GetId()
if m_id == id:
return item.GetLabel()
[docs] def on_remove_column(self, event):
"""
On remove column from the Edit menu Item on the menubar
"""
# I Believe this is no longer used now that we have removed the
# edit menu from the menubar - PDB July 12, 2015
pos = self.panel.notebook.GetSelection()
grid = self.panel.notebook.GetPage(pos)
grid.on_remove_column(event=None)
[docs] def on_save_page(self, event):
"""
Saves data in grid to a csv file.
At this time only the columns displayed get saved. Thus any error
bars not inserted before saving will not be saved in the file
"""
if self.parent is not None:
pos = self.panel.notebook.GetSelection()
grid = self.panel.notebook.GetPage(pos)
if grid.file_name is None or grid.file_name.strip() == "" or \
grid.data is None or len(grid.data) == 0:
name = self.panel.notebook.GetPageText(pos)
msg = " %s has not data to save" % str(name)
wx.PostEvent(self.parent,
StatusEvent(status=msg, info="error"))
return
reader, ext = os.path.splitext(grid.file_name)
path = None
if self.parent is not None:
location = os.path.dirname(grid.file_name)
dlg = wx.FileDialog(self, "Save Project file",
location, grid.file_name, ext, wx.SAVE)
path = None
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
if path is not None:
if self.parent is not None:
data = grid.get_grid_view()
self.parent.write_batch_tofile(data=data,
file_name=path,
details=grid.details)
[docs] def on_open(self, event):
"""
Open file containing batch result
"""
if self.parent is not None:
self.parent.on_read_batch_tofile(self)
[docs] def open_with_excel(self, event):
"""
open excel and display batch result in Excel
"""
if self.parent is not None:
pos = self.panel.notebook.GetSelection()
grid = self.panel.notebook.GetPage(pos)
data = grid.get_grid_view()
if grid.file_name is None or grid.file_name.strip() == "" or \
grid.data is None or len(grid.data) == 0:
name = self.panel.notebook.GetPageText(pos)
msg = " %s has not data to open on excel" % str(name)
wx.PostEvent(self.parent,
StatusEvent(status=msg, info="error"))
return
self.parent.open_with_externalapp(data=data,
file_name=grid.file_name,
details=grid.details)
[docs] def on_close(self, event):
"""
"""
self.Hide()
[docs] def on_append_column(self, event):
"""
Append a new column to the grid
"""
self.panel.add_column()
[docs] def set_data(self, data_inputs, data_outputs, details="", file_name=None):
"""
Set data
"""
self.panel.notebook.set_data(data_inputs=data_inputs,
file_name=file_name,
details=details,
data_outputs=data_outputs)
[docs] def add_table(self, event):
"""
Add a new table
"""
# DO not event.Skip(): it will make 2 pages
self.panel.notebook.add_empty_page()
[docs]class BatchOutputFrame(wx.Frame):
"""
Allow to select where the result of batch will be displayed or stored
"""
def __init__(self, parent, data_inputs, data_outputs, file_name="",
details="", *args, **kwds):
"""
Initialize dialog
:param parent: Window instantiating this dialog
:param result: result to display in a grid or export to an external\
application.
"""
#kwds['style'] = wx.CAPTION|wx.SYSTEM_MENU
wx.Frame.__init__(self, parent, *args, **kwds)
self.parent = parent
self.panel = wx.Panel(self)
self.file_name = file_name
self.details = details
self.data_inputs = data_inputs
self.data_outputs = data_outputs
self.data = {}
for item in (self.data_outputs, self.data_inputs):
self.data.update(item)
self.flag = 1
self.SetSize((300, 200))
self.local_app_selected = None
self.external_app_selected = None
self.save_to_file = None
self._do_layout()
def _do_layout(self):
"""
Draw the content of the current dialog window
"""
vbox = wx.BoxSizer(wx.VERTICAL)
box_description = wx.StaticBox(self.panel, -1, str("Batch Outputs"))
hint_sizer = wx.StaticBoxSizer(box_description, wx.VERTICAL)
selection_sizer = wx.GridBagSizer(5, 5)
button_sizer = wx.BoxSizer(wx.HORIZONTAL)
text = "Open with %s" % self.parent.application_name
self.local_app_selected = wx.RadioButton(self.panel, -1, text, style=wx.RB_GROUP)
self.Bind(wx.EVT_RADIOBUTTON, self.onselect,
id=self.local_app_selected.GetId())
text = "Open with Excel"
self.external_app_selected = wx.RadioButton(self.panel, -1, text)
self.Bind(wx.EVT_RADIOBUTTON, self.onselect, id=self.external_app_selected.GetId())
text = "Save to File"
self.save_to_file = wx.CheckBox(self.panel, -1, text)
self.Bind(wx.EVT_CHECKBOX, self.onselect, id=self.save_to_file.GetId())
self.local_app_selected.SetValue(True)
self.external_app_selected.SetValue(False)
self.save_to_file.SetValue(False)
button_close = wx.Button(self.panel, -1, "Close")
button_close.Bind(wx.EVT_BUTTON, id=button_close.GetId(), handler=self.on_close)
button_apply = wx.Button(self.panel, -1, "Apply")
button_apply.Bind(wx.EVT_BUTTON, id=button_apply.GetId(), handler=self.on_apply)
button_apply.SetFocus()
hint = ""
hint_sizer.Add(wx.StaticText(self.panel, -1, hint))
hint_sizer.Add(selection_sizer)
#draw area containing radio buttons
ix = 0
iy = 0
selection_sizer.Add(self.local_app_selected, (iy, ix),
(1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
iy += 1
selection_sizer.Add(self.external_app_selected, (iy, ix),
(1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
iy += 1
selection_sizer.Add(self.save_to_file, (iy, ix),
(1, 1), wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
#contruction the sizer containing button
button_sizer.Add((20, 20), 1, wx.EXPAND | wx.ADJUST_MINSIZE, 0)
button_sizer.Add(button_close, 0,
wx.LEFT | wx.EXPAND | wx.ADJUST_MINSIZE, 15)
button_sizer.Add(button_apply, 0,
wx.LEFT | wx.RIGHT | wx.ADJUST_MINSIZE, 10)
vbox.Add(hint_sizer, 0, wx.EXPAND | wx.ALL, 10)
vbox.Add(wx.StaticLine(self.panel, -1), 0, wx.EXPAND, 0)
vbox.Add(button_sizer, 0, wx.TOP | wx.BOTTOM, 10)
self.SetSizer(vbox)
[docs] def on_apply(self, event):
"""
Get the user selection and display output to the selected application
"""
if self.flag == 1:
self.parent.open_with_localapp(data_inputs=self.data_inputs,
data_outputs=self.data_outputs)
elif self.flag == 2:
self.parent.open_with_externalapp(data=self.data,
file_name=self.file_name,
details=self.details)
[docs] def on_close(self, event):
"""
close the Window
"""
self.Close()
[docs] def onselect(self, event=None):
"""
Receive event and display data into third party application
or save data to file.
"""
if self.save_to_file.GetValue():
_, ext = os.path.splitext(self.file_name)
path = None
location = os.getcwd()
if self.parent is not None:
location = os.path.dirname(self.file_name)
dlg = wx.FileDialog(self, "Save Project file",
location, self.file_name, ext, wx.SAVE)
path = None
if dlg.ShowModal() == wx.ID_OK:
path = dlg.GetPath()
dlg.Destroy()
if path is not None:
if self.parent is not None and self.data is not None:
self.parent.write_batch_tofile(data=self.data,
file_name=path,
details=self.details)
if self.local_app_selected.GetValue():
self.flag = 1
else:
self.flag = 2
return self.flag
if __name__ == "__main__":
app = wx.App()
try:
data = {}
j = 0
for i in range(4):
j += 1
data["index" + str(i)] = [i / j, i * j, i, i + j]
data_input = copy.deepcopy(data)
data_input["index5"] = [10, 20, 40, 50]
frame = GridFrame(data_outputs=data, data_inputs=data_input)
frame.Show(True)
except:
print(sys.exc_value)
app.MainLoop()