From d86369c9b54f74c1d2e22a86c900517fcb91d9dc Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Thu, 23 Feb 2023 12:50:16 +0100 Subject: [PATCH 1/3] [Arch] fix Arch_Schedule recompute issue --- src/Mod/Arch/ArchSchedule.py | 59 +++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/src/Mod/Arch/ArchSchedule.py b/src/Mod/Arch/ArchSchedule.py index 4ae7bcc29b..29829e8c1f 100644 --- a/src/Mod/Arch/ArchSchedule.py +++ b/src/Mod/Arch/ArchSchedule.py @@ -74,6 +74,24 @@ class CommandArchSchedule: return False +class _ArchScheduleDocObserver: + + "doc observer to monitor all recomputes" + + # https://forum.freecad.org/viewtopic.php?style=3&p=553377#p553377 + + def __init__(self, doc, schedule): + self.doc = doc + self.schedule = schedule + + def slotRecomputedDocument(self, doc): + if doc != self.doc: + return + try: + self.schedule.Proxy.execute(self.schedule) + except: + pass + class _ArchSchedule: @@ -88,6 +106,17 @@ class _ArchSchedule: def onDocumentRestored(self,obj): self.setProperties(obj) + if obj.getTypeIdOfProperty("Result") == "App::PropertyLink": + self.update_properties_0v21(obj) + + def update_properties_0v21(self,obj): + result = obj.Result + obj.removeProperty("Result") + self.setProperties(obj) + obj.Result = result + from draftutils.messages import _wrn + _wrn("v0.21, " + obj.Label + ", " + + translate("Arch", "changed the property type of 'Result', and added property 'AutoUpdate'")) def setProperties(self,obj): @@ -104,18 +133,32 @@ class _ArchSchedule: if not "CreateSpreadsheet" in obj.PropertiesList: obj.addProperty("App::PropertyBool", "CreateSpreadsheet", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, a spreadsheet containing the results is recreated when needed")) if not "Result" in obj.PropertiesList: - obj.addProperty("App::PropertyLink", "Result", "Arch",QT_TRANSLATE_NOOP("App::Property","The spreadsheet to print the results to")) + obj.addProperty("App::PropertyLinkHidden","Result", "Arch",QT_TRANSLATE_NOOP("App::Property","The spreadsheet to print the results to")) if not "DetailedResults" in obj.PropertiesList: - obj.addProperty("App::PropertyBool", "DetailedResults", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, additional lines with each individual object are added to the results")) + obj.addProperty("App::PropertyBool", "DetailedResults", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, additional lines with each individual object are added to the results")) + if not "AutoUpdate" in obj.PropertiesList: + obj.addProperty("App::PropertyBool", "AutoUpdate", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, whenever the document is recomputed this schedule and the linked spreadsheet are recomputed as well")) + obj.AutoUpdate = True + + # To add the doc observer: + self.onChanged(obj,"AutoUpdate") def onChanged(self,obj,prop): - if (prop == "CreateSpreadsheet"): - if hasattr(obj,"CreateSpreadsheet") and obj.CreateSpreadsheet: + if prop == "CreateSpreadsheet": + if obj.CreateSpreadsheet: if not obj.Result: import Spreadsheet sp = FreeCAD.ActiveDocument.addObject("Spreadsheet::Sheet","Result") obj.Result = sp + elif prop == "AutoUpdate": + if obj.AutoUpdate: + if getattr(self, "docObserver", None) is None: + self.docObserver = _ArchScheduleDocObserver(FreeCAD.ActiveDocument, obj) + FreeCAD.addDocumentObserver(self.docObserver) + elif getattr(self, "docObserver", None) is not None: + FreeCAD.removeDocumentObserver(self.docObserver) + self.docObserver = None def setSpreadsheetData(self,obj,force=False): @@ -146,6 +189,7 @@ class _ArchSchedule: obj.Result.set(k,v) # recompute obj.Result.recompute() + obj.Result.purgeTouched() # Remove the confusing blue checkmark from the spreadsheet. def execute(self,obj): @@ -318,10 +362,6 @@ class _ArchSchedule: self.data["C"+str(li)] = unit else: self.data["B"+str(li)] = str(val) - if obj.DetailedResults: - # additional blank line... - li += 1 - self.data["A"+str(li)] = " " if verbose: if tp and unit: v = fs.format(FreeCAD.Units.Quantity(val,tp).getValueAs(unit).Value) @@ -349,6 +389,9 @@ class _ViewProviderArchSchedule: vobj.Proxy = self def getIcon(self): + if self.Object.AutoUpdate is False: + import TechDrawGui + return ":/icons/TechDraw_TreePageUnsync.svg" import Arch_rc return ":/icons/Arch_Schedule.svg" From f842000c252f5a7a7f7110bd8c38f6f80f02fee8 Mon Sep 17 00:00:00 2001 From: Roy-043 <70520633+Roy-043@users.noreply.github.com> Date: Thu, 23 Feb 2023 14:25:09 +0100 Subject: [PATCH 2/3] TechDraw::DrawViewSpreadsheet objects were not recomputed --- src/Mod/Arch/ArchSchedule.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mod/Arch/ArchSchedule.py b/src/Mod/Arch/ArchSchedule.py index 29829e8c1f..6c4f1cfdc6 100644 --- a/src/Mod/Arch/ArchSchedule.py +++ b/src/Mod/Arch/ArchSchedule.py @@ -189,7 +189,10 @@ class _ArchSchedule: obj.Result.set(k,v) # recompute obj.Result.recompute() - obj.Result.purgeTouched() # Remove the confusing blue checkmark from the spreadsheet. + obj.Result.purgeTouched() # Remove the confusing blue checkmark from the spreadsheet. + for ref in obj.Result.InList: # Also recompute TechDraw views. + ref.TypeId == "TechDraw::DrawViewSpreadsheet" + ref.recompute() def execute(self,obj): From 6f5cafccca9e075e9ae9fab4a93baecdbc789d7b Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Sat, 25 Feb 2023 23:23:35 +0100 Subject: [PATCH 3/3] [Arch] Arch_Schedule updated fix --- src/Mod/Arch/ArchSchedule.py | 129 ++++++++++++++-------- src/Mod/Arch/Resources/ui/ArchSchedule.ui | 12 +- 2 files changed, 91 insertions(+), 50 deletions(-) diff --git a/src/Mod/Arch/ArchSchedule.py b/src/Mod/Arch/ArchSchedule.py index 6c4f1cfdc6..46429d3fad 100644 --- a/src/Mod/Arch/ArchSchedule.py +++ b/src/Mod/Arch/ArchSchedule.py @@ -106,17 +106,18 @@ class _ArchSchedule: def onDocumentRestored(self,obj): self.setProperties(obj) - if obj.getTypeIdOfProperty("Result") == "App::PropertyLink": + if hasattr(obj, "Result"): self.update_properties_0v21(obj) def update_properties_0v21(self,obj): - result = obj.Result + sp = obj.Result + if sp is not None: + self.setSchedulePropertySpreadsheet(sp, obj) obj.removeProperty("Result") - self.setProperties(obj) - obj.Result = result from draftutils.messages import _wrn - _wrn("v0.21, " + obj.Label + ", " - + translate("Arch", "changed the property type of 'Result', and added property 'AutoUpdate'")) + _wrn("v0.21, " + obj.Label + ", " + translate("Arch", "removed property 'Result', and added property 'AutoUpdate'")) + if sp is not None: + _wrn("v0.21, " + sp.Label + ", " + translate("Arch", "added property 'Schedule'")) def setProperties(self,obj): @@ -132,25 +133,59 @@ class _ArchSchedule: obj.addProperty("App::PropertyStringList","Filter", "Arch",QT_TRANSLATE_NOOP("App::Property","The filter column")) if not "CreateSpreadsheet" in obj.PropertiesList: obj.addProperty("App::PropertyBool", "CreateSpreadsheet", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, a spreadsheet containing the results is recreated when needed")) - if not "Result" in obj.PropertiesList: - obj.addProperty("App::PropertyLinkHidden","Result", "Arch",QT_TRANSLATE_NOOP("App::Property","The spreadsheet to print the results to")) if not "DetailedResults" in obj.PropertiesList: obj.addProperty("App::PropertyBool", "DetailedResults", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, additional lines with each individual object are added to the results")) if not "AutoUpdate" in obj.PropertiesList: - obj.addProperty("App::PropertyBool", "AutoUpdate", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, whenever the document is recomputed this schedule and the linked spreadsheet are recomputed as well")) + obj.addProperty("App::PropertyBool", "AutoUpdate", "Arch",QT_TRANSLATE_NOOP("App::Property","If True, the schedule and the associated spreadsheet are updated whenever the document is recomputed")) obj.AutoUpdate = True # To add the doc observer: self.onChanged(obj,"AutoUpdate") + def setSchedulePropertySpreadsheet(self, sp, obj): + if not hasattr(sp, "Schedule"): + sp.addProperty( + "App::PropertyLink", + "Schedule", + "Arch", + QT_TRANSLATE_NOOP("App::Property", "The Arch Schedule that uses this spreadsheet")) + sp.Schedule = obj + + def getSpreadSheet(self, obj, force=False): + + """Get the spreadsheet and store it in self.spreadsheet. + + If force is True the spreadsheet is created if required. + """ + try: # Required as self.spreadsheet may get deleted. + if getattr(self, "spreadsheet", None) is not None \ + and getattr(self.spreadsheet, "Schedule", None) == obj: + return self.spreadsheet + except: + pass + else: + for o in FreeCAD.ActiveDocument.Objects: + if o.TypeId == "Spreadsheet::Sheet" \ + and getattr(o, "Schedule", None) == obj: + self.spreadsheet = o + return self.spreadsheet + if force: + self.spreadsheet = FreeCAD.ActiveDocument.addObject("Spreadsheet::Sheet", "Result") + self.setSchedulePropertySpreadsheet(self.spreadsheet, obj) + return self.spreadsheet + else: + return None + def onChanged(self,obj,prop): if prop == "CreateSpreadsheet": if obj.CreateSpreadsheet: - if not obj.Result: - import Spreadsheet - sp = FreeCAD.ActiveDocument.addObject("Spreadsheet::Sheet","Result") - obj.Result = sp + self.getSpreadSheet(obj, force=True) + else: + sp = self.getSpreadSheet(obj) + if sp is not None: + FreeCAD.ActiveDocument.removeObject(sp.Name) + self.spreadsheet = None elif prop == "AutoUpdate": if obj.AutoUpdate: if getattr(self, "docObserver", None) is None: @@ -170,29 +205,26 @@ class _ArchSchedule: return if not self.data: return - if not obj.Result: - if obj.CreateSpreadsheet or force: - import Spreadsheet - sp = FreeCAD.ActiveDocument.addObject("Spreadsheet::Sheet","Result") - obj.Result = sp - else: - return - # clear spreadsheet - obj.Result.clearAll() + if not (obj.CreateSpreadsheet or force): + return + sp = self.getSpreadSheet(obj, force=True) + sp.clearAll() + # clearAll removes the custom property, we need to re-add it: + self.setSchedulePropertySpreadsheet(sp, obj) # set headers - obj.Result.set("A1","Description") - obj.Result.set("B1","Value") - obj.Result.set("C1","Unit") - obj.Result.setStyle('A1:C1', 'bold', 'add') + sp.set("A1","Description") + sp.set("B1","Value") + sp.set("C1","Unit") + sp.setStyle('A1:C1', 'bold', 'add') # write contents for k,v in self.data.items(): - obj.Result.set(k,v) + sp.set(k,v) # recompute - obj.Result.recompute() - obj.Result.purgeTouched() # Remove the confusing blue checkmark from the spreadsheet. - for ref in obj.Result.InList: # Also recompute TechDraw views. - ref.TypeId == "TechDraw::DrawViewSpreadsheet" - ref.recompute() + sp.recompute() + sp.purgeTouched() # Remove the confusing blue checkmark from the spreadsheet. + for o in sp.InList: # Also recompute TechDraw views. + o.TypeId == "TechDraw::DrawViewSpreadsheet" + o.recompute() def execute(self,obj): @@ -205,9 +237,6 @@ class _ArchSchedule: # different number of items in each column if len(obj.Description) != len(p): return - if not hasattr(obj,"Result"): - # silently fail on old schedule objects - return self.data = {} # store all results in self.data, so it lives even without spreadsheet li = 1 # row index - starts at 2 to leave 2 blank rows for the title @@ -243,9 +272,8 @@ class _ArchSchedule: objs = objs[0].Group objs = Draft.get_group_contents(objs) objs = Arch.pruneIncluded(objs,strict=True) - # remove the schedule object and its result from the list - objs = [o for o in objs if not o == obj] - objs = [o for o in objs if not o == obj.Result] + # Remove all schedules and spreadsheets: + objs = [o for o in objs if Draft.get_type(o) not in ["Schedule", "Spreadsheet::Sheet"]] if obj.Filter[i]: # apply filters nobjs = [] @@ -428,23 +456,27 @@ class _ViewProviderArchSchedule: self.edit) menu.addAction(actionEdit) - actionAttachSpreadsheet = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Schedule.svg"), - translate("Arch", "Attach spreadsheet"), + if self.Object.CreateSpreadsheet is True: + msg = translate("Arch", "Remove spreadsheet") + else: + msg = translate("Arch", "Attach spreadsheet") + actionToggleSpreadsheet = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Schedule.svg"), + msg, menu) - QtCore.QObject.connect(actionAttachSpreadsheet, + QtCore.QObject.connect(actionToggleSpreadsheet, QtCore.SIGNAL("triggered()"), - self.attachSpreadsheet) - menu.addAction(actionAttachSpreadsheet) + self.toggleSpreadsheet) + menu.addAction(actionToggleSpreadsheet) def edit(self): FreeCADGui.ActiveDocument.setEdit(self.Object, 0) - def attachSpreadsheet(self): - self.Object.Proxy.setSpreadsheetData(self.Object, force=True) + def toggleSpreadsheet(self): + self.Object.CreateSpreadsheet = not self.Object.CreateSpreadsheet def claimChildren(self): if hasattr(self,"Object"): - return [self.Object.Result] + return [self.Object.Proxy.getSpreadSheet(self.Object)] def __getstate__(self): return None @@ -714,9 +746,7 @@ class ArchScheduleTaskPanel: if FreeCAD.GuiUp: _ViewProviderArchSchedule(self.obj.ViewObject) if hasattr(self.obj,"CreateSpreadsheet") and self.obj.CreateSpreadsheet: - import Spreadsheet - sp = FreeCAD.ActiveDocument.addObject("Spreadsheet::Sheet","Result") - self.obj.Result = sp + self.obj.Proxy.getSpreadSheet(self.obj, force=True) lists = [ [], [], [], [], [] ] for i in range(self.form.list.rowCount()): for j in range(5): @@ -734,6 +764,7 @@ class ArchScheduleTaskPanel: self.obj.Label = self.form.lineEditName.text() self.obj.DetailedResults = self.form.checkDetailed.isChecked() self.obj.CreateSpreadsheet = self.form.checkSpreadsheet.isChecked() + self.obj.AutoUpdate = self.form.checkAutoUpdate.isChecked() FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() diff --git a/src/Mod/Arch/Resources/ui/ArchSchedule.ui b/src/Mod/Arch/Resources/ui/ArchSchedule.ui index 5223160eb5..a37690fcd7 100644 --- a/src/Mod/Arch/Resources/ui/ArchSchedule.ui +++ b/src/Mod/Arch/Resources/ui/ArchSchedule.ui @@ -118,13 +118,23 @@ Leave blank to use all objects from the document - If this is turned on, additional lines will be filled with each object considered. If not, only the totals. + If this is enabled, additional lines will be filled with each object considered. If not, only the totals. Detailed results + + + + If this is enabled, the schedule and the associated spreadsheet are updated whenever the document is recomputed. + + + Auto update + + +