From 4d123e8ae92d4b70002b98ec4c699a1afa4ebf81 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 74f09bbf8d8224fb20de6e4a0c9e3edce320bca6 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 e2f39f59beb574160ebe4fbb160c0de54dff0c68 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 5763e959f6a2233c21f184557bf5879962f826bf 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 7ac57656e13db81fdf92d51f4f675e31e6cbcd55 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;