def merge(self, merge_area, tab): """Merges top left cell with all cells until bottom_right""" top, left, bottom, right = merge_area cursor = self.grid.actions.cursor top_left_code = self.code_array((top, left, cursor[2])) selection = Selection([(top, left)], [(bottom, right)], [], [], []) # Check if the merge area overlaps another merge area error_msg = _("Overlapping merge area at {} prevents merge.") for row in xrange(top, bottom + 1): for col in xrange(left, right + 1): key = row, col, tab if self.code_array.cell_attributes[key]["merge_area"]: post_command_event(self.main_window, self.StatusBarMsg, text=error_msg.format(str(key))) return self.delete_selection(selection) self.set_code((top, left, cursor[2]), top_left_code) attr = {"merge_area": merge_area, "locked": True} self._set_cell_attr(selection, tab, attr) tl_selection = Selection([], [], [], [], [(top, left)]) attr = {"locked": False} self._set_cell_attr(tl_selection, tab, attr)
def set_border_attr(self, attr, value, borders): """Sets border attribute by adjusting selection to borders Parameters ---------- attr: String in ["borderwidth", "bordercolor"] \tBorder attribute that shall be changed value: wx.Colour or Integer \tAttribute value dependent on attribute type borders: Iterable over "top", "bottom", "left", "right", "inner" \tSpecifies to which borders of the selection the attr is applied """ selection = self.grid.selection if not selection: selection.cells.append(self.grid.actions.cursor[:2]) # determine selection for core cells and selection for border cells # Then apply according to inner and outer # A cell is inner iif it is not at the edge of the selection bbox if "inner" in borders: if "top" in borders: adj_selection = selection + (-1, 0) self.set_attr(attr + "_bottom", value, adj_selection) if "bottom" in borders: self.set_attr(attr + "_bottom", value) if "left" in borders: adj_selection = selection + (0, -1) self.set_attr(attr + "_right", value, adj_selection) if "right" in borders: self.set_attr(attr + "_right", value) else: # Adjust selection so that only bounding box edge is in selection bbox_tl, bbox_lr = selection.get_bbox() if "top" in borders: adj_selection = Selection([bbox_tl], [(bbox_tl[0], bbox_lr[1]) ], [], [], []) + (-1, 0) self.set_attr(attr + "_bottom", value, adj_selection) if "bottom" in borders: adj_selection = Selection([(bbox_lr[0], bbox_tl[1])], [bbox_lr], [], [], []) self.set_attr(attr + "_bottom", value, adj_selection) if "left" in borders: adj_selection = Selection([bbox_tl], [(bbox_lr[0], bbox_tl[1]) ], [], [], []) + (0, -1) self.set_attr(attr + "_right", value, adj_selection) if "right" in borders: adj_selection = Selection([(bbox_tl[0], bbox_lr[1])], [bbox_lr], [], [], []) self.set_attr(attr + "_right", value, adj_selection)
def test_getitem(self): """Test __getitem__""" selection_1 = Selection([(2, 2)], [(4, 5)], [55], [55, 66], [(34, 56)]) selection_2 = Selection([], [], [], [], [(32, 53), (34, 56)]) self.cell_attr.append((selection_1, 0, {"testattr": 3})) self.cell_attr.append((selection_2, 0, {"testattr": 2})) assert self.cell_attr[32, 53, 0]["testattr"] == 2 assert self.cell_attr[2, 2, 0]["testattr"] == 3
def change_frozen_attr(self): """Changes frozen state of cell if there is no selection""" # Selections are not supported if self.grid.selection: statustext = _("Freezing selections is not supported.") post_command_event(self.main_window, self.StatusBarMsg, text=statustext) cursor = self.grid.actions.cursor frozen = self.grid.code_array.cell_attributes[cursor]["frozen"] if frozen: # We have an frozen cell that has to be unfrozen # Delete frozen cache content self.grid.code_array.frozen_cache.pop(repr(cursor)) else: # We have an non-frozen cell that has to be frozen # Add frozen cache content res_obj = self.grid.code_array[cursor] self.grid.code_array.frozen_cache[repr(cursor)] = res_obj # Set the new frozen state / code selection = Selection([], [], [], [], [cursor[:2]]) self.set_attr("frozen", not frozen, selection=selection)
def copy_selection_access_string(self): """Copys access_string to selection to the clipboard An access string is Python code to reference the selection If there is no selection then a reference to the current cell is copied """ selection = self.get_selection() if not selection: cursor = self.grid.actions.cursor selection = Selection([], [], [], [], [tuple(cursor[:2])]) shape = self.grid.code_array.shape tab = self.grid.current_table access_string = selection.get_access_string(shape, tab) # Copy access string to clipboard self.grid.main_window.clipboard.set_clipboard(access_string) # Display copy operation and access string in status bar statustext = _("Cell reference copied to clipboard: {access_string}") statustext = statustext.format(access_string=access_string) post_command_event(self.main_window, self.StatusBarMsg, text=statustext)
def test_refresh_selected_frozen_cells(self): """Unit test for refresh_selected_frozen_cells""" cell = (0, 0, 0) code1 = "1" code2 = "2" self.grid.actions.cursor = cell # Fill cell self.grid.code_array[cell] = code1 assert self.grid.code_array[cell] == 1 # Freeze cell self.grid.actions.cursor = cell self.grid.current_table = cell[2] self.grid.actions.change_frozen_attr() res = self.grid.code_array.frozen_cache[repr(cell)] assert res == eval(code1) # Change cell code self.grid.code_array[cell] = code2 assert self.grid.code_array[cell] == 1 # Refresh cell selection = Selection([], [], [], [], [cell[:2]]) self.grid.actions.refresh_selected_frozen_cells(selection=selection) assert self.grid.code_array[cell] == 2
def _adjust_merge_area(self, attrs, insertion_point, no_to_insert, axis): """Returns an updated merge area :param attrs: Cell attribute dictionary that shall be adjusted :type attrs: dict :param insertion_point: Point on axis before insertion takes place :type insertion_point: int :param no_to_insert: Number of rows/cols/tabs that shall be inserted :type no_to_insert: int, >=0 :param axis: Specifies number of dimension, i.e. 0 == row, 1 == col :type axis: int in range(2) """ assert axis in range(2) if "merge_area" not in attrs or attrs["merge_area"] is None: return top, left, bottom, right = attrs["merge_area"] selection = Selection([(top, left)], [(bottom, right)], [], [], []) selection.insert(insertion_point, no_to_insert, axis) __top, __left = selection.block_tl[0] __bottom, __right = selection.block_br[0] # Adjust merge area if it is beyond the grid shape rows, cols, tabs = self.shape if __top < 0 and __bottom < 0 or __top >= rows and __bottom >= rows or\ __left < 0 and __right < 0 or __left >= cols and __right >= cols: return if __top < 0: __top = 0 if __top >= rows: __top = rows - 1 if __bottom < 0: __bottom = 0 if __bottom >= rows: __bottom = rows - 1 if __left < 0: __left = 0 if __left >= cols: __left = cols - 1 if __right < 0: __right = 0 if __right >= cols: __right = cols - 1 return __top, __left, __bottom, __right
def merge(self, merge_area, tab): """Merges top left cell with all cells until bottom_right""" top, left, bottom, right = merge_area selection = Selection([(top, left)], [(bottom, right)], [], [], []) attr = {"merge_area": merge_area} self._set_cell_attr(selection, tab, attr)
def unmerge(self, unmerge_area, tab): """Unmerges all cells in unmerge_area""" top, left, bottom, right = unmerge_area selection = Selection([(top, left)], [(bottom, right)], [], [], []) attr = {"merge_area": None, "locked": False} self._set_cell_attr(selection, tab, attr)
def _code_convert_1_2(self, key, code): """Converts chart and image code from v1.0 to v2.0""" def get_image_code(image_data, width, height): """Returns code string for v2.0""" image_buffer_tpl = 'bz2.decompress(base64.b85decode({data}))' image_array_tpl = 'numpy.frombuffer({buffer}, dtype="uint8")' image_matrix_tpl = '{array}.reshape({height}, {width}, 3)' image_buffer = image_buffer_tpl.format(data=image_data) image_array = image_array_tpl.format(buffer=image_buffer) image_matrix = image_matrix_tpl.format(array=image_array, height=height, width=width) return image_matrix start_str = "bz2.decompress(base64.b64decode('" size_start_str = "wx.ImageFromData(" if size_start_str in code and start_str in code: size_start = code.index(size_start_str) + len(size_start_str) size_str_list = code[size_start:].split(",")[:2] width, height = tuple(map(int, size_str_list)) # We have a cell that displays a bitmap data_start = code.index(start_str) + len(start_str) data_stop = code.find("'", data_start) enc_data = bytes(code[data_start:data_stop], encoding='utf-8') compressed_image_data = b64decode(enc_data) reenc_data = b85encode(compressed_image_data) code = get_image_code(repr(reenc_data), width, height) selection = Selection([], [], [], [], [(key[0], key[1])]) tab = key[2] attrs = {"renderer": "image"} self.cell_attributes_postfixes.append((selection, tab, attrs)) elif "charts.ChartFigure(" in code: # We have a matplotlib figure selection = Selection([], [], [], [], [(key[0], key[1])]) tab = key[2] attrs = {"renderer": "matplotlib"} self.cell_attributes_postfixes.append((selection, tab, attrs)) return code
def OnTimer(self, event): """Update all frozen cells because of timer call""" self.timer_updating = True shape = self.grid.code_array.shape[:2] selection = Selection([(0, 0)], [(shape)], [], [], []) self.grid.actions.refresh_selected_frozen_cells(selection) self.grid.ForceRefresh()
def test_get_merging_cell(self): """Test get_merging_cell""" selection_1 = Selection([], [], [], [], [(2, 2)]) selection_2 = Selection([], [], [], [], [(3, 2)]) self.cell_attr.append((selection_1, 0, {"merge_area": (2, 2, 5, 5)})) self.cell_attr.append((selection_2, 0, {"merge_area": (3, 2, 9, 9)})) self.cell_attr.append((selection_1, 1, {"merge_area": (2, 2, 9, 9)})) # Cell 1. 1, 0 is not merged assert self.cell_attr.get_merging_cell((1, 1, 0)) is None # Cell 3. 3, 0 is merged to cell 3, 2, 0 assert self.cell_attr.get_merging_cell((3, 3, 0)) == (3, 2, 0) # Cell 2. 2, 0 is merged to cell 2, 2, 0 assert self.cell_attr.get_merging_cell((2, 2, 0)) == (2, 2, 0)
def _pys2attributes_10(self, line): """Updates attributes in code_array - for save file version 1.0""" splitline = self._split_tidy(line) selection_data = list(map(ast.literal_eval, splitline[:5])) selection = Selection(*selection_data) tab = int(splitline[5]) attrs = {} old_merged_cells = {} for col, ele in enumerate(splitline[6:]): if not (col % 2): # Odd entries are keys key = ast.literal_eval(ele) else: # Even cols are values value = ast.literal_eval(ele) # Convert old wx color values and merged cells key_, value_ = self._attr_convert_1to2(key, value) if key_ is None and value_ is not None: # We have a merged cell old_merged_cells[value_[:2]] = value_ try: attrs.pop("merge_area") except KeyError: pass attrs[key_] = value_ self.code_array.cell_attributes.append((selection, tab, attrs)) for key in old_merged_cells: selection = Selection([], [], [], [], [key]) attrs = {"merge_area": old_merged_cells[key]} self.code_array.cell_attributes.append((selection, tab, attrs)) old_merged_cells.clear()
def redo(self): row, column, table = current = self.current # Remove and store frozen cache content self.res_obj = self.model.code_array.frozen_cache.pop(repr(current)) # Remove the frozen state selection = Selection([], [], [], [], [(row, column)]) attr = selection, table, {"frozen": False} self.model.setData([], attr, Qt.DecorationRole) self.model.dataChanged.emit(QModelIndex(), QModelIndex())
def test_append(self): """Test append""" selection = Selection([], [], [], [], [(23, 12)]) table = 0 attr = {"angle": 0.2} self.cell_attr.append((selection, table, attr)) # Check if 1 item - the actual action has been added assert not self.cell_attr._attr_cache
def test_clear(self): """Tests empty_grid method""" # Set up self.grid self.grid.code_array[(0, 0, 0)] = "'Test1'" self.grid.code_array[(3, 1, 1)] = "'Test2'" self.grid.actions.set_col_width(1, 23) self.grid.actions.set_col_width(0, 233) self.grid.actions.set_row_height(0, 0) selection = Selection([], [], [], [], [(0, 0)]) self.grid.actions.set_attr("bgcolor", wx.RED, selection) self.grid.actions.set_attr("frozen", "print 'Testcode'", selection) # Clear self.grid self.grid.actions.clear() # Code content assert self.grid.code_array((0, 0, 0)) is None assert self.grid.code_array((3, 1, 1)) is None assert list(self.grid.code_array[:2, 0, 0]) == [None, None] # Cell attributes cell_attributes = self.grid.code_array.cell_attributes assert cell_attributes == [] # Row heights and column widths row_heights = self.grid.code_array.row_heights assert len(row_heights) == 0 col_widths = self.grid.code_array.col_widths assert len(col_widths) == 0 # Undo and redo undolist = self.grid.code_array.unredo.undolist redolist = self.grid.code_array.unredo.redolist assert undolist == [] assert redolist == [] # Caches # Clear self.grid again because lookup is added in resultcache self.grid.actions.clear() result_cache = self.grid.code_array.result_cache assert len(result_cache) == 0
def undo(self): row, column, table = self.key selection = Selection([], [], [], [], [(row, column)]) ca = selection, table, {"button_cell": self.text} self.grid.model.setData([self.index], ca, Qt.DecorationRole) if table == self.grid.table: # Only add widget if we are in the right table button = CellButton(self.text, self.grid, self.key) self.grid.setIndexWidget(self.index, button) self.grid.widget_indices.append(self.index) self.grid.model.dataChanged.emit(self.index, self.index)
def redo(self): row, column, table = self.current # Add frozen cache content res_obj = self.model.code_array[self.current] self.model.code_array.frozen_cache[repr(self.current)] = res_obj # Set the frozen state selection = Selection([], [], [], [], [(row, column)]) attr = selection, table, {"frozen": True} self.model.setData([], attr, Qt.DecorationRole) self.model.dataChanged.emit(QModelIndex(), QModelIndex())
def test_get_new_cell_attr_state(self, cell, attr, before, next): """Unit test for get_new_cell_attr_state""" self.grid.actions.cursor = cell self.grid.current_table = cell[2] selection = Selection([], [], [], [], [cell[:2]]) self.grid.actions.set_attr(attr, before, selection) res = self.grid.actions.get_new_cell_attr_state(cell, attr) assert res == next
def redo(self): # Store content of deleted columns self.old_col_widths = copy(self.model.code_array.col_widths) self.old_cell_attributes = copy(self.model.code_array.cell_attributes) self.old_code = {} columns = list(range(self.first, self.last + 1)) selection = Selection([], [], [], columns, []) for key in selection.cell_generator(self.model.shape, self.grid.table): self.old_code[key] = self.model.code_array(key) with self.model.removing_columns(self.index, self.first, self.last): self.model.removeColumns(self.column, self.count) self.grid.table_choice.on_table_changed(self.grid.current)
def redo(self): # Store content of deleted rows self.old_row_heights = copy(self.model.code_array.row_heights) self.old_cell_attributes = copy(self.model.code_array.cell_attributes) self.old_code = {} rows = list(range(self.first, self.last + 1)) selection = Selection([], [], rows, [], []) for key in selection.cell_generator(self.model.shape, self.grid.table): self.old_code[key] = self.model.code_array(key) with self.model.removing_rows(self.index, self.first, self.last): self.model.removeRows(self.row, self.count) self.grid.table_choice.on_table_changed(self.grid.current)
def test_undoable_append(self): """Test undoable_append""" selection = Selection([], [], [], [], [(23, 12)]) table = 0 attr = {"angle": 0.2} self.cell_attr.undoable_append((selection, table, attr)) # Check if 2 items - the actual action and the marker - have been added assert len(self.cell_attr.unredo.undolist) == 2 assert len(self.cell_attr.unredo.redolist) == 0 assert not self.cell_attr._attr_cache
def test_adjust_cell_attributes(self, inspoint, noins, axis, src, target): """Unit test for _adjust_cell_attributes""" row, col, tab = src val = {"angle": 0.2} attrs = [(Selection([], [], [], [], [(row, col)]), tab, val)] self.data_array._set_cell_attributes(attrs) self.data_array._adjust_cell_attributes(inspoint, noins, axis) for key in val: assert self.data_array.cell_attributes[target][key] == val[key]
def undo(self): if self.index not in self.grid.widget_indices: return row, column, table = self.key selection = Selection([], [], [], [], [(row, column)]) ca = selection, table, {"button_cell": False} self.grid.model.setData([self.index], ca, Qt.DecorationRole) if table == self.grid.table: # Only remove widget if we are in the right table self.grid.setIndexWidget(self.index, None) self.grid.widget_indices.remove(self.index) self.grid.model.dataChanged.emit(self.index, self.index)
def get_selection(self): """Returns selected cells in grid as Selection object""" # GetSelectedCells: individual cells selected by ctrl-clicking # GetSelectedRows: rows selected by clicking on the labels # GetSelectedCols: cols selected by clicking on the labels # GetSelectionBlockTopLeft # GetSelectionBlockBottomRight: For blocks selected by dragging # across the grid cells. block_top_left = self.grid.GetSelectionBlockTopLeft() block_bottom_right = self.grid.GetSelectionBlockBottomRight() rows = self.grid.GetSelectedRows() cols = self.grid.GetSelectedCols() cells = self.grid.GetSelectedCells() return Selection(block_top_left, block_bottom_right, rows, cols, cells)
def test_get_xfstyle(self, key, sec_key, subsec_key, style_key, val, easyxf): """Test _get_xfstyle method""" row, col, tab = key pys_style = {style_key: val} dict_grid = self.code_array.dict_grid selection = Selection([], [], [], [], [(row, col)]) dict_grid.cell_attributes.append((selection, tab, pys_style)) xfstyle = self.xls_in._get_xfstyle([], key) style = xlwt.easyxf(easyxf) assert getattr(getattr(xfstyle, sec_key), subsec_key) == \ getattr(getattr(style, sec_key), subsec_key)
def format_paste_format(self): """Pastes cell formats Pasting starts at cursor or at top left bbox corner """ clipboard = QApplication.clipboard() mime_data = clipboard.mimeData() grid = self.main_window.grid model = grid.model row, column, table = grid.current if "application/x-pyspread-cell-attributes" not in mime_data.formats(): return cas_data = mime_data.data("application/x-pyspread-cell-attributes") cas_data_str = str(cas_data, encoding='utf-8') cas = literal_eval(cas_data_str) assert isinstance(cas, list) tabu_attrs = "merge_area", "renderer", "frozen" description_tpl = "Paste format for selections {}" description = description_tpl.format([ca[0] for ca in cas]) for selection_params, attrs in cas: if not any(tabu_attr in attrs for tabu_attr in tabu_attrs): selection = Selection(*selection_params) shifted_selection = selection.shifted(row, column) new_cell_attribute = shifted_selection, table, attrs selected_idx = [] for key in shifted_selection.cell_generator(model.shape): selected_idx.append(model.index(*key)) command = CommandSetCellFormat(new_cell_attribute, model, grid.currentIndex(), selected_idx, description) self.main_window.undo_stack.push(command)
def _update_video_volume_cell_attributes(self, key): """Updates the panel cell attrutes of a panel cell""" try: video_cell_panel = self.grid_renderer.video_cells[key] except KeyError: return old_video_volume = self.code_array.cell_attributes[key]["video_volume"] new_video_volume = video_cell_panel.volume if old_video_volume == new_video_volume: return selection = Selection([], [], [], [], [key]) self.actions.set_attr("video_volume", new_video_volume, selection, mark_unredo=False)
def test_adjust_cell_attributes(self, inspoint, noins, axis, src, target): """Unit test for _adjust_cell_attributes""" row, col, tab = src val = {"angle": 0.2} attrs = [(Selection([], [], [], [], [(row, col)]), tab, val)] self.data_array._set_cell_attributes(attrs) self.data_array._adjust_cell_attributes(inspoint, noins, axis) if target is None: for key in val: # Should be at default value cell_attributes = self.data_array.cell_attributes default_ca = cell_attributes.default_cell_attributes[key] assert cell_attributes[src][key] == default_ca else: for key in val: assert self.data_array.cell_attributes[target][key] == val[key]
def _pys2attributes(self, line): """Updates attributes in code_array""" splitline = self._split_tidy(line) selection_data = map(ast.literal_eval, splitline[:5]) selection = Selection(*selection_data) tab = int(splitline[5]) attrs = {} for col, ele in enumerate(splitline[6:]): if not (col % 2): # Odd entries are keys key = ast.literal_eval(ele) else: # Even cols are values attrs[key] = ast.literal_eval(ele) self.code_array.cell_attributes.append((selection, tab, attrs))