Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/openpyxl/worksheet/worksheet.py: 22%

454 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1# Copyright (c) 2010-2022 openpyxl 

2 

3"""Worksheet is the 2nd-level container in Excel.""" 

4 

5 

6# Python stdlib imports 

7from itertools import chain 

8from operator import itemgetter 

9from inspect import isgenerator 

10from warnings import warn 

11 

12# compatibility imports 

13from openpyxl.compat import ( 

14 deprecated, 

15) 

16 

17# package imports 

18from openpyxl.utils import ( 

19 column_index_from_string, 

20 get_column_letter, 

21 range_boundaries, 

22 coordinate_to_tuple, 

23 absolute_coordinate, 

24) 

25from openpyxl.cell import Cell, MergedCell 

26from openpyxl.formatting.formatting import ConditionalFormattingList 

27from openpyxl.packaging.relationship import RelationshipList 

28from openpyxl.workbook.child import _WorkbookChild 

29from openpyxl.workbook.defined_name import COL_RANGE_RE, ROW_RANGE_RE 

30from openpyxl.formula.translate import Translator 

31 

32from .datavalidation import DataValidationList 

33from .page import ( 

34 PrintPageSetup, 

35 PageMargins, 

36 PrintOptions, 

37) 

38from .dimensions import ( 

39 ColumnDimension, 

40 RowDimension, 

41 DimensionHolder, 

42 SheetFormatProperties, 

43) 

44from .protection import SheetProtection 

45from .filters import AutoFilter 

46from .views import ( 

47 Pane, 

48 Selection, 

49 SheetViewList, 

50) 

51from .cell_range import MultiCellRange, CellRange 

52from .merge import MergedCellRange 

53from .properties import WorksheetProperties 

54from .pagebreak import RowBreak, ColBreak 

55from .scenario import ScenarioList 

56from .table import TableList 

57 

58 

59class Worksheet(_WorkbookChild): 

60 """Represents a worksheet. 

61 

62 Do not create worksheets yourself, 

63 use :func:`openpyxl.workbook.Workbook.create_sheet` instead 

64 

65 """ 

66 

67 _rel_type = "worksheet" 

68 _path = "/xl/worksheets/sheet{0}.xml" 

69 mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" 

70 

71 BREAK_NONE = 0 

72 BREAK_ROW = 1 

73 BREAK_COLUMN = 2 

74 

75 SHEETSTATE_VISIBLE = 'visible' 

76 SHEETSTATE_HIDDEN = 'hidden' 

77 SHEETSTATE_VERYHIDDEN = 'veryHidden' 

78 

79 # Paper size 

80 PAPERSIZE_LETTER = '1' 

81 PAPERSIZE_LETTER_SMALL = '2' 

82 PAPERSIZE_TABLOID = '3' 

83 PAPERSIZE_LEDGER = '4' 

84 PAPERSIZE_LEGAL = '5' 

85 PAPERSIZE_STATEMENT = '6' 

86 PAPERSIZE_EXECUTIVE = '7' 

87 PAPERSIZE_A3 = '8' 

88 PAPERSIZE_A4 = '9' 

89 PAPERSIZE_A4_SMALL = '10' 

90 PAPERSIZE_A5 = '11' 

91 

92 # Page orientation 

93 ORIENTATION_PORTRAIT = 'portrait' 

94 ORIENTATION_LANDSCAPE = 'landscape' 

95 

96 def __init__(self, parent, title=None): 

97 _WorkbookChild.__init__(self, parent, title) 

98 self._setup() 

99 

100 def _setup(self): 

101 self.row_dimensions = DimensionHolder(worksheet=self, 

102 default_factory=self._add_row) 

103 self.column_dimensions = DimensionHolder(worksheet=self, 

104 default_factory=self._add_column) 

105 self.row_breaks = RowBreak() 

106 self.col_breaks = ColBreak() 

107 self._cells = {} 

108 self._charts = [] 

109 self._images = [] 

110 self._rels = RelationshipList() 

111 self._drawing = None 

112 self._comments = [] 

113 self.merged_cells = MultiCellRange() 

114 self._tables = TableList() 

115 self._pivots = [] 

116 self.data_validations = DataValidationList() 

117 self._hyperlinks = [] 

118 self.sheet_state = 'visible' 

119 self.page_setup = PrintPageSetup(worksheet=self) 

120 self.print_options = PrintOptions() 

121 self._print_rows = None 

122 self._print_cols = None 

123 self._print_area = None 

124 self.page_margins = PageMargins() 

125 self.views = SheetViewList() 

126 self.protection = SheetProtection() 

127 

128 self._current_row = 0 

129 self.auto_filter = AutoFilter() 

130 self.paper_size = None 

131 self.formula_attributes = {} 

132 self.orientation = None 

133 self.conditional_formatting = ConditionalFormattingList() 

134 self.legacy_drawing = None 

135 self.sheet_properties = WorksheetProperties() 

136 self.sheet_format = SheetFormatProperties() 

137 self.scenarios = ScenarioList() 

138 

139 

140 @property 

141 def sheet_view(self): 

142 return self.views.sheetView[0] 

143 

144 

145 @property 

146 def selected_cell(self): 

147 return self.sheet_view.selection[0].sqref 

148 

149 

150 @property 

151 def active_cell(self): 

152 return self.sheet_view.selection[0].activeCell 

153 

154 

155 @property 

156 def page_breaks(self): 

157 return (self.row_breaks, self.col_breaks) # legacy, remove at some point 

158 

159 

160 @property 

161 def show_gridlines(self): 

162 return self.sheet_view.showGridLines 

163 

164 

165 """ To keep compatibility with previous versions""" 

166 @property 

167 def show_summary_below(self): 

168 return self.sheet_properties.outlinePr.summaryBelow 

169 

170 @property 

171 def show_summary_right(self): 

172 return self.sheet_properties.outlinePr.summaryRight 

173 

174 

175 @property 

176 def freeze_panes(self): 

177 if self.sheet_view.pane is not None: 

178 return self.sheet_view.pane.topLeftCell 

179 

180 @freeze_panes.setter 

181 def freeze_panes(self, topLeftCell=None): 

182 if isinstance(topLeftCell, Cell): 

183 topLeftCell = topLeftCell.coordinate 

184 if topLeftCell == 'A1': 

185 topLeftCell = None 

186 

187 if not topLeftCell: 

188 self.sheet_view.pane = None 

189 return 

190 

191 row, column = coordinate_to_tuple(topLeftCell) 

192 

193 view = self.sheet_view 

194 view.pane = Pane(topLeftCell=topLeftCell, 

195 activePane="topRight", 

196 state="frozen") 

197 view.selection[0].pane = "topRight" 

198 

199 if column > 1: 

200 view.pane.xSplit = column - 1 

201 if row > 1: 

202 view.pane.ySplit = row - 1 

203 view.pane.activePane = 'bottomLeft' 

204 view.selection[0].pane = "bottomLeft" 

205 if column > 1: 

206 view.selection[0].pane = "bottomRight" 

207 view.pane.activePane = 'bottomRight' 

208 

209 if row > 1 and column > 1: 

210 sel = list(view.selection) 

211 sel.insert(0, Selection(pane="topRight", activeCell=None, sqref=None)) 

212 sel.insert(1, Selection(pane="bottomLeft", activeCell=None, sqref=None)) 

213 view.selection = sel 

214 

215 

216 def cell(self, row, column, value=None): 

217 """ 

218 Returns a cell object based on the given coordinates. 

219 

220 Usage: cell(row=15, column=1, value=5) 

221 

222 Calling `cell` creates cells in memory when they 

223 are first accessed. 

224 

225 :param row: row index of the cell (e.g. 4) 

226 :type row: int 

227 

228 :param column: column index of the cell (e.g. 3) 

229 :type column: int 

230 

231 :param value: value of the cell (e.g. 5) 

232 :type value: numeric or time or string or bool or none 

233 

234 :rtype: openpyxl.cell.cell.Cell 

235 """ 

236 

237 if row < 1 or column < 1: 

238 raise ValueError("Row or column values must be at least 1") 

239 

240 cell = self._get_cell(row, column) 

241 if value is not None: 

242 cell.value = value 

243 

244 return cell 

245 

246 

247 def _get_cell(self, row, column): 

248 """ 

249 Internal method for getting a cell from a worksheet. 

250 Will create a new cell if one doesn't already exist. 

251 """ 

252 if not 0 < row < 1048577: 

253 raise ValueError("Row numbers must be between 1 and 1048576") 

254 coordinate = (row, column) 

255 if not coordinate in self._cells: 

256 cell = Cell(self, row=row, column=column) 

257 self._add_cell(cell) 

258 return self._cells[coordinate] 

259 

260 

261 def _add_cell(self, cell): 

262 """ 

263 Internal method for adding cell objects. 

264 """ 

265 column = cell.col_idx 

266 row = cell.row 

267 self._current_row = max(row, self._current_row) 

268 self._cells[(row, column)] = cell 

269 

270 

271 def __getitem__(self, key): 

272 """Convenience access by Excel style coordinates 

273 

274 The key can be a single cell coordinate 'A1', a range of cells 'A1:D25', 

275 individual rows or columns 'A', 4 or ranges of rows or columns 'A:D', 

276 4:10. 

277 

278 Single cells will always be created if they do not exist. 

279 

280 Returns either a single cell or a tuple of rows or columns. 

281 """ 

282 if isinstance(key, slice): 

283 if not all([key.start, key.stop]): 

284 raise IndexError("{0} is not a valid coordinate or range".format(key)) 

285 key = "{0}:{1}".format(key.start, key.stop) 

286 

287 if isinstance(key, int): 

288 key = str(key 

289 ) 

290 min_col, min_row, max_col, max_row = range_boundaries(key) 

291 

292 if not any([min_col, min_row, max_col, max_row]): 

293 raise IndexError("{0} is not a valid coordinate or range".format(key)) 

294 

295 if min_row is None: 

296 cols = tuple(self.iter_cols(min_col, max_col)) 

297 if min_col == max_col: 

298 cols = cols[0] 

299 return cols 

300 if min_col is None: 

301 rows = tuple(self.iter_rows(min_col=min_col, min_row=min_row, 

302 max_col=self.max_column, max_row=max_row)) 

303 if min_row == max_row: 

304 rows = rows[0] 

305 return rows 

306 if ":" not in key: 

307 return self._get_cell(min_row, min_col) 

308 return tuple(self.iter_rows(min_row=min_row, min_col=min_col, 

309 max_row=max_row, max_col=max_col)) 

310 

311 

312 def __setitem__(self, key, value): 

313 self[key].value = value 

314 

315 

316 def __iter__(self): 

317 return self.iter_rows() 

318 

319 

320 def __delitem__(self, key): 

321 row, column = coordinate_to_tuple(key) 

322 if (row, column) in self._cells: 

323 del self._cells[(row, column)] 

324 

325 

326 @property 

327 def min_row(self): 

328 """The minimium row index containing data (1-based) 

329 

330 :type: int 

331 """ 

332 min_row = 1 

333 if self._cells: 

334 rows = set(c[0] for c in self._cells) 

335 min_row = min(rows) 

336 return min_row 

337 

338 

339 @property 

340 def max_row(self): 

341 """The maximum row index containing data (1-based) 

342 

343 :type: int 

344 """ 

345 max_row = 1 

346 if self._cells: 

347 rows = set(c[0] for c in self._cells) 

348 max_row = max(rows) 

349 return max_row 

350 

351 

352 @property 

353 def min_column(self): 

354 """The minimum column index containing data (1-based) 

355 

356 :type: int 

357 """ 

358 min_col = 1 

359 if self._cells: 

360 cols = set(c[1] for c in self._cells) 

361 min_col = min(cols) 

362 return min_col 

363 

364 

365 @property 

366 def max_column(self): 

367 """The maximum column index containing data (1-based) 

368 

369 :type: int 

370 """ 

371 max_col = 1 

372 if self._cells: 

373 cols = set(c[1] for c in self._cells) 

374 max_col = max(cols) 

375 return max_col 

376 

377 

378 def calculate_dimension(self): 

379 """Return the minimum bounding range for all cells containing data (ex. 'A1:M24') 

380 

381 :rtype: string 

382 """ 

383 if self._cells: 

384 rows = set() 

385 cols = set() 

386 for row, col in self._cells: 

387 rows.add(row) 

388 cols.add(col) 

389 max_row = max(rows) 

390 max_col = max(cols) 

391 min_col = min(cols) 

392 min_row = min(rows) 

393 else: 

394 return "A1:A1" 

395 

396 return f"{get_column_letter(min_col)}{min_row}:{get_column_letter(max_col)}{max_row}" 

397 

398 

399 @property 

400 def dimensions(self): 

401 """Returns the result of :func:`calculate_dimension`""" 

402 return self.calculate_dimension() 

403 

404 

405 def iter_rows(self, min_row=None, max_row=None, min_col=None, max_col=None, values_only=False): 

406 """ 

407 Produces cells from the worksheet, by row. Specify the iteration range 

408 using indices of rows and columns. 

409 

410 If no indices are specified the range starts at A1. 

411 

412 If no cells are in the worksheet an empty tuple will be returned. 

413 

414 :param min_col: smallest column index (1-based index) 

415 :type min_col: int 

416 

417 :param min_row: smallest row index (1-based index) 

418 :type min_row: int 

419 

420 :param max_col: largest column index (1-based index) 

421 :type max_col: int 

422 

423 :param max_row: largest row index (1-based index) 

424 :type max_row: int 

425 

426 :param values_only: whether only cell values should be returned 

427 :type values_only: bool 

428 

429 :rtype: generator 

430 """ 

431 

432 if self._current_row == 0 and not any([min_col, min_row, max_col, max_row ]): 

433 return iter(()) 

434 

435 

436 min_col = min_col or 1 

437 min_row = min_row or 1 

438 max_col = max_col or self.max_column 

439 max_row = max_row or self.max_row 

440 

441 return self._cells_by_row(min_col, min_row, max_col, max_row, values_only) 

442 

443 

444 def _cells_by_row(self, min_col, min_row, max_col, max_row, values_only=False): 

445 for row in range(min_row, max_row + 1): 

446 cells = (self.cell(row=row, column=column) for column in range(min_col, max_col + 1)) 

447 if values_only: 

448 yield tuple(cell.value for cell in cells) 

449 else: 

450 yield tuple(cells) 

451 

452 

453 @property 

454 def rows(self): 

455 """Produces all cells in the worksheet, by row (see :func:`iter_rows`) 

456 

457 :type: generator 

458 """ 

459 return self.iter_rows() 

460 

461 

462 @property 

463 def values(self): 

464 """Produces all cell values in the worksheet, by row 

465 

466 :type: generator 

467 """ 

468 for row in self.iter_rows(values_only=True): 

469 yield row 

470 

471 

472 def iter_cols(self, min_col=None, max_col=None, min_row=None, max_row=None, values_only=False): 

473 """ 

474 Produces cells from the worksheet, by column. Specify the iteration range 

475 using indices of rows and columns. 

476 

477 If no indices are specified the range starts at A1. 

478 

479 If no cells are in the worksheet an empty tuple will be returned. 

480 

481 :param min_col: smallest column index (1-based index) 

482 :type min_col: int 

483 

484 :param min_row: smallest row index (1-based index) 

485 :type min_row: int 

486 

487 :param max_col: largest column index (1-based index) 

488 :type max_col: int 

489 

490 :param max_row: largest row index (1-based index) 

491 :type max_row: int 

492 

493 :param values_only: whether only cell values should be returned 

494 :type values_only: bool 

495 

496 :rtype: generator 

497 """ 

498 

499 if self._current_row == 0 and not any([min_col, min_row, max_col, max_row]): 

500 return iter(()) 

501 

502 min_col = min_col or 1 

503 min_row = min_row or 1 

504 max_col = max_col or self.max_column 

505 max_row = max_row or self.max_row 

506 

507 return self._cells_by_col(min_col, min_row, max_col, max_row, values_only) 

508 

509 

510 def _cells_by_col(self, min_col, min_row, max_col, max_row, values_only=False): 

511 """ 

512 Get cells by column 

513 """ 

514 for column in range(min_col, max_col+1): 

515 cells = (self.cell(row=row, column=column) 

516 for row in range(min_row, max_row+1)) 

517 if values_only: 

518 yield tuple(cell.value for cell in cells) 

519 else: 

520 yield tuple(cells) 

521 

522 

523 @property 

524 def columns(self): 

525 """Produces all cells in the worksheet, by column (see :func:`iter_cols`)""" 

526 return self.iter_cols() 

527 

528 

529 def set_printer_settings(self, paper_size, orientation): 

530 """Set printer settings """ 

531 

532 self.page_setup.paperSize = paper_size 

533 self.page_setup.orientation = orientation 

534 

535 

536 def add_data_validation(self, data_validation): 

537 """ Add a data-validation object to the sheet. The data-validation 

538 object defines the type of data-validation to be applied and the 

539 cell or range of cells it should apply to. 

540 """ 

541 self.data_validations.append(data_validation) 

542 

543 

544 def add_chart(self, chart, anchor=None): 

545 """ 

546 Add a chart to the sheet 

547 Optionally provide a cell for the top-left anchor 

548 """ 

549 if anchor is not None: 

550 chart.anchor = anchor 

551 self._charts.append(chart) 

552 

553 

554 def add_image(self, img, anchor=None): 

555 """ 

556 Add an image to the sheet. 

557 Optionally provide a cell for the top-left anchor 

558 """ 

559 if anchor is not None: 

560 img.anchor = anchor 

561 self._images.append(img) 

562 

563 

564 def add_table(self, table): 

565 """ 

566 Check for duplicate name in definedNames and other worksheet tables 

567 before adding table. 

568 """ 

569 

570 if self.parent._duplicate_name(table.name): 

571 raise ValueError("Table with name {0} already exists".format(table.name)) 

572 if not hasattr(self, "_get_cell"): 

573 warn("In write-only mode you must add table columns manually") 

574 self._tables.add(table) 

575 

576 

577 @property 

578 def tables(self): 

579 return self._tables 

580 

581 

582 def add_pivot(self, pivot): 

583 self._pivots.append(pivot) 

584 

585 

586 def merge_cells(self, range_string=None, start_row=None, start_column=None, end_row=None, end_column=None): 

587 """ Set merge on a cell range. Range is a cell range (e.g. A1:E1) """ 

588 if range_string is None: 

589 cr = CellRange(range_string=range_string, min_col=start_column, min_row=start_row, 

590 max_col=end_column, max_row=end_row) 

591 range_string = cr.coord 

592 mcr = MergedCellRange(self, range_string) 

593 self.merged_cells.add(mcr) 

594 self._clean_merge_range(mcr) 

595 

596 

597 def _clean_merge_range(self, mcr): 

598 """ 

599 Remove all but the top left-cell from a range of merged cells 

600 and recreate the lost border information. 

601 Borders are then applied 

602 """ 

603 cells = mcr.cells 

604 next(cells) # skip first cell 

605 for row, col in cells: 

606 self._cells[row, col] = MergedCell(self, row, col) 

607 mcr.format() 

608 

609 

610 @property 

611 @deprecated("Use ws.merged_cells.ranges") 

612 def merged_cell_ranges(self): 

613 """Return a copy of cell ranges""" 

614 return self.merged_cells.ranges[:] 

615 

616 

617 def unmerge_cells(self, range_string=None, start_row=None, start_column=None, end_row=None, end_column=None): 

618 """ Remove merge on a cell range. Range is a cell range (e.g. A1:E1) """ 

619 cr = CellRange(range_string=range_string, min_col=start_column, min_row=start_row, 

620 max_col=end_column, max_row=end_row) 

621 

622 if cr.coord not in self.merged_cells: 

623 raise ValueError("Cell range {0} is not merged".format(cr.coord)) 

624 

625 self.merged_cells.remove(cr) 

626 

627 cells = cr.cells 

628 next(cells) # skip first cell 

629 for row, col in cells: 

630 del self._cells[(row, col)] 

631 

632 

633 def append(self, iterable): 

634 """Appends a group of values at the bottom of the current sheet. 

635 

636 * If it's a list: all values are added in order, starting from the first column 

637 * If it's a dict: values are assigned to the columns indicated by the keys (numbers or letters) 

638 

639 :param iterable: list, range or generator, or dict containing values to append 

640 :type iterable: list|tuple|range|generator or dict 

641 

642 Usage: 

643 

644 * append(['This is A1', 'This is B1', 'This is C1']) 

645 * **or** append({'A' : 'This is A1', 'C' : 'This is C1'}) 

646 * **or** append({1 : 'This is A1', 3 : 'This is C1'}) 

647 

648 :raise: TypeError when iterable is neither a list/tuple nor a dict 

649 

650 """ 

651 row_idx = self._current_row + 1 

652 

653 if (isinstance(iterable, (list, tuple, range)) 

654 or isgenerator(iterable)): 

655 for col_idx, content in enumerate(iterable, 1): 

656 if isinstance(content, Cell): 

657 # compatible with write-only mode 

658 cell = content 

659 if cell.parent and cell.parent != self: 

660 raise ValueError("Cells cannot be copied from other worksheets") 

661 cell.parent = self 

662 cell.column = col_idx 

663 cell.row = row_idx 

664 else: 

665 cell = Cell(self, row=row_idx, column=col_idx, value=content) 

666 self._cells[(row_idx, col_idx)] = cell 

667 

668 elif isinstance(iterable, dict): 

669 for col_idx, content in iterable.items(): 

670 if isinstance(col_idx, str): 

671 col_idx = column_index_from_string(col_idx) 

672 cell = Cell(self, row=row_idx, column=col_idx, value=content) 

673 self._cells[(row_idx, col_idx)] = cell 

674 

675 else: 

676 self._invalid_row(iterable) 

677 

678 self._current_row = row_idx 

679 

680 

681 def _move_cells(self, min_row=None, min_col=None, offset=0, row_or_col="row"): 

682 """ 

683 Move either rows or columns around by the offset 

684 """ 

685 reverse = offset > 0 # start at the end if inserting 

686 row_offset = 0 

687 col_offset = 0 

688 

689 # need to make affected ranges contiguous 

690 if row_or_col == 'row': 

691 cells = self.iter_rows(min_row=min_row) 

692 row_offset = offset 

693 key = 0 

694 else: 

695 cells = self.iter_cols(min_col=min_col) 

696 col_offset = offset 

697 key = 1 

698 cells = list(cells) 

699 

700 for row, column in sorted(self._cells, key=itemgetter(key), reverse=reverse): 

701 if min_row and row < min_row: 

702 continue 

703 elif min_col and column < min_col: 

704 continue 

705 

706 self._move_cell(row, column, row_offset, col_offset) 

707 

708 

709 def insert_rows(self, idx, amount=1): 

710 """ 

711 Insert row or rows before row==idx 

712 """ 

713 self._move_cells(min_row=idx, offset=amount, row_or_col="row") 

714 self._current_row = self.max_row 

715 

716 

717 def insert_cols(self, idx, amount=1): 

718 """ 

719 Insert column or columns before col==idx 

720 """ 

721 self._move_cells(min_col=idx, offset=amount, row_or_col="column") 

722 

723 

724 def delete_rows(self, idx, amount=1): 

725 """ 

726 Delete row or rows from row==idx 

727 """ 

728 

729 remainder = _gutter(idx, amount, self.max_row) 

730 

731 self._move_cells(min_row=idx+amount, offset=-amount, row_or_col="row") 

732 

733 # calculating min and max col is an expensive operation, do it only once 

734 min_col = self.min_column 

735 max_col = self.max_column + 1 

736 for row in remainder: 

737 for col in range(min_col, max_col): 

738 if (row, col) in self._cells: 

739 del self._cells[row, col] 

740 self._current_row = self.max_row 

741 if not self._cells: 

742 self._current_row = 0 

743 

744 

745 def delete_cols(self, idx, amount=1): 

746 """ 

747 Delete column or columns from col==idx 

748 """ 

749 

750 remainder = _gutter(idx, amount, self.max_column) 

751 

752 self._move_cells(min_col=idx+amount, offset=-amount, row_or_col="column") 

753 

754 # calculating min and max row is an expensive operation, do it only once 

755 min_row = self.min_row 

756 max_row = self.max_row + 1 

757 for col in remainder: 

758 for row in range(min_row, max_row): 

759 if (row, col) in self._cells: 

760 del self._cells[row, col] 

761 

762 

763 def move_range(self, cell_range, rows=0, cols=0, translate=False): 

764 """ 

765 Move a cell range by the number of rows and/or columns: 

766 down if rows > 0 and up if rows < 0 

767 right if cols > 0 and left if cols < 0 

768 Existing cells will be overwritten. 

769 Formulae and references will not be updated. 

770 """ 

771 if isinstance(cell_range, str): 

772 cell_range = CellRange(cell_range) 

773 if not isinstance(cell_range, CellRange): 

774 raise ValueError("Only CellRange objects can be moved") 

775 if not rows and not cols: 

776 return 

777 

778 down = rows > 0 

779 right = cols > 0 

780 

781 if rows: 

782 cells = sorted(cell_range.rows, reverse=down) 

783 else: 

784 cells = sorted(cell_range.cols, reverse=right) 

785 

786 for row, col in chain.from_iterable(cells): 

787 self._move_cell(row, col, rows, cols, translate) 

788 

789 # rebase moved range 

790 cell_range.shift(row_shift=rows, col_shift=cols) 

791 

792 

793 def _move_cell(self, row, column, row_offset, col_offset, translate=False): 

794 """ 

795 Move a cell from one place to another. 

796 Delete at old index 

797 Rebase coordinate 

798 """ 

799 cell = self._get_cell(row, column) 

800 new_row = cell.row + row_offset 

801 new_col = cell.column + col_offset 

802 self._cells[new_row, new_col] = cell 

803 del self._cells[(cell.row, cell.column)] 

804 cell.row = new_row 

805 cell.column = new_col 

806 if translate and cell.data_type == "f": 

807 t = Translator(cell.value, cell.coordinate) 

808 cell.value = t.translate_formula(row_delta=row_offset, col_delta=col_offset) 

809 

810 

811 def _invalid_row(self, iterable): 

812 raise TypeError('Value must be a list, tuple, range or generator, or a dict. Supplied value is {0}'.format( 

813 type(iterable)) 

814 ) 

815 

816 

817 def _add_column(self): 

818 """Dimension factory for column information""" 

819 

820 return ColumnDimension(self) 

821 

822 def _add_row(self): 

823 """Dimension factory for row information""" 

824 

825 return RowDimension(self) 

826 

827 

828 @property 

829 def print_title_rows(self): 

830 """Rows to be printed at the top of every page (ex: '1:3')""" 

831 if self._print_rows: 

832 return self._print_rows 

833 

834 

835 @print_title_rows.setter 

836 def print_title_rows(self, rows): 

837 """ 

838 Set rows to be printed on the top of every page 

839 format `1:3` 

840 """ 

841 if rows is not None: 

842 if not ROW_RANGE_RE.match(rows): 

843 raise ValueError("Print title rows must be the form 1:3") 

844 self._print_rows = rows 

845 

846 

847 @property 

848 def print_title_cols(self): 

849 """Columns to be printed at the left side of every page (ex: 'A:C')""" 

850 if self._print_cols: 

851 return self._print_cols 

852 

853 

854 @print_title_cols.setter 

855 def print_title_cols(self, cols): 

856 """ 

857 Set cols to be printed on the left of every page 

858 format ``A:C` 

859 """ 

860 if cols is not None: 

861 if not COL_RANGE_RE.match(cols): 

862 raise ValueError("Print title cols must be the form C:D") 

863 self._print_cols = cols 

864 

865 

866 @property 

867 def print_titles(self): 

868 if self.print_title_cols and self.print_title_rows: 

869 return ",".join([self.print_title_rows, self.print_title_cols]) 

870 else: 

871 return self.print_title_rows or self.print_title_cols 

872 

873 

874 @property 

875 def print_area(self): 

876 """ 

877 The print area for the worksheet, or None if not set. To set, supply a range 

878 like 'A1:D4' or a list of ranges. 

879 """ 

880 return self._print_area 

881 

882 

883 @print_area.setter 

884 def print_area(self, value): 

885 """ 

886 Range of cells in the form A1:D4 or list of ranges 

887 """ 

888 if isinstance(value, str): 

889 value = [value] 

890 

891 self._print_area = [absolute_coordinate(v) for v in value] 

892 

893 

894def _gutter(idx, offset, max_val): 

895 """ 

896 When deleting rows and columns are deleted we rely on overwriting. 

897 This may not be the case for a large offset on small set of cells: 

898 range(cells_to_delete) > range(cell_to_be_moved) 

899 """ 

900 gutter = range(max(max_val+1-offset, idx), min(idx+offset, max_val)+1) 

901 return gutter