| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "Arch Schedule" |
| | __author__ = "Yorik van Havre" |
| | __url__ = "https://www.freecad.org" |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import FreeCAD |
| |
|
| | from draftutils import params |
| |
|
| | if FreeCAD.GuiUp: |
| | from PySide import QtCore, QtGui |
| | from PySide.QtCore import QT_TRANSLATE_NOOP |
| | import FreeCADGui |
| | from draftutils.translate import translate |
| | else: |
| | |
| | def translate(ctxt, txt): |
| | return txt |
| |
|
| | def QT_TRANSLATE_NOOP(ctxt, txt): |
| | return txt |
| |
|
| | |
| |
|
| |
|
| | PARAMS = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/BIM") |
| | VERBOSE = True |
| |
|
| |
|
| | class _ArchScheduleDocObserver: |
| | "doc observer to monitor all recomputes" |
| |
|
| | |
| |
|
| | 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: |
| | "the Arch Schedule object" |
| |
|
| | def __init__(self, obj): |
| |
|
| | self.setProperties(obj) |
| | obj.Proxy = self |
| | self.Type = "Schedule" |
| |
|
| | def onDocumentRestored(self, obj): |
| |
|
| | self.setProperties(obj) |
| | if hasattr(obj, "Result"): |
| | self.update_properties_0v21(obj) |
| | if hasattr(obj, "Description"): |
| | self.update_properties_1v1(obj) |
| |
|
| | def update_properties_0v21(self, obj): |
| | from draftutils.messages import _log |
| |
|
| | sp = obj.Result |
| | if sp is not None: |
| | self.setSchedulePropertySpreadsheet(sp, obj) |
| | obj.removeProperty("Result") |
| | _log("v0.21, " + obj.Name + ", removed property 'Result', and added property 'AutoUpdate'") |
| | if sp is not None: |
| | _log("v0.21, " + sp.Name + ", added property 'Schedule'") |
| |
|
| | def update_properties_1v1(self, obj): |
| | from draftutils.messages import _log |
| |
|
| | if obj.getTypeIdOfProperty("Description") == "App::PropertyStringList": |
| | obj.Operation = obj.Description |
| | obj.removeProperty("Description") |
| | _log("v1.1, " + obj.Name + ", renamed property 'Description' to 'Operation'") |
| | for prop in ( |
| | "Operation", |
| | "Value", |
| | "Unit", |
| | "Objects", |
| | "Filter", |
| | "CreateSpreadsheet", |
| | "DetailedResults", |
| | ): |
| | obj.setGroupOfProperty(prop, "Schedule") |
| |
|
| | def setProperties(self, obj): |
| |
|
| | if not "Operation" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "Operation", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP("App::Property", "The operation column"), |
| | locked=True, |
| | ) |
| | if not "Value" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "Value", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP("App::Property", "The values column"), |
| | locked=True, |
| | ) |
| | if not "Unit" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "Unit", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP("App::Property", "The units column"), |
| | locked=True, |
| | ) |
| | if not "Objects" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "Objects", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP("App::Property", "The objects column"), |
| | locked=True, |
| | ) |
| | if not "Filter" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyStringList", |
| | "Filter", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP("App::Property", "The filter column"), |
| | locked=True, |
| | ) |
| | if not "CreateSpreadsheet" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "CreateSpreadsheet", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "If True, a spreadsheet containing the results is recreated when needed", |
| | ), |
| | locked=True, |
| | ) |
| | if not "DetailedResults" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "DetailedResults", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "If True, additional lines with each individual object are added to the results", |
| | ), |
| | locked=True, |
| | ) |
| | if not "AutoUpdate" in obj.PropertiesList: |
| | obj.addProperty( |
| | "App::PropertyBool", |
| | "AutoUpdate", |
| | "Schedule", |
| | QT_TRANSLATE_NOOP( |
| | "App::Property", |
| | "If True, the schedule and the associated spreadsheet are updated whenever the document is recomputed", |
| | ), |
| | locked=True, |
| | ) |
| | obj.AutoUpdate = True |
| |
|
| | |
| | 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 BIM Schedule that uses this spreadsheet"), |
| | locked=True, |
| | ) |
| | 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: |
| | 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: |
| | 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: |
| | 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): |
| | """Fills a spreadsheet with the stored data""" |
| |
|
| | if not hasattr(self, "data"): |
| | self.execute(obj) |
| | if not hasattr(self, "data"): |
| | return |
| | if not self.data: |
| | return |
| | if not (obj.CreateSpreadsheet or force): |
| | return |
| | sp = self.getSpreadSheet(obj, force=True) |
| | widths = [sp.getColumnWidth(col) for col in ("A", "B", "C")] |
| | sp.clearAll() |
| | |
| | for col, width in zip(("A", "B", "C"), widths): |
| | sp.setColumnWidth(col, width) |
| | |
| | sp.set("A1", "Operation") |
| | sp.set("B1", "Value") |
| | sp.set("C1", "Unit") |
| | sp.setStyle("A1:C1", "bold", "add") |
| | |
| | for k, v in self.data.items(): |
| | sp.set(k, v) |
| | |
| | sp.recompute() |
| | sp.purgeTouched() |
| | for o in sp.InList: |
| | o.TypeId == "TechDraw::DrawViewSpreadsheet" |
| | o.recompute() |
| |
|
| | def execute(self, obj): |
| |
|
| | |
| |
|
| | if not obj.Operation: |
| | |
| | return |
| | for p in [obj.Value, obj.Unit, obj.Objects, obj.Filter]: |
| | |
| | if len(obj.Operation) != len(p): |
| | return |
| |
|
| | self.data = {} |
| | self.li = 1 |
| |
|
| | for i in range(len(obj.Operation)): |
| | self.li += 1 |
| | if not obj.Operation[i]: |
| | |
| | continue |
| | |
| | self.data["A" + str(self.li)] = obj.Operation[i] |
| | if VERBOSE: |
| | l = "OPERATION: " + obj.Operation[i] |
| | print("") |
| | print(l) |
| | print(len(l) * "=") |
| |
|
| | |
| |
|
| | objs = obj.Objects[i] |
| | val = obj.Value[i] |
| | unit = obj.Unit[i] |
| | details = obj.DetailedResults |
| | ifcfile = None |
| | elts = None |
| | if val: |
| | import Draft |
| | import Arch |
| |
|
| | if objs: |
| | objs = objs.split(";") |
| | objs = [FreeCAD.ActiveDocument.getObject(o) for o in objs] |
| | objs = [o for o in objs if o is not None] |
| | else: |
| | if hasattr(getattr(FreeCAD.ActiveDocument, "Proxy", None), "ifcfile"): |
| | ifcfile = FreeCAD.ActiveDocument.Proxy.ifcfile |
| | objs = FreeCAD.ActiveDocument.Objects |
| | if len(objs) == 1: |
| | if hasattr(objs[0], "StepId"): |
| | from nativeifc import ifc_tools |
| |
|
| | ifcfile = ifc_tools.get_ifcfile(objs[0]) |
| | |
| | if objs[0].isDerivedFrom("App::DocumentObjectGroup"): |
| | objs = objs[0].Group |
| | objs = Draft.get_group_contents(objs) |
| | objs = self.expandArrays(objs) |
| | |
| | |
| | objs = Arch.pruneIncluded(objs, strict=True, silent=True) |
| | |
| | objs = [ |
| | o for o in objs if Draft.get_type(o) not in ["Schedule", "Spreadsheet::Sheet"] |
| | ] |
| |
|
| | |
| |
|
| | if obj.Filter[i]: |
| | if ifcfile: |
| | elts = self.get_ifc_elements(ifcfile, obj.Filter[i]) |
| | else: |
| | objs = self.apply_filter(objs, obj.Filter[i]) |
| |
|
| | |
| |
|
| | if ifcfile: |
| | if elts: |
| | self.update_from_elts(elts, val, unit, details) |
| | elif objs: |
| | self.update_from_objs(objs, val, unit, details) |
| |
|
| | self.setSpreadsheetData(obj) |
| | self.save_ifc_props(obj) |
| |
|
| | def apply_filter(self, objs, filters): |
| | """Applies the given filters to the given list of objects""" |
| |
|
| | nobjs = [] |
| | for o in objs: |
| | props = [p.upper() for p in o.PropertiesList] |
| | ok = True |
| | for f in filters.split(";"): |
| | args = [a.strip() for a in f.strip().split(":")] |
| | if args[0][0] == "!": |
| | inv = True |
| | prop = args[0][1:].upper() |
| | else: |
| | inv = False |
| | prop = args[0].upper() |
| | fval = args[1].upper() |
| | if prop == "TYPE": |
| | prop = "IFCTYPE" |
| | if inv: |
| | if prop in props: |
| | csprop = o.PropertiesList[props.index(prop)] |
| | if fval in getattr(o, csprop).upper(): |
| | ok = False |
| | else: |
| | if not (prop in props): |
| | ok = False |
| | else: |
| | csprop = o.PropertiesList[props.index(prop)] |
| | if not (fval in getattr(o, csprop).upper()): |
| | ok = False |
| | if ok: |
| | nobjs.append(o) |
| | return nobjs |
| |
|
| | def get_ifc_elements(self, ifcfile, filters): |
| | """Retrieves IFC elements corresponding to the given filters""" |
| |
|
| | elts = [] |
| | for el in ifcfile.by_type("IfcProduct"): |
| | ok = True |
| | for f in filters.split(";"): |
| | args = [a.strip() for a in f.strip().split(":")] |
| | if args[0][0] == "!": |
| | inv = True |
| | prop = args[0][1:] |
| | else: |
| | inv = False |
| | prop = args[0] |
| | fval = args[1] |
| | if prop.upper() in ["CLASS", "IFCCLASS", "IFCTYPE"]: |
| | prop = "is_a" |
| | if inv: |
| | if prop == "is_a": |
| | if not fval.upper().startswith("IFC"): |
| | fval = "Ifc" + fval |
| | fval = fval.replace(" ", "") |
| | if el.is_a(fval): |
| | ok = False |
| | else: |
| | if prop in dir(el): |
| | rval = getattr(el, prop) |
| | if hasattr(rval, "id"): |
| | if fval.startswith("#"): |
| | fval = int(fval[1:]) |
| | if rval == fval: |
| | ok = False |
| | else: |
| | if prop == "is_a": |
| | if not fval.upper().startswith("IFC"): |
| | fval = "Ifc" + fval |
| | fval = fval.replace(" ", "") |
| | if not el.is_a(fval): |
| | ok = False |
| | else: |
| | if prop in dir(el): |
| | rval = getattr(el, prop) |
| | if hasattr(rval, "id"): |
| | if fval.startswith("#"): |
| | fval = int(fval[1:]) |
| | if rval != fval: |
| | ok = False |
| | else: |
| | ok = False |
| | if ok: |
| | elts.append(el) |
| | return elts |
| |
|
| | def update_from_objs(self, objs, val, unit, details): |
| | """Updates the spreadsheet data from FreeCAD objects""" |
| |
|
| | if val.upper() == "COUNT": |
| | val = len(objs) |
| | if VERBOSE: |
| | print(val, ",".join([o.Label for o in objs])) |
| | self.data["B" + str(self.li)] = str(val) |
| | if details: |
| | |
| | self.li += 1 |
| | self.data["A" + str(self.li)] = " " |
| | else: |
| | vals = val.split(".") |
| | if vals[0][0].islower(): |
| | |
| | vals = vals[1:] |
| | sumval = 0 |
| |
|
| | |
| | tp = None |
| | unit = None |
| | q = None |
| | if unit: |
| | unit = unit.replace("^", "") |
| | unit = unit.replace("2", "^2") |
| | unit = unit.replace("3", "^3") |
| | unit = unit.replace("²", "^2") |
| | unit = unit.replace("³", "^3") |
| | if "2" in unit: |
| | tp = FreeCAD.Units.Area |
| | elif "3" in unit: |
| | tp = FreeCAD.Units.Volume |
| | elif "deg" in unit: |
| | tp = FreeCAD.Units.Angle |
| | else: |
| | tp = FreeCAD.Units.Length |
| |
|
| | |
| | dv = params.get_param("Decimals", path="Units") |
| | fs = "{:." + str(dv) + "f}" |
| | for o in objs: |
| | if VERBOSE: |
| | l = o.Name + " (" + o.Label + "):" |
| | print(l + (40 - len(l)) * " ", end="") |
| | try: |
| | d = o |
| | for v in vals: |
| | d = getattr(d, v) |
| | if hasattr(d, "Value"): |
| | d = d.Value |
| | except Exception: |
| | t = translate("Arch", "Unable to retrieve value from object") |
| | FreeCAD.Console.PrintWarning(t + ": " + o.Name + "." + ".".join(vals) + "\n") |
| | else: |
| | if VERBOSE: |
| | if tp and unit: |
| | v = fs.format(FreeCAD.Units.Quantity(d, tp).getValueAs(unit).Value) |
| | print(v, unit) |
| | elif isinstance(d, str): |
| | if d.replace(".", "", 1).isdigit(): |
| | print(fs.format(d)) |
| | else: |
| | print(d) |
| | else: |
| | print(fs.format(d)) |
| | if details: |
| | self.li += 1 |
| | self.data["A" + str(self.li)] = o.Name + " (" + o.Label + ")" |
| | if tp and unit: |
| | q = FreeCAD.Units.Quantity(d, tp) |
| | self.data["B" + str(self.li)] = str(q.getValueAs(unit).Value) |
| | self.data["C" + str(self.li)] = unit |
| | else: |
| | self.data["B" + str(self.li)] = str(d) |
| |
|
| | if sumval: |
| | sumval += d |
| | else: |
| | sumval = d |
| | val = sumval |
| | if tp: |
| | q = FreeCAD.Units.Quantity(val, tp) |
| |
|
| | |
| | if details: |
| | self.li += 1 |
| | self.data["A" + str(self.li)] = "TOTAL" |
| | if q and unit: |
| | self.data["B" + str(self.li)] = str(q.getValueAs(unit).Value) |
| | self.data["C" + str(self.li)] = unit |
| | else: |
| | self.data["B" + str(self.li)] = str(val) |
| | if VERBOSE: |
| | if tp and unit: |
| | v = fs.format(FreeCAD.Units.Quantity(val, tp).getValueAs(unit).Value) |
| | print("TOTAL:" + 34 * " " + v + " " + unit) |
| | elif isinstance(val, str): |
| | if val.replace(".", "", 1).isdigit(): |
| | v = fs.format(val) |
| | print("TOTAL:" + 34 * " " + v) |
| | else: |
| | print("TOTAL:" + 34 * " " + val) |
| | else: |
| | v = fs.format(val) |
| | print("TOTAL:" + 34 * " " + v) |
| |
|
| | def update_from_elts(self, elts, val, unit, details): |
| | """Updates the spreadsheet data from IFC elements""" |
| |
|
| | if val.upper() == "COUNT": |
| | val = len(elts) |
| | if VERBOSE: |
| | print("COUNT:", val, "(", ",".join(["#" + str(e.id()) for e in elts]), ")") |
| | self.data["B" + str(self.li)] = str(val) |
| | if details: |
| | |
| | self.li += 1 |
| | self.data["A" + str(self.li)] = " " |
| | else: |
| | total = 0 |
| | for el in elts: |
| | if val in dir(el): |
| | elval = getattr(el, val, "") |
| | if isinstance(elval, tuple): |
| | if len(elval) == 1: |
| | elval = elval[0] |
| | elif len(elval) == 0: |
| | elval = "" |
| | if hasattr(elval, "is_a") and elval.is_a("IfcRelationship"): |
| | for att in dir(elval): |
| | if att.startswith("Relating"): |
| | targ = getattr(elval, att) |
| | if targ != el: |
| | elval = targ |
| | break |
| | elif att.startswith("Related"): |
| | if not elval in getattr(elval, att): |
| | elval = str(getattr(elval, att)) |
| | break |
| | if details: |
| | self.li += 1 |
| | name = el.Name if el.Name else "" |
| | self.data["A" + str(self.li)] = "#" + str(el.id()) + name |
| | self.data["B" + str(self.li)] = str(elval) |
| | if VERBOSE: |
| | print("#" + str(el.id()) + "." + val + " = " + str(elval)) |
| | if isinstance(elval, str) and elval.replace(".", "", 1).isdigit(): |
| | total += float(elval) |
| | elif isinstance(elval, (int, float)): |
| | total += elval |
| | if total: |
| | if details: |
| | self.li += 1 |
| | self.data["A" + str(self.li)] = "TOTAL" |
| | self.data["B" + str(self.li)] = str(total) |
| | if VERBOSE: |
| | print("TOTAL:", str(total)) |
| |
|
| | def create_ifc(self, obj, ifcfile, export=False): |
| | """Creates an IFC element for this object""" |
| |
|
| | from nativeifc import ifc_tools |
| |
|
| | proj = ifcfile.by_type("IfcProject")[0] |
| | elt = ifc_tools.api_run("root.create_entity", ifcfile, ifc_class="IfcControl") |
| | ifc_tools.set_attribute(ifcfile, elt, "Name", obj.Label) |
| | ifc_tools.api_run( |
| | "project.assign_declaration", ifcfile, definitions=[elt], relating_context=proj |
| | ) |
| | if not export: |
| | ifc_tools.add_properties(obj, ifcfile, elt) |
| | return elt |
| |
|
| | def save_ifc_props(self, obj, ifcfile=None, elt=None): |
| | """Saves the object data to IFC""" |
| |
|
| | from nativeifc import ifc_psets |
| |
|
| | ifc_psets.edit_pset( |
| | obj, "Operation", "::".join(obj.Operation), ifcfile=ifcfile, element=elt |
| | ) |
| | ifc_psets.edit_pset(obj, "Value", "::".join(obj.Value), ifcfile=ifcfile, element=elt) |
| | ifc_psets.edit_pset(obj, "Unit", "::".join(obj.Unit), ifcfile=ifcfile, element=elt) |
| | ifc_psets.edit_pset(obj, "Objects", "::".join(obj.Objects), ifcfile=ifcfile, element=elt) |
| | ifc_psets.edit_pset(obj, "Filter", "::".join(obj.Filter), ifcfile=ifcfile, element=elt) |
| |
|
| | def export_ifc(self, obj, ifcfile): |
| | """Exports the object to IFC (does not modify the FreeCAD object).""" |
| |
|
| | elt = self.create_ifc(obj, ifcfile, export=True) |
| | self.save_ifc_props(obj, ifcfile, elt) |
| | return elt |
| |
|
| | def dumps(self): |
| |
|
| | return self.Type |
| |
|
| | def loads(self, state): |
| |
|
| | if state: |
| | self.Type = state |
| |
|
| | def getIfcClass(self, obj): |
| | """gets the IFC class of this object""" |
| |
|
| | if hasattr(obj, "IfcType"): |
| | return obj.IfcType |
| | elif hasattr(obj, "IfcRole"): |
| | return obj.IfcRole |
| | elif hasattr(obj, "IfcClass"): |
| | return obj.IfcClass |
| | else: |
| | return None |
| |
|
| | def getArray(self, obj): |
| | "returns a count number if this object needs to be duplicated" |
| |
|
| | import Draft |
| |
|
| | elementCount = 0 |
| |
|
| | |
| | |
| | for o in obj.InList: |
| | if Draft.getType(o) == "Array": |
| | elementCount += o.Count |
| |
|
| | return elementCount |
| |
|
| | def expandArrays(self, objs): |
| | """Expands array elements in the given list of objects""" |
| |
|
| | expandedobjs = [] |
| |
|
| | for obj in objs: |
| | ifcClass = self.getIfcClass(obj) |
| | |
| | |
| | if ifcClass: |
| | expandedobjs.append(obj) |
| | |
| | |
| | array = self.getArray(obj) |
| | for i in range(1, array): |
| | expandedobjs.append(obj) |
| |
|
| | return expandedobjs |
| |
|
| |
|
| | class _ViewProviderArchSchedule: |
| | "A View Provider for Schedules" |
| |
|
| | def __init__(self, vobj): |
| | 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" |
| |
|
| | def isShow(self): |
| | return True |
| |
|
| | def attach(self, vobj): |
| | self.Object = vobj.Object |
| |
|
| | def setEdit(self, vobj, mode=0): |
| | if mode != 0: |
| | return None |
| |
|
| | self.taskd = ArchScheduleTaskPanel(vobj.Object) |
| | if not self.taskd.form.isVisible(): |
| | from PySide import QtCore |
| |
|
| | QtCore.QTimer.singleShot(100, self.showEditor) |
| | return True |
| |
|
| | def showEditor(self): |
| |
|
| | if hasattr(self, "taskd"): |
| | self.taskd.form.show() |
| |
|
| | def unsetEdit(self, vobj, mode): |
| | if mode != 0: |
| | return None |
| |
|
| | return True |
| |
|
| | def doubleClicked(self, vobj): |
| | self.edit() |
| | return True |
| |
|
| | def setupContextMenu(self, vobj, menu): |
| |
|
| | if FreeCADGui.activeWorkbench().name() != "BIMWorkbench": |
| | return |
| |
|
| | actionEdit = QtGui.QAction(translate("Arch", "Edit"), menu) |
| | QtCore.QObject.connect(actionEdit, QtCore.SIGNAL("triggered()"), self.edit) |
| | menu.addAction(actionEdit) |
| |
|
| | 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( |
| | actionToggleSpreadsheet, QtCore.SIGNAL("triggered()"), self.toggleSpreadsheet |
| | ) |
| | menu.addAction(actionToggleSpreadsheet) |
| |
|
| | def edit(self): |
| | FreeCADGui.ActiveDocument.setEdit(self.Object, 0) |
| |
|
| | def toggleSpreadsheet(self): |
| | self.Object.CreateSpreadsheet = not self.Object.CreateSpreadsheet |
| |
|
| | def claimChildren(self): |
| | if hasattr(self, "Object"): |
| | return [self.Object.Proxy.getSpreadSheet(self.Object)] |
| |
|
| | def dumps(self): |
| | return None |
| |
|
| | def loads(self, state): |
| | return None |
| |
|
| | def getDisplayModes(self, vobj): |
| | return ["Default"] |
| |
|
| | def getDefaultDisplayMode(self): |
| | return "Default" |
| |
|
| | def setDisplayMode(self, mode): |
| | return mode |
| |
|
| |
|
| | class ArchScheduleTaskPanel: |
| | """The editmode TaskPanel for Schedules""" |
| |
|
| | def __init__(self, obj=None): |
| | """Sets the panel up""" |
| |
|
| | self.obj = obj |
| | self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchSchedule.ui") |
| | self.form.setWindowIcon(QtGui.QIcon(":/icons/Arch_Schedule.svg")) |
| |
|
| | |
| | self.form.buttonAdd.setIcon(QtGui.QIcon(":/icons/list-add.svg")) |
| | self.form.buttonDel.setIcon(QtGui.QIcon(":/icons/list-remove.svg")) |
| | self.form.buttonClear.setIcon(QtGui.QIcon(":/icons/delete.svg")) |
| | self.form.buttonImport.setIcon(QtGui.QIcon(":/icons/document-open.svg")) |
| | self.form.buttonExport.setIcon(QtGui.QIcon(":/icons/document-save.svg")) |
| | self.form.buttonSelect.setIcon(QtGui.QIcon(":/icons/edit-select-all.svg")) |
| |
|
| | |
| | self.form.list.setColumnWidth(0, params.get_param_arch("ScheduleColumnWidth0")) |
| | self.form.list.setColumnWidth(1, params.get_param_arch("ScheduleColumnWidth1")) |
| | self.form.list.setColumnWidth(2, params.get_param_arch("ScheduleColumnWidth2")) |
| | self.form.list.setColumnWidth(3, params.get_param_arch("ScheduleColumnWidth3")) |
| | w = params.get_param_arch("ScheduleDialogWidth") |
| | h = params.get_param_arch("ScheduleDialogHeight") |
| | self.form.resize(w, h) |
| |
|
| | |
| | self.form.checkAutoUpdate.setChecked(PARAMS.GetBool("ScheduleAutoUpdate", False)) |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | self.form.buttonAdd.clicked.connect(self.add) |
| | self.form.buttonDel.clicked.connect(self.remove) |
| | self.form.buttonClear.clicked.connect(self.clear) |
| | self.form.buttonImport.clicked.connect(self.importCSV) |
| | self.form.buttonExport.clicked.connect(self.export) |
| | self.form.buttonSelect.clicked.connect(self.select) |
| | self.form.buttonBox.accepted.connect(self.accept) |
| | self.form.buttonBox.rejected.connect(self.reject) |
| | self.form.rejected.connect(self.reject) |
| | self.form.list.clearContents() |
| |
|
| | if self.obj: |
| | |
| | |
| | |
| | self.form.list.setRowCount(len(obj.Operation)) |
| | for i in range(5): |
| | for j in range(len(obj.Operation)): |
| | try: |
| | text = [obj.Operation, obj.Value, obj.Unit, obj.Objects, obj.Filter][i][j] |
| | except: |
| | text = "" |
| | item = QtGui.QTableWidgetItem(text) |
| | self.form.list.setItem(j, i, item) |
| | self.form.lineEditName.setText(self.obj.Label) |
| | self.form.checkSpreadsheet.setChecked(self.obj.CreateSpreadsheet) |
| | self.form.checkDetailed.setChecked(self.obj.DetailedResults) |
| | self.form.checkAutoUpdate.setChecked(self.obj.AutoUpdate) |
| |
|
| | |
| | mw = FreeCADGui.getMainWindow() |
| | self.form.move( |
| | mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center() |
| | ) |
| |
|
| | self.form.show() |
| |
|
| | def add(self): |
| | """Adds a new row below the last one""" |
| |
|
| | self.form.list.insertRow(self.form.list.currentRow() + 1) |
| |
|
| | def remove(self): |
| | """Removes the current row""" |
| |
|
| | if self.form.list.currentRow() >= 0: |
| | self.form.list.removeRow(self.form.list.currentRow()) |
| |
|
| | def clear(self): |
| | """Clears the list""" |
| |
|
| | self.form.list.clearContents() |
| | self.form.list.setRowCount(0) |
| |
|
| | def importCSV(self): |
| | """Imports a CSV file""" |
| |
|
| | filename = QtGui.QFileDialog.getOpenFileName( |
| | QtGui.QApplication.activeWindow(), |
| | translate("Arch", "Import CSV file"), |
| | None, |
| | "CSV files (*.csv *.CSV)", |
| | ) |
| | if filename: |
| | filename = filename[0] |
| | self.form.list.clearContents() |
| | import csv |
| |
|
| | with open(filename, "r") as csvfile: |
| | r = 0 |
| | for row in csv.reader(csvfile): |
| | self.form.list.insertRow(r) |
| | for i in range(5): |
| | if len(row) > i: |
| | t = row[i] |
| | |
| | |
| | self.form.list.setItem(r, i, QtGui.QTableWidgetItem(t)) |
| | r += 1 |
| |
|
| | def export(self): |
| | """Exports the results as MD or CSV""" |
| |
|
| | |
| | self.writeValues() |
| |
|
| | |
| | if not ("Up-to-date" in self.obj.State): |
| | self.obj.Proxy.execute(self.obj) |
| | if not hasattr(self.obj.Proxy, "data"): |
| | return |
| | if not self.obj.Proxy.data: |
| | return |
| |
|
| | filename = QtGui.QFileDialog.getSaveFileName( |
| | QtGui.QApplication.activeWindow(), |
| | translate("Arch", "Export CSV file"), |
| | None, |
| | "Comma-separated values (*.csv);;TAB-separated values (*.tsv);;Markdown (*.md)", |
| | ) |
| | if filename: |
| | filt = filename[1] |
| | filename = filename[0] |
| | |
| | if ( |
| | (not filename.lower().endswith(".csv")) |
| | and (not filename.lower().endswith(".tsv")) |
| | and (not filename.lower().endswith(".md")) |
| | ): |
| | if "csv" in filt: |
| | filename += ".csv" |
| | elif "tsv" in filt: |
| | filename += ".tsv" |
| | else: |
| | filename += ".md" |
| | if filename.lower().endswith(".csv"): |
| | self.exportCSV(filename, delimiter=",") |
| | elif filename.lower().endswith(".tsv"): |
| | self.exportCSV(filename, delimiter="\t") |
| | elif filename.lower().endswith(".md"): |
| | self.exportMD(filename) |
| | else: |
| | FreeCAD.Console.PrintError( |
| | translate("Arch", "Unable to recognize that file type") + ":" + filename + "\n" |
| | ) |
| |
|
| | def getRows(self): |
| | """get the rows that contain data""" |
| |
|
| | rows = [] |
| | if hasattr(self.obj.Proxy, "data") and self.obj.Proxy.data: |
| | for key in self.obj.Proxy.data.keys(): |
| | n = key[1:] |
| | if not n in rows: |
| | rows.append(n) |
| | rows.sort(key=int) |
| | return rows |
| |
|
| | def exportCSV(self, filename, delimiter="\t"): |
| | """Exports the results as a CSV/TSV file""" |
| |
|
| | import csv |
| |
|
| | with open(filename, "w") as csvfile: |
| | csvfile = csv.writer(csvfile, delimiter=delimiter) |
| | csvfile.writerow( |
| | [ |
| | translate("Arch", "Operation"), |
| | translate("Arch", "Value"), |
| | translate("Arch", "Unit"), |
| | ] |
| | ) |
| | if self.obj.DetailedResults: |
| | csvfile.writerow(["", "", ""]) |
| | for i in self.getRows(): |
| | r = [] |
| | for j in ["A", "B", "C"]: |
| | if j + i in self.obj.Proxy.data: |
| | r.append(str(self.obj.Proxy.data[j + i])) |
| | else: |
| | r.append("") |
| | csvfile.writerow(r) |
| | print("successfully exported ", filename) |
| |
|
| | def exportMD(self, filename): |
| | """Exports the results as a Markdown file""" |
| |
|
| | with open(filename, "w") as mdfile: |
| | mdfile.write( |
| | "| " |
| | + translate("Arch", "Operation") |
| | + " | " |
| | + translate("Arch", "Value") |
| | + " | " |
| | + translate("Arch", "Unit") |
| | + " |\n" |
| | ) |
| | mdfile.write("| --- | --- | --- |\n") |
| | if self.obj.DetailedResults: |
| | mdfile.write("| | | |\n") |
| | for i in self.getRows(): |
| | r = [] |
| | for j in ["A", "B", "C"]: |
| | if j + i in self.obj.Proxy.data: |
| | r.append(str(self.obj.Proxy.data[j + i])) |
| | else: |
| | r.append("") |
| | mdfile.write("| " + " | ".join(r) + " |\n") |
| | print("successfully exported ", filename) |
| |
|
| | def select(self): |
| | """Adds selected objects to current row""" |
| |
|
| | if self.form.list.currentRow() >= 0: |
| | sel = "" |
| | for o in FreeCADGui.Selection.getSelection(): |
| | if o != self.obj: |
| | if sel: |
| | sel += ";" |
| | sel += o.Name |
| | if sel: |
| | self.form.list.setItem(self.form.list.currentRow(), 3, QtGui.QTableWidgetItem(sel)) |
| |
|
| | def accept(self): |
| | """Saves the changes and closes the dialog""" |
| |
|
| | |
| | params.set_param_arch("ScheduleColumnWidth0", self.form.list.columnWidth(0)) |
| | params.set_param_arch("ScheduleColumnWidth1", self.form.list.columnWidth(1)) |
| | params.set_param_arch("ScheduleColumnWidth2", self.form.list.columnWidth(2)) |
| | params.set_param_arch("ScheduleColumnWidth3", self.form.list.columnWidth(3)) |
| | params.set_param_arch("ScheduleDialogWidth", self.form.width()) |
| | params.set_param_arch("ScheduleDialogHeight", self.form.height()) |
| |
|
| | |
| | PARAMS.SetBool("ScheduleAutoUpdate", self.form.checkAutoUpdate.isChecked()) |
| |
|
| | |
| | self.writeValues() |
| | self.form.hide() |
| | FreeCADGui.ActiveDocument.resetEdit() |
| | return True |
| |
|
| | def reject(self): |
| | """Close dialog without saving""" |
| |
|
| | self.form.hide() |
| | FreeCADGui.ActiveDocument.resetEdit() |
| | return True |
| |
|
| | def writeValues(self): |
| | """commits values and recalculate""" |
| |
|
| | if not self.obj: |
| | import Arch |
| |
|
| | self.obj = Arch.makeSchedule() |
| | lists = [[], [], [], [], []] |
| | for i in range(self.form.list.rowCount()): |
| | for j in range(5): |
| | cell = self.form.list.item(i, j) |
| | if cell: |
| | lists[j].append(cell.text()) |
| | else: |
| | lists[j].append("") |
| | FreeCAD.ActiveDocument.openTransaction("Edited Schedule") |
| | self.obj.Operation = lists[0] |
| | self.obj.Value = lists[1] |
| | self.obj.Unit = lists[2] |
| | self.obj.Objects = lists[3] |
| | self.obj.Filter = lists[4] |
| | 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() |
| |
|