# -*- coding: utf-8 -*- # Author: Ruslan Krenzler. # Date: 11 February 2018 # Create a pipe frame box. import math import csv import os.path from PySide import QtCore, QtGui import FreeCAD import Spreadsheet import Sketcher import Part import Draft tu = FreeCAD.Units.parseQuantity def GetMacroPath(): param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro") return param.GetString("MacroPath","") # This version of the macro does not create corners. DIMENSIONS_USED = ["G", "LX", "LY", "LZ", "POD", "PID"] # It must contain unique values in the column "Name" and also, dimensions listened below. PIPE_DIMENSIONS_USED = ["ID", "OD"] CORNER_DIMENSIONS_USED = ["G", "H", "M", "POD", "PID"] PIPE_CSV_TABLE_PATH = GetMacroPath()+"/pipe.csv" CORNER_CSV_TABLE_PATH = GetMacroPath()+"/outer-corner.csv" # The value RELATIVE_EPSILON is used to slightly change the size of a subtracted part # to prevent problems with boolean operations. # This value does not change the appearance of part and can be large. # If the original value is L then we often use the value L*(1+RELATIVE_EPSILON) instead. # The relative deviation is then (L*(1+RELATIVE_EPSILON)-L)/L = RELATIVE_EPSILON. # That is why the constant has "relative" in its name. # On my version of freecad 0.16 The macro works even with RELATIVE_EPSILON = 0.0. # Maybe there is no more problems with boolean operations. RELATIVE_EPSILON = 0.1 def nestedObjects(group): res = [] if group.OutList == []: res.append(group) else: # Append children first. for o in group.OutList: res += nestedObjects(o) res.append(group) return res def toSolid(document, part, name): """Convert object to a solid. Basically those are commands, which FreeCAD runs when user converts a part to a solid. """ s = part.Shape.Faces s = Part.Solid(Part.Shell(s)) o = document.addObject("Part::Feature", name) o.Label=name o.Shape=s return o class Error(Exception): """Base class for exceptions in this module.""" def __init__(self, message): super(Error, self).__init__(message) class UnplausibleDimensions(Error): """Exception raised when dimensions are unplausible. For example if outer diameter is larger than the iner one. Attributes: message -- explanation of the error """ def __init__(self, message): super(UnplausibleDimensions, self).__init__(message) class Pipe: def __init__(self, document): self.document = document self.ID = tu("2 cm") self.OD = tu("3 cm") self.L = tu("1 m") def checkDimensions(self): if not (self.ID > tu("0 mm")): raise UnplausibleDimensions("ID (inner diameter) of the pipe must be positive. It is %s instead"%(self.ID)) if not (self.OD > self.ID): raise UnplausibleDimensions("OD (outer diameter) %s must be larger than ID (inner dimater) %s "%(self.OD, self.ID)) if not (self.L > tu("0 mm")): raise UnplausibleDimensions("Length L=%s must be positive"%self.L) def create(self, convertToSolid): """ A pipe which is a differences of two cilinders: outer cylinder - inner cylinder. :param convertToSolid: if true, the resulting part will be solid. if false, the resulting part will be a cut. :return resulting part. """ self.checkDimensions() # Create outer cylinder. outer_cylinder = self.document.addObject("Part::Cylinder","OuterCylinder") outer_cylinder.Radius = self.OD/2 outer_cylinder.Height = self.L # Create inner cylinder. It is a little bit longer than the outer cylider in both ends. # This should prevent numerical problems when calculating difference # between the outer and innter cylinder. inner_cylinder = self.document.addObject("Part::Cylinder","InnerCylinder") inner_cylinder.Radius = self.ID/2 inner_cylinder.Height = self.L*(1+2*RELATIVE_EPSILON) inner_cylinder.Placement.Base = App.Vector(0,0,-self.L*RELATIVE_EPSILON) pipe = self.document.addObject("Part::Cut","Pipe") pipe.Base = outer_cylinder pipe.Tool = inner_cylinder if convertToSolid: # Before making a solid, recompute documents. Otherwise there will be # s = Part.Solid(Part.Shell(s)) # : Shape is null # exception. self.document.recompute() # Now convert all parts to solid, and remove intermediate data. solid = toSolid(self.document, pipe, "pipe (solid)") # Remove previous (intermediate parts). parts = nestedObjects(pipe) # Document.removeObjects can remove multple objects, when we use # parts directly. To prevent exceptions with deleted objects, # use the name list instead. names_to_remove = [] for part in parts: if part.Name not in names_to_remove: names_to_remove.append(part.Name) for name in names_to_remove: print("Deleting temporary objects %s."%name) self.document.removeObject(name) return solid return pipe class OuterCorner: def __init__(self, document): self.document = document self.G = tu("2 in") self.H = tu("3 in") self.M = tu("3 in") self.POD = tu("2 in") self.PID = tu("1 in") def checkDimensions(self): if not ( self.POD > tu("0 mm") and self.PID > tu("0 mm") ): raise UnplausibleDimensions("Pipe dimensions must be positive. They are POD=%s and PID=%s instead"%(self.POD, self.PID)) if not (self.M > self.POD and self.POD > self.PID): raise UnplausibleDimensions("Outer diameter M %s must be larger than outer pipe POD %s diamter. ", "Outer pipe diameter POD %s must be larger than inner pipe diameter PID %s"%(self.M, self.POD, self.PID)) if not (self.G > self.PID/2): raise UnplausibleDimensions("Length G %s must be larger than inner pipe radius PID/2=%s."%(self.G, self.PID/2)) if not (self.H > self.G): raise UnplausibleDimensions("Length G %s must be larger than H %s."%(self.G, self.H)) def createPrimitiveCorner(self, L, D): """Create corner consisting of two cylinder along x-,y- and y axis and a ball in the center.""" x_cylinder = self.document.addObject("Part::Cylinder","XCynlider") x_cylinder.Radius = D/2 x_cylinder.Height = L x_cylinder.Placement = App.Placement(App.Vector(0,0,0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0)) y_cylinder = self.document.addObject("Part::Cylinder","YCynlider") y_cylinder.Radius = D/2 y_cylinder.Height = L y_cylinder.Placement = App.Placement(App.Vector(0,0,0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0)) z_cylinder = self.document.addObject("Part::Cylinder","ZCynlider") z_cylinder.Radius = D/2 z_cylinder.Height = L sphere = self.document.addObject("Part::Sphere","Sphere") sphere.Radius = D/2 fusion = self.document.addObject("Part::MultiFuse","Fusion") fusion.Shapes = [x_cylinder,y_cylinder,z_cylinder,sphere] return fusion def addSockets(self, fusion): """Add socket cylinders to the fusion.""" x_socket = self.document.addObject("Part::Cylinder","XSocket") x_socket.Radius = self.POD / 2 x_socket.Height = self.H - self.G x_socket.Placement = App.Placement(App.Vector(self.G, 0,0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0)) y_socket = self.document.addObject("Part::Cylinder","YSocket") y_socket.Radius = self.POD / 2 y_socket.Height = self.H - self.G y_socket.Placement = App.Placement(App.Vector(0, self.G,0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0)) z_socket = self.document.addObject("Part::Cylinder","ZSocket") z_socket.Radius = self.POD / 2 z_socket.Height = self.H - self.G z_socket.Placement.Base = App.Vector(0, 0, self.G) fusion.Shapes = fusion.Shapes + [x_socket, y_socket, z_socket] # fusion.Shapes.append does not work. return fusion def createOuterPart(self): return self.createPrimitiveCorner(self.H, self.M) def createInnerPart(self): return self.createPrimitiveCorner(self.H, self.PID) def create(self, convertToSolid): self.checkDimensions() outer = self.createOuterPart() inner = self.createInnerPart() inner = self.addSockets(inner) # Remove inner part of the sockets. corner = self.document.addObject("Part::Cut","Cut") corner.Base = outer corner.Tool = inner if convertToSolid: # Before making a solid, recompute documents. Otherwise there will be # s = Part.Solid(Part.Shell(s)) # : Shape is null # exception. self.document.recompute() # Now convert all parts to solid, and remove intermediate data. solid = toSolid(self.document, corner, "corner (solid)") # Remove previous (intermediate parts). parts = nestedObjects(corner) # Document.removeObjects can remove multple objects, when we use # parts directly. To prevent exceptions with deleted objects, # use the name list instead. names_to_remove = [] for part in parts: if part.Name not in names_to_remove: names_to_remove.append(part.Name) for name in names_to_remove: print("Deleting temporary objects %s."%name) self.document.removeObject(name) return solid return corner class Box: def __init__(self, document): self.document = document self.G = tu("2 in") self.LX = tu("12 in") self.LY = tu("10 in") self.LZ = tu("8 in") self.POD = tu("2 in") self.PID = tu("1 in") self.corner = OuterCorner(document) def checkDimensions(self): if not ( self.POD > tu("0 mm") and self.PID > tu("0 mm") ): raise UnplausibleDimensions("Pipe dimensions must be positive. They are POD=%s and PID=%s instead"%(self.POD, self.PID)) if not (self.LX > 2*self.G): raise UnplausibleDimensions("The length LX %smust be larger than 2*G %s"%(self.LX, 2*self.G)) if not (self.LY > 2*self.G): raise UnplausibleDimensions("The length LY %smust be larger than 2*G %s"%(self.LY, 2*self.G)) if not (self.LZ > 2*self.G): raise UnplausibleDimensions("The length LZ %smust be larger than 2*G %s"%(self.LZ, 2*self.G)) def createPipes(self, group, convertToSolid): # Calculate pipe lengthes x_pipe_l = self.LX - 2*self.G y_pipe_l = self.LY - 2*self.G z_pipe_l = self.LZ - 2*self.G # First 3 pipes around the (0,0,0) origin in X,Y,Z direction pipe = Pipe(self.document) pipe.ID = self.PID pipe.OD = self.POD pipe.L = z_pipe_l zpipe = pipe.create(convertToSolid) group.addObject(zpipe) zpipe.Placement.Base = App.Vector(0,0,self.G) zpipe.Label = "z-"+zpipe.Label pipe.L = x_pipe_l xpipe = pipe.create(convertToSolid) group.addObject(xpipe) xpipe.Placement = App.Placement(App.Vector(self.G, 0,0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0)) xpipe.Label = "x-"+xpipe.Label pipe.L = y_pipe_l ypipe = pipe.create(convertToSolid) group.addObject(ypipe) ypipe.Placement = App.Placement(App.Vector(0, self.G,0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0)) ypipe.Label = "y-"+ypipe.Label # Add 3 clones for each x,y,z-type of axis. Place them on the edges of the quebe # First add z-pipes (because it simple, and does not require rotation). tmp = Draft.clone(zpipe, App.Vector(self.LX,0,self.G)) group.addObject(tmp) tmp = Draft.clone(zpipe, App.Vector(0, self.LY,self.G)) group.addObject(tmp) tmp = Draft.clone(zpipe, App.Vector(self.LX, self.LY,self.G)) group.addObject(tmp) # Then add x pipes. tmp = Draft.clone(xpipe) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.G, self.LY, 0), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0)) tmp = Draft.clone(xpipe) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.G, 0, self.LZ), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0)) tmp = Draft.clone(xpipe) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.G, self.LY, self.LZ), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0)) # Finally add y pipes. tmp = Draft.clone(ypipe) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.LX, self.G, 0), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0)) tmp = Draft.clone(ypipe) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(0, self.G, self.LZ), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0)) tmp = Draft.clone(ypipe) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.LX, self.G, self.LZ), App.Rotation(App.Vector(1,0,0),-90), App.Vector(0,0,0)) def addCorners(self, group, convertToSolid): corner = self.corner.create(convertToSolid) group.addObject(corner) # clone the corners and put them on right positions. tmp = Draft.clone(corner) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(0, 0, self.LZ), App.Rotation(App.Vector(0,1,0),90), App.Vector(0,0,0)) tmp = Draft.clone(corner) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.LX,0, self.LZ), App.Rotation(App.Vector(0,1,0),180), App.Vector(0,0,0)) tmp = Draft.clone(corner) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.LX,0,0), App.Rotation(App.Vector(0,1,0),270), App.Vector(0,0,0)) tmp = Draft.clone(corner) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(self.LX,self.LY,0), App.Rotation(App.Vector(0,0,1),180), App.Vector(0,0,0)) tmp = Draft.clone(corner) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(0,self.LY,0), App.Rotation(App.Vector(0,0,1),270), App.Vector(0,0,0)) tmp = Draft.clone(corner) group.addObject(tmp) tmp.Placement = App.Placement(App.Vector(0,self.LY, self.LZ), App.Rotation(App.Vector(1,0,0),180), App.Vector(0,0,0)) tmp = Draft.clone(corner) group.addObject(tmp) # First rotation. tmp.Placement = App.Placement(App.Vector(0,0,0), App.Rotation(App.Vector(0,0,1),180), App.Vector(0,0,0)) # Second rotation + shift. tmp.Placement = App.Placement(App.Vector(self.LX,self.LY, self.LZ), App.Rotation(App.Vector(1,0,0),90), App.Vector(0,0,0)).multiply(tmp.Placement) def create(self, convertToSolid): self.checkDimensions() group = self.document.addObject("App::DocumentObjectGroup", "frame box group") self.createPipes(group, convertToSolid) self.addCorners(group, convertToSolid) return group class CsvTable: """ Read coupling dimensions from a csv file. one part of the column must be unique and contains a unique key. It is the column "Name". """ def __init__(self, mandatoryDims=[]): """ @param mandatoryDims: list of column names which must be presented in the CSV files apart the "Name" column """ self.headers = [] self.data = [] self.hasValidData = False self.mandatoryDims=mandatoryDims def load(self, filename): """Load data from a CSV file.""" self.hasValidData = False with open(filename, "r") as csvfile: csv_reader = csv.reader(csvfile, delimiter=',', quotechar='"') self.headers = csv_reader.next() # Fill the talble self.data = [] names = [] ni = self.headers.index("Name") for row in csv_reader: # Check if the name is unique name = row[ni] if name in names: print('Error: Not unique name "%s" found in %s'%(name, filename)) exit(1) else: names.append(name) self.data.append(row) csvfile.close() # Should I close this file explicitely? self.hasValidData = self.hasNecessaryColumns() def hasNecessaryColumns(self): """ Check if the data contains all calumns required to create a coupling.""" return all(h in self.headers for h in (self.mandatoryDims + ["Name"])) def findPart(self, name): """Return first first raw with the particular part name as a dictionary.""" # First find out the index of the column "Name". ci = self.headers.index("Name") # Search for the first appereance of the name in this column. for row in self.data: if row[ci] == name: # Convert row to dicionary. return dict(zip(self.headers, row)) return None def getPartName(self, index): """Return part name of a row with the index *index*.""" ci = self.headers.index("Name") return self.data[index][ci] class PipeFromTable: """Create a part with dimensions from a CSV table.""" def __init__ (self, document, table): self.document = document self.table = table def create(self, partName, length, convertToSolid = True): pipe = Pipe(self.document) row = self.table.findPart(partName) if row is None: print("Part not found") return pipe.ID = tu(row["ID"]) pipe.OD = tu(row["OD"]) pipe.L = length part = pipe.create(convertToSolid) part.Label = partName return part class BoxFromTable: """Create a part with dimensions from a CSV table.""" def __init__ (self, document, pipe_table, corner_table): self.document = document self.pipe_table = pipe_table self.corner_table = corner_table self.LX = tu("12 in") self.LY = tu("10 in") self.LZ = tu("8 in") def getCorner(self, partName): corner = OuterCorner(self.document) row = self.corner_table.findPart(partName) if row is None: print('Corner part "%s" not found'%partName) return corner.G = tu(row["G"]) corner.H = tu(row["H"]) corner.M = tu(row["M"]) corner.POD = tu(row["POD"]) corner.PID = tu(row["PID"]) return corner def create(self, pipeName, cornerName, convertToSolid = True): frame_box = Box(self.document) frame_box.LX = self.LX frame_box.LY = self.LY frame_box.LZ = self.LZ # Init corner datata frame_box.corner = self.getCorner(cornerName) frame_box.G = frame_box.corner.G "setup pipe dimensions" row = self.pipe_table.findPart(pipeName) if row is None: print('Pipe part "%s" not found'%pipeName) return frame_box.PID = tu(row["ID"]) frame_box.POD = tu(row["OD"]) return frame_box.create(convertToSolid) class MainDialog(QtGui.QDialog): QSETTINGS_APPLICATION = "OSE piping freecad macros" QSETTINGS_NAME = "fram bos user input" def __init__(self, pipeTable, cornerTable): super(MainDialog, self).__init__() self.pipeTable = pipeTable self.cornerTable = cornerTable self.initUi() def initUi(self): Dialog = self # Added self.result = -1 self.setupUi(self) # Restore previous user input. Ignore exceptions to prevent this part # part of the code to prevent GUI from starting, once settings are broken. try: self.restoreInput() except Exception as e: print ("Could not restore old user input!") print(e) self.show() # The following lines are from QtDesigner .ui-file processed by pyside-uic # pyside-uic --indent=0 create-pipe.ui -o tmp.py # # The file paths needs to be adjusted manually. For example # self.label.setPixmap(QtGui.QPixmap( GetMacroPath()+"/pipe-frame-box-dimensions.png")) # access datata in some special FreeCAD directory. def setupUi(self, Dialog): Dialog.setObjectName("Dialog") Dialog.resize(614, 566) self.verticalLayout = QtGui.QVBoxLayout(Dialog) self.verticalLayout.setObjectName("verticalLayout") self.horizontalWidget = QtGui.QWidget(Dialog) self.horizontalWidget.setMinimumSize(QtCore.QSize(0, 134)) self.horizontalWidget.setLayoutDirection(QtCore.Qt.LeftToRight) self.horizontalWidget.setObjectName("horizontalWidget") self.checkBoxCreateSolid = QtGui.QCheckBox(self.horizontalWidget) self.checkBoxCreateSolid.setGeometry(QtCore.QRect(0, 0, 121, 26)) self.checkBoxCreateSolid.setChecked(True) self.checkBoxCreateSolid.setObjectName("checkBoxCreateSolid") self.label_3 = QtGui.QLabel(self.horizontalWidget) self.label_3.setGeometry(QtCore.QRect(0, 30, 21, 25)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_3.sizePolicy().hasHeightForWidth()) self.label_3.setSizePolicy(sizePolicy) self.label_3.setMaximumSize(QtCore.QSize(200, 16777215)) self.label_3.setObjectName("label_3") self.lineEditLX = QtGui.QLineEdit(self.horizontalWidget) self.lineEditLX.setGeometry(QtCore.QRect(20, 30, 71, 27)) self.lineEditLX.setObjectName("lineEditLX") self.label_5 = QtGui.QLabel(self.horizontalWidget) self.label_5.setGeometry(QtCore.QRect(110, 30, 21, 25)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_5.sizePolicy().hasHeightForWidth()) self.label_5.setSizePolicy(sizePolicy) self.label_5.setMaximumSize(QtCore.QSize(200, 16777215)) self.label_5.setObjectName("label_5") self.lineEditLY = QtGui.QLineEdit(self.horizontalWidget) self.lineEditLY.setGeometry(QtCore.QRect(130, 30, 71, 27)) self.lineEditLY.setObjectName("lineEditLY") self.label_6 = QtGui.QLabel(self.horizontalWidget) self.label_6.setGeometry(QtCore.QRect(220, 30, 21, 25)) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth()) self.label_6.setSizePolicy(sizePolicy) self.label_6.setMaximumSize(QtCore.QSize(200, 16777215)) self.label_6.setObjectName("label_6") self.lineEditLZ = QtGui.QLineEdit(self.horizontalWidget) self.lineEditLZ.setGeometry(QtCore.QRect(240, 30, 71, 27)) self.lineEditLZ.setObjectName("lineEditLZ") self.label_2 = QtGui.QLabel(self.horizontalWidget) self.label_2.setGeometry(QtCore.QRect(0, 70, 91, 17)) self.label_2.setObjectName("label_2") self.lineEditPipeName = QtGui.QLineEdit(self.horizontalWidget) self.lineEditPipeName.setGeometry(QtCore.QRect(80, 70, 171, 27)) self.lineEditPipeName.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lineEditPipeName.setObjectName("lineEditPipeName") self.pushButtonSelectPipe = QtGui.QPushButton(self.horizontalWidget) self.pushButtonSelectPipe.setEnabled(False) self.pushButtonSelectPipe.setGeometry(QtCore.QRect(260, 70, 121, 27)) self.pushButtonSelectPipe.setObjectName("pushButtonSelectPipe") self.pushButtonSelectCorner = QtGui.QPushButton(self.horizontalWidget) self.pushButtonSelectCorner.setEnabled(False) self.pushButtonSelectCorner.setGeometry(QtCore.QRect(260, 100, 121, 27)) self.pushButtonSelectCorner.setObjectName("pushButtonSelectCorner") self.lineEditCornerName = QtGui.QLineEdit(self.horizontalWidget) self.lineEditCornerName.setGeometry(QtCore.QRect(80, 100, 171, 27)) self.lineEditCornerName.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.lineEditCornerName.setObjectName("lineEditCornerName") self.label_7 = QtGui.QLabel(self.horizontalWidget) self.label_7.setGeometry(QtCore.QRect(0, 100, 91, 17)) self.label_7.setObjectName("label_7") self.verticalLayout.addWidget(self.horizontalWidget) self.label = QtGui.QLabel(Dialog) self.label.setText("") self.label.setPixmap(QtGui.QPixmap(GetMacroPath()+"/pipe-frame-box-dimensions.png")) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setObjectName("label") self.verticalLayout.addWidget(self.label) self.buttonBox = QtGui.QDialogButtonBox(Dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) self.buttonBox.setObjectName("buttonBox") self.verticalLayout.addWidget(self.buttonBox) self.retranslateUi(Dialog) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), Dialog.accept) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), Dialog.reject) QtCore.QMetaObject.connectSlotsByName(Dialog) def retranslateUi(self, Dialog): Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "Create frame box", None, QtGui.QApplication.UnicodeUTF8)) self.checkBoxCreateSolid.setText(QtGui.QApplication.translate("Dialog", "Create Solid", None, QtGui.QApplication.UnicodeUTF8)) self.label_3.setText(QtGui.QApplication.translate("Dialog", "LX:", None, QtGui.QApplication.UnicodeUTF8)) self.lineEditLX.setText(QtGui.QApplication.translate("Dialog", "12 in", None, QtGui.QApplication.UnicodeUTF8)) self.label_5.setText(QtGui.QApplication.translate("Dialog", "LY:", None, QtGui.QApplication.UnicodeUTF8)) self.lineEditLY.setText(QtGui.QApplication.translate("Dialog", "8 in", None, QtGui.QApplication.UnicodeUTF8)) self.label_6.setText(QtGui.QApplication.translate("Dialog", "LZ:", None, QtGui.QApplication.UnicodeUTF8)) self.lineEditLZ.setText(QtGui.QApplication.translate("Dialog", "4 in", None, QtGui.QApplication.UnicodeUTF8)) self.label_2.setText(QtGui.QApplication.translate("Dialog", "Pipe name:", None, QtGui.QApplication.UnicodeUTF8)) self.lineEditPipeName.setText(QtGui.QApplication.translate("Dialog", "NPS 1 in PVC SCH 40", None, QtGui.QApplication.UnicodeUTF8)) self.pushButtonSelectPipe.setText(QtGui.QApplication.translate("Dialog", "Select Pipe", None, QtGui.QApplication.UnicodeUTF8)) self.pushButtonSelectCorner.setText(QtGui.QApplication.translate("Dialog", "Select Corner", None, QtGui.QApplication.UnicodeUTF8)) self.lineEditCornerName.setText(QtGui.QApplication.translate("Dialog", "F0013WE", None, QtGui.QApplication.UnicodeUTF8)) self.label_7.setText(QtGui.QApplication.translate("Dialog", "Pipe name:", None, QtGui.QApplication.UnicodeUTF8)) def accept(self): """User clicked OK""" # Update active document. If there is none, show a warning message and do nothing. document = App.activeDocument() if document is not None: # Get dimensions from the table box = BoxFromTable(document, self.pipeTable, self.cornerTable) box.LX = tu(self.lineEditLX.text()) if box.LX == "": msgBox = QtGui.QMessageBox() msgBox.setText("Set LX length.") msgBox.exec_() return box.LY = tu(self.lineEditLY.text()) if box.LY == "": msgBox = QtGui.QMessageBox() msgBox.setText("Set LY length.") msgBox.exec_() return box.LZ = tu(self.lineEditLZ.text()) if box.LZ == "": msgBox = QtGui.QMessageBox() msgBox.setText("Set LZ length.") msgBox.exec_() return pipeName = self.lineEditPipeName.text() if pipeName == "": msgBox = QtGui.QMessageBox() msgBox.setText("Enter pipe name.") msgBox.exec_() return cornerName = self.lineEditCornerName.text() if cornerName == "": msgBox = QtGui.QMessageBox() msgBox.setText("Enter corner name.") msgBox.exec_() return createSolid = self.checkBoxCreateSolid.isChecked() box.create(pipeName, cornerName, createSolid) document.recompute() # Save user input for the next dialog call. self.saveInput() # Call parent class. super(MainDialog, self).accept() else: text = "I have not found any active document were I can create a frame box.\n"\ "Use menu File->New to create a new document first, "\ "then try to create the box again." msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the frame box failed.", text) msgBox.exec_() def saveInput(self): """Store user input for the next run.""" settings = QtCore.QSettings(MainDialog.QSETTINGS_APPLICATION, MainDialog.QSETTINGS_NAME) check = self.checkBoxCreateSolid.checkState() settings.setValue("checkBoxCreateSolid", int(check)) settings.setValue("lineEditLX", self.lineEditLX.text()) settings.setValue("lineEditLY", self.lineEditLY.text()) settings.setValue("lineEditLZ", self.lineEditLZ.text()) settings.setValue("lineEditPipeName", self.lineEditPipeName.text()) settings.setValue("lineEditCornerName", self.lineEditCornerName.text()) settings.sync() def restoreInput(self): settings = QtCore.QSettings(MainDialog.QSETTINGS_APPLICATION, MainDialog.QSETTINGS_NAME) checkState = QtCore.Qt.CheckState(int(settings.value("checkBoxCreateSolid"))) self.checkBoxCreateSolid.setCheckState(checkState) text = settings.value("lineEditLX") if text is not None: self.lineEditLX.setText(text) text = settings.value("lineEditLY") if text is not None: self.lineEditLY.setText(text) text = settings.value("lineEditLZ") if text is not None: self.lineEditLZ.setText(text) text = settings.value("lineEditPipeName") if text is not None: self.lineEditPipeName.setText(text) text = settings.value("lineEditCornerName") if text is not None: self.lineEditCornerName.setText(text) def GuiCheckPipeTable(): # Check if the CSV file exists. if os.path.isfile(PIPE_CSV_TABLE_PATH) == False: text = "This macro requires %s but this file does not exist."%(PIPE_CSV_TABLE_PATH) msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text) msgBox.exec_() exit(1) # Error print("Trying to load CSV file with pipe dimensions: %s"%PIPE_CSV_TABLE_PATH) table = CsvTable(PIPE_DIMENSIONS_USED) table.load(PIPE_CSV_TABLE_PATH) if table.hasValidData == False: text = 'Invalid %s.\n'\ 'It must contain columns %s.'%(PIPE_CSV_TABLE_PATH, ", ".join(PIPE_DIMENSIONS_USED)) msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text) msgBox.exec_() exit(1) # Error return table def GuiCheckCornerTable(): # Check if the CSV file exists. if os.path.isfile(CORNER_CSV_TABLE_PATH) == False: text = "This macro requires %s but this file does not exist."%(CORNER_CSV_TABLE_PATH) msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text) msgBox.exec_() exit(1) # Error print("Trying to load CSV file with dimensions: %s"%CORNER_CSV_TABLE_PATH) table = CsvTable(CORNER_DIMENSIONS_USED) table.load(CORNER_CSV_TABLE_PATH) if table.hasValidData == False: text = 'Invalid %s.\n'\ 'It must contain columns %s.'%(CORNER_CSV_TABLE_PATH, ", ".join(CORNER_DIMENSIONS_USED)) msgBox = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Creating of the box failed.", text) msgBox.exec_() exit(1) # Error return table # Test macros. def TestBox(): document = App.activeDocument() box = Box(document) box.create(True) document.recompute() def TestTable(): document = App.activeDocument() pipe_table = CsvTable(PIPE_DIMENSIONS_USED) corner_table = CsvTable(CORNER_DIMENSIONS_USED) pipe_table.load(PIPE_CSV_TABLE_PATH) corner_table.load(CORNER_CSV_TABLE_PATH) box = BoxFromTable(document, pipe_table, corner_table) pipeName = pipe_table.getPartName(0) cornerName = corner_table.getPartName(0) print("Using pipe %s"%pipeName) print("Using corner %s"%cornerName) box.create(pipeName, cornerName, False) document.recompute() #TestBox() #TestTable() pipeTable = GuiCheckPipeTable() # Open a CSV file, check its content, and return it as a CsvTable object. cornerTable = GuiCheckCornerTable() # Open a CSV file, check its content, and return it as a CsvTable object. form = MainDialog(pipeTable, cornerTable)