# /********************************************************************** # * * # * Copyright (c) 2021 Javier Braña * # * * # * 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())