""" Implement grid used to store data """ import wx import numpy import math import re import os import sys import copy from wx.lib.scrolledpanel import ScrolledPanel import wx.aui from wx.aui import AuiNotebook as nb import wx.lib.sheet as sheet from sas.guiframe.panel_base import PanelBase from sas.guiframe.events import NewPlotEvent from sas.guiframe.events import StatusEvent from sas.plottools import plottables from sas.guiframe.dataFitting import Data1D FUNC_DICT = {"sqrt": "math.sqrt", "pow": "math.sqrt"} class BatchCell(object): """ Object describing a cell in the grid. """ def __init__(self): self.label = "" self.value = None self.col = -1 self.row = -1 self.object = [] 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 """ 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 class SPanel(ScrolledPanel): def __init__(self, parent, *args, **kwds): ScrolledPanel.__init__(self, parent, *args, **kwds) self.SetupScrolling() class GridPage(sheet.CSheet): """ """ def __init__(self, parent, panel=None): """ """ sheet.CSheet.__init__(self, parent) 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) 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) def on_edit_cell(self, event): """ """ row, _ = event.GetRow(), event.GetCol() if row > self.max_row_touse: self.max_row_touse = row if self.data == None: self.data = {} event.Skip() def on_selected_cell(self, event): """ Handler catching cell selection """ flag = event.CmdDown() or event.ControlDown() flag_shift = event.ShiftDown() row, col = event.GetRow(), event.GetCol() cell = (row, col) event.Skip() 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)) def on_left_click(self, event): """ Catch the left click on label mouse event """ event.Skip() 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 = " " def on_right_click(self, event): """ Catch the right click mouse """ 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() def insert_col_menu(self, menu, label, window): """ """ if self.data is None: return id = wx.NewId() title = "Empty" hint = 'Insert empty column before %s' % str(label) menu.Append(id, title, hint) wx.EVT_MENU(window, id, self.on_insert_column) row = 0 col_name = [self.GetCellValue(row, col) for col in range(self.GetNumberCols())] for c_name in self.data.keys(): if c_name not in col_name and self.data[c_name]: wx_id = wx.NewId() hint = "Insert %s column before the " % str(c_name) hint += " %s column" % str(label) menu.Append(wx_id, '&%s' % str(c_name), hint) wx.EVT_MENU(window, wx_id, self.on_insert_column) def insert_after_col_menu(self, menu, label, window): """ """ if self.data is None: return wx_id = wx.NewId() title = "Empty" hint = 'Insert empty column after %s' % str(label) menu.Append(wx_id, title, hint) wx.EVT_MENU(window, wx_id, self.on_insert_after_column) row = 0 col_name = [self.GetCellValue(row, col) for col in range(self.GetNumberCols())] for c_name in self.data.keys(): if c_name not in col_name and self.data[c_name]: wx_id = wx.NewId() hint = "Insert %s column after the " % str(c_name) hint += " %s column" % str(label) menu.Append(wx_id, '&%s' % str(c_name), hint) wx.EVT_MENU(window, wx_id, self.on_insert_after_column) def on_remove_column(self, event=None): """ """ 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) def remove_column(self, col, numCols=1): """ Remove column to 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) def on_insert_column(self, event): """ """ 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 def on_insert_after_column(self, event): """ Insert the given column after the highlighted column """ 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 def insert_column(self, col, col_name): """ """ 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() def on_set_x_axis(self, event): """ """ self.panel.set_xaxis(x=self.axis_value, label=self.axis_label) def on_set_y_axis(self, event): """ """ self.panel.set_yaxis(y=self.axis_value, label=self.axis_label) 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 deplayed """ 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() 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 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 def get_nofrows(self): """ Return number of total rows """ return self._rows def onContextMenu(self, event): """ Default context menu """ wx_id = wx.NewId() c_menu = wx.Menu() copy_menu = c_menu.Append(wx_id, '&Copy', 'Copy the selected cells') wx.EVT_MENU(self, wx_id, self.on_copy) wx_id = wx.NewId() c_menu.Append(wx_id, '&Paste', 'Paste the selected cells') wx.EVT_MENU(self, wx_id, self.on_paste) wx_id = wx.NewId() clear_menu = c_menu.Append(wx_id, '&Clear', 'Clear the selected cells') wx.EVT_MENU(self, wx_id, self.on_clear) # enable from flag has_selection = False selected_cel = self.selected_cells if len(selected_cel) > 0: _row, _col = selected_cel[0] has_selection = self.IsInSelection(_row, _col) if len(self.selected_cols) > 0: has_selection = True if len(self.selected_rows) > 0: has_selection = True copy_menu.Enable(has_selection) clear_menu.Enable(has_selection) try: # mouse event pos pos_evt = event.GetPosition() self.PopupMenu(c_menu, pos_evt) except: return def on_copy(self, event): """ On copy event from the contextmenu """ self.Copy() def on_paste(self, event): """ On paste event from the contextmenu """ if self.data == None: self.data = {} if self.file_name == None: self.file_name = 'copied_data' self.Paste() def on_clear(self, event): """ Clear the cells selected """ self.Clear() 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) 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 def enable_close_button(self): """ display the close button on tab for more than 1 tabs else remove the close button """ if self.GetPageCount() <= 1: style = self.GetWindowStyleFlag() flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB == flag: style = style & ~wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB self.SetWindowStyle(style) else: style = self.GetWindowStyleFlag() flag = wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB if style & wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB != flag: style |= wx.aui.AUI_NB_CLOSE_ON_ACTIVE_TAB self.SetWindowStyle(style) def on_edit_axis(self): """ Return the select cell of a given selected column. Check 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 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) def get_column_labels(self): """ return dictionary of columns labels of 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 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 """ 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 not new_row == 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 not new_row == None: label += create_label(col_name, int(new_row) + 1, None) if row_min != None and row_max != 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 def on_close_page(self, event): """ close the page """ if self.GetPageCount() == 1: event.Veto() wx.CallAfter(self.enable_close_button) 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) def get_odered_results(self, inputs, outputs=None): """ Get ordered the results """ # Let's re-order the data from the keys in 'Data' name. if outputs == 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 = numpy.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 def add_column(self): """ Append a new column to the grid """ pos = self.GetSelection() grid = self.GetPage(pos) grid.AppendCols(1, True) def on_remove_column(self): """ Remove the selected column from the grid """ pos = self.GetSelection() grid = self.GetPage(pos) grid.on_remove_column(event=None) class GridPanel(SPanel): def __init__(self, parent, data_inputs=None, data_outputs=None, *args, **kwds): 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) 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)) 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)) def set_dyaxis(self, label="", dy=None): """ """ if dy is None: dy = [] self.dy = dy self.dy_axis_label.SetValue("%s[:]" % str(label)) 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 def on_view(self, event): """ Get object represented buy the given cell and plot them. """ 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 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 != 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 == None: dy = numpy.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")) 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 == 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 def layout_grid(self): """ Draw the area related to the grid """ 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) def layout_plotting_area(self): """ Draw area containing options to plot """ 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) 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 | wx.EXPAND, 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)]) 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) 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) def edit_axis_helper(self, tcrtl_label, tcrtl_title, label, title): """ get controls to modify """ if label != None: tcrtl_label.SetValue(str(label)) if title != None: tcrtl_title.SetValue(str(title)) def add_column(self): """ """ if self.notebook is not None: self.notebook.add_column() def on_remove_column(self): """ """ if self.notebook is not None: self.notebook.on_remove_column() class GridFrame(wx.Frame): def __init__(self, parent=None, data_inputs=None, data_outputs=None, id=-1, title="Grid Window", size=(800, 500)): 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) self.edit = wx.Menu() add_table_menu = self.edit.Append(-1, 'New Table', 'Add a New Table') self.edit.AppendSeparator() wx.EVT_MENU(self, add_table_menu.GetId(), self.add_table) self.copy_menu = self.edit.Append(-1, 'Copy', 'Copy the selected cells') wx.EVT_MENU(self, self.copy_menu.GetId(), self.on_copy) self.paste_menu = self.edit.Append(-1, 'Paste', 'Paste the selected Cells') wx.EVT_MENU(self, self.paste_menu.GetId(), self.on_paste) self.clear_menu = self.edit.Append(-1, 'Clear', 'Clear the selected Cells') wx.EVT_MENU(self, self.clear_menu.GetId(), self.on_clear) self.edit.AppendSeparator() hint = "Insert column before the selected column" self.insert_before_menu = wx.Menu() self.insertb_sub_menu = self.edit.AppendSubMenu(self.insert_before_menu, 'Insert Before', hint) hint = "Insert column after the selected column" self.insert_after_menu = wx.Menu() self.inserta_sub_menu = self.edit.AppendSubMenu(self.insert_after_menu, 'Insert After', hint) hint = "Remove the selected column" self.remove_menu = self.edit.Append(-1, 'Remove Column', hint) wx.EVT_MENU(self, self.remove_menu.GetId(), self.on_remove_column) self.Bind(wx.EVT_MENU_OPEN, self.on_menu_open) menubar.Append(self.edit, "&Edit") self.Bind(wx.EVT_CLOSE, self.on_close) def on_copy(self, event): """ On Copy """ if event != None: event.Skip() pos = self.panel.notebook.GetSelection() grid = self.panel.notebook.GetPage(pos) grid.Copy() def on_paste(self, event): """ On Paste """ if event != None: event.Skip() pos = self.panel.notebook.GetSelection() grid = self.panel.notebook.GetPage(pos) grid.on_paste(None) def on_clear(self, event): """ On Clear """ pos = self.panel.notebook.GetSelection() grid = self.panel.notebook.GetPage(pos) grid.Clear() 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() def on_remove_column(self, event): """ On remove column """ pos = self.panel.notebook.GetSelection() grid = self.panel.notebook.GetPage(pos) grid.on_remove_column(event=None) def on_menu_open(self, event): """ On menu open """ if self.file == event.GetMenu(): pos = self.panel.notebook.GetSelection() grid = self.panel.notebook.GetPage(pos) has_data = (grid.data != None and grid.data != {}) self.open_excel_menu.Enable(has_data) self.save_menu.Enable(has_data) if self.edit == event.GetMenu(): #get the selected column pos = self.panel.notebook.GetSelection() grid = self.panel.notebook.GetPage(pos) col_list = grid.GetSelectedCols() has_selection = False selected_cel = grid.selected_cells if len(selected_cel) > 0: _row, _col = selected_cel[0] has_selection = grid.IsInSelection(_row, _col) if len(grid.selected_cols) > 0: has_selection = True if len(grid.selected_rows) > 0: has_selection = True self.copy_menu.Enable(has_selection) self.clear_menu.Enable(has_selection) if len(col_list) > 0: self.remove_menu.Enable(True) else: self.remove_menu.Enable(False) if len(col_list) == 0 or len(col_list) > 1: self.insertb_sub_menu.Enable(False) self.inserta_sub_menu.Enable(False) label = "Insert Column Before" self.insertb_sub_menu.SetText(label) label = "Insert Column After" self.inserta_sub_menu.SetText(label) else: self.insertb_sub_menu.Enable(True) self.inserta_sub_menu.Enable(True) col = col_list[0] col_name = grid.GetCellValue(row=0, col=col) label = "Insert Column Before " + str(col_name) self.insertb_sub_menu.SetText(label) for item in self.insert_before_menu.GetMenuItems(): self.insert_before_menu.DeleteItem(item) grid.insert_col_menu(menu=self.insert_before_menu, label=col_name, window=self) label = "Insert Column After " + str(col_name) self.inserta_sub_menu.SetText(label) for item in self.insert_after_menu.GetMenuItems(): self.insert_after_menu.DeleteItem(item) grid.insert_after_col_menu(menu=self.insert_after_menu, label=col_name, window=self) event.Skip() def on_save_page(self, event): """ """ 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 != 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) def on_open(self, event): """ Open file containg batch result """ if self.parent is not None: self.parent.on_read_batch_tofile(self) 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) def on_close(self, event): """ """ self.Hide() def on_append_column(self, event): """ Append a new column to the grid """ self.panel.add_column() 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) def add_table(self, event): """ Add a new table """ # DO not event.Skip(): it will make 2 pages self.panel.notebook.add_empty_page() 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): """ :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 contaning 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) 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) def on_close(self, event): """ close the Window """ self.Close() 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 != 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()