cadquery-freecad-module/Libs/cadquery/cq_cmd.py

198 lines
6.7 KiB
Python

#
# cadquery command line interface
# usage: cq_cmd [-h] [--param-file inputfile ] [--format STEP|STL ] [--output filename] filename
# if input file contains multiple inputs, multiple outputs are created
# if no input filename is provided, stdin is read instead
# if no output filename is provided, output goes to stdout
# default output format is STEP
#
from __future__ import print_function
import sys,os
from cadquery import cqgi,exporters
import argparse
import os.path
import json
import tempfile
class FilepathShapeWriter(object):
#a shape writer that writes a new file in a directory for each object
def __init__(self,file_pattern, shape_format):
self.shape_format=shape_format
self.file_pattern=file_pattern
self.counter = 1
def _compute_file_name(self):
return self.file_pattern % ({ "counter": self.counter,"format": self.shape_format } )
def write_shapes(self,shape_list):
for result in shape_list:
shape = result.shape
file_name = self._compute_file_name()
info("Writing %s Output to '%s'" % (self.shape_format, file_name))
s = open(file_name,'w')
exporters.exportShape(shape,self.shape_format,s)
s.flush()
s.close()
class StdoutShapeWriter(object):
#has extra code to prevent freecad crap from junking up stdout
def __init__(self,shape_format ):
self.shape_format = shape_format
def write_shapes(self,shape_list):
#f = open('/tmp/cqtemp','w')
#with suppress_stdout_stderr():
exporters.exportShape(shape_list[0].shape,self.shape_format,sys.stdout)
#f.flush()
#f.close()
#f = open('/tmp/cqtemp')
#sys.stdout.write(f.read())
def create_shape_writer(out_spec,shape_format):
if out_spec == 'stdout':
return StdoutShapeWriter(shape_format)
else:
return FilepathShapeWriter(out_spec,shape_format)
class ErrorCodes(object):
SCRIPT_ERROR=2
UNKNOWN_ERROR=3
INVALID_OUTPUT_DESTINATION=4
INVALID_OUTPUT_FORMAT=5
INVALID_INPUT=6
MULTIPLE_RESULTS=7
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
def error(msg,exit_code=1):
eprint("ERROR %d: %s" %( exit_code, msg))
sys.exit(exit_code)
def warning(msg):
eprint("WARNING: %s" % msg)
def info(msg):
eprint("INFO: %s" % msg)
def read_file_as_string(file_name):
if os.path.isfile(file_name):
f = open(file_name)
s = f.read()
f.close()
return s
else:
return None
class ParameterHandler(object):
def __init__(self):
self.params = {}
def apply_file(self,file_spec):
if file_spec is not None:
p = read_file_as_string(file_spec)
if p is not None:
d = json.loads(p)
self.params.update(d)
def apply_string(self,param_string):
if param_string is not None:
r = json.loads(param_string)
self.params.update(r)
def get(self):
return self.params
def read_input_script(file_name):
if file_name != "stdin":
s = read_file_as_string(file_name)
if s is None:
error("%s does not appear to be a readable file." % file_name,ErrorCodes.INVALID_INPUT)
else:
return s
else:
s = sys.stdin.read()
return s
def check_input_format(input_format):
valid_types = [exporters.ExportTypes.TJS,exporters.ExportTypes.AMF,
exporters.ExportTypes.STEP,exporters.ExportTypes.STL,exporters.ExportTypes.TJS ]
if input_format not in valid_types:
error("Invalid Input format '%s'. Valid values: %s" % ( input_format, str(valid_types)) ,
ErrorCodes.INVALID_OUTPUT_FORMAT)
def describe_parameters(user_params, script_params):
if len(script_params)> 0:
parameter_names = ",".join(list(script_params.keys()))
info("This script provides parameters %s, which can be customized at build time." % parameter_names)
else:
info("This script provides no customizable build parameters.")
if len(user_params) > 0:
info("User Supplied Parameter Values ( Override Model Defaults):")
for k,v in user_params.items():
info("\tParameter: %s=%s" % (k,v))
else:
info("The script will run with default variable values")
info("use --param_file to provide a json file that contains values to override the defaults")
def run(args):
info("Reading from file '%s'" % args.in_spec)
input_script = read_input_script(args.in_spec)
script_name = 'stdin' if args.in_spec is None else args.in_spec
cq_model = cqgi.parse(input_script)
info("Parsed Script '%s'." % script_name)
param_handler = ParameterHandler()
param_handler.apply_file(args.param_file)
param_handler.apply_string(args.params)
user_params = param_handler.get()
describe_parameters(user_params,cq_model.metadata.parameters)
check_input_format(args.format)
build_result = cq_model.build(build_parameters=user_params)
info("Output Format is '%s'. Use --output-format to change it." % args.format)
info("Output Path is '%s'. Use --out_spec to change it." % args.out_spec)
if build_result.success:
result_list = build_result.results
info("Script Generated %d result Objects" % len(result_list))
shape_writer = create_shape_writer(args.out_spec,args.format)
shape_writer.write_shapes(result_list)
else:
error("Script Error: '%s'" % build_result.exception,ErrorCodes.SCRIPT_ERROR)
if __name__=='__main__':
desc="""
CQ CMD. Runs a cadquery python file, and produces a 3d object.
A script can be provided as a file or as standard input.
Each object created by the script is written the supplied output directory.
"""
filename_pattern_help="""
Filename pattern to use when creating output files.
The sequential file number and the format are available.
Default: cqobject-%%(counter)d.%%(format)s
Use stdout to write to stdout ( can't be used for multiple results though)
"""
parser = argparse.ArgumentParser(description=desc)
parser.add_argument("--format", action="store",default="STEP",help="Output Object format (TJS|STEP|STL|SVG)")
parser.add_argument("--param_file", action="store",help="Parameter Values File, in JSON format")
parser.add_argument("--params",action="store", help="JSON encoded parameter values. They override values provided in param_file")
parser.add_argument("--in_spec", action="store", required=True, help="Input File path. Use stdin to read standard in")
parser.add_argument("--out_spec", action="store",default="./cqobject-%(counter)d.%(format)s",help=filename_pattern_help)
args = parser.parse_args()
run(args)