Commit ac783bdc authored by sumpfralle's avatar sumpfralle

toolpath export is somehow working now


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@1157 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 563826b6
......@@ -2,21 +2,136 @@
<interface>
<!-- interface-requires gtk+ 2.12 -->
<!-- interface-naming-policy project-wide -->
<object class="GtkButton" id="ExportAllToolpathsButton">
<property name="label" translatable="yes">Export all</property>
<object class="GtkFrame" id="ToolpathExportFrame">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="image">ExportAllToolpathsIcon</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="xscale">0</property>
<property name="yscale">0</property>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="spacing">3</property>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes">Postprocessor:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="PostprocessorSelector">
<property name="visible">True</property>
<property name="model">PostprocessorList</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHButtonBox" id="hbuttonbox1">
<property name="visible">True</property>
<property name="layout_style">spread</property>
<child>
<object class="GtkButton" id="ExportGCodeAll">
<property name="label" translatable="yes">_Export all</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ExportGCodeSelected">
<property name="label" translatable="yes">Export selected</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ExportGCodeVisible">
<property name="label" translatable="yes">Export visible</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Save toolpath as GCode&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<object class="GtkImage" id="ExportAllToolpathsIcon">
<property name="visible">True</property>
<property name="stock">gtk-save-as</property>
</object>
<object class="GtkButton" id="ExportVisibleToolpathsButton">
<property name="label" translatable="yes">Export visible</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<object class="GtkListStore" id="PostprocessorList">
<columns>
<!-- column-name label -->
<column type="gchararray"/>
<!-- column-name name -->
<column type="gchararray"/>
</columns>
<data>
<row>
<col id="0" translatable="yes">EMC2</col>
<col id="1" translatable="yes">emc2</col>
</row>
</data>
</object>
</interface>
......@@ -67,7 +67,6 @@ WINDOW_ICON_FILENAMES = ["logo_%dpx.png" % pixels for pixels in (16, 32, 48, 64,
HELP_WIKI_URL = "http://sourceforge.net/apps/mediawiki/pycam/index.php?title=%s"
FILTER_GCODE = (("GCode files", ("*.ngc", "*.nc", "*.gc", "*.gcode")),)
FILTER_MODEL = (("All supported model filetypes",
("*.stl", "*.dxf", "*.svg", "*.eps", "*.ps")),
("STL models", "*.stl"), ("DXF contours", "*.dxf"),
......@@ -785,6 +784,8 @@ class ProjectGui(object):
self.open_task_settings_file(autoload_task_filename)
self.update_all_controls()
self.no_dialog = no_dialog
# TODO: move this to /Gui/...
self.settings.set("get_filename_via_dialog", self.get_filename_via_dialog)
if not self.no_dialog:
# register a logging handler for displaying error messages
pycam.Utils.log.add_gtk_gui(self.window, logging.ERROR)
......
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import pycam.Plugins
from pycam.Exporters.GCodeExporter import GCodeGenerator
class PostprocessorEMC2(pycam.Plugins.PluginBase):
DEPENDS = ["ToolpathExport"]
def setup(self):
self.core.get("register_postprocessor")("emc2", "EMC2", GCodeGenerator)
return True
def teardown(self):
self.core.get("unregister_postprocessor")("emc2", "EMC2")
......@@ -66,6 +66,17 @@ class TaskTypeMilling(pycam.Plugins.PluginBase):
kwargs["feedrate"] = environment["tool"]["feedrate"]
except KeyError:
pass
try:
kwargs["spindle_speed"] = environment["tool"]["spindle_speed"]
except KeyError:
# TODO: somehow spindle_speed is not available???
kwargs["spindle_speed"] = 1000
pass
try:
# TODO: get the tool_id!
kwargs["tool_id"] = 1
except KeyError:
pass
tp = pycam.Toolpath.Toolpath(moves, **kwargs)
return tp
# -*- coding: utf-8 -*-
"""
$Id$
Copyright 2011 Lars Kruse <devel@sumpfralle.de>
This file is part of PyCAM.
PyCAM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PyCAM 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with PyCAM. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import pycam.Plugins
from pycam.Exporters.GCodeExporter import PATH_MODES
from pycam.Geometry.Point import Point
FILTER_GCODE = (("GCode files", ("*.ngc", "*.nc", "*.gc", "*.gcode")),)
class ToolpathExport(pycam.Plugins.PluginBase):
UI_FILE = "toolpath_export.ui"
DEPENDS = ["Toolpaths"]
def setup(self):
self._postprocessors = {}
self.core.set("register_postprocessor",
self.register_postprocessor)
self.core.set("unregister_postprocessor",
self.unregister_postprocessor)
self._last_toolpath_file = None
if self.gui:
self._frame = self.gui.get_object("ToolpathExportFrame")
self._frame.unparent()
self.core.register_ui("toolpath_handling", "Export",
self._frame, -100)
self._postproc_model = self.gui.get_object("PostprocessorList")
self._postproc_selector = self.gui.get_object(
"PostprocessorSelector")
self.gui.get_object("ExportGCodeAll").connect("clicked",
self.export_all)
self.gui.get_object("ExportGCodeSelected").connect("clicked",
self.export_selected)
self.gui.get_object("ExportGCodeVisible").connect("clicked",
self.export_visible)
self.core.register_event("postprocessors-list-changed",
self._update_postprocessors)
self.core.register_event("toolpath-list-changed",
self._update_widgets)
self.core.register_event("toolpath-selection-changed",
self._update_widgets)
self.core.register_event("toolpath-changed",
self._update_widgets)
self._update_postprocessors()
self._update_widgets()
return True
def teardown(self):
if self.gui:
self.core.unregister_ui("toolpath_handling", self._frame)
def register_postprocessor(self, name, label, func):
if name in self._postprocessors:
self.log.debug("Registering postprocessor '%s' again" % name)
processor = {"name": name,
"label": label,
"func": func,
}
self._postprocessors[name] = processor
self.core.emit_event("postprocessors-list-changed")
def unregister_postprocessor(self, name):
if not name in self._postprocessors:
self.log.debug("Tried to unregister an unknown postprocessor: " + \
name)
else:
del self._postprocessors[name]
self.core.emit_event("postprocessors-list-changed")
def get_selected(self):
index = self._postproc_selector.get_active()
if index < 0:
return None
else:
return self._postproc_model[index][1]
def select(self, name):
for index, row in enumerate(self._postproc_model):
if row[1] == name:
self._postproc_selector.set_active(index)
break
else:
self._postproc_selector.set_active(-1)
def _update_postprocessors(self):
selected = self.get_selected()
model = self._postproc_model
model.clear()
processors = self._postprocessors.values()
processors.sort(key=lambda item: item["label"])
for proc in processors:
model.append((proc["label"], proc["name"]))
if selected:
self.select(selected)
elif len(model) > 0:
self._postproc_selector.set_active(0)
else:
pass
def _update_widgets(self):
toolpaths = self.core.get("toolpaths")
for name, filtered in (("ExportGCodeAll", toolpaths),
("ExportGCodeVisible", toolpaths.get_visible()),
("ExportGCodeSelected", toolpaths.get_selected())):
self.gui.get_object(name).set_sensitive(bool(filtered))
def export_all(self, widget=None):
self._export_toolpaths(self.core.get("toolpaths"))
def export_visible(self, widget=None):
self._export_toolpaths(self.core.get("toolpaths").get_visble())
def export_selected(self, widget=None):
self._export_toolpaths(self.core.get("toolpaths").get_selected())
def _export_toolpaths(self, toolpaths):
proc_name = self.get_selected()
processor = self._postprocessors[proc_name]
if not processor:
self.log.warn("Unknown postprocessor: %s" % str(name))
return
generator_func = processor["func"]
# we open a dialog
if self.core.get("gcode_filename_extension"):
filename_extension = self.core.get("gcode_filename_extension")
else:
filename_extension = None
# TODO: separate this away from Gui/Project.py
# TODO: implement "last_model_filename" in core
filename = self.core.get("get_filename_via_dialog")("Save toolpath to ...",
mode_load=False, type_filter=FILTER_GCODE,
filename_templates=(self._last_toolpath_file, self.core.get("last_model_filename")),
filename_extension=filename_extension)
if filename:
self._last_toolpath_file = filename
# no filename given -> exit
if not filename:
return
try:
destination = open(filename, "w")
safety_height = self.core.get("gcode_safety_height")
# TODO: implement "get_meta_data()"
#meta_data = self.get_meta_data()
meta_data = ""
machine_time = 0
# calculate the machine time and store it in the GCode header
for toolpath in toolpaths:
machine_time += toolpath.get_machine_time(safety_height)
all_info = meta_data + os.linesep \
+ "Estimated machine time: %.0f minutes" % machine_time
minimum_steps = [self.core.get("gcode_minimum_step_x"),
self.core.get("gcode_minimum_step_y"),
self.core.get("gcode_minimum_step_z")]
if self.core.get("touch_off_position_type") == "absolute":
pos_x = self.core.get("touch_off_position_x")
pos_y = self.core.get("touch_off_position_y")
pos_z = self.core.get("touch_off_position_z")
touch_off_pos = Point(pos_x, pos_y, pos_z)
else:
touch_off_pos = None
generator = generator_func(destination,
metric_units=(self.core.get("unit") == "mm"),
safety_height=safety_height,
toggle_spindle_status=self.core.get("gcode_start_stop_spindle"),
spindle_delay=self.core.get("gcode_spindle_delay"),
comment=all_info, minimum_steps=minimum_steps,
touch_off_on_startup=self.core.get("touch_off_on_startup"),
touch_off_on_tool_change=self.core.get("touch_off_on_tool_change"),
touch_off_position=touch_off_pos,
touch_off_rapid_move=self.core.get("touch_off_rapid_move"),
touch_off_slow_move=self.core.get("touch_off_slow_move"),
touch_off_slow_feedrate=self.core.get("touch_off_slow_feedrate"),
touch_off_height=self.core.get("touch_off_height"),
touch_off_pause_execution=self.core.get("touch_off_pause_execution"))
path_mode = self.core.get("gcode_path_mode")
if path_mode == 0:
generator.set_path_mode(PATH_MODES["exact_path"])
elif path_mode == 1:
generator.set_path_mode(PATH_MODES["exact_stop"])
elif path_mode == 2:
generator.set_path_mode(PATH_MODES["continuous"])
else:
naive_tolerance = self.core.get("gcode_naive_tolerance")
if naive_tolerance == 0:
naive_tolerance = None
generator.set_path_mode(PATH_MODES["continuous"],
self.core.get("gcode_motion_tolerance"),
naive_tolerance)
for toolpath in toolpaths:
tool_id = toolpath.get_params()["tool_id"]
feedrate = toolpath.get_params()["feedrate"]
spindle_speed = toolpath.get_params()["spindle_speed"]
generator.set_speed(feedrate, spindle_speed)
# TODO: implement toolpath.get_meta_data()
generator.add_moves(toolpath.get_moves(safety_height),
tool_id=tool_id, comment="")
generator.finish()
destination.close()
self.log.info("GCode file successfully written: %s" % str(filename))
except IOError, err_msg:
self.log.error("Failed to save toolpath file: %s" % err_msg)
else:
# TODO: add to recent file list
#self.add_to_recent_file_list(filename)
pass
......@@ -59,6 +59,8 @@ class Toolpaths(pycam.Plugins.ListPluginBase):
# handle table changes
self._modelview.connect("row-activated",
self._list_action_toggle_custom, self.COLUMN_VISIBLE)
self._modelview.connect("row-activated",
lambda *args: self.core.emit_event("toolpath-changed"))
self.gui.get_object("ToolpathVisibleColumn").set_cell_data_func(
self.gui.get_object("ToolpathVisibleSymbol"),
self._visualize_visible_state)
......@@ -73,18 +75,6 @@ class Toolpaths(pycam.Plugins.ListPluginBase):
lambda widget, event: self.core.emit_event(event),
"toolpath-selection-changed")
selection.set_mode(gtk.SELECTION_MULTIPLE)
# configure "export" actions
export_all = self.gui.get_object("ExportGCodeAll")
self.register_gtk_accelerator("toolpaths", export_all,
"<Control><Shift>e", "ExportGCodeAll")
export_all.connect("activate", self.save_toolpath, False)
self.core.register_ui("file_menu", "ExportGCodeAll", export_all, 60)
export_selected = self.gui.get_object("ExportGCodeSelected")
self.register_gtk_accelerator("toolpaths", export_selected,
None, "ExportGCodeSelected")
export_selected.connect("activate", self.save_toolpath, True)
self.core.register_ui("file_menu", "ExportGCodeSelected",
export_selected, 65)
# model handling
def update_model():
if not hasattr(self, "_model_cache"):
......@@ -192,93 +182,3 @@ class Toolpaths(pycam.Plugins.ListPluginBase):
self.core.get("gcode_safety_height")))
cell.set_property("text", text)
def save_toolpath(self, widget=None, only_selected=False):
if only_selected:
toolpaths = self.get_selected()
else:
toolpaths = self
if not toolpaths:
return
if callable(widget):
widget = widget()
if isinstance(widget, basestring):
filename = widget
else:
# we open a dialog
if self.core.get("gcode_filename_extension"):
filename_extension = self.core.get("gcode_filename_extension")
else:
filename_extension = None
# TODO: separate this away from Gui/Project.py
filename = self.get_filename_via_dialog("Save toolpath to ...",
mode_load=False, type_filter=FILTER_GCODE,
filename_templates=(self.last_toolpath_file, self.last_model_uri),
filename_extension=filename_extension)
if filename:
self.last_toolpath_file = filename
self._update_widgets()
# no filename given -> exit
if not filename:
return
try:
destination = open(filename, "w")
safety_height = self.core.get("gcode_safety_height")
meta_data = self.get_meta_data()
machine_time = 0
# calculate the machine time and store it in the GCode header
for toolpath in toolpaths:
machine_time += toolpath.get_machine_time(safety_height)
all_info = meta_data + os.linesep \
+ "Estimated machine time: %.0f minutes" % machine_time
minimum_steps = [self.core.get("gcode_minimum_step_x"),
self.core.get("gcode_minimum_step_y"),
self.core.get("gcode_minimum_step_z")]
if self.core.get("touch_off_position_type") == "absolute":
pos_x = self.core.get("touch_off_position_x")
pos_y = self.core.get("touch_off_position_y")
pos_z = self.core.get("touch_off_position_z")
touch_off_pos = Point(pos_x, pos_y, pos_z)
else:
touch_off_pos = None
generator = GCodeGenerator(destination,
metric_units=(self.core.get("unit") == "mm"),
safety_height=safety_height,
toggle_spindle_status=self.core.get("gcode_start_stop_spindle"),
spindle_delay=self.core.get("gcode_spindle_delay"),
comment=all_info, minimum_steps=minimum_steps,
touch_off_on_startup=self.core.get("touch_off_on_startup"),
touch_off_on_tool_change=self.core.get("touch_off_on_tool_change"),
touch_off_position=touch_off_pos,
touch_off_rapid_move=self.core.get("touch_off_rapid_move"),
touch_off_slow_move=self.core.get("touch_off_slow_move"),
touch_off_slow_feedrate=self.core.get("touch_off_slow_feedrate"),
touch_off_height=self.core.get("touch_off_height"),
touch_off_pause_execution=self.core.get("touch_off_pause_execution"))
path_mode = self.core.get("gcode_path_mode")
if path_mode == 0:
generator.set_path_mode(PATH_MODES["exact_path"])
elif path_mode == 1:
generator.set_path_mode(PATH_MODES["exact_stop"])
elif path_mode == 2:
generator.set_path_mode(PATH_MODES["continuous"])
else:
naive_tolerance = self.core.get("gcode_naive_tolerance")
if naive_tolerance == 0:
naive_tolerance = None
generator.set_path_mode(PATH_MODES["continuous"],
self.core.get("gcode_motion_tolerance"),
naive_tolerance)
for toolpath in toolpaths:
settings = toolpath.get_toolpath_settings()
tool = settings.get_tool_settings()
generator.set_speed(tool["feedrate"], tool["speed"])
generator.add_moves(toolpath.get_moves(safety_height),
tool_id=tool["id"], comment=toolpath.get_meta_data())
generator.finish()
destination.close()
log.info("GCode file successfully written: %s" % str(filename))
except IOError, err_msg:
log.error("Failed to save toolpath file: %s" % err_msg)
else:
self.add_to_recent_file_list(filename)
......@@ -62,15 +62,18 @@ def simplify_toolpath(path):
class Toolpath(object):
def __init__(self, paths, max_safe_distance=0, feedrate=300):
def __init__(self, paths, max_safe_distance=0, feedrate=300, **kwargs):
self.paths = paths
self._max_safe_distance = max_safe_distance
self._feedrate = feedrate
self._other_params = kwargs
def get_params(self):
return {"max_safe_distance": self._max_safe_distance,
result = {"max_safe_distance": self._max_safe_distance,
"feedrate": self._feedrate,
}
result.update(self._other_params)
return result
def _get_limit_generic(self, attr, func):
path_min = []
......
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