Additionally 2 Arch_Window bugs were fixed: * If the W1 value was changed the box tracker was not repositioned relative to the cursor. * The WindowColor was not applied because of a typo in the code. De current default color is quite dark BTW. Note that all dimensional values that were not really defaults, but just the last entered values, have been removed from preferences-archdefaults.ui. As a result the layout looks a bit strange. That will be improved in a next PR.
773 lines
30 KiB
Python
773 lines
30 KiB
Python
# -*- coding: utf8 -*-
|
|
#***************************************************************************
|
|
#* Copyright (c) 2015 Yorik van Havre <yorik@uncreated.net> *
|
|
#* *
|
|
#* This program is free software; you can redistribute it and/or modify *
|
|
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
#* as published by the Free Software Foundation; either version 2 of *
|
|
#* the License, or (at your option) any later version. *
|
|
#* for detail see the LICENCE text file. *
|
|
#* *
|
|
#* This program is distributed in the hope that it will be useful, *
|
|
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
#* GNU Library General Public License for more details. *
|
|
#* *
|
|
#* You should have received a copy of the GNU Library General Public *
|
|
#* License along with this program; if not, write to the Free Software *
|
|
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
#* USA *
|
|
#* *
|
|
#***************************************************************************
|
|
|
|
import FreeCAD
|
|
from draftutils import params
|
|
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui
|
|
from PySide import QtCore, QtGui
|
|
from draftutils.translate import translate
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
else:
|
|
# \cond
|
|
def translate(ctxt,txt):
|
|
return txt
|
|
def QT_TRANSLATE_NOOP(ctxt,txt):
|
|
return txt
|
|
# \endcond
|
|
|
|
## @package ArchSchedule
|
|
# \ingroup ARCH
|
|
# \brief The Schedule object and tools
|
|
#
|
|
# This module provides tools to build Schedule objects.
|
|
# Schedules are objects that can count and gather information
|
|
# about objects in the document, and fill a spreadsheet with the result
|
|
|
|
__title__ = "Arch Schedule"
|
|
__author__ = "Yorik van Havre"
|
|
__url__ = "https://www.freecad.org"
|
|
|
|
|
|
verbose = True # change this for silent recomputes
|
|
|
|
|
|
|
|
class CommandArchSchedule:
|
|
|
|
"the Arch Schedule command definition"
|
|
|
|
def GetResources(self):
|
|
return {'Pixmap': 'Arch_Schedule',
|
|
'MenuText': QT_TRANSLATE_NOOP("Arch_Schedule","Schedule"),
|
|
'ToolTip': QT_TRANSLATE_NOOP("Arch_Schedule","Creates a schedule to collect data from the model")}
|
|
|
|
def Activated(self):
|
|
if hasattr(self,"taskd"):
|
|
if self.taskd:
|
|
self.taskd.form.hide()
|
|
self.taskd = ArchScheduleTaskPanel()
|
|
|
|
def IsActive(self):
|
|
if FreeCAD.ActiveDocument:
|
|
return True
|
|
else:
|
|
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:
|
|
|
|
"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)
|
|
|
|
def update_properties_0v21(self,obj):
|
|
sp = obj.Result
|
|
if sp is not None:
|
|
self.setSchedulePropertySpreadsheet(sp, obj)
|
|
obj.removeProperty("Result")
|
|
from draftutils.messages import _wrn
|
|
_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):
|
|
|
|
if not "Description" in obj.PropertiesList:
|
|
obj.addProperty("App::PropertyStringList","Description", "Arch",QT_TRANSLATE_NOOP("App::Property","The description column"))
|
|
if not "Value" in obj.PropertiesList:
|
|
obj.addProperty("App::PropertyStringList","Value", "Arch",QT_TRANSLATE_NOOP("App::Property","The values column"))
|
|
if not "Unit" in obj.PropertiesList:
|
|
obj.addProperty("App::PropertyStringList","Unit", "Arch",QT_TRANSLATE_NOOP("App::Property","The units column"))
|
|
if not "Objects" in obj.PropertiesList:
|
|
obj.addProperty("App::PropertyStringList","Objects", "Arch",QT_TRANSLATE_NOOP("App::Property","The objects column"))
|
|
if not "Filter" in obj.PropertiesList:
|
|
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 "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, 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:
|
|
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)
|
|
sp.clearAll()
|
|
# clearAll removes the custom property, we need to re-add it:
|
|
self.setSchedulePropertySpreadsheet(sp, obj)
|
|
# set headers
|
|
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():
|
|
sp.set(k,v)
|
|
# 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):
|
|
|
|
# verify the data
|
|
|
|
if not obj.Description:
|
|
# empty description column
|
|
return
|
|
for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]:
|
|
# different number of items in each column
|
|
if len(obj.Description) != len(p):
|
|
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
|
|
|
|
for i in range(len(obj.Description)):
|
|
li += 1
|
|
if not obj.Description[i]:
|
|
# blank line
|
|
continue
|
|
# write description
|
|
self.data["A"+str(li)] = obj.Description[i]
|
|
if verbose:
|
|
l= "OPERATION: "+obj.Description[i]
|
|
print("")
|
|
print (l)
|
|
print (len(l)*"=")
|
|
|
|
# build set of valid objects
|
|
|
|
objs = obj.Objects[i]
|
|
val = obj.Value[i]
|
|
if val:
|
|
import Draft,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:
|
|
objs = FreeCAD.ActiveDocument.Objects
|
|
if len(objs) == 1:
|
|
# remove object itself if the object is a group
|
|
if objs[0].isDerivedFrom("App::DocumentObjectGroup"):
|
|
objs = objs[0].Group
|
|
objs = Draft.get_group_contents(objs)
|
|
objs = Arch.pruneIncluded(objs,strict=True)
|
|
# 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 = []
|
|
for o in objs:
|
|
props = [p.upper() for p in o.PropertiesList]
|
|
ok = True
|
|
for f in obj.Filter[i].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)
|
|
objs = nobjs
|
|
|
|
# perform operation: count or retrieve property
|
|
|
|
if val.upper() == "COUNT":
|
|
val = len(objs)
|
|
if verbose:
|
|
print (val, ",".join([o.Label for o in objs]))
|
|
self.data["B"+str(li)] = str(val)
|
|
if obj.DetailedResults:
|
|
# additional blank line...
|
|
li += 1
|
|
self.data["A"+str(li)] = " "
|
|
else:
|
|
vals = val.split(".")
|
|
if vals[0][0].islower():
|
|
# old-style: first member is not a property
|
|
vals = vals[1:]
|
|
sumval = 0
|
|
|
|
# get unit
|
|
tp = None
|
|
unit = None
|
|
q = None
|
|
if obj.Unit[i]:
|
|
unit = obj.Unit[i]
|
|
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
|
|
|
|
# format value
|
|
dv = params.get_param("Decimals",path="Units")
|
|
fs = "{:."+str(dv)+"f}" # format string
|
|
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:
|
|
FreeCAD.Console.PrintWarning(translate("Arch","Unable to retrieve value from object")+": "+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)
|
|
else:
|
|
print(fs.format(d))
|
|
if obj.DetailedResults:
|
|
li += 1
|
|
self.data["A"+str(li)] = o.Name+" ("+o.Label+")"
|
|
if tp and unit:
|
|
q = FreeCAD.Units.Quantity(d,tp)
|
|
self.data["B"+str(li)] = str(q.getValueAs(unit).Value)
|
|
self.data["C"+str(li)] = unit
|
|
else:
|
|
self.data["B"+str(li)] = str(d)
|
|
|
|
if not sumval:
|
|
sumval = d
|
|
else:
|
|
sumval += d
|
|
val = sumval
|
|
if tp:
|
|
q = FreeCAD.Units.Quantity(val,tp)
|
|
|
|
# write data
|
|
if obj.DetailedResults:
|
|
li += 1
|
|
self.data["A"+str(li)] = "TOTAL"
|
|
if q and unit:
|
|
self.data["B"+str(li)] = str(q.getValueAs(unit).Value)
|
|
self.data["C"+str(li)] = unit
|
|
else:
|
|
self.data["B"+str(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)
|
|
else:
|
|
v = fs.format(val)
|
|
print("TOTAL:"+34*" "+v)
|
|
self.setSpreadsheetData(obj)
|
|
|
|
def dumps(self):
|
|
|
|
return self.Type
|
|
|
|
def loads(self,state):
|
|
|
|
if state:
|
|
self.Type = state
|
|
|
|
|
|
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)
|
|
return True
|
|
|
|
def unsetEdit(self, vobj, mode):
|
|
if mode != 0:
|
|
return None
|
|
|
|
return True
|
|
|
|
def doubleClicked(self, vobj):
|
|
self.edit()
|
|
|
|
def setupContextMenu(self, vobj, menu):
|
|
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"))
|
|
|
|
# set icons
|
|
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"))
|
|
|
|
# restore widths
|
|
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)
|
|
|
|
# set delegate - Not using custom delegates for now...
|
|
#self.form.list.setItemDelegate(ScheduleDelegate())
|
|
#self.form.list.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked)
|
|
|
|
# connect slots
|
|
QtCore.QObject.connect(self.form.buttonAdd, QtCore.SIGNAL("clicked()"), self.add)
|
|
QtCore.QObject.connect(self.form.buttonDel, QtCore.SIGNAL("clicked()"), self.remove)
|
|
QtCore.QObject.connect(self.form.buttonClear, QtCore.SIGNAL("clicked()"), self.clear)
|
|
QtCore.QObject.connect(self.form.buttonImport, QtCore.SIGNAL("clicked()"), self.importCSV)
|
|
QtCore.QObject.connect(self.form.buttonExport, QtCore.SIGNAL("clicked()"), self.export)
|
|
QtCore.QObject.connect(self.form.buttonSelect, QtCore.SIGNAL("clicked()"), self.select)
|
|
QtCore.QObject.connect(self.form.buttonBox, QtCore.SIGNAL("accepted()"), self.accept)
|
|
QtCore.QObject.connect(self.form.buttonBox, QtCore.SIGNAL("rejected()"), self.reject)
|
|
QtCore.QObject.connect(self.form, QtCore.SIGNAL("rejected()"), self.reject)
|
|
self.form.list.clearContents()
|
|
|
|
if self.obj:
|
|
for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]:
|
|
if len(obj.Description) != len(p):
|
|
return
|
|
self.form.list.setRowCount(len(obj.Description))
|
|
for i in range(5):
|
|
for j in range(len(obj.Description)):
|
|
item = QtGui.QTableWidgetItem([obj.Description,obj.Value,obj.Unit,obj.Objects,obj.Filter][i][j])
|
|
self.form.list.setItem(j,i,item)
|
|
self.form.lineEditName.setText(self.obj.Label)
|
|
self.form.checkDetailed.setChecked(self.obj.DetailedResults)
|
|
self.form.checkSpreadsheet.setChecked(self.obj.CreateSpreadsheet)
|
|
|
|
# center over FreeCAD window
|
|
mw = FreeCADGui.getMainWindow()
|
|
self.form.move(mw.frameGeometry().topLeft() + mw.rect().center() - self.form.rect().center())
|
|
|
|
# maintain above FreeCAD window
|
|
self.form.setWindowFlags(self.form.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
|
|
|
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]
|
|
#t = t.replace("²","^2")
|
|
#t = t.replace("³","^3")
|
|
self.form.list.setItem(r,i,QtGui.QTableWidgetItem(t))
|
|
r += 1
|
|
|
|
def export(self):
|
|
|
|
"""Exports the results as MD or CSV"""
|
|
|
|
# commit latest changes
|
|
self.writeValues()
|
|
|
|
# tests
|
|
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]
|
|
# add missing extension
|
|
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","Description"),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","Description")+" | "+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"""
|
|
|
|
# store widths
|
|
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())
|
|
|
|
# commit values
|
|
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:
|
|
self.obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Schedule")
|
|
self.obj.Label = translate("Arch","Schedule")
|
|
_ArchSchedule(self.obj)
|
|
if FreeCAD.GuiUp:
|
|
_ViewProviderArchSchedule(self.obj.ViewObject)
|
|
if hasattr(self.obj,"CreateSpreadsheet") and self.obj.CreateSpreadsheet:
|
|
self.obj.Proxy.getSpreadSheet(self.obj, force=True)
|
|
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.Description = 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()
|
|
|
|
|
|
if FreeCAD.GuiUp:
|
|
FreeCADGui.addCommand('Arch_Schedule',CommandArchSchedule())
|