# SPDX-License-Identifier: LGPL-2.1-or-later # *************************************************************************** # * * # * Copyright (c) 2011 Yorik van Havre * # * * # * This file is part of FreeCAD. * # * * # * FreeCAD is free software: you can redistribute it and/or modify it * # * under the terms of the GNU Lesser General Public License as * # * published by the Free Software Foundation, either version 2.1 of the * # * License, or (at your option) any later version. * # * * # * FreeCAD 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 * # * Lesser General Public License for more details. * # * * # * You should have received a copy of the GNU Lesser General Public * # * License along with FreeCAD. If not, see * # * . * # * * # *************************************************************************** __title__ = "FreeCAD Profile" __author__ = "Yorik van Havre" __url__ = "https://www.freecad.org" ## @package ArchProfile # \ingroup ARCH # \brief Profile tools for ArchStructure # # This module provides tools to build base profiles # for Arch Structure elements import csv import os import FreeCAD import Draft from FreeCAD import Vector 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: # \cond def translate(ctxt, txt): return txt def QT_TRANSLATE_NOOP(ctxt, txt): return txt # \endcond # Presets in the form: Class, Name, Profile type, [profile data] # Search for profiles.csv in data/Mod/Arch/Presets and in the same folder as this file # and in the user path profilefiles = [ os.path.join(FreeCAD.getResourceDir(), "Mod", "BIM", "Presets", "profiles.csv"), os.path.join(os.path.dirname(__file__), "Presets", "profiles.csv"), os.path.join(FreeCAD.getUserAppDataDir(), "BIM", "profiles.csv"), ] def readPresets(): Presets = [] bid = 1 # Unique index for profilefile in profilefiles: if os.path.exists(profilefile): try: with open(profilefile, "r") as csvfile: beamreader = csv.reader(csvfile) for row in beamreader: if (not row) or row[0].startswith("#"): continue try: r = [bid, row[0], row[1], row[2]] for i in range(3, len(row)): r = r + [float(row[i])] if not r in Presets: Presets.append(r) bid = bid + 1 except ValueError: print("Skipping bad line: " + str(row)) except IOError: print("Could not open ", profilefile) return Presets class _Profile(Draft._DraftObject): """Superclass for Profile classes""" def __init__(self, obj, profile): self.Profile = profile Draft._DraftObject.__init__(self, obj, "Profile") def dumps(self): if hasattr(self, "Profile"): return self.Profile def loads(self, state): if isinstance(state, list): self.Profile = state self.Type = "Profile" def cleanProperties(self, obj): """Remove all Profile properties""" for prop in obj.PropertiesList: if obj.getGroupOfProperty(prop) == "Draft": obj.setPropertyStatus(prop, "-LockDynamic") obj.removeProperty(prop) class _ProfileC(_Profile): """A parametric circular tubeprofile. Profile data: [Outside diameter, Wall thickness]""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "OutDiameter", "Draft", QT_TRANSLATE_NOOP("App::Property", "Outside Diameter"), locked=True, ).OutDiameter = profile[4] obj.addProperty( "App::PropertyLength", "Thickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Wall thickness"), locked=True, ).Thickness = profile[5] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part pl = obj.Placement c1 = Part.Circle() c1.Radius = obj.OutDiameter.Value / 2 c2 = Part.Circle() c2.Radius = obj.OutDiameter.Value / 2 - obj.Thickness.Value cs1 = c1.toShape() cs2 = c2.toShape() p = Part.makeRuledSurface(cs2, cs1) p.reverse() obj.Shape = p obj.Placement = pl class _ProfileH(_Profile): """A parametric H or I beam profile. Profile data: [width, height, web thickness, flange thickness] (see http://en.wikipedia.org/wiki/I-beam for reference)""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "Width", "Draft", QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), locked=True, ).Width = profile[4] obj.addProperty( "App::PropertyLength", "Height", "Draft", QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), locked=True, ).Height = profile[5] obj.addProperty( "App::PropertyLength", "WebThickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the web"), locked=True, ).WebThickness = profile[6] obj.addProperty( "App::PropertyLength", "FlangeThickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the flanges"), locked=True, ).FlangeThickness = profile[7] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part pl = obj.Placement p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) p3 = Vector(obj.Width.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0) p4 = Vector( obj.WebThickness.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0 ) p5 = Vector(obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) p6 = Vector(obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) p7 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) p8 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) p9 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) p10 = Vector( -obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0 ) p11 = Vector( -obj.WebThickness.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0 ) p12 = Vector(-obj.Width.Value / 2, (-obj.Height.Value / 2) + obj.FlangeThickness.Value, 0) p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p1]) p = Part.Face(p) # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileR(_Profile): """A parametric rectangular beam profile based on [Width, Height]""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "Width", "Draft", QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), locked=True, ).Width = profile[4] obj.addProperty( "App::PropertyLength", "Height", "Draft", QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), locked=True, ).Height = profile[5] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part pl = obj.Placement p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) p4 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) p = Part.makePolygon([p1, p2, p3, p4, p1]) p = Part.Face(p) # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileRH(_Profile): """A parametric Rectangular hollow beam profile. Profile data: [width, height, thickness]""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "Width", "Draft", QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), locked=True, ).Width = profile[4] obj.addProperty( "App::PropertyLength", "Height", "Draft", QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), locked=True, ).Height = profile[5] obj.addProperty( "App::PropertyLength", "Thickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the sides"), locked=True, ).Thickness = profile[6] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part pl = obj.Placement p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) p4 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) q1 = Vector( -obj.Width.Value / 2 + obj.Thickness.Value, -obj.Height.Value / 2 + obj.Thickness.Value, 0, ) q2 = Vector( obj.Width.Value / 2 - obj.Thickness.Value, -obj.Height.Value / 2 + obj.Thickness.Value, 0, ) q3 = Vector( obj.Width.Value / 2 - obj.Thickness.Value, obj.Height.Value / 2 - obj.Thickness.Value, 0 ) q4 = Vector( -obj.Width.Value / 2 + obj.Thickness.Value, obj.Height.Value / 2 - obj.Thickness.Value, 0, ) p = Part.makePolygon([p1, p2, p3, p4, p1]) q = Part.makePolygon([q1, q2, q3, q4, q1]) # r = Part.Face([p,q]) # r.reverse() p = Part.Face(p) q = Part.Face(q) r = p.cut(q) obj.Shape = r obj.Placement = pl class _ProfileU(_Profile): """A parametric U profile. Profile data: [width, height, web thickness, flange thickness] (see https://en.wikipedia.org/wiki/Structural_channel for reference)""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "Width", "Draft", QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), locked=True, ).Width = profile[4] obj.addProperty( "App::PropertyLength", "Height", "Draft", QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), locked=True, ).Height = profile[5] obj.addProperty( "App::PropertyLength", "WebThickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the webs"), locked=True, ).WebThickness = profile[6] obj.addProperty( "App::PropertyLength", "FlangeThickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the flange"), locked=True, ).FlangeThickness = profile[7] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part pl = obj.Placement p1 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) p2 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) p4 = Vector(obj.Width.Value / 2 - obj.FlangeThickness.Value, obj.Height.Value / 2, 0) p5 = Vector( obj.Width.Value / 2 - obj.FlangeThickness.Value, obj.WebThickness.Value - obj.Height.Value / 2, 0, ) p6 = Vector( -obj.Width.Value / 2 + obj.FlangeThickness.Value, obj.WebThickness.Value - obj.Height.Value / 2, 0, ) p7 = Vector(-obj.Width.Value / 2 + obj.FlangeThickness.Value, obj.Height.Value / 2, 0) p8 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p7, p8, p1]) p = Part.Face(p) # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileL(_Profile): """A parametric L profile. Profile data: [width, height, thickness]""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "Width", "Draft", QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), locked=True, ).Width = profile[4] obj.addProperty( "App::PropertyLength", "Height", "Draft", QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), locked=True, ).Height = profile[5] obj.addProperty( "App::PropertyLength", "Thickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the legs"), locked=True, ).Thickness = profile[6] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part pl = obj.Placement p1 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) p2 = Vector(-obj.Width.Value / 2, -obj.Height.Value / 2, 0) p3 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2, 0) p4 = Vector(obj.Width.Value / 2, -obj.Height.Value / 2 + obj.Thickness.Value, 0) p5 = Vector( -obj.Width.Value / 2 + obj.Thickness.Value, -obj.Height.Value / 2 + obj.Thickness.Value, 0, ) p6 = Vector(-obj.Width.Value / 2 + obj.Thickness.Value, obj.Height.Value / 2, 0) p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p1]) p = Part.Face(p) # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileT(_Profile): """A parametric T profile. Profile data: [width, height, web thickness, flange thickness]""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "Width", "Draft", QT_TRANSLATE_NOOP("App::Property", "Width of the beam"), locked=True, ).Width = profile[4] obj.addProperty( "App::PropertyLength", "Height", "Draft", QT_TRANSLATE_NOOP("App::Property", "Height of the beam"), locked=True, ).Height = profile[5] obj.addProperty( "App::PropertyLength", "WebThickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the web"), locked=True, ).WebThickness = profile[6] obj.addProperty( "App::PropertyLength", "FlangeThickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the flanges"), locked=True, ).FlangeThickness = profile[7] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part pl = obj.Placement p1 = Vector(obj.WebThickness.Value / 2, -obj.Height.Value / 2, 0) p2 = Vector(obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) p3 = Vector(obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) p4 = Vector(obj.Width.Value / 2, obj.Height.Value / 2, 0) p5 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2, 0) p6 = Vector(-obj.Width.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0) p7 = Vector( -obj.WebThickness.Value / 2, obj.Height.Value / 2 - obj.FlangeThickness.Value, 0 ) p8 = Vector(-obj.WebThickness.Value / 2, -obj.Height.Value / 2, 0) p = Part.makePolygon([p1, p2, p3, p4, p5, p6, p7, p8, p1]) p = Part.Face(p) # p.reverse() obj.Shape = p obj.Placement = pl class _ProfileTSLOT(_Profile): """T slot profile common made of aluminum""" def __init__(self, obj, profile): self.cleanProperties(obj) obj.addProperty( "App::PropertyLength", "Size", "Draft", QT_TRANSLATE_NOOP("App::Property", "Overall size"), locked=True, ).Size = profile[4] obj.addProperty( "App::PropertyLength", "SlotSize", "Draft", QT_TRANSLATE_NOOP("App::Property", "Slot size"), locked=True, ).SlotSize = profile[5] obj.addProperty( "App::PropertyLength", "WallThickness", "Draft", QT_TRANSLATE_NOOP("App::Property", "Thickness of the wall"), locked=True, ).WallThickness = profile[6] obj.addProperty( "App::PropertyLength", "TnutSlotWidth", "Draft", QT_TRANSLATE_NOOP("App::Property", "T-nut slot width"), locked=True, ).TnutSlotWidth = profile[7] obj.addProperty( "App::PropertyLength", "TnutSlotDepth", "Draft", QT_TRANSLATE_NOOP("App::Property", "T-nut slot depth"), locked=True, ).TnutSlotDepth = profile[8] obj.addProperty( "App::PropertyLength", "CoreSize", "Draft", QT_TRANSLATE_NOOP("App::Property", "Internal core size"), locked=True, ).CoreSize = profile[9] obj.addProperty( "App::PropertyLength", "HoleDiameter", "Draft", QT_TRANSLATE_NOOP("App::Property", "Internal hole diameter"), locked=True, ).HoleDiameter = profile[10] obj.addProperty( "App::PropertyLength", "FilletRadius", "Draft", QT_TRANSLATE_NOOP("App::Property", "Corner fillet radius"), locked=True, ).FilletRadius = profile[11] _Profile.__init__(self, obj, profile) def execute(self, obj): import Part import math # import DraftGeomUtils pl = obj.Placement nut_depth = obj.CoreSize / 2 - (obj.WallThickness / math.sqrt(2)) core_dim = obj.CoreSize / 2 wall_dim = obj.WallThickness slot_dim = obj.SlotSize / 2 nut_width_dim = obj.TnutSlotWidth / 2 templist = list() templist.append(Vector(nut_depth, core_dim, 0.0)) templist.append(Vector(nut_width_dim, nut_width_dim + (wall_dim / math.sqrt(2)), 0.0)) templist.append(Vector(nut_width_dim, obj.Size / 2 - wall_dim, 0.0)) templist.append(Vector(slot_dim, obj.Size / 2 - wall_dim, 0.0)) templist.append(Vector(slot_dim, obj.Size / 2, 0.0)) templist.append(Vector(obj.Size / 2, obj.Size / 2, 0.0)) # TODO: # add fillet four profile corners # sub_poly1 = Part.makePolygon(templist) # rd1=Vector(obj.Size/2-(obj.FilletRadius),obj.Size,0) # rd2=Vector(obj.Size/2-(obj.FilletRadius-obj.FilletRadius*math.sin(math.pi/4)),obj.Size/2-(obj.FilletRadius-obj.FilletRadius*math.sin(math.pi/4)),0) # rd3=Vector(obj.Size,obj.Size/2-(obj.FilletRadius),0) # templist.append(Part.Arc(rd1,rd2,rd3)) templist.append(Vector(obj.Size / 2, slot_dim, 0.0)) templist.append(Vector(obj.Size / 2 - wall_dim, slot_dim, 0.0)) templist.append(Vector(obj.Size / 2 - wall_dim, nut_width_dim, 0.0)) templist.append(Vector(nut_width_dim + (wall_dim / math.sqrt(2)), nut_width_dim, 0.0)) templist.append(Vector(core_dim, nut_depth, 0.0)) # sub_poly2 = Part.makePolygon(templist3) # l1=Part.LineSegment(Vector(obj.SlotSize/2, obj.Size/2, 0.0),Vector(obj.Size/2, obj.Size/2, 0.0)) # l2=Part.LineSegment(Vector(obj.Size/2, obj.Size/2, 0.0),Vector(obj.Size/2, 2.7500000000022977, 0.0)) # round=DraftGeomUtils.fillet([l2.toShape(),l1.toShape()],obj.FilletRadius) # wire=Part.Wire(round) # p = Part.Shape([sub_poly1,round,sub_poly2]) # p=Part.Face(Shape.Edges) templist2 = list() for vec in reversed(templist): templist2.append(Vector(vec.x, vec.y * -1, vec.z)) templist = templist + templist2 templist2.clear() for vec in reversed(templist): templist2.append(Vector(vec.x * -1, vec.y, vec.z)) templist = templist + templist2 templist.append(templist[0]) poly = Part.makePolygon(templist) pf = Part.Face(poly) hole = Part.makeCircle(obj.HoleDiameter / 2, FreeCAD.Vector(0, 0, 0)) cf = Part.Face(Part.Wire(hole)) p = pf.cut(cf) # p.reverse() obj.Shape = p obj.Placement = pl class ViewProviderProfile(Draft._ViewProviderDraft): """General view provider for Profile classes""" def __init__(self, vobj): Draft._ViewProviderDraft.__init__(self, vobj) def getIcon(self): import Arch_rc return ":/icons/Arch_Profile.svg" def setEdit(self, vobj, mode): if mode == 1 or mode == 2: return None taskd = ProfileTaskPanel(vobj.Object) FreeCADGui.Control.showDialog(taskd) return True def unsetEdit(self, vobj, mode): if mode == 1 or mode == 2: return None FreeCADGui.Control.closeDialog() return True class ProfileTaskPanel: """The editmode TaskPanel for Profile objects""" def __init__(self, obj): self.obj = obj self.Profile = None if hasattr(obj.Proxy, "Profile"): self.Profile = obj.Proxy.Profile if isinstance(self.obj.Proxy, _ProfileC): self.type = "C" elif isinstance(self.obj.Proxy, _ProfileH): self.type = "H" elif isinstance(self.obj.Proxy, _ProfileR): self.type = "R" elif isinstance(self.obj.Proxy, _ProfileRH): self.type = "RH" elif isinstance(self.obj.Proxy, _ProfileU): self.type = "U" elif isinstance(self.obj.Proxy, _ProfileL): self.type = "L" elif isinstance(self.obj.Proxy, _ProfileT): self.type = "T" elif isinstance(self.obj.Proxy, _ProfileTSLOT): self.type = "TSLOT" else: self.type = "Building Element Proxy" self.form = QtGui.QWidget() layout = QtGui.QVBoxLayout(self.form) self.comboCategory = QtGui.QComboBox(self.form) layout.addWidget(self.comboCategory) self.comboProfile = QtGui.QComboBox(self.form) layout.addWidget(self.comboProfile) QtCore.QObject.connect( self.comboCategory, QtCore.SIGNAL("currentTextChanged(QString)"), self.changeCategory ) QtCore.QObject.connect( self.comboProfile, QtCore.SIGNAL("currentIndexChanged(int)"), self.changeProfile ) # Read preset profiles and add relevant ones self.categories = [] self.presets = readPresets() for pre in self.presets: if pre[1] not in self.categories: self.categories.append(pre[1]) self.comboCategory.addItem(" ") if self.categories: self.comboCategory.addItems(self.categories) # Find current profile by label if not self.Profile: for pre in self.presets: if self.type == pre[1]: if self.obj.Label in pre[2]: self.Profile = pre break if not self.Profile: # try to find by size if hasattr(self.obj, "Width") and hasattr(self.obj, "Height"): for pre in self.presets: if self.type == pre[1]: if ( abs(self.obj.Width - self.Profile[4]) < 0.1 and abs(self.obj.Height - self.Profile[5]) < 0.1 ): self.Profile = pre break if self.Profile: origprofile = list(self.Profile) # the operation below will change self.profile self.comboCategory.setCurrentIndex(1 + self.categories.index(origprofile[1])) self.changeCategory(origprofile[1]) self.comboProfile.setCurrentIndex(self.currentpresets.index(origprofile)) self.retranslateUi(self.form) def changeCategory(self, text): self.comboProfile.clear() self.currentpresets = [] for pre in self.presets: if pre[1] == text: self.currentpresets.append(pre) f = FreeCAD.Units.Quantity(pre[4], FreeCAD.Units.Length).getUserPreferred() d = params.get_param("Decimals", path="Units") s1 = str(round(pre[4] / f[1], d)) s2 = str(round(pre[5] / f[1], d)) s3 = str(f[2]) self.comboProfile.addItem(pre[2] + " (" + s1 + "x" + s2 + s3 + ")") def changeProfile(self, idx): self.Profile = self.currentpresets[idx] def accept(self): self.obj.Label = self.Profile[2] + "_" if self.Profile: if self.Profile[3] == "C": _ProfileC(self.obj, self.Profile) elif self.Profile[3] == "H": _ProfileH(self.obj, self.Profile) elif self.Profile[3] == "R": _ProfileR(self.obj, self.Profile) elif self.Profile[3] == "RH": _ProfileRH(self.obj, self.Profile) elif self.Profile[3] == "U": _ProfileU(self.obj, self.Profile) elif self.Profile[3] == "L": _ProfileL(self.obj, self.Profile) elif self.Profile[3] == "T": _ProfileT(self.obj, self.Profile) elif self.Profile[3] == "TSLOT": _ProfileTSLOT(self.obj, self.Profile) else: print("Profile not supported") FreeCAD.ActiveDocument.recompute() FreeCADGui.ActiveDocument.resetEdit() return True def reject(self): FreeCADGui.ActiveDocument.resetEdit() return True def retranslateUi(self, TaskPanel): self.form.setWindowTitle( self.type + " " + QtGui.QApplication.translate("Arch", "Profile", None) )