FreeCAD/src/Mod/Arch/ArchSchedule.py

393 lines
16 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, time
if FreeCAD.GuiUp:
import FreeCADGui, Arch_rc, os
from PySide import QtCore, QtGui
from DraftTools 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__ = "http://www.freecadweb.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):
taskd = _ArchScheduleTaskPanel()
FreeCADGui.Control.showDialog(taskd)
def IsActive(self):
if FreeCAD.ActiveDocument:
return True
else:
return False
class _ArchSchedule:
"the Arch Schedule object"
def __init__(self,obj):
obj.addProperty("App::PropertyStringList","Description","Arch",QT_TRANSLATE_NOOP("App::Property","The description column"))
obj.addProperty("App::PropertyStringList","Value", "Arch",QT_TRANSLATE_NOOP("App::Property","The values column"))
obj.addProperty("App::PropertyStringList","Unit", "Arch",QT_TRANSLATE_NOOP("App::Property","The units column"))
obj.addProperty("App::PropertyStringList","Objects", "Arch",QT_TRANSLATE_NOOP("App::Property","The objects column"))
obj.addProperty("App::PropertyStringList","Filter", "Arch",QT_TRANSLATE_NOOP("App::Property","The filter column"))
obj.addProperty("App::PropertyLink", "Result", "Arch",QT_TRANSLATE_NOOP("App::Property","The spreadsheet to print the results to"))
obj.Proxy = self
self.Type = "Schedule"
def execute(self,obj):
# fills columns A, B and C of the spreadsheet
if not obj.Description:
return
for p in [obj.Value,obj.Unit,obj.Objects,obj.Filter]:
if len(obj.Description) != len(p):
return
if not hasattr(obj,"Result"):
# silently fail on old schedule objects
return
if not obj.Result:
FreeCAD.Console.PrintError(translate("Arch","No spreadsheet attached to this schedule\n"))
return
obj.Result.clearAll()
obj.Result.set("A1","Description")
obj.Result.set("B1","Value")
obj.Result.set("C1","Unit")
obj.Result.setStyle('A1:C1', 'bold', 'add')
for i in range(len(obj.Description)):
if not obj.Description[i]:
# blank line
continue
# write description
obj.Result.set("A"+str(i+2),obj.Description[i].encode("utf8"))
if verbose:
l= "OPERATION: "+obj.Description[i]
print (l)
print (len(l)*"=")
# get list of 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]
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.getGroupContents(objs,walls=True,addgroups=True)
objs = Arch.pruneIncluded(objs,strict=True)
if obj.Filter[i]:
# apply filters
nobjs = []
for o in objs:
ok = True
for f in obj.Filter[i].split(";"):
args = [a.strip() for a in f.strip().split(":")]
if args[0].upper() == "NAME":
if not(args[1].upper() in o.Name.upper()):
ok = False
elif args[0].upper() == "!NAME":
if (args[1].upper() in o.Name.upper()):
ok = False
elif args[0].upper() == "LABEL":
if not(args[1].upper() in o.Label.upper()):
ok = False
elif args[0].upper() == "!LABEL":
if args[1].upper() in o.Label.upper():
ok = False
elif args[0].upper() == "TYPE":
if Draft.getType(o).upper() != args[1].upper():
ok = False
elif args[0].upper() == "!TYPE":
if Draft.getType(o).upper() == args[1].upper():
ok = False
elif args[0].upper() == "ROLE":
if hasattr(o,"Role"):
if o.Role.upper() != args[1].upper():
ok = False
else:
ok = False
elif args[0].upper() == "!ROLE":
if hasattr(o,"Role"):
if o.Role.upper() == args[1].upper():
ok = False
if ok:
nobjs.append(o)
objs = nobjs
# perform operation
if val.upper() == "COUNT":
val = len(objs)
if verbose:
print (val, ",".join([o.Label for o in objs]))
obj.Result.set("B"+str(i+2),str(val))
else:
vals = val.split(".")
sumval = 0
for o in objs:
if verbose:
l = o.Name+" ("+o.Label+"):"
print (l+(40-len(l))*" ",)
try:
d = o
for v in vals[1:]:
d = getattr(d,v)
if verbose:
print (d)
if hasattr(d,"Value"):
d = d.Value
except:
FreeCAD.Console.PrintWarning(translate("Arch","Unable to retrieve value from object")+": "+o.Name+"."+".".join(vals)+"\n")
else:
if not sumval:
sumval = d
else:
sumval += d
val = sumval
# get unit
if obj.Unit[i]:
ustr = obj.Unit[i].encode("utf8")
unit = ustr.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
q = FreeCAD.Units.Quantity(val,tp)
obj.Result.set("B"+str(i+2),str(q.getValueAs(unit).Value))
obj.Result.set("C"+str(i+2),ustr)
else:
obj.Result.set("B"+str(i+2),str(val))
if verbose:
print ("TOTAL:"+34*" "+str(val))
def __getstate__(self):
return self.Type
def __setstate__(self,state):
if state:
self.Type = state
class _ViewProviderArchSchedule:
"A View Provider for Schedules"
def __init__(self,vobj):
vobj.Proxy = self
def getIcon(self):
import Arch_rc
return ":/icons/Arch_Schedule.svg"
def attach(self, vobj):
self.Object = vobj.Object
def setEdit(self,vobj,mode):
taskd = _ArchScheduleTaskPanel(vobj.Object)
FreeCADGui.Control.showDialog(taskd)
return True
def doubleClicked(self,vobj):
taskd = _ArchScheduleTaskPanel(vobj.Object)
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self,vobj,mode):
FreeCADGui.Control.closeDialog()
return
def claimChildren(self):
if hasattr(self,"Object"):
return [self.Object.Result]
def __getstate__(self):
return None
def __setstate__(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):
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchSchedule.ui")
self.form.setWindowIcon(QtGui.QIcon(":/icons/Arch_Schedule.svg"))
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.exportCSV)
QtCore.QObject.connect(self.form.buttonSelect, QtCore.SIGNAL("clicked()"), self.select)
self.form.list.clearContents()
if self.obj:
if not obj.Description:
return
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)
def add(self):
self.form.list.insertRow(self.form.list.currentRow()+1)
def remove(self):
if self.form.list.currentRow() >= 0:
self.form.list.removeRow(self.form.list.currentRow())
def clear(self):
self.form.list.clearContents()
self.form.list.setRowCount(0)
def importCSV(self):
filename = QtGui.QFileDialog.getOpenFileName(QtGui.qApp.activeWindow(), translate("Arch","Import CSV File"), None, "CSV file (*.csv)");
if filename:
self.form.list.clearContents()
import csv
with open(filename[0], 'rb') 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 exportCSV(self):
if self.obj:
if self.obj.Result:
filename = QtGui.QFileDialog.getSaveFileName(QtGui.qApp.activeWindow(), translate("Arch","Export CSV File"), None, "CSV file (*.csv)");
if filename:
# the following line crashes, couldn't fnid out why
# self.obj.Result.exportFile(str(filename[0].encode("utf8")))
import csv
if not("Up-to-date" in self.obj.State):
self.obj.Proxy.execute(self.obj)
numrows = len(self.obj.Description)+1
with open(filename[0].encode("utf8"), 'wb') as csvfile:
csvfile = csv.writer(csvfile,delimiter="\t")
for i in range(numrows):
r = []
for j in ["A","B","C"]:
r.append(self.obj.Result.getContents(j+str(i+1)))
csvfile.writerow(r)
print "successfully exported ",filename[0]
def select(self):
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):
if not self.obj:
import Spreadsheet
self.obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Schedule")
self.obj.Label = translate("Arch","Schedule")
_ArchSchedule(self.obj)
sp = FreeCAD.ActiveDocument.addObject("Spreadsheet::Sheet","Result")
self.obj.Result = sp
if FreeCAD.GuiUp:
_ViewProviderArchSchedule(self.obj.ViewObject)
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("")
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]
FreeCAD.ActiveDocument.recompute()
return True
if FreeCAD.GuiUp:
FreeCADGui.addCommand('Arch_Schedule',_CommandArchSchedule())