Macro Alias For Table For Object/de

Generisches Makro-Symbol Erstellen Sie Ihr persönliches Symbol mit demselben Namen des Makros Alias für Tabelle für Objekt

Beschreibung
Dieses Makro erstellt automatisch Aliase in einer zweidimensionalen Tabelle unter Verwendung der Namen der Zeilen und Spalten.

Eine zweite Funktion kann eine Verknüpfung zwischen einer Zelle und einem Eigenschaftswert in einem Objekt herstellen.
Um diese Funktion nutzen zu können, müssen Sie lediglich den Namen des entsprechenden Objekts in die Spalte und den Namen der Eigenschaft in die Zeile eingeben.

Versionsmakro : beta
Datum der letzten Änderung : 2024-02-22
FreeCAD version : Alle
Autor: 2cv001

Autor
2cv001
Herunterladen
Links
Macro-Version
beta
Datum der letzten Änderung
2024-02-22
FreeCAD-Version(s)
Alle
Standardverknüpfung
None
Siehe auch
None

Beschreibung

Dieses Makro erstellt automatisch Aliase in einer zweidimensionalen Tabelle unter Verwendung der Namen der Zeilen und Spalten. Eine zweite Funktion kann eine Verknüpfung zwischen einer Zelle und einem Eigenschaftswert eines Objekts herstellen. Um diese Funktion nutzen zu können, muss lediglich der Namen des entsprechenden Objekts in die Spalte und den Namen der Eigenschaft in die Zeile eingeben werden.

Die Syntax für den vom Makro erstellten Alias lautet: LabelObject_Property.

Das Makro kann auch automatisch Eigenschaftswerte von Objekten (wie Körper, Skizze usw.) basierend auf diesen Aliasen ausfüllen.

Anwendung

Automatische Alias-Erzeugung

Fill a spreadsheet with a column containing object labels (Body, sketch, Pad, ...) and a row corresponding to properties. In the code you will find the correspondence between the properties and what to put in this row. See dico= below.

For example, use posy for property Placement.Base.y.

Select the cells in the table with the object labels column and properties row, and then run the Macro and check the first option:

The macro will create aliases with this syntax: LabelObject_Property. For instance: BodyRect_posy.

Part of the dico (check the code of the macro for the full dico):

dico={
        "posx" : "Placement.Base.x",
        "posy" : "Placement.Base.y",
        "posz" : "Placement.Base.z",
        "posX" : "Placement.Base.x",
        "posY" : "Placement.Base.y",
        "posZ" : "Placement.Base.z",
        "angle": "Placement.Rotation.Angle",
        "ang"  : "Placement.Rotation.Angle",
        "axisx": "Placement.Rotation.Axis.x",
        "axisy": "Placement.Rotation.Axis.y",       
        "axisz": "Placement.Rotation.Axis.z", 
        "axeX" : "Placement.Rotation.Axis.x",
        "axeY" : "Placement.Rotation.Axis.y",       
        "axeZ" : "Placement.Rotation.Axis.z", 
        "attachementX" : "AttachmentOffset.Base.x",
        "attachementY" : "AttachmentOffset.Base.y",
        "attachementZ" : "AttachmentOffset.Base.z",
        "attachementAngle" : "AttachmentOffset.Rotation.Angle",
        "attachementAxisX" : "AttachmentOffset.Rotation.Axis.x",
        "attachementAxisY" : "AttachmentOffset.Rotation.Axis.y",        
        "attachementAxisZ" : "AttachmentOffset.Rotation.Axis.z",
        "Length" : "Length",
        "Length2": "Length2",
        "Radius" : "Radius",
        "Height" : "Height",
        "FirstAngle" : "FirstAngle",
        "SecondAngle": "SecondAngle",
        "Angle1" : "Angle1",
        "Angle2" : "Angle2",
        "Angle3" : "Angle3",

Automatische Werte in Eigenschaften

Select some cells in the table with values and aliases created previously. Run the macro and select the second option (Both options can be selected at the same time to chain them together.)

The macro will assign values to the properties via an expression referencing the alias of the cells. From now on, if you change a value in the table, the property value will change and everything will be recalculated.


Beispiele

Aufpolsterung

Skizzen-Randbedingungen

Der Randbedingung einen Namen geben (hier: Breite) (auf die Groß-/Kleinschreibung achten). In der Zeile Eigenschaften eine Spalte mit diesem Randbedinungs-Namen hinzufügen und eine Zeile mit der Bezeichnung der Skizze hinzufügen (hier: Skizze). Das Makro ausführen.

Danksagung

Dieses Makro wurde auf Grundlage einer Idee von Esprit entwickelt. Vielen Dank auch an ihn für die zahlreichen Ideen und Tests, die er durchgeführt hat.

Versionsinformation

ver 25/02/2024 von 2cv001 Alias_For_Table_For_Object.FCMacro

Code

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ========================================================================
# Macro Alias for table for object
# file name : Alias_For_Table_For_Object.FCMacro
# ========================================================================


__Name__   = "Alias for table for object"
__Comment__ ="Automatically creates aliases in a two-dimensional table using the names of the rows and columns"
__Author__ = "2cv001"
__Title__   = "Alias For Table For Object"
__Date__    = "2024/02/20"    #YYYY/MM/DD 14;21 Béta
__Version__ = __Date__
__License__ = "Apache-2.0"
__Wiki__ = "https://wiki.freecad.org/Macro_Alias_For_Table_For_Object"
# https://forum.freecad.org/viewtopic.php?t=84878
#===========================================================================================
# The macro automatically creates alias in a two-dimensional table using the name of the row
# and the name of the column. To be able to use the "Table to object" function
# put the name of the concerned object in the column and the name of the property in the row.
# the alias creation function will create alias (except dico, see below)
# with this syntax  "LabelObject_property"
#
# The macro can also automatically fill the properties of objects (body, sketch, ...) based on
# aliases created by 'Create alias for table' and which indicate the object and the property
#
# The dico (see further in the code) is used to convert what you put in the top line to match
# what needs to be put in the expression. For example, if you have "posx" in the
# first line of your selection, in the expression, for the property part, there will be
# "Placement.Base.x"
#==========================================================================================



import FreeCAD, FreeCADGui
import re
from PySide import QtGui, QtCore, QtWidgets

addNumberIfOther=True

class CheckBoxDialog(QtWidgets.QDialog):
    def __init__(self, parent=None):
        super(CheckBoxDialog, self).__init__(parent)

        self.setWindowTitle("Macro FreeCAD")

        self.createAliasForTableCheckBox = QtWidgets.QCheckBox("Alias creation")
        self.createAliasForTableCheckBox.setToolTip("Creates an alias in each selected cell" +
                "\nbased on the first selected row and column" +
                "\nThe aliases will be in the format 'LabelObject_property'")
        self.tableToObjectCheckBox = QtWidgets.QCheckBox("Properties in objects based on aliases")
        self.tableToObjectCheckBox.setToolTip("Automatically fills the properties of objects (body, sketch, ...)" +
                "\nbased on aliases which must be in the format 'LabelObject_property'")
        buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

        layout = QtWidgets.QVBoxLayout()
        layout.addWidget(self.createAliasForTableCheckBox)
        layout.addWidget(self.tableToObjectCheckBox)
        layout.addWidget(buttonBox)
        self.setLayout(layout)

    def accept(self):
        super(CheckBoxDialog, self).accept()




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)




#################################################################################
# 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 = False # 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'],['ê','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
        first=False
    if premierCaractereEnMinuscule :
        chaineResult = chaineResult[:1].lower() + chaineResult[1:]
    return chaineResult


def create_alias_for_table_cells_Selected():
    mySpreadsheet = Gui.ActiveDocument.ActiveView.getSheet()
    aw = Gui.getMainWindow().centralWidget().activeSubWindow() # Store the active window
    # To get list of all selected cells
    sel_items = aw.widget().findChild(QtGui.QTableView).selectedIndexes()

    getCellName = lambda r,c:'{}{}{}'.format(chr(c//26 + 64) if c//26 > 0 else '', chr(c%26 + 65), r + 1)



    for item in sel_items: # The selected cells are scanned

        if  (sel_items[0].column()!=item.column() and sel_items[0].row()!=item.row()) :
            cellName=getCellName(item.row(), item.column())
            firstcellName=getCellName(sel_items[0].row(), sel_items[0].column())

            cellTextLine=get_column(firstcellName)+str(get_row(cellName))
            cellTextColumn=get_column(cellName)+ str(get_row(firstcellName))
            try :
                textLine = mySpreadsheet.get(cellTextLine)
                textColumn = mySpreadsheet.get(cellTextColumn)

            except :
                #print('Des cellules en première colonne ou en première ligne ne sont pas conformes')
                continue
            textAlias =  str(textLine) + '_'  + str(textColumn)
            textAlias=traitementChaineSource(textAlias, separateur, nouveauCaract, majuscule)
            if addNumberIfOther :
                startIndexForTextAlias=1
                for i in range(startIndexForTextAlias, 201):
                    try:
                        if i == 1:
                            mySpreadsheet.setAlias(cellName, textAlias)
                        else:
                            mySpreadsheet.setAlias(cellName, textAlias + '_' + str(i-1))
                            startIndexForTextAlias=i+1
                        break  # Arrête la boucle si aucune exception n'est levée
                    except:
                        continue  # Passe à l'itération suivante si une exception est levée





# The dictionary is used to convert what you put in the top line to match
# what needs to be put in the expression. For example, if you have “posx” in the
# first line of your selection, in the expression, for the property part, there will be
# "Placement.Base.x"
dico={
        "posx" : "Placement.Base.x",
        "posy" : "Placement.Base.y",
        "posz" : "Placement.Base.z",
        "posX" : "Placement.Base.x",
        "posY" : "Placement.Base.y",
        "posZ" : "Placement.Base.z",
        "angle": "Placement.Rotation.Angle",
        "ang": "Placement.Rotation.Angle",
        "axisx": "Placement.Rotation.Axis.x",
        "axisy": "Placement.Rotation.Axis.y",
        "axisz": "Placement.Rotation.Axis.z",
        "axeX": "Placement.Rotation.Axis.x",
        "axeY": "Placement.Rotation.Axis.y",
        "axeZ": "Placement.Rotation.Axis.z",
        "attachementX" : "AttachmentOffset.Base.x",
        "attachementY" : "AttachmentOffset.Base.y",
        "attachementZ" : "AttachmentOffset.Base.z",
        "attachementAngle" : "AttachmentOffset.Rotation.Angle",
        "attachementAxisX" : "AttachmentOffset.Rotation.Axis.x",
        "attachementAxisY" : "AttachmentOffset.Rotation.Axis.y",
        "attachementAxisZ" : "AttachmentOffset.Rotation.Axis.z",
        "Length" : "Length",
        "Length2" : "Length2",
        "Radius" : "Radius",
        "Height" : "Height",
        "FirstAngle" : "FirstAngle",
        "SecondAngle" : "SecondAngle",
        "Angle1" : "Angle1",
        "Angle2" : "Angle2",
        "Angle3" : "Angle3",
     }


def set_constraint_expression(sketchLabel, constraint_name, expression):
    # Get all Sketches with this label
    findConstraint = False
    sketches = App.ActiveDocument.getObjectsByLabel(sketchLabel)
    if len(sketches) > 1 :
        print("Warning, multiple sketches have the same label. "
              " All these sketches will be processed. "
              "This may cause problems if different sketches with the same label "
              "have constraints with the same name.")
    # Go through all Sketches

    for sketch in sketches:

        # Go through all constraints in the Sketch
        for i in range(sketch.ConstraintCount):
            # Get the constraint
            constraint = sketch.Constraints[i]

            # Check if the constraint name matches the one we're looking for
            if constraint.Name == constraint_name:
                # Modify the constraint value
                sketch.setExpression(f'Constraints[{i}]', expression)
                findConstraint=True
                break
    return findConstraint


def set_property_based_on_alias(sheet,cell):

    # alias of the cell
    alias = sheet.getAlias(cell)
    # Check if the alias is valid
    if sheet.getContents(cell)!='' :
        if alias is None:
            print(f"The selected cell {cell} does not have an alias.")
            return

    # Separate the alias into body name and property name
    # ex 'body_Name_posX' -> body_Name for the object and posX for property name
    parts = alias.rsplit('_', 1)
    if len(parts) != 2:
        print("The alias must be in the form 'BodyName_PropertyName'.")
        return

    objLabel, property_name = parts
    try :
        obj=App.ActiveDocument.getObjectsByLabel(objLabel)[0]
    except :
        print('no object with this label : ',objLabel)
        return
    try :
        property_name=dico[property_name]
    except :
        try :
            if  obj.TypeId != 'Sketcher::SketchObject' :
                print (f'property {property_name} not found in the dictionary. See at the top of the source code')
        except :
            pass



    if obj is None:
        print(f"There is no object named '{objLabel}' in the document.")
        return

    # Create an expression that refers to the cell's alias
    expression = f"<<{sheet.Label}>>.{alias}"

    # Modify the object's property
    # if it's a constraint
    if  obj.TypeId == 'Sketcher::SketchObject' :
        findConstraint=set_constraint_expression(objLabel, property_name, expression)
        if findConstraint : # if not we continue to find a property
            return

    if hasattr(obj, property_name.split('.')[0]): # Placement.Base.x -> Placement
        obj.setExpression(property_name, expression)
    else:
        print(f"The object '{objLabel}' does not have a property '{property_name}'.")


def tableToObject() :
    sheet = Gui.ActiveDocument.ActiveView.getSheet()
    # Check if the spreadsheet is active
    if sheet.TypeId != 'Spreadsheet::Sheet':
        print("Please select cells in a spreadsheet.")
        return

    # active cell
    #cell = Gui.activeView().currentIndex()

    aw = Gui.getMainWindow().centralWidget().activeSubWindow() # Store the active window
    # To get list of all selected cells
    sel_items = aw.widget().findChild(QtGui.QTableView).selectedIndexes()
    getCellName = lambda r,c:'{}{}{}'.format(chr(c//26 + 64) if c//26 > 0 else '', chr(c%26 + 65), r + 1)
    for cell in sel_items: # The selected cells are scanned
        cellName=getCellName(cell.row(), cell.column())

        if str(sheet.getContents(cellName))!= '' :
            set_property_based_on_alias(sheet, cellName)






def main():
    dialog = CheckBoxDialog()
    result = dialog.exec_()
    if result and dialog.createAliasForTableCheckBox.isChecked() :
        create_alias_for_table_cells_Selected()
    if result and dialog.tableToObjectCheckBox.isChecked() :
        tableToObject()
    return result



if __name__ == '__main__':
    main()