From 0c4fb60225712ad69d20ebb76efa3adb48e4b479 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 5 Mar 2025 16:06:32 +0100 Subject: [PATCH 1/5] Sheet: Disable zooming With PR 16130 the zooming capability was added to spreadsheet view. The SheetTableView is added to a QGraphicsScene of a QGraphicsView that provides the method scale(). However, this causes some problems with header of the table view and makes resizing columns or rows very unintuitive. A correctly working table view is probably more important than the possibility to zoom in or out and that's why the latter will be disabled. This fixes issue 19863 --- src/Mod/Spreadsheet/Gui/DlgSettingsImp.cpp | 1 + src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Mod/Spreadsheet/Gui/DlgSettingsImp.cpp b/src/Mod/Spreadsheet/Gui/DlgSettingsImp.cpp index f301917e46..fa3fa5368a 100644 --- a/src/Mod/Spreadsheet/Gui/DlgSettingsImp.cpp +++ b/src/Mod/Spreadsheet/Gui/DlgSettingsImp.cpp @@ -37,6 +37,7 @@ DlgSettingsImp::DlgSettingsImp(QWidget* parent) , ui(new Ui_DlgSettings) { ui->setupUi(this); + ui->dZLSpinBox->setDisabled(true); } /** diff --git a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp index bfea61b06f..8dab3b284c 100644 --- a/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp +++ b/src/Mod/Spreadsheet/Gui/SpreadsheetView.cpp @@ -74,9 +74,15 @@ SheetView::SheetView(Gui::Document* pcDocument, App::DocumentObject* docObj, QWi ui = new Ui::Sheet(); QWidget* w = new QWidget(this); ui->setupUi(w); + ui->zoomMinus->hide(); + ui->zoomPlus->hide(); + ui->zoomSlider->hide(); + ui->zoomTB->hide(); + ui->realSB_h->hide(); + ui->realSB_v->hide(); setCentralWidget(w); - new ZoomableView(ui); + // new ZoomableView(ui); delegate = new SpreadsheetDelegate(sheet); ui->cells->setModel(model); From c215bd7be9c50664fb9b5de0720c5d03575227c8 Mon Sep 17 00:00:00 2001 From: wmayer Date: Fri, 7 Mar 2025 17:46:09 +0100 Subject: [PATCH 2/5] Sheet: Fix reading xlsx files for the specs of the XLSX file format see: https://jkp-ads.com/articles/excel2007fileformat00.aspx or https://www.data2type.de/xml-xslt-xslfo/spreadsheetml/xlsx-format In order to find the correct sheet file one has to first read-in xl/_rels/workbook.xml.rels and save the relations of Id and Target. The file xl/workbook.xml contains a list of sheet elements where for each element the attribute 'r:id' is set. This attribute can be used to look up for the actual data file. This fixes issue 19757 --- src/Mod/Spreadsheet/importXLSX.py | 36 ++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/Mod/Spreadsheet/importXLSX.py b/src/Mod/Spreadsheet/importXLSX.py index 2b06bfa45e..6c28188544 100644 --- a/src/Mod/Spreadsheet/importXLSX.py +++ b/src/Mod/Spreadsheet/importXLSX.py @@ -364,8 +364,22 @@ def handleCells(cellList, actCellSheet, sList): actCellSheet.set(ref, (sList[int(theValue)])) -def handleWorkBook(theBook, sheetDict, Doc): +def handleWorkBookRels(theBookRels): + theRels = theBookRels.getElementsByTagName("Relationship") + idTarget = {} + for rel in theRels: + relAtts = rel.attributes + idRef = relAtts.getNamedItem("Id") + relRef = getText(idRef.childNodes) + targetRef = relAtts.getNamedItem("Target") + relTarget = getText(targetRef.childNodes) + idTarget[relRef] = relTarget + return idTarget + + +def handleWorkBook(theBook, theBookRels, sheetDict, Doc): theSheets = theBook.getElementsByTagName("sheet") + theIdTargetMap = handleWorkBookRels(theBookRels) # print("theSheets: ", theSheets) for sheet in theSheets: sheetAtts = sheet.attributes @@ -373,7 +387,7 @@ def handleWorkBook(theBook, sheetDict, Doc): sheetName = getText(nameRef.childNodes) # print("table name: ", sheetName) idRef = sheetAtts.getNamedItem("r:id") - sheetFile = "sheet" + getText(idRef.childNodes)[3:] + ".xml" + sheetFile = theIdTargetMap[getText(idRef.childNodes)] # print("sheetFile: ", sheetFile) # add FreeCAD-spreadsheet sheetDict[sheetName] = (Doc.addObject("Spreadsheet::Sheet", sheetName), sheetFile) @@ -397,10 +411,10 @@ def handleWorkBook(theBook, sheetDict, Doc): def handleStrings(theStr, sList): - print("process Strings: ") + # print("process Strings: ") stringElements = theStr.getElementsByTagName("t") for sElem in stringElements: - print("string: ", getText(sElem.childNodes)) + # print("string: ", getText(sElem.childNodes)) sList.append(getText(sElem.childNodes)) @@ -416,8 +430,11 @@ def open(nameXLSX): theBookFile = z.open("xl/workbook.xml") theBook = xml.dom.minidom.parse(theBookFile) - handleWorkBook(theBook, sheetDict, theDoc) + theBookRelsFile = z.open("xl/_rels/workbook.xml.rels") + theBookRels = xml.dom.minidom.parse(theBookRelsFile) + handleWorkBook(theBook, theBookRels, sheetDict, theDoc) theBook.unlink() + theBookRels.unlink() if "xl/sharedStrings.xml" in z.namelist(): theStringFile = z.open("xl/sharedStrings.xml") @@ -428,7 +445,7 @@ def open(nameXLSX): for sheetSpec in sheetDict: # print("sheetSpec: ", sheetSpec) theSheet, sheetFile = sheetDict[sheetSpec] - f = z.open("xl/worksheets/" + sheetFile) + f = z.open("xl/" + sheetFile) myDom = xml.dom.minidom.parse(f) handleWorkSheet(myDom, theSheet, stringList) @@ -455,8 +472,11 @@ def insert(nameXLSX, docname): z = zipfile.ZipFile(nameXLSX) theBookFile = z.open("xl/workbook.xml") theBook = xml.dom.minidom.parse(theBookFile) - handleWorkBook(theBook, sheetDict, theDoc) + theBookRelsFile = z.open("xl/_rels/workbook.xml.rels") + theBookRels = xml.dom.minidom.parse(theBookRelsFile) + handleWorkBook(theBook, theBookRels, sheetDict, theDoc) theBook.unlink() + theBookRels.unlink() if "xl/sharedStrings.xml" in z.namelist(): theStringFile = z.open("xl/sharedStrings.xml") @@ -467,7 +487,7 @@ def insert(nameXLSX, docname): for sheetSpec in sheetDict: # print("sheetSpec: ", sheetSpec) theSheet, sheetFile = sheetDict[sheetSpec] - f = z.open("xl/worksheets/" + sheetFile) + f = z.open("xl/" + sheetFile) myDom = xml.dom.minidom.parse(f) handleWorkSheet(myDom, theSheet, stringList) From a46f79aa684f3bd0a1e9a6f7be7d41b3bb02a145 Mon Sep 17 00:00:00 2001 From: wmayer Date: Sun, 16 Mar 2025 14:42:06 +0100 Subject: [PATCH 3/5] Sheet: Fix crash in Cell::setContent This fixes issue 14697 --- src/Mod/Spreadsheet/App/Cell.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Mod/Spreadsheet/App/Cell.cpp b/src/Mod/Spreadsheet/App/Cell.cpp index 9668a52d14..80c6cd13ee 100644 --- a/src/Mod/Spreadsheet/App/Cell.cpp +++ b/src/Mod/Spreadsheet/App/Cell.cpp @@ -287,20 +287,21 @@ void Cell::setContent(const char* value) clearException(); if (value) { - if (owner->sheet()->isRestoring()) { + Sheet* sheet = owner->sheet(); + if (sheet && sheet->isRestoring()) { if (value[0] == '\0' || (value[0] == '\'' && value[1] == '\0')) { return; } - expression = std::make_unique(owner->sheet(), value); + expression = std::make_unique(sheet, value); setUsed(EXPRESSION_SET, true); return; } if (*value == '=') { try { - newExpr = ExpressionPtr(App::ExpressionParser::parse(owner->sheet(), value + 1)); + newExpr = ExpressionPtr(App::ExpressionParser::parse(sheet, value + 1)); } catch (Base::Exception& e) { - newExpr = std::make_unique(owner->sheet(), value); + newExpr = std::make_unique(sheet, value); setParseException(e.what()); } } @@ -309,7 +310,7 @@ void Cell::setContent(const char* value) value = nullptr; } else { - newExpr = std::make_unique(owner->sheet(), value + 1); + newExpr = std::make_unique(sheet, value + 1); } } else if (*value != '\0') { @@ -320,8 +321,7 @@ void Cell::setContent(const char* value) if (errno == 0) { const bool isEndEmpty = *end == '\0' || strspn(end, " \t\n\r") == strlen(end); if (isEndEmpty) { - newExpr = std::make_unique(owner->sheet(), - Quantity(float_value)); + newExpr = std::make_unique(sheet, Quantity(float_value)); } } @@ -329,7 +329,7 @@ void Cell::setContent(const char* value) const bool isStartingWithNumber = value != end; if (!newExpr && isStartingWithNumber) { try { - ExpressionPtr parsedExpr(App::ExpressionParser::parse(owner->sheet(), value)); + ExpressionPtr parsedExpr(App::ExpressionParser::parse(sheet, value)); if (const auto fraction = freecad_cast(parsedExpr.get())) { if (fraction->getOperator() == OperatorExpression::UNIT) { @@ -384,7 +384,7 @@ void Cell::setContent(const char* value) } if (!newExpr && value && *value != '\0') { - newExpr = std::make_unique(owner->sheet(), value); + newExpr = std::make_unique(sheet, value); } // trying to add an empty string will make newExpr = nullptr From 513260d29d23e583bb10c060c7030d899cc72ffb Mon Sep 17 00:00:00 2001 From: wmayer Date: Thu, 20 Mar 2025 16:16:58 +0100 Subject: [PATCH 4/5] Sheet: Improve handling of unsupported formulas With the example file of issue 20299 the parser stops with an exception and reads in the formulas only partially. This PR doesn't fix the issue but avoids to raise an exception so that as much as possible will be read in. For unsupported formulas a suitable message is printed with the content of the cell. --- src/Mod/Spreadsheet/importXLSX.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Mod/Spreadsheet/importXLSX.py b/src/Mod/Spreadsheet/importXLSX.py index 6c28188544..2fb7dc4f42 100644 --- a/src/Mod/Spreadsheet/importXLSX.py +++ b/src/Mod/Spreadsheet/importXLSX.py @@ -346,9 +346,18 @@ def handleCells(cellList, actCellSheet, sList): formulaRef = cell.getElementsByTagName("f") if len(formulaRef) == 1: theFormula = getText(formulaRef[0].childNodes) - # print("theFormula: ", theFormula) - fTrans = FormulaTranslator() - actCellSheet.set(ref, fTrans.translateForm(theFormula)) + if theFormula: + # print("theFormula: ", theFormula) + fTrans = FormulaTranslator() + actCellSheet.set(ref, fTrans.translateForm(theFormula)) + else: + attrs = formulaRef[0].attributes + attrRef = attrs.getNamedItem("t") + attrName = getText(attrRef.childNodes) + indexRef = attrs.getNamedItem("si") + indexName = getText(indexRef.childNodes) + content = "".format(attrName, indexName) + print(f"Unsupported formula in cell {ref}: {content}") else: valueRef = cell.getElementsByTagName("v") From b38c8867d0c150095a00d7f53ed5c48db5953322 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 21 May 2025 11:56:02 +0200 Subject: [PATCH 5/5] Sheet: Further cleanup in Sheet class With PR 21445 an unused function has been removed. But the underlying class member isn't used anywhere else and can be removed as well. --- src/Mod/Spreadsheet/App/Sheet.cpp | 5 ----- src/Mod/Spreadsheet/App/Sheet.h | 4 ---- 2 files changed, 9 deletions(-) diff --git a/src/Mod/Spreadsheet/App/Sheet.cpp b/src/Mod/Spreadsheet/App/Sheet.cpp index eb4fea54c5..6fd311dfa7 100644 --- a/src/Mod/Spreadsheet/App/Sheet.cpp +++ b/src/Mod/Spreadsheet/App/Sheet.cpp @@ -146,11 +146,6 @@ void Sheet::clearAll() cellErrors.clear(); columnWidths.clear(); rowHeights.clear(); - - for (auto& observer : observers) { - delete observer.second; - } - observers.clear(); } // validate import/export parameters diff --git a/src/Mod/Spreadsheet/App/Sheet.h b/src/Mod/Spreadsheet/App/Sheet.h index d189ec9ec6..f08a3d35fe 100644 --- a/src/Mod/Spreadsheet/App/Sheet.h +++ b/src/Mod/Spreadsheet/App/Sheet.h @@ -290,10 +290,6 @@ protected: /* Row heights */ PropertyRowHeights rowHeights; - /* Document observers to track changes to external properties */ - using ObserverMap = std::map; - ObserverMap observers; - int currentRow = -1; int currentCol = -1;