Commit 0125e03a authored by sumpfralle's avatar sumpfralle

some commandline interface improvements


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@599 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 07e33061
...@@ -18,7 +18,15 @@ include /usr/share/cdbs/1/rules/debhelper.mk ...@@ -18,7 +18,15 @@ include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/python-distutils.mk include /usr/share/cdbs/1/class/python-distutils.mk
include /usr/share/cdbs/1/rules/simple-patchsys.mk include /usr/share/cdbs/1/rules/simple-patchsys.mk
# clean the manpage
clean:
make -C man clean
# build the manpage
build:
make -C man
# install the .desktop file # install the .desktop file
install/pycam:: install:
cp -v desktop/pycam.desktop `pwd`/debian/pycam/usr/share/applications/ cp -v desktop/pycam.desktop `pwd`/debian/pycam/usr/share/applications/
...@@ -29,14 +29,15 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src")) ...@@ -29,14 +29,15 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
from pycam.Physics.ode_physics import override_ode_availability from pycam.Physics.ode_physics import override_ode_availability
import pycam.Gui.common as GuiCommon import pycam.Gui.common as GuiCommon
import pycam.Gui.Settings import pycam.Gui.Settings
import pycam.Importers.STLImporter
import pycam.Importers.TestModel import pycam.Importers.TestModel
import pycam.Importers
import pycam.Exporters.SimpleGCodeExporter import pycam.Exporters.SimpleGCodeExporter
import pycam.Toolpath.Generator import pycam.Toolpath.Generator
import pycam.Toolpath from pycam.Toolpath import Bounds, Toolpath
from pycam import VERSION from pycam import VERSION
import pycam.Utils.log import pycam.Utils.log
import logging import logging
import time
log = pycam.Utils.log.get_logger() log = pycam.Utils.log.get_logger()
...@@ -45,6 +46,8 @@ EXAMPLE_MODEL_LOCATIONS = ( ...@@ -45,6 +46,8 @@ EXAMPLE_MODEL_LOCATIONS = (
os.path.join(sys.prefix, "share", "pycam", "samples"), os.path.join(sys.prefix, "share", "pycam", "samples"),
os.path.join("usr", "share", "pycam", "samples")) os.path.join("usr", "share", "pycam", "samples"))
DEFAULT_MODEL_FILE = "pycam.stl" DEFAULT_MODEL_FILE = "pycam.stl"
EXIT_CODES = {"ok": 0, "requirements": 1, "load_model_failed": 2,
"write_output_failed": 3}
def show_gui(inputfile=None, task_settings_file=None): def show_gui(inputfile=None, task_settings_file=None):
...@@ -62,7 +65,7 @@ def show_gui(inputfile=None, task_settings_file=None): ...@@ -62,7 +65,7 @@ def show_gui(inputfile=None, task_settings_file=None):
full_report.append("") full_report.append("")
full_report.append("Detailed list of requirements: %s" % GuiCommon.REQUIREMENTS_LINK) full_report.append("Detailed list of requirements: %s" % GuiCommon.REQUIREMENTS_LINK)
log.critical(os.linesep.join(full_report)) log.critical(os.linesep.join(full_report))
sys.exit(1) sys.exit(EXIT_CODES["requirements"])
gui = gui_class() gui = gui_class()
...@@ -98,23 +101,48 @@ def get_default_model(): ...@@ -98,23 +101,48 @@ def get_default_model():
", ".join(EXAMPLE_MODEL_LOCATIONS))) ", ".join(EXAMPLE_MODEL_LOCATIONS)))
return pycam.Importers.TestModel.get_test_model() return pycam.Importers.TestModel.get_test_model()
def load_model_file(filename, program_locations):
if not os.path.isfile(filename):
log.warn("The input file ('%') was not found!" % filename)
return None
filetype, importer = pycam.Importers.detect_file_type(filename)
model = importer(filename, program_locations=program_locations)
if model is None:
log.warn("Failed to load the model file (%s)." % filename)
return None
else:
return model
def get_output_handler(destination):
if destination == "-":
handler = sys.stdout
closer = lambda: None
else:
try:
handler = open(destination, "w")
except IOError, err_msg:
log.error("Failed to open output file (%s) for writing: %s" \
% (destination, err_msg))
return None, None
closer = handler.close
return (handler, closer)
# check if we were started as a separate program # check if we were started as a separate program
if __name__ == "__main__": if __name__ == "__main__":
parser = OptionParser(prog="PyCAM", parser = OptionParser(prog="PyCAM",
usage="usage: %prog [options] [inputfile]\n\n" \ usage="usage: pycam [options] [inputfile]\n\n" \
+ "Use any of the '--export-???' parameters to disable " \ + "Start the PyCAM toolpath generator. Supplying one of " \
+ "the graphical user interface (GUI).\n" \ + "the '--export-?' parameters will cause PyCAM to start " \
+ "When starting the GUI (the default) all arguments " \ + "in batch mode. Most parameters are useful only for " \
+ "except 'inputfile' are ignored.\n" \ + "batch mode.",
+ "'inputfile' may be an STL or DXF file.",
epilog="Take a look at the wiki for more information: " \ epilog="Take a look at the wiki for more information: " \
+ "http://sourceforge.net/apps/mediawiki/pycam/.\n" \ + "http://sourceforge.net/apps/mediawiki/pycam/.\n" \
+ "Bug reports: http://sourceforge.net/tracker/?group_id=237831&atid=1104176") + "Bug reports: http://sourceforge.net/tracker/?group_id=237831&atid=1104176")
group_general = parser.add_option_group("General options") group_general = parser.add_option_group("General options")
group_export = parser.add_option_group("Export formats", group_export = parser.add_option_group("Export formats",
"Export the resulting toolpath or meta-data in various formats. " \ "Export the resulting toolpath or meta-data in various formats. " \
+ "This option triggers the non-interactive mode. Thus the GUI " \ + "These options trigger the non-interactive mode. Thus the GUI " \
+ "is disabled.") + "is disabled.")
group_tool = parser.add_option_group("Tool definition", group_tool = parser.add_option_group("Tool definition",
"Specify the tool paramters. The default tool is spherical and " \ "Specify the tool paramters. The default tool is spherical and " \
...@@ -149,7 +177,7 @@ if __name__ == "__main__": ...@@ -149,7 +177,7 @@ if __name__ == "__main__":
+ "numbers. By default 'mm' is assumed.") + "numbers. By default 'mm' is assumed.")
group_general.add_option("", "--collision-engine", dest="collision_engine", group_general.add_option("", "--collision-engine", dest="collision_engine",
default="triangles", action="store", type="choice", default="triangles", action="store", type="choice",
choices=["triangles", "ode", "help"], choices=["triangles", "ode"],
help="choose a specific collision detection engine. The default " \ help="choose a specific collision detection engine. The default " \
+ "is 'triangles'. Use 'help' to get a list of possible " \ + "is 'triangles'. Use 'help' to get a list of possible " \
+ "engines.") + "engines.")
...@@ -191,7 +219,7 @@ if __name__ == "__main__": ...@@ -191,7 +219,7 @@ if __name__ == "__main__":
group_tool.add_option("", "--tool-feedrate", dest="tool_feedrate", group_tool.add_option("", "--tool-feedrate", dest="tool_feedrate",
default=1000, action="store", type="float", default=1000, action="store", type="float",
help="allowed movement velocity of the tool (units/minute)") help="allowed movement velocity of the tool (units/minute)")
group_tool.add_option("", "--tool-speed", dest="tool_speed", group_tool.add_option("", "--tool-spindle-speed", dest="tool_spindle_speed",
default=250, action="store", type="float", default=250, action="store", type="float",
help="rotation speed of the tool (per minute)") help="rotation speed of the tool (per minute)")
group_tool.add_option("", "--tool-id", dest="tool_id", group_tool.add_option("", "--tool-id", dest="tool_id",
...@@ -292,7 +320,7 @@ if __name__ == "__main__": ...@@ -292,7 +320,7 @@ if __name__ == "__main__":
print "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>." print "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>."
print "This is free software: you are free to change and redistribute it." print "This is free software: you are free to change and redistribute it."
print "There is NO WARRANTY, to the extent permitted by law." print "There is NO WARRANTY, to the extent permitted by law."
sys.exit(0) sys.exit(EXIT_CODES["ok"])
if not opts.disable_psyco: if not opts.disable_psyco:
try: try:
...@@ -315,7 +343,7 @@ if __name__ == "__main__": ...@@ -315,7 +343,7 @@ if __name__ == "__main__":
"toroidal": "ToroidalCutter", "toroidal": "ToroidalCutter",
}[opts.tool_shape] }[opts.tool_shape]
tps.set_tool(opts.tool_id, tool_shape, 0.5 * opts.tool_diameter, tps.set_tool(opts.tool_id, tool_shape, 0.5 * opts.tool_diameter,
0.5 * opts.tool_torus_diameter, opts.tool_speed, 0.5 * opts.tool_torus_diameter, opts.tool_spindle_speed,
opts.tool_feedrate) opts.tool_feedrate)
if opts.support_grid_enabled: if opts.support_grid_enabled:
tps.set_support_grid(opts.support_grid_distance, tps.set_support_grid(opts.support_grid_distance,
...@@ -335,21 +363,38 @@ if __name__ == "__main__": ...@@ -335,21 +363,38 @@ if __name__ == "__main__":
opts.process_path_direction, opts.process_material_allowance, opts.process_path_direction, opts.process_material_allowance,
opts.safety_height, opts.process_overlap_percent / 100.0, opts.safety_height, opts.process_overlap_percent / 100.0,
opts.process_step_down, opts.process_engrave_offset) opts.process_step_down, opts.process_engrave_offset)
model = get_model(inputfile) # set locations of external programs
program_locations = {}
if opts.external_program_inkscape:
program_locations["inkscape"] = opts.external_program_inkscape
if opts.external_program_pstoedit:
program_locations["pstoedit"] = opts.external_program_pstoedit
# load the model
if inputfile is None:
model = get_default_model()
# the "get_default_model" function returns a string or a model
if isinstance(model, basestring):
model = load_model_file(model, program_locations)
else:
model = load_model_file(inputfile, program_locations)
if model is None:
# something went wrong - we quit
sys.exit(EXIT_CODES["load_model_failed"])
# calculate the processing boundary # calculate the processing boundary
bounds = pycam.Toolpath.Bounds() bounds = Bounds()
bounds.set_reference(model.get_bounds())
# set the bounds type and let the default bounding box match the model # set the bounds type and let the default bounding box match the model
if opts.bounds_type == "relative-margin": if opts.bounds_type == "relative-margin":
bounds.set_type(pycam.Toolpath.Bounds.TYPE_RELATIVE_MARGIN) bounds.set_type(Bounds.TYPE_RELATIVE_MARGIN)
bounds_default_low = (10, 10, 0) bounds_default_low = (10, 10, 0)
bounds_default_high = (10, 10, 0) bounds_default_high = (10, 10, 0)
elif opts.bounds_type == "fixed-margin": elif opts.bounds_type == "fixed-margin":
bounds.set_type(pycam.Toolpath.Bounds.TYPE_FIXED_MARGIN) bounds.set_type(Bounds.TYPE_FIXED_MARGIN)
bounds_default_low = (10, 10, 0) bounds_default_low = (10, 10, 0)
bounds_default_high = (10, 10, 0) bounds_default_high = (10, 10, 0)
else: else:
# custom boundary setting # custom boundary setting
bounds.set_type(pycam.Toolpath.Bounds.TYPE_CUSTOM) bounds.set_type(Bounds.TYPE_CUSTOM)
bounds_default_low = (model.minx, model.miny, model.minz) bounds_default_low = (model.minx, model.miny, model.minz)
bounds_default_high = (model.maxx, model.maxy, model.maxz) bounds_default_high = (model.maxx, model.maxy, model.maxz)
# TODO: use the optparse conversion callback instead # TODO: use the optparse conversion callback instead
...@@ -392,29 +437,31 @@ if __name__ == "__main__": ...@@ -392,29 +437,31 @@ if __name__ == "__main__":
tps.set_bounds(process_bounds) tps.set_bounds(process_bounds)
if opts.export_gcode: if opts.export_gcode:
# generate the toolpath # generate the toolpath
tp = pycam.Toolpath.Generator.generate_toolpath_from_settings(model, tps) start_time = time.time()
toolpath = pycam.Toolpath.Generator.generate_toolpath_from_settings(model, tps)
log.info("Toolpath generation time: %f" \
% (time.time() - start_time))
# write result # write result
if isinstance(tp, basestring): if isinstance(toolpath, basestring):
# an error occoured # an error occoured
log.error(tp) log.error(toolpath)
else:
start_pos = tp.get_start_position()
meta_data = os.linesep.join(tp.get_meta_data())
if opts.export_gcode == "-":
destination = sys.stdout
close_destination = False
else: else:
destination = open(opts.export_gcode, "w") description = "Toolpath generated via PyCAM v%s" % VERSION
close_destination = True tp_obj = Toolpath(toolpath, description, tps)
pycam.Exporters.SimpleGCodeExporter.ExportPathList(destination, handler, closer = get_output_handler(opts.export_gcode)
tp.get_path(), opts.unit_size, start_pos.x, if handler is None:
start_pos.y, start_pos.z, opts.tool_feedrate, sys.exit(EXIT_CODES["write_output_failed"])
opts.tool_speed, safety_height=opts.safety_height, pycam.Exporters.SimpleGCodeExporter.ExportPathList(handler,
tp_obj.get_path(), opts.unit_size, opts.tool_feedrate,
opts.tool_spindle_speed,
safety_height=opts.safety_height,
max_skip_safety_distance=opts.tool_diameter, max_skip_safety_distance=opts.tool_diameter,
comment=meta_data) comment=tp_obj.get_meta_data())
if close_destination: closer()
destination.close()
if opts.export_task_config: if opts.export_task_config:
raise NotImplementedError("Export of task config is not " \ handler, closer = get_output_handler(opts.export_task_config)
+ "implemented, yet!") if handler is None:
sys.exit(EXIT_CODES["write_output_failed"])
print >>handler, tps.get_string()
closer()
...@@ -233,7 +233,7 @@ class ProjectGui: ...@@ -233,7 +233,7 @@ class ProjectGui:
self.log_model = self.gui.get_object("LogWindowList") self.log_model = self.gui.get_object("LogWindowList")
# set defaults # set defaults
self.model = None self.model = None
self.toolpath = pycam.Toolpath.ToolPathList() self.toolpath = pycam.Toolpath.ToolpathList()
self.cutter = None self.cutter = None
self.tool_list = [] self.tool_list = []
self.process_list = [] self.process_list = []
......
...@@ -725,6 +725,9 @@ class ToolpathSettings: ...@@ -725,6 +725,9 @@ class ToolpathSettings:
+ "(%s -> %s): %s" % (section, key, value_raw)) + "(%s -> %s): %s" % (section, key, value_raw))
config_dict[key] = value config_dict[key] = value
def __str__(self):
return self.get_string()
def get_string(self): def get_string(self):
result = [] result = []
for config_dict, section in ((self.bounds, "Bounds"), for config_dict, section in ((self.bounds, "Bounds"),
......
...@@ -113,7 +113,7 @@ def generate_toolpath(model, tool_settings=None, ...@@ -113,7 +113,7 @@ def generate_toolpath(model, tool_settings=None,
@type calculation_backend: str | None @type calculation_backend: str | None
@value calculation_backend: any member of the CALCULATION_BACKENDS set @value calculation_backend: any member of the CALCULATION_BACKENDS set
The default is the triangular collision detection. The default is the triangular collision detection.
@rtype: pycam.Toolpath.ToolPath | str @rtype: pycam.Toolpath.Toolpath | str
@return: the resulting toolpath object or an error string in case of invalid @return: the resulting toolpath object or an error string in case of invalid
arguments arguments
""" """
......
...@@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License ...@@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>. along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
""" """
__all__ = ["simplify_toolpath", "ToolPathList", "ToolPath", "Generator"] __all__ = ["simplify_toolpath", "ToolpathList", "Toolpath", "Generator"]
from pycam.Geometry.Point import Point from pycam.Geometry.Point import Point
from pycam.Geometry.utils import number from pycam.Geometry.utils import number
...@@ -58,13 +58,13 @@ def simplify_toolpath(path): ...@@ -58,13 +58,13 @@ def simplify_toolpath(path):
index += 1 index += 1
class ToolPathList(list): class ToolpathList(list):
def add_toolpath(self, toolpath, name, toolpath_settings): def add_toolpath(self, toolpath, name, toolpath_settings):
self.append(ToolPath(toolpath, name, toolpath_settings)) self.append(Toolpath(toolpath, name, toolpath_settings))
class ToolPath: class Toolpath(object):
def __init__(self, toolpath, name, toolpath_settings): def __init__(self, toolpath, name, toolpath_settings):
self.toolpath = toolpath self.toolpath = toolpath
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment