#!/usr/bin/python3 # SPDX-License-Identifier: LGPL-2.1-or-later # *************************************************************************** # * Copyright (c) 2021 sliptonic * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * # * as published by the Free Software Foundation; either version 2 of * # * the License, or (at your option) any later version. * # * for detail see the LICENCE text file. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU Library General Public License for more details. * # * * # * You should have received a copy of the GNU Library General Public * # * License along with this program; if not, write to the Free Software * # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * # * USA * # * * # *************************************************************************** # # Script for manipulating toolbit shapes in a batch. This is handy for making # attributes consistent over multiple or all shape file. # # Most commands are straight forward, except for --set which can be used to # set the actual value of a property, or, in the case of enumerations it can # also be used to set the enum values. This might be particularly useful when # adding more materials. # # The following example moves all properties from the "Extra" group into the # "Attributes" group. Note that the Attributes group might or might not # already exist. If it does exist the specified group gets merged in. # # ./toolbit-attributes.py --move 'Extra:Attributes' src/Mod/CAM/Tools/Shape/*.fcstd # # This example sets the Flutes value of all Shapes to 0: # # ./toolbit-attributes.py --set Flutes=0 src/Mod/CAM/Tools/Shape/*.fcstd # # Finally, this example sets the enumerations of the Material attribute: # # ./toolbit-attributes.py --set 'Material=[HSS,Carbide,Tool Steel,Titanium]' src/Mod/CAM/Tools/Shape/*.fcstd # # After running this tool it might be necessary to open the shape files # manually and make sure they are visible and the thumbprint image is # saved. # # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!! Final note: this is a dangerous tool and should not be shipped with FC. !!!! # !!! It's sole purpose is to make life of the developers easier and ensure !!!! # !!! consistent attributes across all toobit shapes. !!!! # !!! A single typo can ruin a lot of toolbit shapes - make sure to only use !!!! # !!! it if those shape files are under a version control system and you can !!!! # !!! back out the changes easily. !!!! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! import argparse import os import sys parser = argparse.ArgumentParser() parser.add_argument("path", nargs="+", help="Shape file to process") parser.add_argument( "--move", metavar=":", help="Move attributes from group 1 into group 2", ) parser.add_argument("--delete", metavar="prop", help="Delete the given attribute") parser.add_argument("--set", metavar="prop=value", help="Set property value") parser.add_argument( "--print", action="store_true", help="If set attributes are printed as discovered" ) parser.add_argument( "--print-all", action="store_true", help="If set Shape attributes are also printed" ) parser.add_argument( "--print-groups", action="store_true", help="If set all custom property groups are printed", ) parser.add_argument( "--save-changes", action="store_true", help="Unless specified the file is not saved" ) parser.add_argument("--freecad", help="Directory FreeCAD binaries (libFreeCAD.so) if not installed") args = parser.parse_args() if args.freecad: sys.path.append(args.freecad) import FreeCAD import Path import Path.Base.PropertyBag as PathPropertyBag import Path.Base.Util as PathUtil set_var = None set_val = None GroupMap = {} if args.move: g = args.move.split(":") if len(g) != 2: print("ERROR: {} not a valid group mapping".format(args.move)) sys.exit(1) GroupMap[g[0]] = g[1] if args.set: s = args.set.split("=") if len(s) != 2: print("ERROR: {} not a valid group mapping".format(args.move)) sys.exit(1) set_var = s[0] set_val = s[1] for i, fname in enumerate(args.path): # print(fname) doc = FreeCAD.openDocument(fname, False) print("{}:".format(doc.Name)) for o in doc.Objects: if PathPropertyBag.IsPropertyBag(o): if args.print_groups: print(" {}: {}".format(o.Label, sorted(o.CustomPropertyGroups))) else: print(" {}:".format(o.Label)) for p in o.Proxy.getCustomProperties(): grp = o.getGroupOfProperty(p) typ = o.getTypeIdOfProperty(p) ttp = PathPropertyBag.getPropertyTypeName(typ) val = PathUtil.getProperty(o, p) dsc = o.getDocumentationOfProperty(p) enm = "" enum = [] if ttp == "Enumeration": enum = o.getEnumerationsOfProperty(p) enm = "{}".format(",".join(enum)) if GroupMap.get(grp): group = GroupMap.get(grp) print("move: {}.{} -> {}".format(grp, p, group)) o.removeProperty(p) o.Proxy.addCustomProperty(typ, p, group, dsc) if enum: print("enum {}.{}: {}".format(group, p, enum)) setattr(o, p, enum) PathUtil.setProperty(o, p, val) if p == set_var: print("set {}.{} = {}".format(grp, p, set_val)) if ttp == "Enumeration" and set_val[0] == "[": enum = set_val[1:-1].split(",") setattr(o, p, enum) else: PathUtil.setProperty(o, p, set_val) if p == args.delete: print("delete {}.{}".format(grp, p)) o.removeProperty(p) if not args.print_all and grp == "Shape": continue if args.print or args.print_all: print(" {:10} {:20} {:20} {:10} {}".format(grp, p, ttp, str(val), enm)) o.Proxy.refreshCustomPropertyGroups() if args.save_changes: doc.recompute() doc.save() FreeCAD.closeDocument(doc.Name) print("-done-")