345 lines
14 KiB
Python
345 lines
14 KiB
Python
# /**********************************************************************
|
|
# * *
|
|
# * Copyright (c) 2021 Javier Braña <javier.branagutierrez@gmail.com> *
|
|
# * *
|
|
# * 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
|
|
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui, os
|
|
from PySide import QtCore
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
else:
|
|
# \cond
|
|
def translate(ctxt, txt):
|
|
return txt
|
|
def QT_TRANSLATE_NOOP(ctxt, txt):
|
|
return txt
|
|
# \endcond
|
|
|
|
try:
|
|
_fromUtf8 = QtCore.QString.fromUtf8
|
|
except AttributeError:
|
|
def _fromUtf8(s):
|
|
return s
|
|
|
|
import openpyxl
|
|
from openpyxl.styles import Alignment, Border, Side, PatternFill, Font
|
|
import PVPlantResources
|
|
import PVPlantSite
|
|
|
|
# Estilos:
|
|
thin = Side(border_style="thin", color="7DA4B8")
|
|
double = Side(border_style="double", color="ff0000")
|
|
border_thin = Border(top=thin, left=thin, right=thin, bottom=thin)
|
|
border_fat = Border(top=thin, left=thin, right=thin, bottom=thin)
|
|
# fill = PatternFill("solid", fgColor="DDDDDD")
|
|
# fill = GradientFill(stop=("000000", "FFFFFF"))
|
|
|
|
scale = 0.001 # milimeters to meter
|
|
|
|
def style_range(ws, cell_range, border=Border(), fill=None, font=None, alignment=None):
|
|
"""
|
|
Apply styles to a range of cells as if they were a single cell.
|
|
|
|
:param ws: Excel worksheet instance
|
|
:param range: An excel range to style (e.g. A1:F20)
|
|
:param border: An openpyxl Border
|
|
:param fill: An openpyxl PatternFill or GradientFill
|
|
:param font: An openpyxl Font object
|
|
"""
|
|
|
|
top = Border(top=border.top)
|
|
left = Border(left=border.left)
|
|
right = Border(right=border.right)
|
|
bottom = Border(bottom=border.bottom)
|
|
|
|
first_cell = ws[cell_range.split(":")[0]]
|
|
if alignment:
|
|
first_cell.alignment = alignment
|
|
|
|
rows = ws[cell_range]
|
|
if font:
|
|
first_cell.font = font
|
|
|
|
for cell in rows[0]:
|
|
cell.border = cell.border + top
|
|
for cell in rows[-1]:
|
|
cell.border = cell.border + bottom
|
|
|
|
for row in rows:
|
|
l = row[0]
|
|
r = row[-1]
|
|
l.border = l.border + left
|
|
r.border = r.border + right
|
|
if fill:
|
|
for c in row:
|
|
c.fill = fill
|
|
|
|
def spreadsheetBOQFrames(sheet, sel):
|
|
sheet['A1'] = 'Index'
|
|
sheet['B1'] = 'Frame'
|
|
sheet['C1'] = 'Frame Type'
|
|
sheet['D1'] = 'X'
|
|
sheet['E1'] = 'Y'
|
|
sheet['F1'] = 'Z'
|
|
sheet['G1'] = 'Angle N-S'
|
|
sheet['H1'] = 'Angle L-W'
|
|
sheet['I1'] = 'Nº Poles'
|
|
|
|
sheet.column_dimensions['A'].width = 8
|
|
sheet.column_dimensions['B'].width = 30
|
|
sheet.column_dimensions['C'].width = 20
|
|
sheet.column_dimensions['D'].width = 20
|
|
sheet.column_dimensions['E'].width = 20
|
|
sheet.column_dimensions['F'].width = 20
|
|
sheet.column_dimensions['G'].width = 15
|
|
sheet.column_dimensions['H'].width = 15
|
|
sheet.column_dimensions['I'].width = 15
|
|
sheet.row_dimensions[1].height = 40
|
|
|
|
style_range(sheet, 'A1:I1',
|
|
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
|
fill=PatternFill("solid", fgColor="7DA4B8"),
|
|
font=Font(name='Quicksand', size=10, b=True, color="FFFFFF"),
|
|
alignment=Alignment(horizontal="center", vertical="center"))
|
|
|
|
for ind in range(0, len(sel)):
|
|
row = ind + 2
|
|
sheet['A{0}'.format(row)] = ind + 1
|
|
sheet['B{0}'.format(row)] = sel[ind].Label
|
|
sheet['C{0}'.format(row)] = sel[ind].Setup.Label
|
|
sheet['D{0}'.format(row)] = sel[ind].Placement.Base.x * scale
|
|
sheet['E{0}'.format(row)] = sel[ind].Placement.Base.y * scale
|
|
sheet['R{0}'.format(row)] = sel[ind].Placement.Base.z * scale
|
|
sheet['G{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[0]
|
|
sheet['H{0}'.format(row)] = sel[ind].Placement.Rotation.toEuler()[1]
|
|
sheet['I{0}'.format(row)] = sel[ind].Setup.NumberPole.Value
|
|
style_range(sheet, 'A' + str(row) + ':I' + str(row),
|
|
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
|
font=Font(name='Quicksand', size=10),
|
|
alignment=Alignment(horizontal="center", vertical="center"))
|
|
|
|
def spreadsheetBOQPoles(sheet, sel):
|
|
import MeshPart as mp
|
|
from Mechanical.Frame import PVPlantFrame
|
|
|
|
# Data:
|
|
terrain = PVPlantSite.get().Terrain.Mesh # Shape
|
|
|
|
# Headers:
|
|
sheet['A1'] = 'Frame'
|
|
sheet['B1'] = 'Pole'
|
|
sheet['C1'] = 'Pole Type'
|
|
sheet['D1'] = 'X'
|
|
sheet['E1'] = 'Y'
|
|
sheet['F1'] = 'Z frame attach'
|
|
sheet['G1'] = 'Z aerial head'
|
|
sheet['H1'] = 'Pole length'
|
|
sheet['I1'] = 'Pole aerial length'
|
|
sheet['J1'] = 'Pole terrain enter length'
|
|
|
|
sheet.column_dimensions['A'].width = 30
|
|
sheet.column_dimensions['B'].width = 8
|
|
sheet.column_dimensions['C'].width = 20
|
|
sheet.column_dimensions['D'].width = 20
|
|
sheet.column_dimensions['E'].width = 20
|
|
sheet.column_dimensions['F'].width = 20
|
|
sheet.column_dimensions['G'].width = 20
|
|
sheet.column_dimensions['H'].width = 20
|
|
sheet.column_dimensions['I'].width = 20
|
|
sheet.column_dimensions['J'].width = 20
|
|
sheet.row_dimensions[1].height = 40
|
|
style_range(sheet, 'A1:J1',
|
|
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
|
fill=PatternFill("solid", fgColor="7DA4B8"),
|
|
font=Font(name='Quicksand', size=11, b=True, color="FFFFFF"),
|
|
alignment=Alignment(horizontal="center", vertical="center"))
|
|
sheet['A2'] = ""
|
|
sheet.row_dimensions[2].height = 5
|
|
|
|
data = {"Frame": [],
|
|
#"FrameType": [],
|
|
"Pole": [],
|
|
"PoleType": [],
|
|
"PoleLength": [],
|
|
"Center": [],
|
|
"Head": []}
|
|
|
|
cnt = 0
|
|
for frame_ind, frame in enumerate(sel):
|
|
poles = frame.Shape.SubShapes[1].SubShapes[0].SubShapes
|
|
numpoles = int(frame.Setup.NumberPole.Value)
|
|
seq = frame.Setup.PoleSequence
|
|
if len(seq) < numpoles:
|
|
seq = PVPlantFrame.getarray(frame.Setup.PoleSequence, numpoles)
|
|
for pole_ind in range(numpoles):
|
|
pole = poles[pole_ind]
|
|
poletype = frame.Setup.PoleType[seq[pole_ind]]
|
|
data["Frame"].append(frame.Label)
|
|
#data["FrameType"].append(frame.Setup.Label)
|
|
data["Pole"].append(pole_ind + 1)
|
|
data["PoleType"].append(poletype.Label)
|
|
data["PoleLength"].append(int(poletype.Height))
|
|
data["Center"].append(pole.BoundBox.Center)
|
|
data["Head"].append(pole.BoundBox.ZMax)
|
|
cnt += 1
|
|
|
|
pts = mp.projectPointsOnMesh(data["Center"], terrain, FreeCAD.Vector(0, 0, 1))
|
|
#if cnt == len(pts):
|
|
data["Soil"] = pts
|
|
|
|
row = 3
|
|
group_from = row
|
|
f = data["Frame"][0]
|
|
for i in range(0, len(data["Frame"])):
|
|
if f != data["Frame"][i]:
|
|
style_range(sheet, 'A' + str(group_from) + ':F' + str(row - 1),
|
|
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
|
font=Font(name='Quicksand', size=11, ),
|
|
alignment=Alignment(horizontal="center", vertical="center"))
|
|
sheet.merge_cells('A' + str(group_from) + ':A' + str(row - 1))
|
|
style_range(sheet, 'A' + str(group_from) + ':A' + str(row - 1),
|
|
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
|
font=Font(name='Quicksand', size=11, ),
|
|
alignment=Alignment(horizontal="center", vertical="center"))
|
|
#sheet['A{0}'.format(row)] = ""
|
|
sheet.row_dimensions[row].height = 5
|
|
row += 1
|
|
f = data["Frame"][i]
|
|
group_from = row
|
|
|
|
sheet['A{0}'.format(row)] = data['Frame'][i]
|
|
sheet['B{0}'.format(row)] = data['Pole'][i]
|
|
sheet['C{0}'.format(row)] = data['PoleType'][i]
|
|
sheet['D{0}'.format(row)] = round(data['Center'][i].x, 0) * scale
|
|
sheet['D{0}'.format(row)].number_format = "0.000"
|
|
sheet['E{0}'.format(row)] = round(data['Center'][i].y, 0) * scale
|
|
sheet['E{0}'.format(row)].number_format = "0.000"
|
|
try:
|
|
sheet['F{0}'.format(row)] = round(data['Soil'][i].z, 0) * scale
|
|
sheet['F{0}'.format(row)].number_format = "0.000"
|
|
except:
|
|
pass
|
|
sheet['G{0}'.format(row)] = round(data['Head'][i]) * scale
|
|
sheet['G{0}'.format(row)].number_format = "0.000"
|
|
sheet['H{0}'.format(row)] = data["PoleLength"][i] * scale
|
|
sheet['H{0}'.format(row)].number_format = "0.000"
|
|
sheet['I{0}'.format(row)] = '=G{0}-F{0}'.format(row)
|
|
sheet['I{0}'.format(row)].number_format = "0.000"
|
|
sheet['J{0}'.format(row)] = '=H{0}-I{0}'.format(row)
|
|
sheet['J{0}'.format(row)].number_format = "0.000"
|
|
|
|
style_range(sheet, 'A' + str(row) + ':J' + str(row),
|
|
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
|
font=Font(name='Quicksand', size=11,),
|
|
alignment=Alignment(horizontal="center", vertical="center"))
|
|
row += 1
|
|
|
|
def spreadsheetBOQPanelCollision(sheet, sel):
|
|
# Headers:
|
|
sheet['A1'] = 'Frame'
|
|
sheet['B1'] = 'Nombre'
|
|
sheet['C1'] = 'X'
|
|
sheet['D1'] = 'Y'
|
|
sheet['E1'] = 'Z'
|
|
sheet['G1'] = 'Ángulo E-O'
|
|
sheet['H1'] = 'Nº Hincas'
|
|
|
|
sheet.column_dimensions['A'].width = 30
|
|
sheet.column_dimensions['B'].width = 8
|
|
sheet.column_dimensions['C'].width = 20
|
|
sheet.column_dimensions['D'].width = 20
|
|
sheet.column_dimensions['E'].width = 20
|
|
sheet.column_dimensions['F'].width = 20
|
|
sheet.column_dimensions['G'].width = 20
|
|
sheet.column_dimensions['H'].width = 20
|
|
sheet.column_dimensions['I'].width = 20
|
|
sheet.row_dimensions[1].height = 40
|
|
style_range(sheet, 'A1:I1',
|
|
border=Border(top=thin, left=thin, right=thin, bottom=thin),
|
|
fill=PatternFill("solid", fgColor="7DA4B8"),
|
|
font=Font(name='Quicksand', size=11, b=True, color="FFFFFF"),
|
|
alignment=Alignment(horizontal="center", vertical="center"))
|
|
sheet['A2'] = ""
|
|
sheet.row_dimensions[2].height = 5
|
|
|
|
# Data:
|
|
for frame_ind in range(0, len(sel)):
|
|
frame = sel[frame_ind]
|
|
sheet['A{0}'.format(ind + 2)] = ind
|
|
sheet['B{0}'.format(ind + 2)] = sel[ind].Label
|
|
sheet['C{0}'.format(ind + 2)] = sel[ind].Placement.Base.x * scale
|
|
sheet['D{0}'.format(ind + 2)] = sel[ind].Placement.Base.y * scale
|
|
sheet['E{0}'.format(ind + 2)] = sel[ind].Placement.Base.z * scale
|
|
sheet['F{0}'.format(ind + 2)] = sel[ind].Placement.Rotation.toEuler()[0]
|
|
sheet['G{0}'.format(ind + 2)] = sel[ind].Placement.Rotation.toEuler()[1]
|
|
sheet['H{0}'.format(ind + 2)] = sel[ind].NumberPole.Value
|
|
|
|
class CommandBOQMechanical:
|
|
def GetResources(self):
|
|
return {'Pixmap': str(os.path.join(PVPlantResources.DirIcons, "boqm.svg")),
|
|
'Accel': "R, M",
|
|
'MenuText': QT_TRANSLATE_NOOP("Placement", "BOQ Mecánico"),
|
|
'ToolTip': QT_TRANSLATE_NOOP("Placement", "Calcular el BOQ de la")}
|
|
|
|
def Activated(self):
|
|
# make file global:
|
|
#sel = FreeCAD.ActiveDocument.findObjects(Name="Tracker")
|
|
sel = []
|
|
for obj in FreeCAD.ActiveDocument.Objects:
|
|
'''if not hasattr(obj, "Proxy"):
|
|
continue
|
|
if issubclass(obj.Proxy.__class__, PVPlantRack.Frame):
|
|
objects.append(obj)'''
|
|
if obj.Name.startswith("Tracker") and not obj.Name.startswith("TrackerSetup"):
|
|
sel.append(obj)
|
|
sel = sorted(sel, key=lambda x: x.Label)
|
|
if len(sel) > 0:
|
|
path = os.path.dirname(FreeCAD.ActiveDocument.FileName)
|
|
filename = os.path.join(path, "BOQMechanical.xlsx")
|
|
mywb = openpyxl.Workbook()
|
|
sheet = mywb.active
|
|
sheet.title = 'Frames information'
|
|
spreadsheetBOQFrames(sheet, sel)
|
|
|
|
sheet = mywb.create_sheet("Poles information")
|
|
spreadsheetBOQPoles(sheet, sel)
|
|
mywb.save(filename)
|
|
|
|
print("Se ha generado el BOQMechanical: ")
|
|
print(filename)
|
|
'''import sys
|
|
path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
|
|
sys.path.append(path)'''
|
|
|
|
import subprocess
|
|
subprocess.Popen('explorer ' + path)
|
|
|
|
def IsActive(self):
|
|
if FreeCAD.ActiveDocument:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
'''if FreeCAD.GuiUp:
|
|
FreeCADGui.addCommand('BOQMechanical', CommandBOQMechanical())'''
|