Macro Sketch Constraint From Spreadsheet: Difference between revisions
No edit summary |
No edit summary |
||
(43 intermediate revisions by 4 users not shown) | |||
Line 5: | Line 5: | ||
{{Macro |
{{Macro |
||
|Name=Macro Sketch Constraint From Spreadsheet |
|Name=Macro Sketch Constraint From Spreadsheet |
||
|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). |
|Description=Macro which, with a simple click on a spreadsheet cell, adds a length constraint to a line, a circle, or between 2 points using a spreadsheet cell alias or address (ex. C2). Future changes to the spreadsheet will update the constraint. The macro can create alias for you.<br>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, circles or arcs of circles.<br> |
||
|Author=2cv001 |
|Author=2cv001 |
||
|Download=[https://wiki.freecad.org/images/d/dc/Macro_Sketch_Constraint_From_Spreadsheet.svg ToolBar icon] |
|Download=[https://wiki.freecad.org/images/d/dc/Macro_Sketch_Constraint_From_Spreadsheet.svg ToolBar icon] |
||
|Version=01. |
|Version=01.03 |
||
|Date= |
|Date=2024-01-15 |
||
|FCVersion=All |
|FCVersion=All |
||
}} |
}} |
||
Line 19: | Line 19: | ||
<!--T:4--> |
<!--T:4--> |
||
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. The macro can create alias for you. |
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. The macro can create alias for you. |
||
<!--T:5--> |
<!--T:5--> |
||
Line 29: | Line 29: | ||
</translate> |
</translate> |
||
{{Codeextralink|https://raw.githubusercontent.com/2cv001/ |
{{Codeextralink|https://raw.githubusercontent.com/2cv001/FreeCAD-macros/master/Spreadsheet/Sketch_Constraint_From_Spreadsheet.FCMacro}} |
||
<translate> |
<translate> |
||
== Usage == <!--T:6--> |
== Usage == <!--T:6--> |
||
=== Automatic Object creation === |
|||
=== Automatic Object creation === <!--T:27--> |
|||
<!--T:23--> |
<!--T:23--> |
||
Line 41: | Line 42: | ||
[[File:SketchConstraintFromSpreadsheet7.gif]] |
[[File:SketchConstraintFromSpreadsheet7.gif]] |
||
<translate> |
<translate> |
||
=== Automatic Alias creation === <!--T:28--> |
|||
<!--T:24--> |
<!--T:24--> |
||
It is not mandatory, but it is better to use aliases in your spreadsheet. The macro can create aliases from texts in cells.<br> |
|||
=== Automatic Alias creation === |
|||
Two modes: |
|||
It is not mandatory, but it is better to use aliases in your spreadsheet. The macro can create aliases from texts in cells. |
|||
* a manual mode where you select yourself the cells with text for alias and an automatic mode. |
|||
* and an automatic mode: |
|||
<br> |
|||
==== Automatique mode ==== |
|||
==== Automatic mode ==== <!--T:29--> |
|||
An automatic mode where alias are automatically created using a text zone defined by a cell. The zone includes the cell and those below. These texts correspond to the alias name. The alias is created to the right of its text.” The designated cell (here A3) is editable in these dialog boxes : |
|||
<!--T:30--> |
|||
An automatic mode where alias are automatically created using a text zone defined by a cell. The zone includes the cell and those below. These texts correspond to the alias name. The alias is created to the right of its text. The designated cell (here A3) is editable in these dialog boxes: |
|||
</translate> |
</translate> |
||
[[File:SketchConstraintFromSpreadsheet2302.png|alt=Alias automatic creation|Alias automatic creation]] |
[[File:SketchConstraintFromSpreadsheet2302.png|alt=Alias automatic creation|Alias automatic creation]] |
||
Line 54: | Line 61: | ||
[[File:SketchConstraintFromSpreadsheet2303.png|alt=Alias automatic creation|Alias automatic creation]] |
[[File:SketchConstraintFromSpreadsheet2303.png|alt=Alias automatic creation|Alias automatic creation]] |
||
[[File:SketchConstraintFromSpreadsheet2304.gif|alt=Alias automatic creation|Alias automatic creation]] |
[[File:SketchConstraintFromSpreadsheet2304.gif|alt=Alias automatic creation|Alias automatic creation]] |
||
<translate> |
|||
<br> |
<br> |
||
<translate> |
|||
==== Manual mode ==== |
|||
==== Manual mode ==== <!--T:31--> |
|||
To use manual mode don't ckeck the case "Automatic alias. |
|||
<!--T:32--> |
|||
To use manual mode don't check the "Automatic alias" option. |
|||
</translate> |
</translate> |
||
[[File:SketchConstraintFromSpreadsheet2301.png|alt=Alias creation|Alias creation]] |
[[File:SketchConstraintFromSpreadsheet2301.png|alt=Alias creation|Alias creation]] |
||
<translate> |
<translate> |
||
<br> |
|||
</translate> |
</translate> |
||
[[File:SketchConstraintFromSpreadsheet8.gif|alt=Alias creation|Alias creation]] |
[[File:SketchConstraintFromSpreadsheet8.gif|alt=Alias creation|Alias creation]] |
||
<translate> |
<translate> |
||
=== Constraints creation === <!--T:34--> |
|||
<!--T:7--> |
<!--T:7--> |
||
=== Constraints creation === |
|||
1) Select: |
1) Select: |
||
* a line, |
* a line, |
||
Line 130: | Line 138: | ||
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 apply a horizontal constraint. If the line is vertical rather than horizontal, the focus will be on the button to apply a vertical constraint. In both cases, simply press the enter key if you are happy with this choice. |
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 apply a horizontal constraint. If the line is vertical rather than horizontal, the focus will be on the button to apply a vertical constraint. In both cases, simply press the enter key if you are happy with this choice. |
||
<!--T:15--> |
|||
</translate> |
|||
[[File:SketchConstraintFromSpreadsheet5.gif]] |
[[File:SketchConstraintFromSpreadsheet5.gif]] |
||
<translate> |
|||
<!--T:38--> |
|||
The macro also works for Pads (property Length), Pockets (property Length), Fillets (property Radius), Chamfers (property Size) and Thicknesses (property Value). If you click on a Pad, for example, and then on a cell with the desired value, the Length property of the pad is automatically modified. |
|||
</translate> |
|||
<br> |
|||
[[File:SketchConstraintFromSpreadsheet20240801.gif]] |
|||
<translate> |
|||
== Links == <!--T:16--> |
== Links == <!--T:16--> |
||
Line 137: | Line 155: | ||
<!--T:17--> |
<!--T:17--> |
||
* [https://forum.freecad.org/viewtopic.php?t=75972 Forum discussion (French)] |
* [https://forum.freecad.org/viewtopic.php?t=75972 Forum discussion (French)] |
||
* [https://forum.freecad.org/viewtopic.php?style=5&t=76990 Forum discussion (English)] |
|||
* [[Macros_recipes|Macros recipes]] |
* [[Macros_recipes|Macros recipes]] |
||
* [[How_to_install_macros|How to install macros]] |
* [[How_to_install_macros|How to install macros]] |
||
Line 146: | Line 165: | ||
Thanks to openBrain, mario52 and onekk for their help on the code!<br/> |
Thanks to openBrain, mario52 and onekk for their help on the code!<br/> |
||
Thanks to Syres for tests and for the help on the format in the code.<br/> |
Thanks to Syres for tests and for the help on the format in the code.<br/> |
||
Thanks to Roy043 and David69 for the various reviews and improvements to the wiki. |
Thanks to Roy043 and David69 for the various reviews and improvements to the wiki.<br/> |
||
Thanks to L'ami René for tests and ideas. |
|||
== Script == <!--T:20--> |
== Script == <!--T:20--> |
||
Line 156: | Line 176: | ||
</translate> |
</translate> |
||
ver |
ver 01.03 2024/01/27 by 2cv001 '''Macro_Sketch_Constraint_From_Spreadsheet.FCMacro''' |
||
<translate> |
|||
==== Download ==== <!--T:37--> |
|||
</translate> |
|||
<br> |
|||
{{CodeDownload|https://raw.githubusercontent.com/2cv001/FreeCAD-macros/master/Spreadsheet/Sketch_Constraint_From_Spreadsheet.FCMacro| Download latest version of the macro}} |
|||
<translate> |
|||
<!--pre tags required as the macro contains piping characters--> |
|||
<pre> |
|||
</translate> |
|||
#!/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 select external objetcs == |
|||
# ======================================================================== |
|||
# ======================================================================== |
|||
__author__ = "2cv001" |
|||
__title__ = "Macro Sketch Constraint From Spreadsheet" |
|||
__date__ = "2023/01/05" #YYYY/MM/DD |
|||
__version__ = __date__ |
|||
__icon__ = "https://wiki.freecad.org/Images/1/15/Macro_Sketch_Constraint_From_Spreadsheet.svg" |
|||
__Wiki__ = "https://wiki.freecad.org/Macro_sketchConstraintFromSpreadsheet" |
|||
from PySide import QtGui, QtCore |
|||
from PySide2.QtGui import QIcon |
|||
from PySide2 import QtWidgets |
|||
from PySide2.QtGui import QGuiApplication |
|||
import configparser |
|||
import os |
|||
import FreeCAD |
|||
import re |
|||
import itertools |
|||
# macro directory (for ini file) |
|||
macroDirectory = FreeCAD.getUserMacroDir(True) |
|||
# save objects names in ini file |
|||
#typeObject : spreadsheet, body.... |
|||
def save_settings_Name(typeObject,objectName): |
|||
config = configparser.ConfigParser() |
|||
ini_file = os.path.join(macroDirectory, "sketchConstraintFromSpreadsheet.ini") |
|||
config.read(ini_file) |
|||
if 'SettingNames' not in config: |
|||
config.add_section('SettingNames') |
|||
config.set('SettingNames', typeObject, objectName) |
|||
with open(ini_file, 'w') as configfile: |
|||
config.write(configfile) |
|||
# Read name given by user for object choiseObject in ini file |
|||
def getIniName(choiceObject): |
|||
config = configparser.ConfigParser() |
|||
ini_file = os.path.join(macroDirectory, "sketchConstraintFromSpreadsheet.ini") |
|||
if os.path.exists(ini_file): |
|||
config.read(ini_file) |
|||
label=config.get('SettingNames', choiceObject, fallback=choiceObject) |
|||
return label |
|||
return choiceObject |
|||
# Read column for alias labelss value in ini file |
|||
def getIniCellForAlias(): |
|||
config = configparser.ConfigParser() |
|||
ini_file = os.path.join(macroDirectory, "sketchConstraintFromSpreadsheet.ini") |
|||
if os.path.exists(ini_file): |
|||
config.read(ini_file) |
|||
cell=config.get('SettingsAlias', 'textboxAlias', fallback='B1') |
|||
if cell=='' : |
|||
cell='B1' |
|||
return cell |
|||
else: |
|||
return 'B1' |
|||
# read checkbox value in ini file |
|||
def getIniCheckboxStateForAlias(): |
|||
config = configparser.ConfigParser() |
|||
ini_file = os.path.join(macroDirectory, "sketchConstraintFromSpreadsheet.ini") |
|||
if os.path.exists(ini_file): |
|||
config.read(ini_file) |
|||
return config.getboolean('SettingsAlias', 'checkboxAlias', fallback=True) |
|||
else: |
|||
return True |
|||
# save column for alias labels and checkbox for alias value in ini file |
|||
def save_settings_Alias(cell,checked): |
|||
config = configparser.ConfigParser() |
|||
ini_file = os.path.join(macroDirectory, "sketchConstraintFromSpreadsheet.ini") |
|||
config.read(ini_file) |
|||
if 'SettingsAlias' not in config: |
|||
config.add_section('SettingsAlias') |
|||
config.set('SettingsAlias', 'textboxAlias', cell.upper()) |
|||
config.set('SettingsAlias', 'checkboxAlias', str(checked)) |
|||
with open(ini_file, 'w') as configfile: |
|||
config.write(configfile) |
|||
################################################################################# |
|||
# part code for objects creation at the begining of the macro execution. |
|||
# If there is no spreadsheet or sketch: ask for creation. |
|||
################################################################################# |
|||
#=============================================================================== |
|||
# dialog box class for objects creations |
|||
#=============================================================================== |
|||
class Dialog(QtGui.QDialog): |
|||
def __init__(self, documentState, spreadsheetState, textdocumentState, bodyState, sketchState, text='', parent=None): |
|||
super(Dialog, self).__init__(parent) |
|||
toolLip=("If checked, alias will automatically be created during each macro execution. "+ |
|||
"Alias will be created at the right of cells with text. Cells with text is this one or cells and under :") |
|||
self.documentState = documentState |
|||
self.textdocumentState = textdocumentState |
|||
self.spreadsheetState = spreadsheetState |
|||
self.bodyState = bodyState |
|||
self.sketchState = sketchState |
|||
self.text_widget = QtGui.QLabel(text) |
|||
self.checkbox1Document = self.create_checkbox("Document", documentState) |
|||
self.checkboxTextDocument = self.create_checkbox("Text document", textdocumentState) |
|||
self.checkboxSpreadsheet = self.create_checkbox("Spreadsheets", spreadsheetState) |
|||
self.checkbox3Body = self.create_checkbox("Body", bodyState) |
|||
self.checkbox4Sketch = self.create_checkbox("Sketch", sketchState) |
|||
self.checkboxAlias = QtGui.QCheckBox("Text for Alias in this cell and under :") |
|||
self.checkboxAlias.setToolTip(toolLip) |
|||
self.textboxSpreadsheetName = QtGui.QLineEdit(getIniName('spreadsheet')) |
|||
self.textboxSpreadsheetName.setFixedWidth(200) # size |
|||
self.textboxSpreadsheetName.setToolTip('Name for the spreadsheet') |
|||
self.textboxTextdocumentName = QtGui.QLineEdit(getIniName('textdocument')) |
|||
self.textboxTextdocumentName.setFixedWidth(200) # size |
|||
self.textboxTextdocumentName.setToolTip('Name for the Text Document') |
|||
self.textboxBodyName = QtGui.QLineEdit(getIniName('body')) |
|||
self.textboxBodyName.setFixedWidth(200) # size |
|||
self.textboxBodyName.setToolTip('Name for the body') |
|||
self.textboxSketchName = QtGui.QLineEdit(getIniName('sketch')) |
|||
self.textboxSketchName.setMaximumWidth(200) # Set the maximum width of the text box |
|||
self.textboxSketchName.setFixedWidth(200) # size |
|||
self.textboxSketchName.setToolTip('Name for the sketch') |
|||
layout_checkbox1Document = QtGui.QHBoxLayout() |
|||
#layout_checkbox1Document.addSpacing(20) |
|||
if documentState !='no' : |
|||
layout_checkbox1Document.addWidget(self.checkbox1Document) |
|||
layout_checkboxSpreadSheet = QtGui.QHBoxLayout() |
|||
layout_checkboxSpreadSheet.addSpacing(20) |
|||
if spreadsheetState !='no' : |
|||
layout_checkboxSpreadSheet.addWidget(self.checkboxSpreadsheet) |
|||
layout_checkboxSpreadSheet.addWidget(self.textboxSpreadsheetName) |
|||
layout_checkboxTextDocument = QtGui.QHBoxLayout() |
|||
layout_checkboxTextDocument.addSpacing(20) |
|||
if textdocumentState!= 'no' : |
|||
layout_checkboxTextDocument.addWidget(self.checkboxTextDocument) |
|||
layout_checkboxTextDocument.addWidget(self.textboxTextdocumentName) |
|||
layout_checkbox3Body = QtGui.QHBoxLayout() |
|||
layout_checkbox3Body.addSpacing(20) |
|||
if bodyState != 'no' : |
|||
layout_checkbox3Body.addWidget(self.checkbox3Body) |
|||
layout_checkbox3Body.addWidget(self.textboxBodyName) |
|||
layout_checkbox4Sketch = QtGui.QHBoxLayout() |
|||
layout_checkbox4Sketch.addSpacing(40) |
|||
if sketchState != 'no' : |
|||
layout_checkbox4Sketch.addWidget(self.checkbox4Sketch) |
|||
layout_checkbox4Sketch.addWidget(self.textboxSketchName) |
|||
self.radiobutton1 = QtGui.QRadioButton("XY Plane") |
|||
self.radiobutton2 = QtGui.QRadioButton("XZ Plane") |
|||
self.radiobutton3 = QtGui.QRadioButton("YZ Plane") |
|||
self.radiobutton1.setIcon(QIcon(":/icons/view-top")) |
|||
self.radiobutton2.setIcon(QIcon(":/icons/view-front")) |
|||
self.radiobutton3.setIcon(QIcon(":/icons/view-right")) |
|||
# Set the XY Plane radio button to be checked by default |
|||
self.radiobutton1.setChecked(True) |
|||
self.textboxAlias = QtGui.QLineEdit(self.getIniCellForAlias()) |
|||
self.textboxAlias.setMaximumWidth(50) # Set the maximum width of the text box |
|||
self.textboxAlias.setMaxLength(4) # Limit the text box to 4 characters |
|||
self.textboxAlias.setToolTip(toolLip) |
|||
regex = QtCore.QRegExp("^[a-zA-Z]+[0-9]+$") |
|||
validator = QtGui.QRegExpValidator(regex, self) |
|||
self.textboxAlias.setValidator(validator) |
|||
self.textboxAlias.textChanged.connect(self.onTextChanged) |
|||
# Set the checkboxes |
|||
self.checkbox1Document.setChecked(documentState == "exist" or documentState == "on") |
|||
self.checkboxTextDocument.setChecked(True) |
|||
self.checkboxSpreadsheet.setChecked(spreadsheetState == "exist" or spreadsheetState == "on") |
|||
self.checkbox3Body.setChecked(bodyState == "exist" or bodyState == "on") |
|||
self.checkbox4Sketch.setChecked(sketchState == "exist" or sketchState == "on") |
|||
self.checkboxAlias.setChecked(self.getIniCheckboxStateForAlias()) |
|||
self.checkbox1Document.stateChanged.connect(self.update_document_checkbox) |
|||
self.checkboxTextDocument.stateChanged.connect(self.update_textdocument_checkbox) |
|||
self.checkboxSpreadsheet.stateChanged.connect(self.update_spreadsheet_checkbox) |
|||
self.checkbox3Body.stateChanged.connect(self.update_body_checkbox) |
|||
self.checkbox4Sketch.stateChanged.connect(self.update_sketch_checkbox) |
|||
layout = QtGui.QVBoxLayout() |
|||
if text!='' : |
|||
layout.addWidget(self.text_widget) |
|||
layout.addLayout(layout_checkbox1Document) |
|||
layout.addLayout(layout_checkboxSpreadSheet) |
|||
layout.addLayout(layout_checkboxTextDocument) |
|||
layout.addLayout(layout_checkbox3Body) |
|||
layout.addLayout(layout_checkbox4Sketch) |
|||
layout_radio1 = QtGui.QHBoxLayout() |
|||
layout_radio1.addSpacing(60) |
|||
if sketchState !='no' : |
|||
layout_radio1.addWidget(self.radiobutton1) |
|||
layout_radio2 = QtGui.QHBoxLayout() |
|||
layout_radio2.addSpacing(60) |
|||
if sketchState !='no' : |
|||
layout_radio2.addWidget(self.radiobutton2) |
|||
layout_radio3 = QtGui.QHBoxLayout() |
|||
layout_radio3.addSpacing(60) |
|||
if sketchState !='no' : |
|||
layout_radio3.addWidget(self.radiobutton3) |
|||
layout.addLayout(layout_radio1) |
|||
layout.addLayout(layout_radio2) |
|||
layout.addLayout(layout_radio3) |
|||
layout_Alias = QtGui.QHBoxLayout() |
|||
layout_Alias.addWidget(self.checkboxAlias) |
|||
layout_Alias.addWidget(self.textboxAlias) |
|||
layout_Alias.addItem(QtGui.QSpacerItem(0, 0, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred)) |
|||
layout.addLayout(layout_Alias) |
|||
# Add OK and Cancel buttons |
|||
self.buttons = QtGui.QDialogButtonBox( |
|||
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel, |
|||
QtCore.Qt.Horizontal, self) |
|||
self.buttons.accepted.connect(self.accept) |
|||
self.buttons.rejected.connect(self.reject) |
|||
layout.addWidget(self.buttons) |
|||
self.setLayout(layout) |
|||
self.update_document_checkbox() |
|||
self.update_spreadsheet_checkbox() |
|||
self.update_textdocument_checkbox() |
|||
self.update_body_checkbox() |
|||
self.update_sketch_checkbox() |
|||
self.update_radiobuttons() |
|||
def onTextChanged(self, text): |
|||
# Convertir le texte en majuscules |
|||
uppercase_text = text.upper() |
|||
self.textboxAlias.blockSignals(True) |
|||
self.textboxAlias.setText(uppercase_text) |
|||
self.textboxAlias.blockSignals(False) |
|||
def update_document_checkbox(self): |
|||
self.checkboxTextDocument.setEnabled(self.checkbox1Document.isChecked()) |
|||
if not self.checkbox1Document.isChecked() : |
|||
self.checkboxTextDocument.setChecked(False) |
|||
self.checkboxSpreadsheet.setEnabled(self.checkbox1Document.isChecked()) |
|||
self.checkboxSpreadsheet.setChecked(self.checkbox1Document.isChecked()) |
|||
self.checkboxTextDocument.setEnabled(self.checkbox1Document.isChecked()) |
|||
self.checkboxTextDocument.setChecked(self.checkbox1Document.isChecked()) |
|||
self.checkbox3Body.setEnabled(self.checkbox1Document.isChecked()) |
|||
self.checkbox3Body.setChecked(self.checkbox1Document.isChecked()) |
|||
self.update_textdocument_checkbox() |
|||
self.update_spreadsheet_checkbox() |
|||
self.update_body_checkbox() |
|||
def update_textdocument_checkbox(self): |
|||
if self.textdocumentState == 'exist' : |
|||
self.checkboxTextDocument.setEnabled(False) |
|||
self.checkboxTextDocument.setChecked(True) |
|||
self.textboxTextdocumentName.setEnabled(self.checkboxTextDocument.isChecked() |
|||
and self.checkboxTextDocument.isEnabled()) |
|||
def update_spreadsheet_checkbox(self): |
|||
if self.spreadsheetState == 'exist' : |
|||
self.checkboxSpreadsheet.setEnabled(False) |
|||
self.checkboxSpreadsheet.setChecked(True) |
|||
self.textboxSpreadsheetName.setEnabled(self.checkboxSpreadsheet.isChecked() |
|||
and self.checkboxSpreadsheet.isEnabled()) |
|||
def update_body_checkbox(self): |
|||
if self.bodyState == 'exist' : |
|||
self.checkbox3Body.setEnabled(False) |
|||
self.checkbox3Body.setChecked(True) |
|||
self.textboxBodyName.setEnabled(self.checkbox3Body.isChecked() |
|||
and self.checkbox3Body.isEnabled()) |
|||
self.checkbox4Sketch.setEnabled(self.checkbox3Body.isChecked()) |
|||
self.checkbox4Sketch.setChecked(self.checkbox3Body.isChecked()) |
|||
self.update_sketch_checkbox () |
|||
def update_sketch_checkbox(self): |
|||
# checked = self.checkbox3Body.isChecked() |
|||
if self.sketchState =='exist' : |
|||
self.checkbox4Sketch.setEnabled(False) |
|||
self.checkbox4Sketch.setChecked(True) |
|||
self.textboxSketchName.setEnabled(self.checkbox4Sketch.isChecked() |
|||
and self.checkbox4Sketch.isEnabled()) |
|||
self.update_radiobuttons() |
|||
def update_radiobuttons(self): |
|||
enable = self.checkbox4Sketch.isChecked() and self.checkbox3Body.isChecked() |
|||
if not self.checkbox4Sketch.isChecked() or not self.checkbox3Body.isChecked(): |
|||
enable=False |
|||
if not self.checkbox4Sketch.isChecked() : |
|||
enable=False |
|||
if not self.checkbox4Sketch.isEnabled() : |
|||
enable=False |
|||
self.radiobutton1.setEnabled(enable) |
|||
self.radiobutton2.setEnabled(enable) |
|||
self.radiobutton3.setEnabled(enable) |
|||
def create_checkbox(self, text, state): |
|||
checkbox = QtGui.QCheckBox(text+' found' if state == "exist" else "No " + |
|||
text + " found, create one?") |
|||
checkbox.setEnabled(state != "off") |
|||
checkbox.setChecked(state != "off") |
|||
if state == "exist" : |
|||
checkbox.setEnabled(False) |
|||
checkbox.setChecked(True) |
|||
return checkbox |
|||
def getIniCellForAlias(self): |
|||
return getIniCellForAlias() |
|||
def getIniCheckboxStateForAlias(self): |
|||
return getIniCheckboxStateForAlias() |
|||
def save_settings(self): |
|||
save_settings_Alias(self.textboxAlias.text(),self.checkboxAlias.isChecked()) |
|||
save_settings_Name('body',self.textboxBodyName.text()) |
|||
save_settings_Name('spreadsheet', self.textboxSpreadsheetName.text()) |
|||
save_settings_Name('textdocument', self.textboxTextdocumentName.text()) |
|||
save_settings_Name('sketch', self.textboxSketchName.text()) |
|||
# Retrieve the results after displaying the object creation dialogue box. |
|||
def get_user_input(documentState, spreadsheetState, textdocumentState, bodyState, sketchState, text=''): |
|||
dialog = Dialog(documentState, spreadsheetState, textdocumentState, bodyState, sketchState, text) |
|||
result = dialog.exec_() # Display the dialog box and wait for the user to make their choices |
|||
if result : |
|||
dialog.save_settings() # Save the settings to the INI file |
|||
# Return the state of the checkboxes and radio buttons, and the result of the dialog box |
|||
return { |
|||
'checkbox1Document': dialog.checkbox1Document.isChecked(), |
|||
'checkboxSpreadsheet': dialog.checkboxSpreadsheet.isChecked(), |
|||
'checkboxTextDocument': dialog.checkboxTextDocument.isChecked(), |
|||
'checkbox3Body': dialog.checkbox3Body.isChecked(), |
|||
'checkbox4Sketch': dialog.checkbox4Sketch.isChecked(), |
|||
'radiobutton1': dialog.radiobutton1.isChecked(), |
|||
'radiobutton2': dialog.radiobutton2.isChecked(), |
|||
'radiobutton3': dialog.radiobutton3.isChecked(), |
|||
'checkboxAlias': dialog.checkboxAlias.isChecked(), |
|||
'textboxAlias': dialog.textboxAlias.text(), |
|||
'textboxSpreadsheetName' : dialog.textboxSpreadsheetName.text(), |
|||
'textboxTextdocumentName' : dialog.textboxTextdocumentName.text(), |
|||
'textboxBodyName' : dialog.textboxBodyName.text(), |
|||
'textboxSketchName' : dialog.textboxSketchName.text(), |
|||
'result': 'OK' if result == QtGui.QDialog.Accepted else 'Cancel' if result == QtGui.QDialog.Rejected else 'Closed' |
|||
} |
|||
#========================================================= |
|||
# Function that manages everything to add |
|||
# necessary or desired objects. |
|||
#========================================================= |
|||
def addTheObjects(): |
|||
def firstObject(type, doc = App.ActiveDocument): |
|||
if doc==None : |
|||
return None |
|||
for obj in doc.Objects: |
|||
if obj.TypeId == type: |
|||
return obj |
|||
doc = Gui.ActiveDocument |
|||
firstTextdocument=None |
|||
firstSpreadsheet=None |
|||
firstBody=None |
|||
firstSketch=None |
|||
if not doc : |
|||
paramDocument='on' |
|||
paramTextdocument ='on' |
|||
paramSpreadsheet ='on' |
|||
paramBody ='on' |
|||
paramSketch ='on' |
|||
else : |
|||
paramDocument='exist' |
|||
firstTextdocument=firstObject('App::TextDocument') |
|||
if firstTextdocument!=None : |
|||
paramTextdocument= 'exist' |
|||
else : |
|||
paramTextdocument= 'off' |
|||
firstSpreadsheet=firstObject('Spreadsheet::Sheet') |
|||
if firstSpreadsheet!=None : |
|||
paramSpreadsheet= 'exist' |
|||
else : |
|||
paramSpreadsheet= 'on' |
|||
firstBody=firstObject('PartDesign::Body' ) |
|||
if firstBody!=None : |
|||
paramBody= 'exist' |
|||
else : |
|||
paramBody= 'on' |
|||
firstSketch=firstObject('Sketcher::SketchObject' ) |
|||
if firstSketch!=None : |
|||
paramSketch= 'exist' |
|||
else : |
|||
paramSketch= 'on' |
|||
# The creation of the objects themselves. Return the object |
|||
def createObjects(user_input): |
|||
objCreate=False |
|||
if user_input['checkbox1Document'] and not Gui.ActiveDocument: |
|||
firstDocument=App.newDocument() |
|||
objCreate=True |
|||
if firstObject('Spreadsheet::Sheet')==None and user_input['checkboxSpreadsheet'] : |
|||
firstSpreadsheet = App.activeDocument().addObject('Spreadsheet::Sheet', user_input['textboxSpreadsheetName']) |
|||
Gui.ActiveDocument.getObject(firstSpreadsheet.Name).doubleClicked() |
|||
objCreate=True |
|||
if firstObject('App::TextDocument')==None and user_input['checkboxTextDocument'] : |
|||
firstTextdocument = App.activeDocument().addObject('App::TextDocument', user_input['textboxTextdocumentName']) |
|||
#Gui.ActiveDocument.getObject(firstTextdocument.Name).doubleClicked() |
|||
objCreate=True |
|||
if firstObject('PartDesign::Body' )==None and user_input['checkbox3Body'] : |
|||
firstBody = App.activeDocument().addObject('PartDesign::Body', user_input['textboxBodyName']) |
|||
objCreate=True |
|||
else : |
|||
firstBody = firstObject('PartDesign::Body' ) |
|||
if firstObject('Sketcher::SketchObject' )==None and user_input['checkbox4Sketch'] : |
|||
if user_input['radiobutton1']: |
|||
sketchSupport='XY_Plane' |
|||
elif user_input['radiobutton2']: |
|||
sketchSupport='XZ_Plane' |
|||
elif user_input['radiobutton3']: |
|||
sketchSupport='YZ_Plane' |
|||
# attachedTo=App.ActiveDocument.getObject('Body') |
|||
# attachedTo=firstObject('PartDesign::Body' ) |
|||
firstSketch=firstBody.newObject('Sketcher::SketchObject', user_input['textboxSketchName']) |
|||
firstSketch.Support = (App.ActiveDocument.getObject(sketchSupport),['']) |
|||
firstSketch.MapMode = 'FlatFace' |
|||
Gui.ActiveDocument.getObject(firstSketch.Name).doubleClicked() |
|||
Gui.runCommand('Std_TileWindows',0) |
|||
objCreate=True |
|||
return objCreate |
|||
# Use the function to display the dialog box and get the user's input |
|||
if paramSpreadsheet!='exist' or paramSketch!='exist' : |
|||
user_input = get_user_input(paramDocument, paramSpreadsheet, paramTextdocument, |
|||
paramBody, paramSketch) |
|||
if user_input['result']=='OK' : |
|||
return createObjects(user_input) |
|||
else : |
|||
return True # To stop macro because canceled |
|||
# App.ActiveDocument.recompute() |
|||
# end of def addTheObjects() |
|||
############################################################# |
|||
############################################################# |
|||
# end part for objects creation at the begining. |
|||
############################################################## |
|||
############################################################## |
|||
################################################################################# |
|||
# 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 allias itself |
|||
premierCaractereEnMinuscule = True # Force the first character to be in lower case |
|||
# list of characters to be replaced by an equivalent. for example an @ will be replaced by 'a' |
|||
# 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 traitementChaineSource(chaineSource, separateur, nouveauCaract, majuscule): |
|||
# If separator is ' ' and nouveauCaract is '_', and majuscule is True |
|||
# transforms "Diametre du cylindre" into "Diametre_Du_Cylindre |
|||
def remplaceCararcDansMot(mot): |
|||
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 |
|||
#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 |
|||
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 get_column(cell): |
|||
column = ''.join([c for c in cell if not c.isdigit()]) |
|||
return column |
|||
def get_row(cell): |
|||
row = ''.join([c for c in cell if c.isdigit()]) |
|||
if row=='' : |
|||
row='1' |
|||
return int(row) |
|||
def get_next_column(column): |
|||
# Check the last character of the column |
|||
last_char = column[-1] |
|||
if last_char != 'Z': |
|||
# If the last character is not 'Z', just increment this character |
|||
next_column = column[:-1] + chr(ord(last_char) + 1) |
|||
else: |
|||
# If the last character is 'Z', add an 'A' to the end of the column |
|||
next_column = column + 'A' if column != 'Z' else 'AA' |
|||
return next_column |
|||
def nextCell(cell): |
|||
colonne, ligne = re.match(r"([A-Z]+)([0-9]+)", cell).groups() |
|||
num = 0 |
|||
for c in colonne: |
|||
num = num * 26 + ord(c) - ord('A') + 1 |
|||
num += 1 |
|||
colonne_suivante = "" |
|||
while num > 0: |
|||
num, reste = divmod(num-1, 26) |
|||
colonne_suivante = chr(65 + reste) + colonne_suivante |
|||
return colonne_suivante + ligne |
|||
def setAlias(): |
|||
# 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. |
|||
sels = Gui.Selection.getSelectionEx() |
|||
try : |
|||
mySpreadSheet = Gui.ActiveDocument.ActiveView.getSheet() |
|||
except : |
|||
QtWidgets.QMessageBox.warning(Gui.getMainWindow(), "Warning", |
|||
"No active spreadsheet with selected cells."+ |
|||
"\nSelect objects in sketch, \n"+ |
|||
"and then select a cell in spreadsheet") |
|||
return True |
|||
aw = Gui.getMainWindow().centralWidget().activeSubWindow() # Store the active window |
|||
# To get list of all selected cells |
|||
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) |
|||
tabCells=[] |
|||
if getIniCheckboxStateForAlias() : |
|||
sellStart=getIniCellForAlias() |
|||
columnTextAlias=get_column(sellStart) |
|||
rowStartAlias=get_row(sellStart) |
|||
for i in range(rowStartAlias, 101): |
|||
tabCells.append(columnTextAlias+format(i)) |
|||
else : |
|||
for item in sel_items: # The selected cells are scanned |
|||
tabCells.append( cellName(item.row(), item.column())) # We retrieve the cell ID in tabCells |
|||
# we check that one of the cells is not a numeric. If not, message and exit |
|||
for cell in tabCells: # The selected cells are scanned |
|||
activeCellContenu = mySpreadSheet.getContents(cell) |
|||
f = '' |
|||
try : |
|||
f = float(activeCellContenu) |
|||
except : |
|||
pass |
|||
if f != '' : |
|||
QtWidgets.QMessageBox.warning(Gui.getMainWindow(), |
|||
"Warning", "cell: "+cell+ "\n"+"There is a number in one of the selected cells." + |
|||
" Alias is not possible. \nTry again after correction") |
|||
return False |
|||
changed=False |
|||
for cell in tabCells: # The selected cells are scanned |
|||
next_cell=nextCell(cell)# cell at 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 |
|||
if alias!='' : |
|||
mySpreadSheet.setAlias(next_cell, alias) # The alias is assigned to the right-hand neighbouring cell |
|||
changed=True |
|||
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)) |
|||
if not changed and getIniCheckboxStateForAlias(): |
|||
QtWidgets.QMessageBox.warning(Gui.getMainWindow(), |
|||
"Warning","No alias have been created. Be sure to have the name of the alias under cell "+getIniCellForAlias() |
|||
+ '\nand a numeric value in the next column. Select the case with the numeric value.' ) |
|||
if getIniCheckboxStateForAlias() : # checked => create alias but continue (False) |
|||
return False # the macro continue |
|||
else : |
|||
return True |
|||
################################################################################# |
|||
# End part code for alias |
|||
################################################################################# |
|||
#======================================================= |
|||
# 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): |
|||
toolLip="If checked, alias will automatically be created during each macro execution. Alias will be built for the right cell from the text in this cell." |
|||
self.setWindowIcon(QtGui.QIcon('dialog_icon.png')) |
|||
gridLayout = QtGui.QGridLayout() |
|||
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) |
|||
self.textboxAlias = QtGui.QLineEdit(self.getIniCellForAlias()) |
|||
self.textboxAlias.setMaximumWidth(50) # Set the maximum width of the text box |
|||
self.textboxAlias.setMaxLength(4) # Limit the text box to 4 characters |
|||
self.textboxAlias.setToolTip(toolLip) |
|||
gridLayout.addWidget(option1Button, 0, 0) |
|||
gridLayout.addWidget(option2Button, 0, 1) |
|||
gridLayout.addWidget(option3Button, 1, 0) |
|||
gridLayout.addWidget(option4Button, 1, 1) |
|||
gridLayout.addWidget(self.textboxAlias,4,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.checkboxConflic = QtGui.QCheckBox("Conflict detection") |
|||
self.checkboxConflic.setChecked(True) |
|||
gridLayout.addWidget(self.checkboxConflic, 2, 0, 1, 2) |
|||
self.checkboxConflic.clicked.connect(self.onOptionCheckBox) |
|||
self.checkboxAlias = QtGui.QCheckBox("Automatic alias labels for this cell, and under :") |
|||
self.checkboxAlias.setToolTip(toolLip) |
|||
# Only letter in textboxAlias |
|||
regex = QtCore.QRegExp("^[a-zA-Z]+[0-9]+$") |
|||
validator = QtGui.QRegExpValidator(regex, self) |
|||
self.textboxAlias.setValidator(validator) |
|||
self.textboxAlias.textChanged.connect(self.onTextChanged) |
|||
# self.checkboxAlias.setChecked(True) |
|||
self.checkboxAlias.setChecked(getIniCheckboxStateForAlias()) |
|||
gridLayout.addWidget(self.checkboxAlias, 4, 0) |
|||
horizontal_line = QtGui.QFrame() |
|||
horizontal_line.setFrameShape(QtGui.QFrame.HLine) |
|||
horizontal_line.setFrameShadow(QtGui.QFrame.Sunken) |
|||
gridLayout.addWidget(horizontal_line, 3, 0,1,2) |
|||
# read ini file to get last checkBoxState |
|||
config = configparser.ConfigParser() |
|||
# macroDirectory = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro").GetString("MacroPath") + "\\" |
|||
macroDirectory = FreeCAD.getUserMacroDir(True) |
|||
try : |
|||
filePath = os.path.join(macroDirectory, "sketchConstraintFromSpreadsheet.ini") |
|||
config.read(filePath) |
|||
# read ini file to know last time state |
|||
lasChecked = config.getboolean('ConflictDetection', 'save_checkbox_state') |
|||
self.checkboxConflic.setChecked(lasChecked) |
|||
except : |
|||
pass |
|||
# window positioning |
|||
centerPoint = QGuiApplication.screens()[0].geometry().center() |
|||
self.move(centerPoint - self.frameGeometry().center()) |
|||
def onTextChanged(self, text): |
|||
# Convertir le texte en majuscules |
|||
uppercase_text = text.upper() |
|||
self.textboxAlias.blockSignals(True) |
|||
self.textboxAlias.setText(uppercase_text) |
|||
self.textboxAlias.blockSignals(False) |
|||
def onOption1(self): |
|||
self.choiceConstraint = 'DistanceX' |
|||
self.save_settings() |
|||
self.close() |
|||
def onOption2(self): |
|||
self.choiceConstraint = 'DistanceY' |
|||
self.save_settings() |
|||
self.close() |
|||
def onOption3(self): |
|||
self.save_settings() |
|||
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") + "\\" |
|||
macroDirectory = FreeCAD.getUserMacroDir(True) |
|||
# Save checkbox state to file |
|||
filePath = os.path.join(macroDirectory, "sketchConstraintFromSpreadsheet.ini") |
|||
config = configparser.ConfigParser() |
|||
config['Conflict_detection'] = {'save_checkbox_state': str(int(self.getCheckBoxState()))} |
|||
with open(filePath, 'w') as configfile: |
|||
config.write(configfile) |
|||
def save_settings(self): |
|||
save_settings_Alias(self.textboxAlias.text(),self.checkboxAlias.isChecked()) |
|||
def getIniCellForAlias(self): |
|||
return getIniCellForAlias() |
|||
def getIniCheckboxStateForAlias(self): |
|||
return getIniCheckboxStateForAlias() |
|||
def getCheckBoxState(self): |
|||
return self.checkboxConflic.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 |
|||
# - typeIdGeometry : Part::GeomLineSegment .... |
|||
# - typeInSubElementName : Edge, vertex ... |
|||
# - 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 |
|||
# - isInternalObject : True if the objetct is on the active sketch |
|||
##################################################################################### |
|||
def featuresObjSelected (mySketch, sel, numOrdreObjSelected, indexExtremite) : |
|||
indexExtremiteLine = 1 |
|||
indexObjectHavingPoint = -10 |
|||
typeIdGeometry = None |
|||
x, y = 0, 0 |
|||
isInternalObject=True |
|||
itemName = sel.SubElementNames[numOrdreObjSelected] # ex Edge5 ( line) |
|||
tabExternalGeomery = [] |
|||
for item in ActiveSketch.ExternalGeometry : |
|||
for objName in item [1] : |
|||
# to get something like |
|||
# [('Sketch', 'Edge2'), ('Sketch001', 'Edge2'), ('Sketch001', 'Edge1'), ('Sketch', 'Edge1')] |
|||
tabExternalGeomery.append((item[0].Name, objName)) |
|||
if itemName == 'RootPoint' : |
|||
typeInSubElementName = 'RootPoint' |
|||
indexObjectHavingPoint = -1 |
|||
typeIdGeometry = mySketch.Geometry[indexObjectHavingPoint].TypeId |
|||
x, y = 0, 0 |
|||
else : |
|||
# for Edge5, typeInSubElementName ->'Edge' and '5' |
|||
typeInSubElementName, numInNameStr = [''.join(c) for _, c in itertools.groupby(itemName, str.isalpha)] |
|||
numInName = int(numInNameStr) |
|||
# 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 |
|||
if typeInSubElementName == 'ExternalEdge' : |
|||
isInternalObject = False |
|||
indexObjectHavingPoint = - numInName - 2 |
|||
indexExtremiteLine = indexExtremite |
|||
externalSketchName, externalGeometryName = tabExternalGeomery[numInName - 1] |
|||
iTypeExternal, iNumStr = [''.join(c) for _, c in itertools.groupby(externalGeometryName, str.isalpha)] |
|||
iNumExtGeometry = int(iNumStr) |
|||
typeIdGeometry = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry - 1].TypeId |
|||
if len(sel.SubElementNames) == 1 : |
|||
if typeIdGeometry == 'Part::GeomLineSegment' : |
|||
if indexExtremite == 1 : |
|||
x = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry - 1].StartPoint.x |
|||
y = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry - 1].StartPoint.y |
|||
elif indexExtremite == 2 : |
|||
x = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry - 1].EndPoint.x |
|||
y = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry - 1].EndPoint.y |
|||
if typeIdGeometry in ['Part::GeomCircle', 'Part::GeomArcOfCircle'] : |
|||
x = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry - 1].Center.x |
|||
y = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry - 1].Center.y |
|||
indexExtremiteLine = 3 # 3 for center |
|||
# We selected an internal circle but for center (two objects selected) |
|||
if typeInSubElementName == 'Edge'and len(sel.SubElementNames) == 2 : |
|||
typeIdGeometry = mySketch.Geometry[numInName - 1].TypeId |
|||
if typeIdGeometry in ['Part::GeomCircle', 'Part::GeomArcOfCircle'] : |
|||
indexObjectHavingPoint = numInName - 1 |
|||
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) |
|||
if indexObjectHavingPoint >= 0 : # internal vertex |
|||
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 indexObjectHavingPoint < 0 : # external vertex = vertex of another sketch |
|||
isInternalObject = False |
|||
externalSketchName, externalGeometryName = tabExternalGeomery[-indexObjectHavingPoint-3] |
|||
iType, iNumStr = [''.join(c) for _, c in itertools.groupby(externalGeometryName, str.isalpha)] |
|||
iNumExtGeometry = int(iNumStr) |
|||
typeExternal=App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry-1].TypeId |
|||
typeIdGeometry = typeExternal |
|||
if typeExternal=='Part::GeomLineSegment' : |
|||
if indexExtremiteLine == 1 : |
|||
x = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry-1].StartPoint.x |
|||
y = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry-1].StartPoint.y |
|||
if indexExtremiteLine == 0 : |
|||
x = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry-1].EndPoint.x |
|||
y = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry-1].EndPoint.y |
|||
if typeExternal == 'Part::GeomCircle' : |
|||
x = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry-1].Center.x |
|||
y = App.ActiveDocument.getObject(externalSketchName).Geometry[iNumExtGeometry-1].Center.y |
|||
if typeInSubElementName == 'Constraint' and len(sel.SubElementNames) == 1 : |
|||
indexConstraint = numInName - 1 |
|||
indexObjectHavingPoint = indexConstraint |
|||
typeIdGeometry = 'Constraint' |
|||
return typeIdGeometry, typeInSubElementName, indexObjectHavingPoint, indexExtremiteLine, x ,y, isInternalObject |
|||
########################################## |
|||
# 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 addTheObjects():# Creation of missing objects if wanted |
|||
return |
|||
#Part of setAlias : problem to create alias ? |
|||
if not getIniCheckboxStateForAlias() : |
|||
sels = Gui.Selection.getSelectionEx() |
|||
if not(len(sels) != 0 and len(sels[0].SubElementNames) != 0) : # nothing selected |
|||
print('Anything selected') |
|||
### Ask if the user is sure |
|||
if getIniCheckboxStateForAlias() : |
|||
message=('Do you want to create alias automatically for cells at the right ' |
|||
+'of cells '+ getIniCellForAlias()+' and under?') |
|||
else : |
|||
message=("You did not select any object in an active sketch" |
|||
+ "\nDoes it mean you want to create alias at the rtight of selected cells " |
|||
+ "using text in selected cells ?") |
|||
if QtGui.QMessageBox.warning(Gui.getMainWindow(), |
|||
'Warning', message, |
|||
QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel) == QtGui.QMessageBox.Cancel: |
|||
# if user don't want |
|||
return True |
|||
setAlias() |
|||
return |
|||
try : |
|||
mySpreadSheet = Gui.ActiveDocument.ActiveView.getSheet() |
|||
except : |
|||
QtWidgets.QMessageBox.warning(Gui.getMainWindow(), "Warning", |
|||
"No active spreadsheet with selected cells."+ |
|||
"\nSelect objects in sketch, \n"+ |
|||
"and then select a cell in spreadsheet") |
|||
return True |
|||
####If we select a Pad not in edition mode and a cell |
|||
myPad=None |
|||
try : |
|||
sels = Gui.Selection.getSelectionEx() |
|||
if sels[0].Object.TypeId=='PartDesign::Pad' : |
|||
myPad=sels[0].Object |
|||
typeIdGeometry1='PartDesign::Pad' |
|||
except : |
|||
pass |
|||
try : |
|||
mySketch = ActiveSketch |
|||
except : |
|||
if myPad==None : |
|||
QtWidgets.QMessageBox.information(None, "Warning", |
|||
"Select object (point, line...) in the sketch in edition mode") |
|||
# activateSketchEditingWindow() |
|||
return |
|||
# 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) |
|||
# ajouter un controle |
|||
#si cellCode est bien à droite des cellules avec alias ou pas |
|||
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 |
|||
column = get_column(cellCode) |
|||
# Part sketch |
|||
#---------------------------- |
|||
sels = Gui.Selection.getSelectionEx() |
|||
if (len(sels) == 0 or len(sels[0].SubElementNames) == 0) and myPad==None : |
|||
QtWidgets.QMessageBox.information(None, "Warning", "Anything 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, isInternalObject1)=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, isInternalObject2) = featuresObjSelected (ActiveSketch, sels[0], 0, 2) |
|||
# two obj selected |
|||
#------------------------ |
|||
if len(sels[0].SubElementNames) == 2: # two obj selected |
|||
(typeIdGeometry1,typeInSubElementName1, indexObjectHavingPoint1, indexExtremiteLine1, |
|||
x1 ,y1, isInternalObject1) = featuresObjSelected (ActiveSketch, sels[0], 0, 1) |
|||
(typeIdGeometry2,typeInSubElementName2, indexObjectHavingPoint2, indexExtremiteLine2, |
|||
x2 ,y2, isInternalObject2) = featuresObjSelected (ActiveSketch, sels[0], 1, 1) |
|||
if ((typeInSubElementName1 not in ('Vertex', 'RootPoint', 'ExternalEdge') \ |
|||
or typeInSubElementName2 not in ('Vertex', 'RootPoint', 'ExternalEdge')) |
|||
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'] )) : |
|||
# 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' |
|||
# ------------------------------------------------ |
|||
# ask the user what kind of constraint he wants |
|||
#------------------------------------------------ |
|||
form = getConstraintType(buttonHavingFocus) |
|||
form.exec_() |
|||
# conflict : 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 |
|||
isInternalObject1, isInternalObject2 = isInternalObject2, isInternalObject1 |
|||
x1, x2, y1, y2 = x2, x1, y2, y1 |
|||
if column!=get_next_column(get_column(getIniCellForAlias())) and getIniCheckboxStateForAlias(): |
|||
get_user_input("no", "no", "no", "no", "no","In spreadsheet, a value have been selected in a wrong cell. Put your value in column " + |
|||
get_column(nextCell(getIniCellForAlias())) + " in cell " + nextCell(getIniCellForAlias()) + " or under. " + |
|||
"\n\n Or change value below. ") |
|||
return |
|||
if getIniCheckboxStateForAlias() : |
|||
setAlias() |
|||
cellAlias = App.ActiveDocument.getObject(mySpreadSheetName).getAlias(cellCode) |
|||
if cellAlias == None and getIniCheckboxStateForAlias(): |
|||
get_user_input("no", "no", "no", "no", "no","In spreadsheet, a value have been selected in a wcell without alias. Put your value in column " + |
|||
get_column(nextCell(getIniCellForAlias())) + " in cell " + nextCell(getIniCellForAlias()) + " or under. " + |
|||
"\n\n Or change value below. ") |
|||
return |
|||
# create constraint |
|||
#================================= |
|||
# get value for constraint |
|||
cellAlias = App.ActiveDocument.getObject(mySpreadSheetName).getAlias(cellCode) |
|||
if cellAlias == None : |
|||
cellExpression = mySpreadSheetName + '.' + cellCode |
|||
else : |
|||
cellExpression = mySpreadSheetName + '.' + cellAlias |
|||
if myPad!=None : |
|||
#myPad.setExpression('Length', u'SpreadSheetDatas12.B5') |
|||
myPad.setExpression('Length', cellExpression) |
|||
return |
|||
# create constraint for internal or external circle |
|||
if (len(sels[0].SubElementNames) == 1 and typeIdGeometry1 in['Part::GeomCircle', 'Part::GeomArcOfCircle'] ) : |
|||
indexConstraint = mySketch.addConstraint(Sketcher.Constraint('Diameter' |
|||
, indexObjectHavingPoint1, cellContents)) |
|||
# create constraint for line or points |
|||
elif typeIdGeometry1 != 'Constraint' : # no selected constraint, just line or points |
|||
#create the constraint |
|||
indexConstraint = mySketch.addConstraint(Sketcher.Constraint(myConstraint |
|||
, indexObjectHavingPoint1, indexExtremiteLine1, indexObjectHavingPoint2 |
|||
, indexExtremiteLine2, cellContents)) |
|||
# if one external line or only one external circle or two external object |
|||
# was selected : constraint is a reference |
|||
if len(sels[0].SubElementNames) == 1 and not isInternalObject1 \ |
|||
or len(sels[0].SubElementNames) == 2 and not isInternalObject1 and not isInternalObject2 : |
|||
mySketch.setDriving(indexConstraint, False) # External edge constraint is a reference |
|||
else : # 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 creation? ", |
|||
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() |
|||
</pre> |
Latest revision as of 20:57, 27 January 2024
Description |
---|
Macro which, with a simple click on a spreadsheet cell, adds a length constraint to a line, a circle, or between 2 points using a spreadsheet cell alias or address (ex. C2). Future changes to the spreadsheet will update the constraint. The 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, circles or arcs of circles. Macro version: 01.03 Last modified: 2024-01-15 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 |
01.03 |
Date last modified |
2024-01-15 |
FreeCAD Version(s) |
All |
Default shortcut |
None |
See also |
None |
Description
Contact me in case of any problems.
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. The 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, circles or arcs of circles.
Temporary code for external macro link. Do not use this code. This code is used exclusively by Addon Manager. Link for optional manual installation: Macro
# This code is copied instead of the original macro code # to guide the user to the online download page. # Use it if the code of the macro is larger than 64 KB and cannot be included in the wiki # or if the RAW code URL is somewhere else in the wiki. from PySide import QtGui, QtCore diag = QtGui.QMessageBox(QtGui.QMessageBox.Information, "Information", "This macro must be downloaded from this link\n" "\n" "https://raw.githubusercontent.com/2cv001/FreeCAD-macros/master/Spreadsheet/Sketch_Constraint_From_Spreadsheet.FCMacro" + "\n" "\n" "Quit this window to access the download page") diag.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) diag.setWindowModality(QtCore.Qt.ApplicationModal) diag.exec_() import webbrowser webbrowser.open("https://raw.githubusercontent.com/2cv001/FreeCAD-macros/master/Spreadsheet/Sketch_Constraint_From_Spreadsheet.FCMacro")
Usage
Automatic Object creation
If you run the macro and have not yet created a spreadsheet, a body or a sketch, the macro suggests to create one and then opens the sketch in edition mode and the spreadsheet so that you can start filling it.
Automatic Alias creation
It is not mandatory, but it is better to use aliases in your spreadsheet. The macro can create aliases from texts in cells.
Two modes:
- a manual mode where you select yourself the cells with text for alias and an automatic mode.
- and an automatic mode:
Automatic mode
An automatic mode where alias are automatically created using a text zone defined by a cell. The zone includes the cell and those below. These texts correspond to the alias name. The alias is created to the right of its text. The designated cell (here A3) is editable in these dialog boxes:
Manual mode
To use manual mode don't check the "Automatic alias" option.
Constraints creation
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 its value for a value from a spreadsheet:
The macro can also handle external geometry from another sketch:
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 apply a horizontal constraint. If the line is vertical rather than horizontal, the focus will be on the button to apply a vertical constraint. In both cases, simply press the enter key if you are happy with this choice.
The macro also works for Pads (property Length), Pockets (property Length), Fillets (property Radius), Chamfers (property Size) and Thicknesses (property Value). If you click on a Pad, for example, and then on a cell with the desired value, the Length property of the pad is automatically modified.
Links
- Forum discussion (French)
- Forum discussion (English)
- Macros recipes
- How to install macros
- How to customize toolbars
Credits
Thanks to openBrain, mario52 and onekk for their help on the code!
Thanks to Syres for tests and for the help on the format in the code.
Thanks to Roy043 and David69 for the various reviews and improvements to the wiki.
Thanks to L'ami René for tests and ideas.
Script
Code
ver 01.03 2024/01/27 by 2cv001 Macro_Sketch_Constraint_From_Spreadsheet.FCMacro
Download