Spreadsheet: Add getUsedRange() and getNonEmptyRange()
This commit is contained in:
@@ -151,6 +151,38 @@ bool PropertySheet::isValidAlias(const std::string &candidate)
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// A utility function that gets the range (the minimum and maximum row and column) out of a
|
||||
// vector of cell addresses. Note that it's possible that neither cell in the tuple itself
|
||||
// has data in it, but operating on all cells in the inclusive range specified by the tuple
|
||||
// is guaranteed to include all cells in the passed-in vector, and no cells exist to the
|
||||
// left, right, above, or below the block specified.
|
||||
std::tuple<CellAddress, CellAddress> extractRange(const std::vector<CellAddress> &cells)
|
||||
{
|
||||
CellAddress firstRowAndColumn;
|
||||
CellAddress lastRowAndColumn;
|
||||
for (const auto & cell : cells) {
|
||||
int row = cell.row();
|
||||
int column = cell.col();
|
||||
if (row < firstRowAndColumn.row() || !firstRowAndColumn.isValid()) {
|
||||
firstRowAndColumn.setRow(row);
|
||||
}
|
||||
if (column < firstRowAndColumn.col() || !firstRowAndColumn.isValid()) {
|
||||
firstRowAndColumn.setCol(column);
|
||||
}
|
||||
if (row > lastRowAndColumn.row() || !lastRowAndColumn.isValid()) {
|
||||
lastRowAndColumn.setRow(row);
|
||||
}
|
||||
if (column > lastRowAndColumn.col() || !lastRowAndColumn.isValid()) {
|
||||
lastRowAndColumn.setCol(column);
|
||||
}
|
||||
}
|
||||
return std::make_tuple(firstRowAndColumn, lastRowAndColumn);
|
||||
}
|
||||
}// namespace
|
||||
|
||||
std::vector<CellAddress> PropertySheet::getUsedCells() const
|
||||
{
|
||||
std::vector<CellAddress> usedSet;
|
||||
@@ -163,6 +195,12 @@ std::vector<CellAddress> PropertySheet::getUsedCells() const
|
||||
return usedSet;
|
||||
}
|
||||
|
||||
std::tuple<CellAddress, CellAddress> PropertySheet::getUsedRange() const
|
||||
{
|
||||
auto usedCells = getUsedCells();
|
||||
return extractRange(usedCells);
|
||||
}
|
||||
|
||||
std::vector<CellAddress> PropertySheet::getNonEmptyCells() const
|
||||
{
|
||||
std::vector<CellAddress> usedSet;
|
||||
@@ -177,6 +215,12 @@ std::vector<CellAddress> PropertySheet::getNonEmptyCells() const
|
||||
return usedSet;
|
||||
}
|
||||
|
||||
std::tuple<CellAddress, CellAddress> PropertySheet::getNonEmptyRange() const
|
||||
{
|
||||
auto nonEmptyCells = getNonEmptyCells();
|
||||
return extractRange(nonEmptyCells);
|
||||
}
|
||||
|
||||
void PropertySheet::setDirty(CellAddress address)
|
||||
{
|
||||
/* Merged cells will automatically force an update of the top left cell
|
||||
|
||||
@@ -115,8 +115,12 @@ public:
|
||||
|
||||
std::vector<App::CellAddress> getUsedCells() const;
|
||||
|
||||
std::tuple<App::CellAddress, App::CellAddress> getUsedRange() const;
|
||||
|
||||
std::vector<App::CellAddress> getNonEmptyCells() const;
|
||||
|
||||
std::tuple<App::CellAddress, App::CellAddress> getNonEmptyRange() const;
|
||||
|
||||
Sheet * sheet() const { return owner; }
|
||||
|
||||
const std::set<App::CellAddress> & getDirty() { return dirty; }
|
||||
|
||||
@@ -201,5 +201,32 @@ Get a list of the names of all cells with data in them.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
|
||||
<Methode Name="getUsedRange">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getUsedRange()
|
||||
|
||||
Get a the total range of the used cells in a sheet, as a pair of strings
|
||||
representing the lowest row and column that are used, and the highest row and
|
||||
column that are used (inclusive). Note that the actual first and last cell
|
||||
of the block are not necessarily used.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
|
||||
<Methode Name="getNonEmptyRange">
|
||||
<Documentation>
|
||||
<UserDocu>
|
||||
getNonEmptyRange()
|
||||
|
||||
Get a the total range of the used cells in a sheet, as a pair of cell addresses
|
||||
representing the lowest row and column that contain data, and the highest row and
|
||||
column that contain data (inclusive). Note that the actual first and last cell
|
||||
of the block do not necessarily contain anything.
|
||||
</UserDocu>
|
||||
</Documentation>
|
||||
</Methode>
|
||||
|
||||
</PythonExport>
|
||||
</GenerateModel>
|
||||
|
||||
@@ -990,16 +990,34 @@ PyObject *SheetPy::getUsedCells(PyObject *args)
|
||||
return Py::new_reference_to(pyCellList);
|
||||
}
|
||||
|
||||
PyObject *SheetPy::getUsedRange(PyObject *args)
|
||||
{
|
||||
auto usedRange = getSheetPtr()->getCells()->getUsedRange();
|
||||
Py::Tuple pyTuple(2);
|
||||
pyTuple[0] = Py::String(std::get<0>(usedRange).toString());
|
||||
pyTuple[1] = Py::String(std::get<1>(usedRange).toString());
|
||||
return Py::new_reference_to(pyTuple);
|
||||
}
|
||||
|
||||
PyObject *SheetPy::getNonEmptyCells(PyObject *args)
|
||||
{
|
||||
auto usedCells = getSheetPtr()->getCells()->getNonEmptyCells();
|
||||
auto nonEmptyCells = getSheetPtr()->getCells()->getNonEmptyCells();
|
||||
Py::List pyCellList;
|
||||
for (const auto &cell : usedCells) {
|
||||
for (const auto &cell : nonEmptyCells) {
|
||||
pyCellList.append(Py::String(cell.toString()));
|
||||
}
|
||||
return Py::new_reference_to(pyCellList);
|
||||
}
|
||||
|
||||
PyObject *SheetPy::getNonEmptyRange(PyObject *args)
|
||||
{
|
||||
auto nonEmptyRange = getSheetPtr()->getCells()->getNonEmptyRange();
|
||||
Py::Tuple pyTuple(2);
|
||||
pyTuple[0] = Py::String(std::get<0>(nonEmptyRange).toString());
|
||||
pyTuple[1] = Py::String(std::get<1>(nonEmptyRange).toString());
|
||||
return Py::new_reference_to(pyTuple);
|
||||
}
|
||||
|
||||
|
||||
// +++ custom attributes implementer ++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
|
||||
@@ -1362,6 +1362,20 @@ class SpreadsheetCases(unittest.TestCase):
|
||||
non_empty_cells = sheet.getUsedCells()
|
||||
self.assertEqual(len(non_empty_cells), len(test_cells)) # Alignment counts as "used"
|
||||
|
||||
def testGetUsedRange(self):
|
||||
sheet = self.doc.addObject('Spreadsheet::Sheet','Spreadsheet')
|
||||
test_cells = ['C5','Z3','D10','E20']
|
||||
for i,cell in enumerate(test_cells):
|
||||
sheet.set(cell,str(i))
|
||||
used_range = sheet.getUsedRange()
|
||||
self.assertEquals(used_range,("C3","Z20"))
|
||||
|
||||
for i,cell in enumerate(test_cells):
|
||||
sheet.set(cell,"")
|
||||
sheet.setAlignment(cell,"center")
|
||||
used_range = sheet.getUsedRange()
|
||||
self.assertEquals(used_range,("C3","Z20"))
|
||||
|
||||
def testGetNonEmptyCells(self):
|
||||
sheet = self.doc.addObject('Spreadsheet::Sheet','Spreadsheet')
|
||||
test_cells = ['B13','C14','D15']
|
||||
@@ -1379,6 +1393,23 @@ class SpreadsheetCases(unittest.TestCase):
|
||||
non_empty_cells = sheet.getNonEmptyCells()
|
||||
self.assertEqual(len(non_empty_cells), 0) # Alignment does not count as "non-empty"
|
||||
|
||||
def testGetNonEmptyRange(self):
|
||||
sheet = self.doc.addObject('Spreadsheet::Sheet','Spreadsheet')
|
||||
test_cells = ['C5','Z3','D10','E20']
|
||||
for i,cell in enumerate(test_cells):
|
||||
sheet.set(cell,str(i))
|
||||
non_empty_range = sheet.getNonEmptyRange()
|
||||
self.assertEquals(non_empty_range,("C3","Z20"))
|
||||
|
||||
for i,cell in enumerate(test_cells):
|
||||
sheet.set(cell,"")
|
||||
sheet.setAlignment(cell,"center")
|
||||
more_cells = ['D10','X5','E10','K15']
|
||||
for i,cell in enumerate(more_cells):
|
||||
sheet.set(cell,str(i))
|
||||
non_empty_range = sheet.getNonEmptyRange()
|
||||
self.assertEquals(non_empty_range,("D5","X15"))
|
||||
|
||||
def tearDown(self):
|
||||
#closing doc
|
||||
FreeCAD.closeDocument(self.doc.Name)
|
||||
|
||||
Reference in New Issue
Block a user