Makrodefinicja: Wiązania szkicownika z arkusza kalkulacyjnego
Description |
---|
Macro which, with a simple click on a spreadsheet cell, adds a length constraint to a line or between 2 points using a spreadsheet cell alias or address (ex. C2). Macro can create alias for you. Macro version: 00.02 Last modified: 2023-03-16 FreeCAD version: All Download: ToolBar icon Author: 2cv001 |
Author |
2cv001 |
Download |
ToolBar icon |
Links |
Macros recipes How to install macros How to customize toolbars |
Macro Version |
00.02 |
Date last modified |
2023-03-16 |
FreeCAD Version(s) |
All |
Default shortcut |
None |
See also |
None |
Description
Work in progress. It works for me. Tell me if anything is wrong.
Macro which, with a simple click on a spreadsheet cell, adds a length constraint to a line or between 2 points using a spreadsheet cell alias or address (ex. C2). Future changes to the spreadsheet will update the constraint. Macro can create alias for you.
Just select 1 line, 2 points or a constraint, click on a spreadsheet cell and run the macro. You can select lines, points at the ends of a line, points, circle, arc of circle.
Usage
If we run the macro when we have not yet created a spreadsheet, it suggests to create one and opens it so that we can start filling it in.
It is not mandatory, but it is better to have alias in our spreadsheet. Macro can create alias from texts in cells.
1) Select:
- a line,
- two points (end of a line, center of a circle, etc.)
- or a length constraint
2) Click on a spreadsheet cell, with or without an alias, that has a numerical value:
3) Run the macro.
4) Select the desired type of constraint:
If the cell has an alias, the length property of the constraint will be something like 'Spreadsheet.alias'. Otherwise, something like 'Spreadsheet.D4'.
5) If the constraint causes a conflict in the sketch and the "conflict detection" box is checked, the macro will offer to delete the new constraint:
If you select an existing constraint, you can change the value for a cell in the spreadsheet:
To make things even more refined, if, for example, a line is horizontal rather than vertical, when the dialogue box opens, the focus will be on the button for imposing a horizontal constraint. If the line is vertical rather than horizontal, the focus will be on the button to impose a vertical constraint. In both cases, simply press the enter key if you are happy with this choice.
Links
Credits
Thanks to openBrain, mario52 and onekk for their help on the code!
Thanks to Roy043 and David69 for the various reviews and improvements to the wiki.
Script
Code
ver 00.02.4 16/03/2023 by 2cv001 Macro_Sketch_Constraint_From_Spreadsheet.FCMacro
#!/usr/bin/env python # -*- coding: utf-8 -*- # ========================================================================= # Macro Sketch Constraint From Spreadsheet # file name : sketchConstraintFromSpreadsheet.FCMacro # ======================================================================== # == == # == Adds a length constraint to a line or between 2 points == # == using a spreadsheet cell alias or name (ex. C2). == # == Future changes to the spreadsheet will update the constraint. == # == if necessary, the macro help you to create alias == # == USE: == # == 1) Select 1 line, 2 points or a constraint == # == 2) Click on a spreadsheet cell == # == 3) Launch the macro == # == if the cell has an alias, the length property will be something == # == like 'Spreadsheet.alias'. == # == if not, just something like 'Spreadsheet.C2' == # == You can select lines, points line, points, circle... == # == You can't select external objetcs (next version will be able to == # ======================================================================== # ======================================================================== __author__ = "2cv001" __title__ = "Macro Sketch Constraint From Spreadsheet" __date__ = "2023/03/16" #YYYY/MM/DD __version__ = __date__ __icon__ = "https://wiki.freecad.org/File:SketchConstraintFromSpreadsheet.svg" __Wiki__ = "https://wiki.freecad.org/Macro_sketchConstraintFromSpreadsheet" import FreeCAD, FreeCADGui from PySide import QtGui, QtCore import PySide2 from PySide2.QtGui import QGuiApplication import PySide from PySide2 import QtWidgets import Draft, Part, Sketcher import itertools import configparser import os from importlib import reload importAliasOk=True ''' try : import sketchConstraintFromSpreadsheetAlias reload(sketchConstraintFromSpreadsheetAlias) from sketchConstraintFromSpreadsheetAlias import setAlias except : importAliasOk=False App.Console.PrintWarning('You could Automatiquely add alias if you had'+ 'sketchConstraintFromSpreadsheetAlias.py file in same directory'+ 'than sketchConstraintFromSpreadsheetAlias') # On prévient l'utilisateur dans le rapport ''' ################################################################################# # part code for alias ################################################################################# # parameters for alias creation separateur=" " # typically put " " so blanks will be replaced by nouveauCaract nouveauCaract='' #Put for example "_" to have the separators replaced by "_". Put "" to have no separator majuscule=True #set to True if you want "Diametre du cercle" to be "DiametreDuCercle" changeTexteCellule=False # the text will only be changed if changeCellText is True. # This does not change anything for the alias itself premierCaractereEnMinuscule=True # Force the first character to be in lower case # list of characters to be replaced by an equivalent. for example 'é' will be replaced by 'e # if you add characters, please send me a private message. Il will eventually add it in my code. caracEquivalents =[ ['é','e'],['è','e'],['à','a'],['@','a'],['&','e'],['ç','c'],['²','2'],["'",''] ] def remplaceCartatParEquivalent(caractere): # replaces a character with its equivalent if it exists caracResult=caractere for couple in caracEquivalents: if (couple[0]==caractere): caracResult=couple[1] break return caracResult def remplaceCararcDansMot(mot): #replaces all characters of the word with its equivalent if it exists motResult=mot for caract in mot: a=remplaceCartatParEquivalent(caract) motResult=motResult.replace(caract,a) return motResult def traitementChaineSource(chaineSource,separateur,nouveauCaract,majuscule): # If separator is ' ' and nouveauCaract is '_', and majuscule is True # transforms "Diametre du cylindre" into "Diametre_Du_Cylindre chaineResult='' first=True carctDeSeparation='' for mots in chaineSource.split(separateur): mots=remplaceCararcDansMot(mots) if (not (first)): carctDeSeparation=nouveauCaract if (majuscule): chaineResult=chaineResult+nouveauCaract+mots[:1].upper()+mots[1:] # We use "[:1]" instead of "[0]", # for no crash in case of an empty string (which happens if the cell is empty) else: chaineResult=chaineResult+nouveauCaract+mots if premierCaractereEnMinuscule : chaineResult=chaineResult[:1].lower()+chaineResult[1:] return chaineResult def setAlias(): sels = Gui.Selection.getSelectionEx() # If the user wants to trigger the creation of alias # it is sufficient that he does not select any objects in the sketch # but select cells containing strings in the left hand column # of where he want the alias to be created # so test if an object is selected : # result : False if macro can continue. if len(sels)!=0 : return False else : ### A dialogue is displayed asking the user if he is sure if QtGui.QMessageBox.warning(Gui.getMainWindow(), 'Warning','You did not select any object in an active sketch.'+ '\nDoes it mean you want to create alias automatiquely for cells at the right of selected cells ?', QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Cancel: # if user dont want return False aw = Gui.getMainWindow().centralWidget().activeSubWindow() # Store the active window #mySpreadSheet=trouveSheet(aw) try : mySpreadSheet=Gui.ActiveDocument.ActiveView.getSheet() except : QtWidgets.QMessageBox.warning(Gui.getMainWindow(), "Warning", "No active spreadsheet with selected cells.") return True # On récupère la liste complète de toutes les cellules sélectionnées sel_items = aw.widget().findChild(QtGui.QTableView).selectedIndexes() ### We define a function that will return the cell identifier from its row (r) and column (c) numbers ### Numbers start at 0 for the first row/column ### Columns are correctly managed with a 2-letter identifier cellName = lambda r,c:'{}{}{}'.format(chr(c//26 + 64) if c//26 > 0 else '', chr(c%26+65), r+1) # we check that one of the cells is not a numeric. If not, message and exit for item in sel_items: # The selected cells are scanned cell = cellName(item.row(),item.column()) # We retrieve the cell ID activeCellContenu=mySpreadSheet.getContents(cell) f='' try : f=float(activeCellContenu) except : pass if f!='' : QtWidgets.QMessageBox.warning(Gui.getMainWindow(), "Warning", "There is a number in one of the selected cells."+ " Alias is not possible. \nTry again after correction") return False for item in sel_items: # The selected cells are scanned cell = cellName(item.row(),item.column()) # The cell identifier is retrieved next_cell = cellName(item.row(), item.column()+1) # We get the ID of the cell next to the right activeCellContenu=mySpreadSheet.getContents(cell) # processing the character string contained in the cell activeCellContenu=traitementChaineSource(activeCellContenu,separateur,nouveauCaract,majuscule) if changeTexteCellule:# if the changeCellText parameter is set to True then we replace the text in the cell mySpreadSheet.set(cell, activeCellContenu) alias=activeCellContenu try: # Bloc try to recover errors mySpreadSheet.setAlias(next_cell, alias) # The alias is assigned to the right-hand neighbouring cell except ValueError: # If a "ValueError" is triggered (which happens when the alias is not valid) # The user is warned in the report App.Console.PrintWarning("Can't set alias for cell {} : {} isn't valid\n".format(next_cell, alias)) return True ################################################################################# # End part code for alias ################################################################################# #======================================================= # if no spreadsheet detected, ask for a creation # return : # 'exist' if one already exists # 'new' if a new is create # 'no' if user does not want it #======================================================= def isExistSpreadSheet() : def firstSpreadSheet(doc=App.ActiveDocument): for obj in doc.Objects: if obj.TypeId == 'Spreadsheet::Sheet': return obj return None mySpreadSheet=firstSpreadSheet() if not mySpreadSheet : wantNewSpreadSheet = QtWidgets.QMessageBox.question(None, "No spreadsheet detected", "No spreadsheet in your document. Create one ?", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) if wantNewSpreadSheet == QtWidgets.QMessageBox.Yes: mySpreadSheet=App.activeDocument().addObject('Spreadsheet::Sheet','Spreadsheet') mySpreadSheet.ViewObject.doubleClicked() return 'new' else : return 'no' return 'exist' #======================================================= # Dialog box # Ask user which sort of constraint is required #======================================================= class getConstraintType(QtGui.QDialog): def __init__(self, widgetToFocus=None): super(getConstraintType, self).__init__() self.widgetToFocus = widgetToFocus self.initUI() def initUI(self): self.setWindowIcon(QtGui.QIcon('dialog_icon.png')) gridLayout = QtGui.QGridLayout() #macroDirectory=FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro").GetString("MacroPath")+"\\" # LINUX ? option1Button = QtGui.QPushButton(QtGui.QIcon(":/icons/constraints/Constraint_HorizontalDistance.svg"), "") option2Button = QtGui.QPushButton(QtGui.QIcon(":/icons/constraints/Constraint_VerticalDistance.svg"), "") option3Button = QtGui.QPushButton(QtGui.QIcon(":/icons/constraints/Constraint_Length.svg"), "") option1Button.setText("Lenght constrainte X") option2Button.setText("Lenght constrainte Y") option3Button.setText("Lenght constrainte") option1Button.setToolTip("Lenght constrainte X") option2Button.setToolTip("Lenght constrainte Y") option3Button.setToolTip("Lenght constrainte") option1Button.clicked.connect(self.onOption1) option2Button.clicked.connect(self.onOption2) option3Button.clicked.connect(self.onOption3) option4Button = QtGui.QPushButton(QtGui.QIcon(":/icons/application-exit.svg"), "Cancel") option4Button.setToolTip("Option 4 tooltip") option4Button.clicked.connect(self.onOption4) gridLayout.addWidget(option1Button, 0, 0) gridLayout.addWidget(option2Button, 0, 1) gridLayout.addWidget(option3Button, 1, 0) gridLayout.addWidget(option4Button, 1, 1) self.setLayout(gridLayout) self.setGeometry(250, 250, 0, 50) self.setWindowTitle("Choose a constraint type") self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) self.choiceConstraint = '' option1Button.setFocusPolicy(QtCore.Qt.NoFocus) option2Button.setFocusPolicy(QtCore.Qt.NoFocus) option3Button.setFocusPolicy(QtCore.Qt.NoFocus) option4Button.setFocusPolicy(QtCore.Qt.NoFocus) # set focus to specified widget if self.widgetToFocus == 'DistanceX': option1Button.setFocus() elif self.widgetToFocus == 'DistanceY': option2Button.setFocus() elif self.widgetToFocus == 'Distance': option3Button.setFocus() # Add checkbox self.checkBox = QtGui.QCheckBox("Conflict detection") self.checkBox.setChecked(True) gridLayout.addWidget(self.checkBox, 2, 0, 1, 2) self.checkBox.clicked.connect(self.onOptionCheckBox) # read ini file to get last checkBoxState config = configparser.ConfigParser() macroDirectory = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro").GetString("MacroPath") + "\\" try : config.read(macroDirectory + 'constraintFromTabIcone.ini') # read ini file to know last time state lasChecked=config.getboolean('DEFAULT', 'save_checkbox_state') self.checkBox.setChecked(lasChecked) except : print('no ini file. Ini file will be create for next launch') # window positioning centerPoint = QGuiApplication.screens()[0].geometry().center() self.move(centerPoint - self.frameGeometry().center()) def onOption1(self): self.choiceConstraint = 'DistanceX' self.close() def onOption2(self): self.choiceConstraint = 'DistanceY' self.close() def onOption3(self): self.choiceConstraint = 'Distance' self.close() def onOption4(self): self.choiceConstraint = 'Cancel' self.close() def onOptionCheckBox(self): macroDirectory = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro").GetString("MacroPath") + "\\" # Save checkbox state to file filePath = os.path.join(macroDirectory, "constraintFromTabIcone.ini") config = configparser.ConfigParser() config['DEFAULT'] = {'save_checkbox_state': str(int(self.getCheckBoxState()))} with open(filePath, 'w') as configfile: config.write(configfile) def getCheckBoxState(self): return self.checkBox.isChecked() #======================================================= # Give the focus to editing sketch window # no parameter # use : activateSketchEditingWindow() #======================================================= def activateSketchEditingWindow(): def searchForNode(tree, childName, maxLevel=0): return recursiveSearchForNode(tree, childName, maxLevel, 1) def recursiveSearchForNode(tree, childName, maxLevel, currentLevel): try: if tree.getByName(childName): return True; elif maxLevel > 0 and currentLevel >= maxLevel: return False; else: for child in tree.getChildren(): if recursiveSearchForNode(child, childName, maxLevel, currentLevel+1): return True except: pass return False doc = Gui.ActiveDocument if not doc: QtWidgets.QMessageBox.information(Gui.getMainWindow(), "Activate window", "No active document") return views = Gui.ActiveDocument.mdiViewsOfType("Gui::View3DInventor") if not views: QtWidgets.QMessageBox.information(Gui.getMainWindow(), "Activate window", "No 3D view opened for active document") return editView=None for view in views: if searchForNode(view.getSceneGraph(), "Sketch_EditRoot",3): editView = view break if not editView: QtWidgets.QMessageBox.information(Gui.getMainWindow(), "Activate window", "No 3D view has sketch in edit mode for active document") return for win in Gui.getMainWindow().centralWidget().subWindowList(): if editView.graphicsView() in win.findChildren(QtWidgets.QGraphicsView): Gui.getMainWindow().centralWidget().setActiveSubWindow(win) break ###################################################################################### # to get necessary values for the constraint # Parameters : # sel : selection of objects (a line, 2 points..). # numOrdreObjSelected if we want the first objetc selected or the second. # indexExtremite if we want the start point (1) or the end point (2), if exist, of the sel # return features of a point # - typeInSubElementName # - indexObjectHavingPoint index of the object having the point (line...) # - indexExtremiteLine index of the ends (points) of the line (=start point or end point) # -x,y : coordinates of the point ##################################################################################### def featuresObjSelected (mySketch,sel,numOrdreObjSelected,indexExtremite) : indexExtremiteLine=1 indexObjectHavingPoint=-10 typeIdGeometry=None x,y=0,0 itemName=sel.SubElementNames[numOrdreObjSelected] # ex Edge5 ( line) if itemName=='RootPoint' : typeInSubElementName='RootPoint' indexObjectHavingPoint=-1 typeIdGeometry=mySketch.Geometry[indexObjectHavingPoint].TypeId x,y=0,0 else : typeInSubElementName, numInNameStr = [''.join(c) for _, c in itertools.groupby(itemName, str.isalpha)] # Edge5 renvoie'Edge' et '5' numInName=int(numInNameStr) # index de ce qui a été sélectionné =numInName1-1 car commence à 0 # only one selected object if typeInSubElementName == 'Edge'and len(sel.SubElementNames)==1: # selection is only one line indexObjectHavingPoint=numInName-1 indexExtremiteLine=indexExtremite typeIdGeometry=mySketch.Geometry[indexObjectHavingPoint].TypeId # typeIdGeometry : 'Part::GeomCircle' or 'Part::GeomLineSegment' if typeIdGeometry in ['Part::GeomLineSegment'] : if indexExtremite == 1 : x=mySketch.Geometry[indexObjectHavingPoint].StartPoint.x y=mySketch.Geometry[indexObjectHavingPoint].StartPoint.y elif indexExtremite == 2 : x=mySketch.Geometry[indexObjectHavingPoint].EndPoint.x y=mySketch.Geometry[indexObjectHavingPoint].EndPoint.y # We selected a circle but for center (two objects selected) if typeInSubElementName == 'Edge'and len(sel.SubElementNames)==2: indexObjectHavingPoint=numInName-1 typeIdGeometry=mySketch.Geometry[indexObjectHavingPoint].TypeId print('425 typeIdGeometry',typeIdGeometry) if typeIdGeometry in ['Part::GeomCircle','Part::GeomArcOfCircle'] : x=mySketch.Geometry[indexObjectHavingPoint].Location.x y=mySketch.Geometry[indexObjectHavingPoint].Location.y indexExtremiteLine=3 # 3 for center # We selected a vertex if typeInSubElementName=='Vertex' : # selection is 2 points. sel is a vertex (a point of a line) : indexObjectHavingPoint, indexExtremiteLine = sel.Object.getGeoVertexIndex(numInName-1) typeIdGeometry=mySketch.Geometry[indexObjectHavingPoint].TypeId if mySketch.Geometry[indexObjectHavingPoint].TypeId=='Part::GeomLineSegment' : if indexExtremiteLine==1 : x=mySketch.Geometry[indexObjectHavingPoint].StartPoint.x y=mySketch.Geometry[indexObjectHavingPoint].StartPoint.y if indexExtremiteLine==2: x=mySketch.Geometry[indexObjectHavingPoint].EndPoint.x y=mySketch.Geometry[indexObjectHavingPoint].EndPoint.y if mySketch.Geometry[indexObjectHavingPoint].TypeId=='Part::GeomPoint' : x=mySketch.Geometry[indexObjectHavingPoint].X y=mySketch.Geometry[indexObjectHavingPoint].Y # we select a vertex Circle (so the center) if mySketch.Geometry[indexObjectHavingPoint].TypeId in ['Part::GeomCircle','Part::GeomArcOfCircle'] : x=mySketch.Geometry[indexObjectHavingPoint].Location.x y=mySketch.Geometry[indexObjectHavingPoint].Location.y if typeInSubElementName=='Constraint' and len(sel.SubElementNames)==1 : indexConstraint=numInName-1 indexObjectHavingPoint=indexConstraint typeIdGeometry='Constraint' return typeIdGeometry,typeInSubElementName, indexObjectHavingPoint, indexExtremiteLine, x ,y ########################################## # call at end ########################################## def procEnd(): activateSketchEditingWindow() ########################################## # function returning selected objects at GUI level # =Sketch, SpreadSheet .... # parameter : # '' = no filter # 'Spreadsheet::Sheet' for spreadsheets only # 'Sketcher::SketchObject' for sketches etc... # output: an array of sketch objects, spreadsheets etc. ########################################## def getGuiObjsSelect(type=''): tabGObjSelect=[] selections=Gui.Selection.getCompleteSelection() for sel in (selections): if hasattr(sel, 'Object'): # depend freecad version if type=='' or sel.Object.TypeId==type : tabGObjSelect.append(sel.Object) else : obj=App.ActiveDocument.getObject(sel.Name) if type=='' or obj.TypeId==type : tabGObjSelect.append(obj) return tabGObjSelect ########################################## # Main proceddure ########################################## def main(): #initialization sheckBoxConstraintConflicState=False indexConstraint=-1 if isExistSpreadSheet() in ['no','new'] : return # have a look if user wants alias if importAliasOk : if setAlias(): return try : mySketch=ActiveSketch except : QtWidgets.QMessageBox.information(None,"Warning","Select object must be done in edition mode") activateSketchEditingWindow() return mySketchName=mySketch.Name #actually not use # Part SpreadSheet #--------------------------------- sheets=getGuiObjsSelect('Spreadsheet::Sheet') for sheet in sheets : Gui.Selection.removeSelection(FreeCAD.ActiveDocument.Name,sheet.Name) try : mySpreadSheet=Gui.ActiveDocument.ActiveView.getSheet() except : QtWidgets.QMessageBox.information(None,"Warning", "1- Select a line or 2 points"+ "\n 2- go to a spreadsheet"+ "\n 3- select the cell containing the value."+ "\n 4- stay in the spreadsheet and launch the macro") activateSketchEditingWindow() return mySpreadSheetName = mySpreadSheet.Name # select the Spreadsheet To be able to retrieve the selected cell : mySpreadSheet.ViewObject.doubleClicked() # retrieve the selected cell ci = lambda :Gui.getMainWindow().centralWidget().activeSubWindow().widget().findChild(QtGui.QTableView).currentIndex() cellCode = '{}{}{}'.format(chr(ci().column()//28 + 64) if ci().column()//26 > 0 else '', chr(ci().column()%26+65), ci().row()+1) try: cellContents = float(mySpreadSheet.get(cellCode)) except: QtWidgets.QMessageBox.information(None,"Warning", "Click on a cell with a numeric value before runing the macro") return cellAlias= App.ActiveDocument.getObject(mySpreadSheetName).getAlias(cellCode) # Part sketch #---------------------------- sels = Gui.Selection.getSelectionEx() if len(sels)==0 or len(sels[0].SubElementNames)==0 : QtWidgets.QMessageBox.information(None,"Warning","Anathing is select.\n"+ "Select 1 line, 2 points or a constraint in a sketch before selecting a cell in the spreadsheet") activateSketchEditingWindow() return elif len(sels[0].SubElementNames) >2 : QtWidgets.QMessageBox.information(None,"Warning","Too many objects selected.\n"+ "Select 1 line, 2 points or a constraint in a sketch before selecting a cell in the spreadsheet") activateSketchEditingWindow() return else : # only one obj selected #------------------------ if len(sels[0].SubElementNames)==1 : # only one obj selected #startPoint of the line (typeIdGeometry1,typeInSubElementName1, indexObjectHavingPoint1, indexExtremiteLine1, x1 ,y1)=featuresObjSelected (ActiveSketch, sels[0],0,1) if typeInSubElementName1=='Constraint' and len(sels[0].SubElementNames)==1 : indexConstraint=indexObjectHavingPoint1 elif typeIdGeometry1=='Part::GeomLineSegment' : (typeIdGeometry2, typeInSubElementName2, indexObjectHavingPoint2, indexExtremiteLine2, x2 ,y2)=featuresObjSelected (ActiveSketch, sels[0],0,2) # two obj selected #------------------------ if len(sels[0].SubElementNames)== 2: # two obj selected (typeIdGeometry1,typeInSubElementName1, indexObjectHavingPoint1, indexExtremiteLine1, x1 ,y1)=featuresObjSelected (ActiveSketch, sels[0],0,1) print('575 typeIdGeometry1',typeIdGeometry1) (typeIdGeometry2,typeInSubElementName2, indexObjectHavingPoint2, indexExtremiteLine2, x2 ,y2)=featuresObjSelected (ActiveSketch, sels[0],1,1) if ((typeInSubElementName1 not in ('Vertex' , 'RootPoint') or typeInSubElementName2 not in ('Vertex','RootPoint')) and not(typeIdGeometry1 in ('Part::GeomCircle','Part::GeomArcOfCircle') and typeInSubElementName1 in ['Edge'])) : QtWidgets.QMessageBox.information(None,"Warning","2 objects are selected but not 2 points .\n"+ "Select 1 line, 2 points or a constraint in a sketch before selecting a cell in the spreadsheet") activateSketchEditingWindow() return #-------------------------------------- # line or points have been selected have a look if we need to swap points # ------------------------------------- if ((len(sels[0].SubElementNames)== 1 and typeIdGeometry1 in['Part::GeomLineSegment'] ) or (len(sels[0].SubElementNames)== 2 and typeIdGeometry1 in['Part::GeomLineSegment','Part::GeomCircle', 'Part::GeomArcOfCircle', 'Part::GeomPoint'] )) : # ask the user what kind of constraint he wants #------------------------------------ # to give focus on the good button # (Button DistanceX if the two points are more horizontal than vertical) if abs(x1-x2) > abs(y1-y2) : buttonHavingFocus='DistanceX' else : buttonHavingFocus='DistanceY' form = getConstraintType(buttonHavingFocus) form.exec_() # is the checkboxSheced ? sheckBoxConstraintConflicState=form.getCheckBoxState() if form.choiceConstraint in ('Cancel','') : activateSketchEditingWindow() return myConstraint=form.choiceConstraint # 'DistanceX' or 'DistanceY' or 'Distance' if (myConstraint == 'DistanceX' and x1>x2) or (myConstraint == 'DistanceY' and y1>y2) : indexObjectHavingPoint1,indexObjectHavingPoint2=indexObjectHavingPoint2,indexObjectHavingPoint1 indexExtremiteLine1,indexExtremiteLine2=indexExtremiteLine2,indexExtremiteLine1 # create constraint #================================= if cellAlias==None : cellExpression= mySpreadSheetName+'.'+cellCode else : cellExpression= mySpreadSheetName+'.'+cellAlias if (len(sels[0].SubElementNames)== 1 and typeIdGeometry1 in['Part::GeomCircle','Part::GeomArcOfCircle'] ) : indexConstraint=mySketch.addConstraint(Sketcher.Constraint('Diameter', indexObjectHavingPoint1, cellContents)) elif typeIdGeometry1!='Constraint' : # no selected constraint, just line or points #create the constraint indexConstraint=mySketch.addConstraint(Sketcher.Constraint(myConstraint , indexObjectHavingPoint1,indexExtremiteLine1,indexObjectHavingPoint2,indexExtremiteLine2, cellContents)) # for all type, set the constraint'formula' (ex : 'spreadSheet.unAlias' mySketch.setExpression('Constraints['+str(indexConstraint)+']',cellExpression) # put Sketch window ahead activateSketchEditingWindow() FreeCADGui.Selection.clearSelection() # FreeCAD.ActiveDocument.recompute() ActiveSketch.touch() ActiveSketch.recompute() #if Gui.ActiveDocument.getInEdit() == Gui.ActiveDocument.Sketch: #Gui.ActiveDocument.Sketch.doubleClicked() # is ther constraintes conflicts ? if sheckBoxConstraintConflicState : #if App.activeDocument().isTouched(): # isTouched is not ok in Daily Freecad if 'Invalid' in mySketch.State : a=QtWidgets.QMessageBox.question(None, "", "Constraints conflic detected. Cancel constraint ? ", QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.No) if a == QtWidgets.QMessageBox.Yes: mySketch.delConstraint(indexConstraint) FreeCAD.ActiveDocument.recompute() activateSketchEditingWindow() return if __name__ == '__main__': main() #procEnd()