Merged in test_beta_8.906 (pull request #131)

Test beta 8.906
This commit is contained in:
Marius Stanciu 2019-02-05 20:35:21 +00:00
commit c14f28d35e
42 changed files with 5182 additions and 1197 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
############################################################
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, QSettings
import FlatCAMApp
from camlib import *
from FlatCAMTool import FlatCAMTool
@ -590,6 +590,7 @@ class FCCircle(FCShapeTool):
self.points.append(point)
if len(self.points) == 1:
self.draw_app.app.inform.emit("Click on Circle perimeter point to complete ...")
return "Click on perimeter to complete ..."
if len(self.points) == 2:
@ -638,9 +639,11 @@ class FCArc(FCShapeTool):
self.points.append(point)
if len(self.points) == 1:
self.draw_app.app.inform.emit("Click on Start arc point ...")
return "Click on 1st point ..."
if len(self.points) == 2:
self.draw_app.app.inform.emit("Click on End arc point to complete ...")
return "Click on 2nd point to complete ..."
if len(self.points) == 3:
@ -850,6 +853,7 @@ class FCPolygon(FCShapeTool):
self.points.append(point)
if len(self.points) > 0:
self.draw_app.app.inform.emit("Click on next Point or click Right mouse button to complete ...")
return "Click on next point or hit ENTER to complete ..."
return ""
@ -1239,7 +1243,7 @@ class FCText(FCShapeTool):
self.geometry = DrawToolShape(affinity.translate(self.text_gui.text_path, xoff=dx, yoff=dy))
except Exception as e:
log.debug("Font geometry is empty or incorrect: %s" % str(e))
self.draw_app.app.inform.emit("[error]Font not supported. Only Regular, Bold, Italic and BoldItalic are "
self.draw_app.app.inform.emit("[ERROR]Font not supported. Only Regular, Bold, Italic and BoldItalic are "
"supported. Error: %s" % str(e))
self.text_gui.text_path = []
self.text_gui.hide_tool()
@ -1416,7 +1420,7 @@ class FCDrillAdd(FCShapeTool):
self.draw_app.tools_table_exc.setCurrentItem(item)
except KeyError:
self.draw_app.app.inform.emit("[warning_notcl] To add a drill first select a tool")
self.draw_app.app.inform.emit("[WARNING_NOTCL] To add a drill first select a tool")
self.draw_app.select_tool("select")
return
@ -1500,7 +1504,7 @@ class FCDrillArray(FCShapeTool):
item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
self.draw_app.tools_table_exc.setCurrentItem(item)
except KeyError:
self.draw_app.app.inform.emit("[warning_notcl] To add an Drill Array first select a tool in Tool Table")
self.draw_app.app.inform.emit("[WARNING_NOTCL] To add an Drill Array first select a tool in Tool Table")
return
geo = self.utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y), static=True)
@ -1525,7 +1529,7 @@ class FCDrillArray(FCShapeTool):
self.flag_for_circ_array = True
self.set_origin(point)
self.draw_app.app.inform.emit("Click on the circular array Start position")
self.draw_app.app.inform.emit("Click on the Drill Circular Array Start position")
else:
self.destination = point
self.make()
@ -1547,10 +1551,10 @@ class FCDrillArray(FCShapeTool):
self.drill_angle = float(self.draw_app.drill_angle_entry.get_value())
except TypeError:
self.draw_app.app.inform.emit(
"[error_notcl] The value is not Float. Check for comma instead of dot separator.")
"[ERROR_NOTCL] The value is not Float. Check for comma instead of dot separator.")
return
except Exception as e:
self.draw_app.app.inform.emit("[error_notcl] The value is mistyped. Check the value.")
self.draw_app.app.inform.emit("[ERROR_NOTCL] The value is mistyped. Check the value.")
return
if self.drill_array == 'Linear':
@ -1630,7 +1634,7 @@ class FCDrillArray(FCShapeTool):
self.geometry.append(DrawToolShape(geo))
else:
if (self.drill_angle * self.drill_array_size) > 360:
self.draw_app.app.inform.emit("[warning_notcl]Too many drills for the selected spacing angle.")
self.draw_app.app.inform.emit("[WARNING_NOTCL]Too many drills for the selected spacing angle.")
return
radius = distance(self.destination, self.origin)
@ -1676,7 +1680,7 @@ class FCDrillResize(FCShapeTool):
try:
new_dia = self.draw_app.resdrill_entry.get_value()
except:
self.draw_app.app.inform.emit("[error_notcl]Resize drill(s) failed. Please enter a diameter for resize.")
self.draw_app.app.inform.emit("[ERROR_NOTCL]Resize drill(s) failed. Please enter a diameter for resize.")
return
if new_dia not in self.draw_app.olddia_newdia:
@ -1890,9 +1894,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app = app
self.canvas = app.plotcanvas
self.app.ui.geo_edit_toolbar.setDisabled(disabled)
self.app.ui.snap_max_dist_entry.setDisabled(disabled)
self.app.ui.geo_add_circle_menuitem.triggered.connect(lambda: self.select_tool('circle'))
self.app.ui.geo_add_arc_menuitem.triggered.connect(lambda: self.select_tool('arc'))
self.app.ui.geo_add_rectangle_menuitem.triggered.connect(lambda: self.select_tool('rectangle'))
@ -1969,6 +1970,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.move_timer = QtCore.QTimer()
self.move_timer.setSingleShot(True)
# this var will store the state of the toolbar before starting the editor
self.toolbar_old_state = False
self.key = None # Currently pressed key
self.geo_key_modifiers = None
self.x = None # Current mouse cursor pos
@ -1991,12 +1995,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.tools[tool]["button"].setCheckable(True) # Checkable
self.app.ui.grid_snap_btn.triggered.connect(self.on_grid_toggled)
self.app.ui.corner_snap_btn.setCheckable(True)
self.app.ui.corner_snap_btn.triggered.connect(lambda: self.toolbar_tool_toggle("corner_snap"))
self.options = {
"global_gridx": 0.1,
"global_gridy": 0.1,
"snap_max": 0.05,
"global_snap_max": 0.05,
"grid_snap": True,
"corner_snap": False,
"grid_gap_link": True
@ -2009,7 +2014,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.ui.grid_gap_x_entry.setText(str(self.options["global_gridx"]))
self.app.ui.grid_gap_y_entry.setText(str(self.options["global_gridy"]))
self.app.ui.snap_max_dist_entry.setText(str(self.options["snap_max"]))
self.app.ui.snap_max_dist_entry.setText(str(self.options["global_snap_max"]))
self.app.ui.grid_gap_link_cb.setChecked(True)
self.rtree_index = rtindex.Index()
@ -2048,10 +2053,27 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.shapes.enabled = True
self.tool_shape.enabled = True
self.app.app_cursor.enabled = True
self.app.ui.snap_max_dist_entry.setDisabled(False)
self.app.ui.snap_max_dist_entry.setEnabled(True)
self.app.ui.corner_snap_btn.setEnabled(True)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.geo_editor_menu.setDisabled(False)
self.app.ui.geo_editor_menu.menuAction().setVisible(True)
self.app.ui.update_obj_btn.setEnabled(True)
self.app.ui.g_editor_cmenu.setEnabled(True)
self.app.ui.geo_edit_toolbar.setDisabled(False)
self.app.ui.geo_edit_toolbar.setVisible(True)
self.app.ui.snap_toolbar.setDisabled(False)
# prevent the user to change anything in the Selected Tab while the Geo Editor is active
sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget)
for w in sel_tab_widget_list:
w.setEnabled(False)
# Tell the App that the editor is active
self.editor_active = True
@ -2059,11 +2081,33 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.disconnect_canvas_event_handlers()
self.clear()
self.app.ui.geo_edit_toolbar.setDisabled(True)
self.app.ui.geo_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setDisabled(True)
self.app.ui.corner_snap_btn.setEnabled(False)
# never deactivate the snap toolbar - MS
# self.app.ui.snap_toolbar.setDisabled(True) # TODO: Combine and move into tool
settings = QSettings("Open Source", "FlatCAM")
if settings.contains("theme"):
theme = settings.value('theme', type=str)
if theme == 'standard':
# self.app.ui.geo_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
elif theme == 'compact':
# self.app.ui.geo_edit_toolbar.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
else:
# self.app.ui.geo_edit_toolbar.setVisible(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
# set the Editor Toolbar visibility to what was before entering in the Editor
self.app.ui.geo_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
else self.app.ui.geo_edit_toolbar.setVisible(True)
# Disable visuals
self.shapes.enabled = False
@ -2071,6 +2115,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.app_cursor.enabled = False
self.app.ui.geo_editor_menu.setDisabled(True)
self.app.ui.geo_editor_menu.menuAction().setVisible(False)
self.app.ui.update_obj_btn.setEnabled(False)
self.app.ui.g_editor_cmenu.setEnabled(False)
self.app.ui.e_editor_cmenu.setEnabled(False)
# Tell the app that the editor is no longer active
self.editor_active = False
@ -2230,9 +2281,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.add_shape(DrawToolShape(shape))
self.replot()
self.app.ui.geo_edit_toolbar.setDisabled(False)
self.app.ui.geo_edit_toolbar.setVisible(True)
self.app.ui.snap_toolbar.setDisabled(False)
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() == False:
@ -2561,7 +2610,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
if event.key.name == 'Escape':
# TODO: ...?
# self.on_tool_select("select")
self.app.inform.emit("[warning_notcl]Cancelled.")
self.app.inform.emit("[WARNING_NOTCL]Cancelled.")
self.delete_utility_geometry()
@ -2723,47 +2772,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# Show Shortcut list
if event.key.name == '`':
self.on_shortcut_list()
def on_shortcut_list(self):
msg = '''<b>Shortcut list in Geometry Editor</b><br>
<br>
<b>1:</b> Zoom Fit<br>
<b>2:</b> Zoom Out<br>
<b>3:</b> Zoom In<br>
<b>A:</b> Add an 'Arc'<br>
<b>B:</b> Add a Buffer Geo<br>
<b>C:</b> Copy Geo Item<br>
<b>E:</b> Intersection Tool<br>
<b>G:</b> Grid Snap On/Off<br>
<b>I:</b> Paint Tool<br>
<b>K:</b> Corner Snap On/Off<br>
<b>M:</b> Move Geo Item<br>
<br>
<b>N:</b> Add an 'Polygon'<br>
<b>O:</b> Add a 'Circle'<br>
<b>P:</b> Add a 'Path'<br>
<b>R:</b> Add an 'Rectangle'<br>
<b>S:</b> Substraction Tool<br>
<b>T:</b> Add Text Geometry<br>
<b>U:</b> Union Tool<br>
<br>
<b>X:</b> Cut Path<br>
<br>
<b>~:</b> Show Shortcut List<br>
<br>
<b>Space:</b> Rotate selected Geometry<br>
<b>Enter:</b> Finish Current Action<br>
<b>Escape:</b> Select Tool (Exit any other Tool)<br>
<b>Delete:</b> Delete Obj'''
helpbox =QtWidgets.QMessageBox()
helpbox.setText(msg)
helpbox.setWindowTitle("Help")
helpbox.setWindowIcon(QtGui.QIcon('share/help.png'))
helpbox.setStandardButtons(QtWidgets.QMessageBox.Ok)
helpbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
helpbox.exec_()
self.app.on_shortcut_list()
def on_canvas_key_release(self, event):
self.key = None
@ -2945,7 +2954,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
nearest_pt, shape = self.storage.nearest((x, y))
nearest_pt_distance = distance((x, y), nearest_pt)
if nearest_pt_distance <= self.options["snap_max"]:
if nearest_pt_distance <= float(self.options["global_snap_max"]):
snap_distance = nearest_pt_distance
snap_x, snap_y = nearest_pt
except (StopIteration, AssertionError):
@ -3037,7 +3046,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
results = shapes[0].geo
except Exception as e:
log.debug("FlatCAMGeoEditor.intersection() --> %s" % str(e))
self.app.inform.emit("[warning_notcl]A selection of at least 2 geo items is required to do Intersection.")
self.app.inform.emit("[WARNING_NOTCL]A selection of at least 2 geo items is required to do Intersection.")
self.select_tool('select')
return
@ -3075,7 +3084,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
if buf_distance < 0:
self.app.inform.emit(
"[error_notcl]Negative buffer value is not accepted. Use Buffer interior to generate an 'inside' shape")
"[ERROR_NOTCL]Negative buffer value is not accepted. Use Buffer interior to generate an 'inside' shape")
# deselect everything
self.selected = []
@ -3083,11 +3092,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
return
if len(selected) == 0:
self.app.inform.emit("[warning_notcl] Nothing selected for buffering.")
self.app.inform.emit("[WARNING_NOTCL] Nothing selected for buffering.")
return
if not isinstance(buf_distance, float):
self.app.inform.emit("[warning_notcl] Invalid distance for buffering.")
self.app.inform.emit("[WARNING_NOTCL] Invalid distance for buffering.")
# deselect everything
self.selected = []
@ -3097,7 +3106,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
pre_buffer = cascaded_union([t.geo for t in selected])
results = pre_buffer.buffer(buf_distance - 1e-10, resolution=32, join_style=join_style)
if results.is_empty:
self.app.inform.emit("[error_notcl]Failed, the result is empty. Choose a different buffer value.")
self.app.inform.emit("[ERROR_NOTCL]Failed, the result is empty. Choose a different buffer value.")
# deselect everything
self.selected = []
self.replot()
@ -3112,18 +3121,18 @@ class FlatCAMGeoEditor(QtCore.QObject):
if buf_distance < 0:
self.app.inform.emit(
"[error_notcl]Negative buffer value is not accepted. Use Buffer interior to generate an 'inside' shape")
"[ERROR_NOTCL]Negative buffer value is not accepted. Use Buffer interior to generate an 'inside' shape")
# deselect everything
self.selected = []
self.replot()
return
if len(selected) == 0:
self.app.inform.emit("[warning_notcl] Nothing selected for buffering.")
self.app.inform.emit("[WARNING_NOTCL] Nothing selected for buffering.")
return
if not isinstance(buf_distance, float):
self.app.inform.emit("[warning_notcl] Invalid distance for buffering.")
self.app.inform.emit("[WARNING_NOTCL] Invalid distance for buffering.")
# deselect everything
self.selected = []
self.replot()
@ -3132,7 +3141,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
pre_buffer = cascaded_union([t.geo for t in selected])
results = pre_buffer.buffer(-buf_distance + 1e-10, resolution=32, join_style=join_style)
if results.is_empty:
self.app.inform.emit("[error_notcl]Failed, the result is empty. Choose a smaller buffer value.")
self.app.inform.emit("[ERROR_NOTCL]Failed, the result is empty. Choose a smaller buffer value.")
# deselect everything
self.selected = []
self.replot()
@ -3152,7 +3161,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# return
#
# if not isinstance(buf_distance, float):
# self.app.inform.emit("[warning] Invalid distance for buffering.")
# self.app.inform.emit("[WARNING] Invalid distance for buffering.")
# return
#
# pre_buffer = cascaded_union([t.geo for t in selected])
@ -3182,7 +3191,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
selected = self.get_selected()
if buf_distance < 0:
self.app.inform.emit("[error_notcl]Negative buffer value is not accepted. "
self.app.inform.emit("[ERROR_NOTCL]Negative buffer value is not accepted. "
"Use Buffer interior to generate an 'inside' shape")
# deselect everything
self.selected = []
@ -3190,11 +3199,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
return
if len(selected) == 0:
self.app.inform.emit("[warning_notcl] Nothing selected for buffering.")
self.app.inform.emit("[WARNING_NOTCL] Nothing selected for buffering.")
return
if not isinstance(buf_distance, float):
self.app.inform.emit("[warning_notcl] Invalid distance for buffering.")
self.app.inform.emit("[WARNING_NOTCL] Invalid distance for buffering.")
# deselect everything
self.selected = []
self.replot()
@ -3203,7 +3212,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
pre_buffer = cascaded_union([t.geo for t in selected])
results = pre_buffer.buffer(buf_distance - 1e-10, resolution=32, join_style=join_style)
if results.is_empty:
self.app.inform.emit("[error_notcl]Failed, the result is empty. Choose a different buffer value.")
self.app.inform.emit("[ERROR_NOTCL]Failed, the result is empty. Choose a different buffer value.")
# deselect everything
self.selected = []
self.replot()
@ -3221,13 +3230,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
# selected = self.get_selected()
#
# if len(selected) == 0:
# self.app.inform.emit("[warning] Nothing selected for painting.")
# self.app.inform.emit("[WARNING] Nothing selected for painting.")
# return
#
# for param in [tooldia, overlap, margin]:
# if not isinstance(param, float):
# param_name = [k for k, v in locals().items() if v is param][0]
# self.app.inform.emit("[warning] Invalid value for {}".format(param))
# self.app.inform.emit("[WARNING] Invalid value for {}".format(param))
#
# # Todo: Check for valid method.
#
@ -3279,19 +3288,19 @@ class FlatCAMGeoEditor(QtCore.QObject):
selected = self.get_selected()
if len(selected) == 0:
self.app.inform.emit("[warning_notcl]Nothing selected for painting.")
self.app.inform.emit("[WARNING_NOTCL]Nothing selected for painting.")
return
for param in [tooldia, overlap, margin]:
if not isinstance(param, float):
param_name = [k for k, v in locals().items() if v is param][0]
self.app.inform.emit("[warning] Invalid value for {}".format(param))
self.app.inform.emit("[WARNING] Invalid value for {}".format(param))
results = []
if tooldia >= overlap:
self.app.inform.emit(
"[error_notcl] Could not do Paint. Overlap value has to be less than Tool Dia value.")
"[ERROR_NOTCL] Could not do Paint. Overlap value has to be less than Tool Dia value.")
return
def recurse(geometry, reset=True):
@ -3350,7 +3359,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit(
"[error] Could not do Paint. Try a different combination of parameters. "
"[ERROR] Could not do Paint. Try a different combination of parameters. "
"Or a different method of Paint\n%s" % str(e))
return
@ -3694,6 +3703,9 @@ class FlatCAMExcEditor(QtCore.QObject):
# this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
self.launched_from_shortcuts = False
# this var will store the state of the toolbar before starting the editor
self.toolbar_old_state = False
self.app.ui.delete_drill_btn.triggered.connect(self.on_delete_btn)
self.name_entry.returnPressed.connect(self.on_name_activate)
self.addtool_btn.clicked.connect(self.on_tool_add)
@ -3704,6 +3716,17 @@ class FlatCAMExcEditor(QtCore.QObject):
self.drill_axis_radio.activated_custom.connect(self.on_linear_angle_radio)
self.app.ui.exc_add_array_drill_menuitem.triggered.connect(self.exc_add_drill_array)
self.app.ui.exc_add_drill_menuitem.triggered.connect(self.exc_add_drill)
self.app.ui.exc_resize_drill_menuitem.triggered.connect(self.exc_resize_drills)
self.app.ui.exc_copy_drill_menuitem.triggered.connect(self.exc_copy_drills)
self.app.ui.exc_delete_drill_menuitem.triggered.connect(self.on_delete_btn)
self.app.ui.exc_move_drill_menuitem.triggered.connect(self.exc_move_drills)
# Init GUI
self.drill_array_size_entry.set_value(5)
self.drill_pitch_entry.set_value(2.54)
self.drill_angle_entry.set_value(12)
@ -4046,7 +4069,7 @@ class FlatCAMExcEditor(QtCore.QObject):
# each time a tool diameter is edited or added
self.olddia_newdia[tool_dia] = tool_dia
else:
self.app.inform.emit("[warning_notcl]Tool already in the original or actual tool list.\n"
self.app.inform.emit("[WARNING_NOTCL]Tool already in the original or actual tool list.\n"
"Save and reedit Excellon if you need to add this tool. ")
return
@ -4084,7 +4107,7 @@ class FlatCAMExcEditor(QtCore.QObject):
else:
deleted_tool_dia_list.append(float('%.4f' % dia))
except:
self.app.inform.emit("[warning_notcl]Select a tool in Tool Table")
self.app.inform.emit("[WARNING_NOTCL]Select a tool in Tool Table")
return
for deleted_tool_dia in deleted_tool_dia_list:
@ -4172,8 +4195,26 @@ class FlatCAMExcEditor(QtCore.QObject):
self.shapes.enabled = True
self.tool_shape.enabled = True
# self.app.app_cursor.enabled = True
self.app.ui.snap_max_dist_entry.setDisabled(False)
self.app.ui.snap_max_dist_entry.setEnabled(True)
self.app.ui.corner_snap_btn.setEnabled(True)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
self.app.ui.exc_editor_menu.setDisabled(False)
self.app.ui.exc_editor_menu.menuAction().setVisible(True)
self.app.ui.update_obj_btn.setEnabled(True)
self.app.ui.e_editor_cmenu.setEnabled(True)
self.app.ui.exc_edit_toolbar.setDisabled(False)
self.app.ui.exc_edit_toolbar.setVisible(True)
# self.app.ui.snap_toolbar.setDisabled(False)
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
# Tell the App that the editor is active
self.editor_active = True
@ -4181,9 +4222,35 @@ class FlatCAMExcEditor(QtCore.QObject):
self.disconnect_canvas_event_handlers()
self.clear()
self.app.ui.exc_edit_toolbar.setDisabled(True)
self.app.ui.exc_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setDisabled(True)
self.app.ui.corner_snap_btn.setEnabled(False)
settings = QSettings("Open Source", "FlatCAM")
if settings.contains("theme"):
theme = settings.value('theme', type=str)
if theme == 'standard':
# self.app.ui.exc_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
elif theme == 'compact':
# self.app.ui.exc_edit_toolbar.setVisible(True)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(True)
self.app.ui.corner_snap_btn.setVisible(True)
else:
# self.app.ui.exc_edit_toolbar.setVisible(False)
self.app.ui.snap_max_dist_entry.setEnabled(False)
self.app.ui.corner_snap_btn.setEnabled(False)
self.app.ui.snap_magnet.setVisible(False)
self.app.ui.corner_snap_btn.setVisible(False)
# set the Editor Toolbar visibility to what was before entering in the Editor
self.app.ui.exc_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
else self.app.ui.exc_edit_toolbar.setVisible(True)
# Disable visuals
self.shapes.enabled = False
@ -4193,6 +4260,14 @@ class FlatCAMExcEditor(QtCore.QObject):
# Tell the app that the editor is no longer active
self.editor_active = False
self.app.ui.exc_editor_menu.setDisabled(True)
self.app.ui.exc_editor_menu.menuAction().setVisible(False)
self.app.ui.update_obj_btn.setEnabled(False)
self.app.ui.g_editor_cmenu.setEnabled(False)
self.app.ui.e_editor_cmenu.setEnabled(False)
# Show original geometry
if self.exc_obj:
self.exc_obj.visible = True
@ -4250,7 +4325,7 @@ class FlatCAMExcEditor(QtCore.QObject):
# self.storage = FlatCAMExcEditor.make_storage()
self.replot()
def edit_exc_obj(self, exc_obj):
def edit_fcexcellon(self, exc_obj):
"""
Imports the geometry from the given FlatCAM Excellon object
into the editor.
@ -4298,15 +4373,8 @@ class FlatCAMExcEditor(QtCore.QObject):
self.storage_dict[tool_dia] = storage_elem
self.replot()
self.app.ui.exc_edit_toolbar.setDisabled(False)
self.app.ui.exc_edit_toolbar.setVisible(True)
self.app.ui.snap_toolbar.setDisabled(False)
# start with GRID toolbar activated
if self.app.ui.grid_snap_btn.isChecked() is False:
self.app.ui.grid_snap_btn.trigger()
def update_exc_obj(self, exc_obj):
def update_fcexcellon(self, exc_obj):
"""
Create a new Excellon object that contain the edited content of the source Excellon object
@ -4411,6 +4479,21 @@ class FlatCAMExcEditor(QtCore.QObject):
# Switch notebook to Selected page
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
def update_options(self, obj):
try:
if not obj.options:
obj.options = {}
obj.options['xmin'] = 0
obj.options['ymin'] = 0
obj.options['xmax'] = 0
obj.options['ymax'] = 0
return True
else:
return False
except AttributeError:
obj.options = {}
return True
def new_edited_excellon(self, outname):
"""
Creates a new Excellon object for the edited Excellon. Thread-safe.
@ -4430,14 +4513,15 @@ class FlatCAMExcEditor(QtCore.QObject):
excellon_obj.drills = self.new_drills
excellon_obj.tools = self.new_tools
excellon_obj.slots = self.new_slots
excellon_obj.options['name'] = outname
try:
excellon_obj.create_geometry()
except KeyError:
self.app.inform.emit(
"[error_notcl] There are no Tools definitions in the file. Aborting Excellon creation.")
"[ERROR_NOTCL] There are no Tools definitions in the file. Aborting Excellon creation.")
except:
msg = "[error] An internal error has ocurred. See shell.\n"
msg = "[ERROR] An internal error has ocurred. See shell.\n"
msg += traceback.format_exc()
app_obj.inform.emit(msg)
raise
@ -4469,7 +4553,7 @@ class FlatCAMExcEditor(QtCore.QObject):
# self.draw_app.select_tool('select')
self.complete = True
current_tool = 'select'
self.app.inform.emit("[warning_notcl]Cancelled. There is no Tool/Drill selected")
self.app.inform.emit("[WARNING_NOTCL]Cancelled. There is no Tool/Drill selected")
# This is to make the group behave as radio group
if current_tool in self.tools_exc:
@ -4813,7 +4897,7 @@ class FlatCAMExcEditor(QtCore.QObject):
if event.key.name == 'Escape':
# TODO: ...?
# self.on_tool_select("select")
self.app.inform.emit("[warning_notcl]Cancelled.")
self.app.inform.emit("[WARNING_NOTCL]Cancelled.")
self.delete_utility_geometry()
@ -4830,7 +4914,7 @@ class FlatCAMExcEditor(QtCore.QObject):
self.delete_selected()
self.replot()
else:
self.app.inform.emit("[warning_notcl]Cancelled. Nothing selected to delete.")
self.app.inform.emit("[WARNING_NOTCL]Cancelled. Nothing selected to delete.")
return
if event.key == '1':
@ -4862,7 +4946,7 @@ class FlatCAMExcEditor(QtCore.QObject):
self.on_tool_select('copy')
self.active_tool.set_origin((self.snap_x, self.snap_y))
else:
self.app.inform.emit("[warning_notcl]Cancelled. Nothing selected to copy.")
self.app.inform.emit("[WARNING_NOTCL]Cancelled. Nothing selected to copy.")
return
# Add Drill Hole Tool
@ -4899,7 +4983,7 @@ class FlatCAMExcEditor(QtCore.QObject):
self.on_tool_select('move')
self.active_tool.set_origin((self.snap_x, self.snap_y))
else:
self.app.inform.emit("[warning_notcl]Cancelled. Nothing selected to move.")
self.app.inform.emit("[WARNING_NOTCL]Cancelled. Nothing selected to move.")
return
# Resize Tool
@ -4923,39 +5007,9 @@ class FlatCAMExcEditor(QtCore.QObject):
# Show Shortcut list
if event.key.name == '`':
self.on_shortcut_list()
self.app.on_shortcut_list()
return
def on_shortcut_list(self):
msg = '''<b>Shortcut list in Geometry Editor</b><br>
<br>
<b>1:</b> Zoom Fit<br>
<b>2:</b> Zoom Out<br>
<b>3:</b> Zoom In<br>
<b>A:</b> Add an 'Drill Array'<br>
<b>C:</b> Copy Drill Hole<br>
<b>D:</b> Add an Drill Hole<br>
<b>G:</b> Grid Snap On/Off<br>
<b>K:</b> Corner Snap On/Off<br>
<b>M:</b> Move Drill Hole<br>
<br>
<b>R:</b> Resize a 'Drill Hole'<br>
<b>S:</b> Select Tool Active<br>
<br>
<b>~:</b> Show Shortcut List<br>
<br>
<b>Enter:</b> Finish Current Action<br>
<b>Escape:</b> Abort Current Action<br>
<b>Delete:</b> Delete Drill Hole'''
helpbox =QtWidgets.QMessageBox()
helpbox.setText(msg)
helpbox.setWindowTitle("Help")
helpbox.setWindowIcon(QtGui.QIcon('share/help.png'))
helpbox.setStandardButtons(QtWidgets.QMessageBox.Ok)
helpbox.setDefaultButton(QtWidgets.QMessageBox.Ok)
helpbox.exec_()
def on_canvas_key_release(self, event):
self.key = None
@ -5197,10 +5251,18 @@ class FlatCAMExcEditor(QtCore.QObject):
self.select_tool('add_array')
return
def exc_resize_drills(self):
self.select_tool('resize')
return
def exc_copy_drills(self):
self.select_tool('copy')
return
def exc_move_drills(self):
self.select_tool('move')
return
def distance(pt1, pt2):
return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,6 @@
from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets
from PyQt5 import QtGui, QtCore, QtWidgets
from PyQt5.QtCore import pyqtSignal, pyqtSlot
from copy import copy
import re
import logging
@ -550,6 +552,478 @@ class FCTab(QtWidgets.QTabWidget):
self.tabBar().setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
class FCDetachableTab(QtWidgets.QTabWidget):
# From here: https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
def __init__(self, protect=None, protect_by_name=None, parent=None):
super().__init__()
self.tabBar = self.FCTabBar(self)
self.tabBar.onDetachTabSignal.connect(self.detachTab)
self.tabBar.onMoveTabSignal.connect(self.moveTab)
self.tabBar.detachedTabDropSignal.connect(self.detachedTabDrop)
self.setTabBar(self.tabBar)
# Used to keep a reference to detached tabs since their QMainWindow
# does not have a parent
self.detachedTabs = {}
# a way to make sure that tabs can't be closed after they attach to the parent tab
self.protect_tab = True if protect is not None and protect is True else False
self.protect_by_name = protect_by_name if isinstance(protect_by_name, list) else None
# Close all detached tabs if the application is closed explicitly
QtWidgets.qApp.aboutToQuit.connect(self.closeDetachedTabs) # @UndefinedVariable
# used by the property self.useOldIndex(param)
self.use_old_index = None
self.old_index = None
self.setTabsClosable(True)
self.tabCloseRequested.connect(self.closeTab)
def useOldIndex(self, param):
if param:
self.use_old_index = True
else:
self.use_old_index = False
def deleteTab(self, currentIndex):
widget = self.widget(currentIndex)
if widget is not None:
widget.deleteLater()
self.removeTab(currentIndex)
def closeTab(self, currentIndex):
self.removeTab(currentIndex)
def protectTab(self, currentIndex):
# self.FCTabBar().setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
self.tabBar.setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
##
# The default movable functionality of QTabWidget must remain disabled
# so as not to conflict with the added features
def setMovable(self, movable):
pass
##
# Move a tab from one position (index) to another
#
# @param fromIndex the original index location of the tab
# @param toIndex the new index location of the tab
@pyqtSlot(int, int)
def moveTab(self, fromIndex, toIndex):
widget = self.widget(fromIndex)
icon = self.tabIcon(fromIndex)
text = self.tabText(fromIndex)
self.removeTab(fromIndex)
self.insertTab(toIndex, widget, icon, text)
self.setCurrentIndex(toIndex)
##
# Detach the tab by removing it's contents and placing them in
# a DetachedTab window
#
# @param index the index location of the tab to be detached
# @param point the screen position for creating the new DetachedTab window
@pyqtSlot(int, QtCore.QPoint)
def detachTab(self, index, point):
self.old_index = index
# Get the tab content
name = self.tabText(index)
icon = self.tabIcon(index)
if icon.isNull():
icon = self.window().windowIcon()
contentWidget = self.widget(index)
try:
contentWidgetRect = contentWidget.frameGeometry()
except AttributeError:
return
# Create a new detached tab window
detachedTab = self.FCDetachedTab(name, contentWidget)
detachedTab.setWindowModality(QtCore.Qt.NonModal)
detachedTab.setWindowIcon(icon)
detachedTab.setGeometry(contentWidgetRect)
detachedTab.onCloseSignal.connect(self.attachTab)
detachedTab.onDropSignal.connect(self.tabBar.detachedTabDrop)
detachedTab.move(point)
detachedTab.show()
# Create a reference to maintain access to the detached tab
self.detachedTabs[name] = detachedTab
##
# Re-attach the tab by removing the content from the DetachedTab window,
# closing it, and placing the content back into the DetachableTabWidget
#
# @param contentWidget the content widget from the DetachedTab window
# @param name the name of the detached tab
# @param icon the window icon for the detached tab
# @param insertAt insert the re-attached tab at the given index
def attachTab(self, contentWidget, name, icon, insertAt=None):
# Make the content widget a child of this widget
contentWidget.setParent(self)
# Remove the reference
del self.detachedTabs[name]
# helps in restoring the tab to the same index that it was before was detached
insert_index = self.old_index if self.use_old_index is True else insertAt
# Create an image from the given icon (for comparison)
if not icon.isNull():
try:
tabIconPixmap = icon.pixmap(icon.availableSizes()[0])
tabIconImage = tabIconPixmap.toImage()
except IndexError:
tabIconImage = None
else:
tabIconImage = None
# Create an image of the main window icon (for comparison)
if not icon.isNull():
try:
windowIconPixmap = self.window().windowIcon().pixmap(icon.availableSizes()[0])
windowIconImage = windowIconPixmap.toImage()
except IndexError:
windowIconImage = None
else:
windowIconImage = None
# Determine if the given image and the main window icon are the same.
# If they are, then do not add the icon to the tab
if tabIconImage == windowIconImage:
if insert_index is None:
index = self.addTab(contentWidget, name)
else:
index = self.insertTab(insert_index, contentWidget, name)
else:
if insert_index is None:
index = self.addTab(contentWidget, icon, name)
else:
index = self.insertTab(insert_index, contentWidget, icon, name)
# on reattaching the tab if protect is true then the closure button is not added
if self.protect_tab is True:
self.protectTab(index)
# on reattaching the tab disable the closure button for the tabs with the name in the self.protect_by_name list
if self.protect_by_name is not None:
for tab_name in self.protect_by_name:
for index in range(self.count()):
if str(tab_name) == str(self.tabText(index)):
self.protectTab(index)
# Make this tab the current tab
if index > -1:
self.setCurrentIndex(insert_index) if self.use_old_index else self.setCurrentIndex(index)
##
# Remove the tab with the given name, even if it is detached
#
# @param name the name of the tab to be removed
def removeTabByName(self, name):
# Remove the tab if it is attached
attached = False
for index in range(self.count()):
if str(name) == str(self.tabText(index)):
self.removeTab(index)
attached = True
break
# If the tab is not attached, close it's window and
# remove the reference to it
if not attached:
for key in self.detachedTabs:
if str(name) == str(key):
self.detachedTabs[key].onCloseSignal.disconnect()
self.detachedTabs[key].close()
del self.detachedTabs[key]
break
##
# Handle dropping of a detached tab inside the DetachableTabWidget
#
# @param name the name of the detached tab
# @param index the index of an existing tab (if the tab bar
# determined that the drop occurred on an
# existing tab)
# @param dropPos the mouse cursor position when the drop occurred
@QtCore.pyqtSlot(str, int, QtCore.QPoint)
def detachedTabDrop(self, name, index, dropPos):
# If the drop occurred on an existing tab, insert the detached
# tab at the existing tab's location
if index > -1:
# Create references to the detached tab's content and icon
contentWidget = self.detachedTabs[name].contentWidget
icon = self.detachedTabs[name].windowIcon()
# Disconnect the detached tab's onCloseSignal so that it
# does not try to re-attach automatically
self.detachedTabs[name].onCloseSignal.disconnect()
# Close the detached
self.detachedTabs[name].close()
# Re-attach the tab at the given index
self.attachTab(contentWidget, name, icon, index)
# If the drop did not occur on an existing tab, determine if the drop
# occurred in the tab bar area (the area to the side of the QTabBar)
else:
# Find the drop position relative to the DetachableTabWidget
tabDropPos = self.mapFromGlobal(dropPos)
# If the drop position is inside the DetachableTabWidget...
if self.rect().contains(tabDropPos):
# If the drop position is inside the tab bar area (the
# area to the side of the QTabBar) or there are not tabs
# currently attached...
if tabDropPos.y() < self.tabBar.height() or self.count() == 0:
# Close the detached tab and allow it to re-attach
# automatically
self.detachedTabs[name].close()
##
# Close all tabs that are currently detached.
def closeDetachedTabs(self):
listOfDetachedTabs = []
for key in self.detachedTabs:
listOfDetachedTabs.append(self.detachedTabs[key])
for detachedTab in listOfDetachedTabs:
detachedTab.close()
##
# When a tab is detached, the contents are placed into this QMainWindow. The tab
# can be re-attached by closing the dialog or by dragging the window into the tab bar
class FCDetachedTab(QtWidgets.QMainWindow):
onCloseSignal = pyqtSignal(QtWidgets.QWidget, str, QtGui.QIcon)
onDropSignal = pyqtSignal(str, QtCore.QPoint)
def __init__(self, name, contentWidget):
QtWidgets.QMainWindow.__init__(self, None)
self.setObjectName(name)
self.setWindowTitle(name)
self.contentWidget = contentWidget
self.setCentralWidget(self.contentWidget)
self.contentWidget.show()
self.windowDropFilter = self.WindowDropFilter()
self.installEventFilter(self.windowDropFilter)
self.windowDropFilter.onDropSignal.connect(self.windowDropSlot)
##
# Handle a window drop event
#
# @param dropPos the mouse cursor position of the drop
@QtCore.pyqtSlot(QtCore.QPoint)
def windowDropSlot(self, dropPos):
self.onDropSignal.emit(self.objectName(), dropPos)
##
# If the window is closed, emit the onCloseSignal and give the
# content widget back to the DetachableTabWidget
#
# @param event a close event
def closeEvent(self, event):
self.onCloseSignal.emit(self.contentWidget, self.objectName(), self.windowIcon())
##
# An event filter class to detect a QMainWindow drop event
class WindowDropFilter(QtCore.QObject):
onDropSignal = pyqtSignal(QtCore.QPoint)
def __init__(self):
QtCore.QObject.__init__(self)
self.lastEvent = None
##
# Detect a QMainWindow drop event by looking for a NonClientAreaMouseMove (173)
# event that immediately follows a Move event
#
# @param obj the object that generated the event
# @param event the current event
def eventFilter(self, obj, event):
# If a NonClientAreaMouseMove (173) event immediately follows a Move event...
if self.lastEvent == QtCore.QEvent.Move and event.type() == 173:
# Determine the position of the mouse cursor and emit it with the
# onDropSignal
mouseCursor = QtGui.QCursor()
dropPos = mouseCursor.pos()
self.onDropSignal.emit(dropPos)
self.lastEvent = event.type()
return True
else:
self.lastEvent = event.type()
return False
class FCTabBar(QtWidgets.QTabBar):
onDetachTabSignal = pyqtSignal(int, QtCore.QPoint)
onMoveTabSignal = pyqtSignal(int, int)
detachedTabDropSignal = pyqtSignal(str, int, QtCore.QPoint)
def __init__(self, parent=None):
QtWidgets.QTabBar.__init__(self, parent)
self.setAcceptDrops(True)
self.setElideMode(QtCore.Qt.ElideRight)
self.setSelectionBehaviorOnRemove(QtWidgets.QTabBar.SelectLeftTab)
self.dragStartPos = QtCore.QPoint()
self.dragDropedPos = QtCore.QPoint()
self.mouseCursor = QtGui.QCursor()
self.dragInitiated = False
# Send the onDetachTabSignal when a tab is double clicked
#
# @param event a mouse double click event
def mouseDoubleClickEvent(self, event):
event.accept()
self.onDetachTabSignal.emit(self.tabAt(event.pos()), self.mouseCursor.pos())
# Set the starting position for a drag event when the mouse button is pressed
#
# @param event a mouse press event
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.dragStartPos = event.pos()
self.dragDropedPos.setX(0)
self.dragDropedPos.setY(0)
self.dragInitiated = False
QtWidgets.QTabBar.mousePressEvent(self, event)
# Determine if the current movement is a drag. If it is, convert it into a QDrag. If the
# drag ends inside the tab bar, emit an onMoveTabSignal. If the drag ends outside the tab
# bar, emit an onDetachTabSignal.
#
# @param event a mouse move event
def mouseMoveEvent(self, event):
# Determine if the current movement is detected as a drag
if not self.dragStartPos.isNull() and ((event.pos() - self.dragStartPos).manhattanLength() < QtWidgets.QApplication.startDragDistance()):
self.dragInitiated = True
# If the current movement is a drag initiated by the left button
if (((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated):
# Stop the move event
finishMoveEvent = QtGui.QMouseEvent(QtCore.QEvent.MouseMove, event.pos(), QtCore.Qt.NoButton, QtCore.Qt.NoButton, QtCore.Qt.NoModifier)
QtWidgets.QTabBar.mouseMoveEvent(self, finishMoveEvent)
# Convert the move event into a drag
drag = QtGui.QDrag(self)
mimeData = QtCore.QMimeData()
# mimeData.setData('action', 'application/tab-detach')
drag.setMimeData(mimeData)
# screen = QScreen(self.parentWidget().currentWidget().winId())
# Create the appearance of dragging the tab content
try:
pixmap = self.parent().widget(self.tabAt(self.dragStartPos)).grab()
except Exception as e:
log.debug("GUIElements.FCDetachable. FCTabBar.mouseMoveEvent() --> %s" % str(e))
return
targetPixmap = QtGui.QPixmap(pixmap.size())
targetPixmap.fill(QtCore.Qt.transparent)
painter = QtGui.QPainter(targetPixmap)
painter.setOpacity(0.85)
painter.drawPixmap(0, 0, pixmap)
painter.end()
drag.setPixmap(targetPixmap)
# Initiate the drag
dropAction = drag.exec_(QtCore.Qt.MoveAction | QtCore.Qt.CopyAction)
# For Linux: Here, drag.exec_() will not return MoveAction on Linux. So it
# must be set manually
if self.dragDropedPos.x() != 0 and self.dragDropedPos.y() != 0:
dropAction = QtCore.Qt.MoveAction
# If the drag completed outside of the tab bar, detach the tab and move
# the content to the current cursor position
if dropAction == QtCore.Qt.IgnoreAction:
event.accept()
self.onDetachTabSignal.emit(self.tabAt(self.dragStartPos), self.mouseCursor.pos())
# Else if the drag completed inside the tab bar, move the selected tab to the new position
elif dropAction == QtCore.Qt.MoveAction:
if not self.dragDropedPos.isNull():
event.accept()
self.onMoveTabSignal.emit(self.tabAt(self.dragStartPos), self.tabAt(self.dragDropedPos))
else:
QtWidgets.QTabBar.mouseMoveEvent(self, event)
# Determine if the drag has entered a tab position from another tab position
#
# @param event a drag enter event
def dragEnterEvent(self, event):
mimeData = event.mimeData()
# formats = mcd imeData.formats()
# if formats.contains('action') and mimeData.data('action') == 'application/tab-detach':
# event.acceptProposedAction()
QtWidgets.QTabBar.dragMoveEvent(self, event)
# Get the position of the end of the drag
#
# @param event a drop event
def dropEvent(self, event):
self.dragDropedPos = event.pos()
QtWidgets.QTabBar.dropEvent(self, event)
# Determine if the detached tab drop event occurred on an existing tab,
# then send the event to the DetachableTabWidget
def detachedTabDrop(self, name, dropPos):
tabDropPos = self.mapFromGlobal(dropPos)
index = self.tabAt(tabDropPos)
self.detachedTabDropSignal.emit(name, index, dropPos)
class VerticalScrollArea(QtWidgets.QScrollArea):
"""
This widget extends QtGui.QScrollArea to make a vertical-only

View File

@ -46,57 +46,66 @@ class KeySensitiveListView(QtWidgets.QTreeView):
event.ignore()
def dragMoveEvent(self, event):
self.setDropIndicatorShown(True)
if event.mimeData().hasUrls:
event.accept()
else:
event.ignore()
def dropEvent(self, event):
if event.mimeData().hasUrls:
event.setDropAction(QtCore.Qt.CopyAction)
drop_indicator = self.dropIndicatorPosition()
m = event.mimeData()
if m.hasUrls:
event.accept()
for url in event.mimeData().urls():
for url in m.urls():
self.filename = str(url.toLocalFile())
if self.filename == "":
self.app.inform.emit("Open cancelled.")
# file drop from outside application
if drop_indicator == QtWidgets.QAbstractItemView.OnItem:
if self.filename == "":
self.app.inform.emit("Open cancelled.")
else:
if self.filename.lower().rpartition('.')[-1] in self.app.grb_list:
self.app.worker_task.emit({'fcn': self.app.open_gerber,
'params': [self.filename]})
else:
event.ignore()
if self.filename.lower().rpartition('.')[-1] in self.app.exc_list:
self.app.worker_task.emit({'fcn': self.app.open_excellon,
'params': [self.filename]})
else:
event.ignore()
if self.filename.lower().rpartition('.')[-1] in self.app.gcode_list:
self.app.worker_task.emit({'fcn': self.app.open_gcode,
'params': [self.filename]})
else:
event.ignore()
if self.filename.lower().rpartition('.')[-1] in self.app.svg_list:
object_type = 'geometry'
self.app.worker_task.emit({'fcn': self.app.import_svg,
'params': [self.filename, object_type, None]})
if self.filename.lower().rpartition('.')[-1] in self.app.dxf_list:
object_type = 'geometry'
self.app.worker_task.emit({'fcn': self.app.import_dxf,
'params': [self.filename, object_type, None]})
if self.filename.lower().rpartition('.')[-1] in self.app.prj_list:
# self.app.open_project() is not Thread Safe
self.app.open_project(self.filename)
else:
event.ignore()
else:
if self.filename.lower().rpartition('.')[-1] in self.app.grb_list:
self.app.worker_task.emit({'fcn': self.app.open_gerber,
'params': [self.filename]})
else:
event.ignore()
if self.filename.lower().rpartition('.')[-1] in self.app.exc_list:
self.app.worker_task.emit({'fcn': self.app.open_excellon,
'params': [self.filename]})
else:
event.ignore()
if self.filename.lower().rpartition('.')[-1] in self.app.gcode_list:
self.app.worker_task.emit({'fcn': self.app.open_gcode,
'params': [self.filename]})
else:
event.ignore()
if self.filename.lower().rpartition('.')[-1] in self.app.svg_list:
object_type = 'geometry'
self.app.worker_task.emit({'fcn': self.app.import_svg,
'params': [self.filename, object_type, None]})
if self.filename.lower().rpartition('.')[-1] in self.app.dxf_list:
object_type = 'geometry'
self.app.worker_task.emit({'fcn': self.app.import_dxf,
'params': [self.filename, object_type, None]})
if self.filename.lower().rpartition('.')[-1] in self.app.prj_list:
# self.app.open_project() is not Thread Safe
self.app.open_project(self.filename)
else:
event.ignore()
pass
else:
event.ignore()
class TreeItem:
"""
Item of a tree model
@ -221,9 +230,15 @@ class ObjectCollection(QtCore.QAbstractItemModel):
### View
self.view = KeySensitiveListView(app)
self.view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.view.setModel(self)
self.view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
# self.view.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
# self.view.setDragEnabled(True)
# self.view.setAcceptDrops(True)
# self.view.setDropIndicatorShown(True)
font = QtGui.QFont()
font.setPixelSize(12)
font.setFamily("Seagoe UI")
@ -273,6 +288,11 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if key == QtCore.Qt.Key_S:
self.app.on_file_saveproject()
# Toggle Plot Area
if key == QtCore.Qt.Key_F10:
self.app.on_toggle_plotarea()
return
elif modifiers == QtCore.Qt.ShiftModifier:
@ -324,7 +344,20 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if key == QtCore.Qt.Key_Y:
self.app.on_skewy()
return
elif modifiers == QtCore.Qt.AltModifier:
# Eanble all plots
if key == Qt.Key_1:
self.app.enable_all_plots()
# Disable all plots
if key == Qt.Key_2:
self.app.disable_all_plots()
# Disable all other plots
if key == Qt.Key_3:
self.app.disable_other_plots()
# 2-Sided PCB Tool
if key == QtCore.Qt.Key_D:
self.app.dblsidedtool.run()
@ -354,17 +387,17 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if key == QtCore.Qt.Key_F2:
webbrowser.open(self.app.video_url)
# Zoom Fit
# Switch to Project Tab
if key == QtCore.Qt.Key_1:
self.app.on_zoom_fit(None)
self.app.on_select_tab('project')
# Zoom In
# Switch to Selected Tab
if key == QtCore.Qt.Key_2:
self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'], self.app.mouse)
self.app.on_select_tab('selected')
# Zoom Out
# Switch to Tool Tab
if key == QtCore.Qt.Key_3:
self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'], self.app.mouse)
self.app.on_select_tab('tool')
# Delete
if key == QtCore.Qt.Key_Delete and active:
@ -444,6 +477,14 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if key == QtCore.Qt.Key_Y:
self.app.on_flipy()
# Zoom In
if key == QtCore.Qt.Key_Equal:
self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'], self.app.mouse)
# Zoom Out
if key == QtCore.Qt.Key_Minus:
self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'], self.app.mouse)
# Show shortcut list
if key == QtCore.Qt.Key_Ampersand:
self.app.on_shortcut_list()
@ -483,13 +524,13 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parent_item = self.root_item
else:
parent_item = parent.internalPointer()
# if not parent.isValid():
# parent_item = self.root_item
# else:
# parent_item = parent.internalPointer()
parent_item = parent.internalPointer() if parent.isValid() else self.root_item
child_item = parent_item.child(row)
if child_item:
return self.createIndex(row, column, child_item)
else:
@ -569,39 +610,27 @@ class ObjectCollection(QtCore.QAbstractItemModel):
"setData() --> Could not remove the old object name from auto-completer model list")
obj.build_ui()
self.app.inform.emit("Object renamed from %s to %s" % (old_name, new_name))
self.app.inform.emit("Object renamed from %s to %s" % (old_name, new_name))
return True
def supportedDropActions(self):
return Qt.MoveAction
def flags(self, index):
default_flags = QtCore.QAbstractItemModel.flags(self, index)
if not index.isValid():
return 0
return Qt.ItemIsEnabled | default_flags
# Prevent groups from selection
if not index.internalPointer().obj:
return Qt.ItemIsEnabled
else:
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable | \
Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
return QtWidgets.QAbstractItemModel.flags(self, index)
# def data(self, index, role=Qt.Qt.DisplayRole):
# if not index.isValid() or not 0 <= index.row() < self.rowCount():
# return QtCore.QVariant()
# row = index.row()
# if role == Qt.Qt.DisplayRole:
# return self.object_list[row].options["name"]
# if role == Qt.Qt.DecorationRole:
# return self.icons[self.object_list[row].kind]
# # if role == Qt.Qt.CheckStateRole:
# # if row in self.checked_indexes:
# # return Qt.Qt.Checked
# # else:
# # return Qt.Qt.Unchecked
def print_list(self):
for obj in self.get_list():
print(obj)
# return QtWidgets.QAbstractItemModel.flags(self, index)
def append(self, obj, active=False):
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.append()")
@ -611,8 +640,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
# Check promises and clear if exists
if name in self.promises:
self.promises.remove(name)
FlatCAMApp.App.log.debug("Promised object %s became available." % name)
FlatCAMApp.App.log.debug("%d promised objects remaining." % len(self.promises))
# FlatCAMApp.App.log.debug("Promised object %s became available." % name)
# FlatCAMApp.App.log.debug("%d promised objects remaining." % len(self.promises))
# Prevent same name
while name in self.get_names():
## Create a new name

View File

@ -556,15 +556,38 @@ class ExcellonObjectUI(ObjectUI):
self.ois_dwell = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
# postprocessor selection
pp_excellon_label = QtWidgets.QLabel("Postprocessor")
pp_excellon_label = QtWidgets.QLabel("Postprocessor:")
pp_excellon_label.setToolTip(
"The json file that dictates\n"
"gcode output."
)
self.tools_box.addWidget(pp_excellon_label)
self.pp_excellon_name_cb = FCComboBox()
self.pp_excellon_name_cb.setFocusPolicy(QtCore.Qt.StrongFocus)
self.tools_box.addWidget(self.pp_excellon_name_cb)
grid1.addWidget(pp_excellon_label, 10, 0)
grid1.addWidget(self.pp_excellon_name_cb, 10, 1)
# Probe depth
self.pdepth_label = QtWidgets.QLabel("Probe Z depth:")
self.pdepth_label.setToolTip(
"The maximum depth that the probe is allowed\n"
"to probe. Negative value, in current units."
)
grid1.addWidget(self.pdepth_label, 11, 0)
self.pdepth_entry = FCEntry()
grid1.addWidget(self.pdepth_entry, 11, 1)
self.pdepth_label.hide()
self.pdepth_entry.setVisible(False)
# Probe feedrate
self.feedrate_probe_label = QtWidgets.QLabel("Feedrate Probe:")
self.feedrate_probe_label.setToolTip(
"The feedrate used while the probe is probing."
)
grid1.addWidget(self.feedrate_probe_label, 12, 0)
self.feedrate_probe_entry = FCEntry()
grid1.addWidget(self.feedrate_probe_entry, 12, 1)
self.feedrate_probe_label.hide()
self.feedrate_probe_entry.setVisible(False)
choose_tools_label = QtWidgets.QLabel(
"Select from the Tools Table above\n"
@ -708,6 +731,8 @@ class GeometryObjectUI(ObjectUI):
self.geo_tools_table.setColumnWidth(0, 20)
self.geo_tools_table.setHorizontalHeaderLabels(['#', 'Dia', 'Offset', 'Type', 'TT', '', 'P'])
self.geo_tools_table.setColumnHidden(5, True)
# stylesheet = "::section{Background-color:rgb(239,239,245)}"
# self.geo_tools_table.horizontalHeader().setStyleSheet(stylesheet)
self.geo_tools_table.horizontalHeaderItem(0).setToolTip(
"This is the Tool Number.\n"
@ -758,7 +783,7 @@ class GeometryObjectUI(ObjectUI):
"cut and negative for 'inside' cut."
)
self.grid1.addWidget(self.tool_offset_lbl, 0, 0)
self.tool_offset_entry = FloatEntry()
self.tool_offset_entry = FCEntry()
spacer_lbl = QtWidgets.QLabel(" ")
spacer_lbl.setFixedWidth(80)
@ -777,7 +802,7 @@ class GeometryObjectUI(ObjectUI):
self.addtool_entry_lbl.setToolTip(
"Diameter for the new tool"
)
self.addtool_entry = FloatEntry()
self.addtool_entry = FCEntry()
# hlay.addWidget(self.addtool_label)
# hlay.addStretch()
@ -1004,11 +1029,34 @@ class GeometryObjectUI(ObjectUI):
self.pp_geometry_name_cb.setFocusPolicy(QtCore.Qt.StrongFocus)
self.grid3.addWidget(self.pp_geometry_name_cb, 16, 1)
# Probe depth
self.pdepth_label = QtWidgets.QLabel("Probe Z depth:")
self.pdepth_label.setToolTip(
"The maximum depth that the probe is allowed\n"
"to probe. Negative value, in current units."
)
self.grid3.addWidget(self.pdepth_label, 17, 0)
self.pdepth_entry = FCEntry()
self.grid3.addWidget(self.pdepth_entry, 17, 1)
self.pdepth_label.hide()
self.pdepth_entry.setVisible(False)
# Probe feedrate
self.feedrate_probe_label = QtWidgets.QLabel("Feedrate Probe:")
self.feedrate_probe_label.setToolTip(
"The feedrate used while the probe is probing."
)
self.grid3.addWidget(self.feedrate_probe_label, 18, 0)
self.feedrate_probe_entry = FCEntry()
self.grid3.addWidget(self.feedrate_probe_entry, 18, 1)
self.feedrate_probe_label.hide()
self.feedrate_probe_entry.setVisible(False)
warning_lbl = QtWidgets.QLabel(
"Add at least one tool in the tool-table.\n"
"Click the header to select all, or Ctrl + LMB\n"
"for custom selection of tools.")
self.grid3.addWidget(warning_lbl, 17, 0, 1, 2)
self.grid3.addWidget(warning_lbl, 19, 0, 1, 2)
# Button
self.generate_cnc_button = QtWidgets.QPushButton('Generate')
@ -1067,15 +1115,26 @@ class CNCObjectUI(ObjectUI):
self.plot_options_label = QtWidgets.QLabel("<b>Plot Options:</b>")
self.custom_box.addWidget(self.plot_options_label)
# # Tool dia for plot
# tdlabel = QtWidgets.QLabel('Tool dia:')
# tdlabel.setToolTip(
# "Diameter of the tool to be\n"
# "rendered in the plot."
# )
# grid0.addWidget(tdlabel, 1, 0)
# self.tooldia_entry = LengthEntry()
# grid0.addWidget(self.tooldia_entry, 1, 1)
self.cncplot_method_label = QtWidgets.QLabel("Plot kind:")
self.cncplot_method_label.setToolTip(
"This selects the kind of geometries on the canvas to plot.\n"
"Those can be either of type 'Travel' which means the moves\n"
"above the work piece or it can be of type 'Cut',\n"
"which means the moves that cut into the material."
)
self.cncplot_method_combo = RadioSet([
{"label": "All", "value": "all"},
{"label": "Travel", "value": "travel"},
{"label": "Cut", "value": "cut"}
], stretch=False)
f_lay = QtWidgets.QFormLayout()
self.custom_box.addLayout(f_lay)
f_lay.addRow(self.cncplot_method_label, self.cncplot_method_combo)
e1_lbl = QtWidgets.QLabel('')
self.custom_box.addWidget(e1_lbl)
hlay = QtWidgets.QHBoxLayout()
self.custom_box.addLayout(hlay)
@ -1115,6 +1174,8 @@ class CNCObjectUI(ObjectUI):
self.cnc_tools_table.setColumnWidth(0, 20)
self.cnc_tools_table.setHorizontalHeaderLabels(['#', 'Dia', 'Offset', 'Type', 'TT', '', 'P'])
self.cnc_tools_table.setColumnHidden(5, True)
# stylesheet = "::section{Background-color:rgb(239,239,245)}"
# self.cnc_tools_table.horizontalHeader().setStyleSheet(stylesheet)
# Update plot button
self.updateplot_button = QtWidgets.QPushButton('Update Plot')

View File

@ -287,8 +287,8 @@ class ParseFont():
elif font_type == 'regular':
path_filename = regular_dict[font_name]
except Exception as e:
self.app.inform.emit("[error_notcl] Font not supported, try another one.")
log.debug("[error_notcl] Font Loading: %s" % str(e))
self.app.inform.emit("[ERROR_NOTCL] Font not supported, try another one.")
log.debug("[ERROR_NOTCL] Font Loading: %s" % str(e))
return "flatcam font parse failed"
face = ft.Face(path_filename)

View File

@ -121,7 +121,7 @@ def path2shapely(path, object_type, res=1.0):
# geo_element = Polygon(points)
geo_element = LineString(points)
else:
log.error("[error]: Not a valid target object.")
log.error("[ERROR]: Not a valid target object.")
if not points:
continue
else:
@ -639,7 +639,7 @@ def parse_svg_transform(trstr):
continue
# raise Exception("Don't know how to parse: %s" % trstr)
log.error("[error] Don't know how to parse: %s" % trstr)
log.error("[ERROR] Don't know how to parse: %s" % trstr)
return trlist

View File

@ -9,9 +9,93 @@ CAD program, and create G-Code for Isolation routing.
=================================================
6.02.2019
- fixed the units calculators crash FlatCAM when using comma as decimal separator
5.02.3019
- added a text in the Selected Tab which is showed whenever the Selected Tab is selected but without having an object selected to display it's properties
- added an initial text in the Tools tab
- added possibility to use the shortcut key for shortcut list in the Notebook tabs
- added a way to set the Probe depth if Toolchange_Probe postprocessors are selected
- finished the postprocessor file for MACH3 tool probing on toolchange event
- added a new parameter to set the feedrate of the probing in case the used postprocessor does probing (has toolchange_probe in it's name)
- fixed bug in Marlin postprocessor for the Excellon files; the header and toolchange event always used the parenthesis witch is not compatible with GCode for Marlin
- fixed a issue with a move to Z_move before any toolchange
4.02.2019
- modified the Toolchange_Probe_general postprocessor file to remove any Z moves before the actual toolchange event
- created a prototype postprocessor file for usage with tool probing in MACH3
- added the default values for Tool Film and Tool Panelize to the Edit -> Preferences
- added a new parameter in the Tool Film which control the thickness of the stroke width in the resulting SVG. It's a scale parameter.
- whatever was the visibility of the corresponding toolbar when we enter in the Editor, it will be set after exit from the Editor (either Geometry Editor or Excellon Editor).
- added ability to be detached for the tabs in the Notebook section (Project, Selected and Tool)
- added ability for all detachable tabs to be restored to the same position from where they were detached.
- changed the shortcut keys for Zoom In, Zoom Out and Zoom Fit from 1, 2, 3 to '-', '=' respectively 'V'. Added new shortcut keys '1', '2', '3' for Select Project Tab, Select Selected Tab and Select Tool Tab.
- formatted the Shortcut List Tab into a HTML table
3.3.2019
- updated the new shortcut list with the shortcuts added lately
- now the special messages in the Shell are color coded according to the level. Before they all were RED. Now the WARNINGS are yellow, ERRORS are red and SUCCESS is a dark green. Also the level is in CAPS LOCK to make them more obvious
- some more changes to GUI interface (solved issues)
- added some status bar messages in the Geometry Editor to guide the user when using the Geometry Tools
- now the '`' shortcut key that shows the 'shortcut key list' in Editors points to the same window which is created in a tab no longer as a pop-up window. This tab can be detached if needed.
- added a remove_tools() function before install_tools() in the init_tools() that is called when creating a new project. Should solve the issue with having double menu entry's in the TOOLS menu
- fixed remove_tools() so the Tcl Shell action is readded to the Tools menu and reconnected to it's slot function
- added an automatic name on each save operation based on the object name and/or the current date
- added more information's for the statistics
2.2.2019
- code cleanup in Tools
- some GUI structure optimization's
- added protection against entering float numbers with comma separator instead of decimal dot separator in key points of FlatCAM (not everywhere)
- added a choice of plotting the kind of geometry for the CNC plot (all, travel and cut kind of geometries) in CNCJob Selected Tab
- added a new postprocessor file named: 'probe_from_zmove' which allow probing to be done from z_move position on toolchange event
- fixed the snap magnet button in Geometry Editor, restored the checkable property to True
- some more changes in the Editors GUI in deactivate() function
- a fix for saving as empty an edited new and empty Excellon Object
1.02.2019
- fixed postprocessor files so now the bounds values are right aligned (assuming max string length of 9 chars which means 4 digits and 4 decimals)
- corrected small type in list_sys Tcl command; added a protection of the Plot Area Tab after a successful edit.
- remade the way FlatCAM saves the GUI position data from a file (previously) to use PyQt QSettings
- added a 'theme' combo selection in Edit -> Preferences. Two themes are available: standard and compact.
- some code cleanup
- fixed a source of possible errors in DetachableTab Widget.
- fixed gcode conversion/scale (on units change) when multiple values are found on each line
- replaced the pop-up window for the shortcut list with a new detachable tab
- removed the pop-up messages from the rotate, skew, flip commands
31.01.2019
- added a parameter ('Fast plunge' in Edit -> Preferences -> Geometry Options and Excellon Options) to control if the fast move to Z_move is done or not
- added new function to toggle fullscreen status in Menu -> View -> Toggle Full Screen. Shortcut key: Alt+F10
- added key shortcuts for Enable Plots, Disable Plots and Disable other plots functions (Alt+1, Alt+2, Alt+3)
- hidden the snap magnet entry and snap magnet toggle from the main view; they are now active only in Editor Mode
- updated the camlib.CNCJob.scale() function so now the GCode is scaled also (quite a HACK :( it will need to be replaced at some point)). Units change work now on the GCODE also.
- added the bounds coordinates to the GCODE header
- FlatCAM saves now to a file in self.data_path the toolbar positions and the position of TCL Shell
- Plot Area Tab view can now be toggled, added entry in View Menu and shortcut key CTRL+F10
- All the tabs in the GUI right side are (Plot Are, Preferences etc) are now detachable to a separate windows which when closed it returns in the previous location in the toolbar. Those detached tabs can be also reattached by drag and drop.
30.01.2019
- added a space before Y coordinate in end_code() function in some of the postprocessor files
- added in Calculators Tool an Electroplating Calculator.
- remade the App Menu for Editors: now they will be showed only when the respective Editor is active and hidden when the Editor is closed.
- added a traceback report in the TCL Shell for the errors that don't allow creation of an object; useful to trace exceptions/errors
- in case that the Toolchange X,Y parameter in Selected (or in Preferences) are deleted then the app will still do the job using the current coordinates for toolchange
- fixed an issue in camlib.CNCJob where tha variable self.toolchange_xy was used for 2 different purposes which created loss of information.
- fixed unit conversion functions in case the toolchange_xy parameter is None
- more fixes in camlib.CNCJob regarding usage of toolchange (in case it is None)
- fixed postprocessor files to work with toolchange_xy parameter value = None (no values in Edit - Preferences fields)
- fixed Tcl commands CncJob and DrillCncJob to work with toolchange
- added to the postprocessor files the command after toolchange to go with G00 (fastest) to "Z Move" value of Z pozition.
29.01.2019
@ -243,7 +327,7 @@ CAD program, and create G-Code for Isolation routing.
- solved a small bug that didn't allow the Paint Job to be done with lines when the results were geometries not iterable
- added protection for the case when trying to run the cncjob Tcl Command on a Geometry object that do not have solid geometry or one that is multi-tool
- Paint Tool Table: now it is possible to edit a tool to a new diameter and then edit another tool to the former diameter of the first edited tool
- added a new type of warning, [warning_notcl]
- added a new type of warning, [WARNING_NOTCL]
- fixed conflict with "space" keyboard shortcut for CNC job
16.12.2018

392
camlib.py
View File

@ -186,7 +186,7 @@ class Geometry(object):
if isinstance(self.solid_geometry, list):
return len(self.solid_geometry) == 0
self.app.inform.emit("[error_notcl] self.solid_geometry is neither BaseGeometry or list.")
self.app.inform.emit("[ERROR_NOTCL] self.solid_geometry is neither BaseGeometry or list.")
return
def subtract_polygon(self, points):
@ -300,7 +300,7 @@ class Geometry(object):
# else:
# return self.solid_geometry.bounds
# except Exception as e:
# self.app.inform.emit("[error_notcl] Error cause: %s" % str(e))
# self.app.inform.emit("[ERROR_NOTCL] Error cause: %s" % str(e))
# log.debug("Geometry->bounds()")
# if self.solid_geometry is None:
@ -1361,7 +1361,7 @@ class Geometry(object):
self.solid_geometry = mirror_geom(self.solid_geometry)
self.app.inform.emit('[success]Object was mirrored ...')
except AttributeError:
self.app.inform.emit("[error_notcl] Failed to mirror. No object selected")
self.app.inform.emit("[ERROR_NOTCL] Failed to mirror. No object selected")
@ -1401,7 +1401,7 @@ class Geometry(object):
self.solid_geometry = rotate_geom(self.solid_geometry)
self.app.inform.emit('[success]Object was rotated ...')
except AttributeError:
self.app.inform.emit("[error_notcl] Failed to rotate. No object selected")
self.app.inform.emit("[ERROR_NOTCL] Failed to rotate. No object selected")
def skew(self, angle_x, angle_y, point):
"""
@ -1437,7 +1437,7 @@ class Geometry(object):
self.solid_geometry = skew_geom(self.solid_geometry)
self.app.inform.emit('[success]Object was skewed ...')
except AttributeError:
self.app.inform.emit("[error_notcl] Failed to skew. No object selected")
self.app.inform.emit("[ERROR_NOTCL] Failed to skew. No object selected")
# if type(self.solid_geometry) == list:
# self.solid_geometry = [affinity.skew(g, angle_x, angle_y, origin=(px, py))
@ -2454,9 +2454,11 @@ class Gerber (Geometry):
region = Polygon()
else:
region = Polygon(path)
if not region.is_valid:
if not follow:
region = region.buffer(0, int(self.steps_per_circle / 4))
if not region.is_empty:
poly_buffer.append(region)
@ -2531,8 +2533,8 @@ class Gerber (Geometry):
pass
last_path_aperture = current_aperture
else:
self.app.inform.emit("[warning] Coordinates missing, line ignored: %s" % str(gline))
self.app.inform.emit("[warning_notcl] GERBER file might be CORRUPT. Check the file !!!")
self.app.inform.emit("[WARNING] Coordinates missing, line ignored: %s" % str(gline))
self.app.inform.emit("[WARNING_NOTCL] GERBER file might be CORRUPT. Check the file !!!")
elif current_operation_code == 2:
if len(path) > 1:
@ -2550,7 +2552,7 @@ class Gerber (Geometry):
geo = Polygon(path)
except ValueError:
log.warning("Problem %s %s" % (gline, line_num))
self.app.inform.emit("[error] Region does not have enough points. "
self.app.inform.emit("[ERROR] Region does not have enough points. "
"File will be processed but there are parser errors. "
"Line number: %s" % str(line_num))
else:
@ -2574,8 +2576,8 @@ class Gerber (Geometry):
if linear_x is not None and linear_y is not None:
path = [[linear_x, linear_y]] # Start new path
else:
self.app.inform.emit("[warning] Coordinates missing, line ignored: %s" % str(gline))
self.app.inform.emit("[warning_notcl] GERBER file might be CORRUPT. Check the file !!!")
self.app.inform.emit("[WARNING] Coordinates missing, line ignored: %s" % str(gline))
self.app.inform.emit("[WARNING_NOTCL] GERBER file might be CORRUPT. Check the file !!!")
# Flash
# Not allowed in region mode.
@ -2838,6 +2840,7 @@ class Gerber (Geometry):
if self.use_buffer_for_union:
log.debug("Union by buffer...")
new_poly = MultiPolygon(poly_buffer)
new_poly = new_poly.buffer(0.00000001)
new_poly = new_poly.buffer(-0.00000001)
@ -2857,8 +2860,9 @@ class Gerber (Geometry):
traceback.print_tb(tb)
#print traceback.format_exc()
log.error("PARSING FAILED. Line %d: %s" % (line_num, gline))
self.app.inform.emit("[error] Gerber Parser ERROR.\n Line %d: %s" % (line_num, gline), repr(err))
log.error("Gerber PARSING FAILED. Line %d: %s" % (line_num, gline))
loc = 'Gerber Line #%d Gerber Line Content: %s\n' % (line_num, gline) + repr(err)
self.app.inform.emit("[ERROR]Gerber Parser ERROR.\n%s:" % loc)
@staticmethod
def create_flash_geometry(location, aperture, steps_per_circle=None):
@ -3035,7 +3039,7 @@ class Gerber (Geometry):
try:
xfactor = float(xfactor)
except:
self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.")
self.app.inform.emit("[ERROR_NOTCL] Scale factor has to be a number: integer or float.")
return
if yfactor is None:
@ -3044,7 +3048,7 @@ class Gerber (Geometry):
try:
yfactor = float(yfactor)
except:
self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.")
self.app.inform.emit("[ERROR_NOTCL] Scale factor has to be a number: integer or float.")
return
if point is None:
@ -3096,7 +3100,7 @@ class Gerber (Geometry):
try:
dx, dy = vect
except TypeError:
self.app.inform.emit("[error_notcl]An (x,y) pair of values are needed. "
self.app.inform.emit("[ERROR_NOTCL]An (x,y) pair of values are needed. "
"Probable you entered only one value in the Offset field.")
return
@ -3460,7 +3464,7 @@ class Excellon(Geometry):
# and we need to exit from here
if self.detect_gcode_re.search(eline):
log.warning("This is GCODE mark: %s" % eline)
self.app.inform.emit('[error_notcl] This is GCODE mark: %s' % eline)
self.app.inform.emit('[ERROR_NOTCL] This is GCODE mark: %s' % eline)
return
# Header Begin (M48) #
@ -3987,10 +3991,13 @@ class Excellon(Geometry):
# from self.defaults['excellon_units']
log.info("Zeros: %s, Units %s." % (self.zeros, self.units))
except Exception as e:
log.error("PARSING FAILED. Line %d: %s" % (line_num, eline))
self.app.inform.emit('[error] Excellon Parser ERROR.\nPARSING FAILED. Line %d: %s' % (line_num, eline))
log.error("Excellon PARSING FAILED. Line %d: %s" % (line_num, eline))
msg = "[ERROR_NOTCL] An internal error has ocurred. See shell.\n"
msg += '[ERROR] Excellon Parser error.\nParsing Failed. Line %d: %s\n' % (line_num, eline)
msg += traceback.format_exc()
self.app.inform.emit(msg)
return "fail"
def parse_number(self, number_str):
@ -4059,7 +4066,7 @@ class Excellon(Geometry):
for drill in self.drills:
# poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0)
if drill['tool'] is '':
self.app.inform.emit("[warning] Excellon.create_geometry() -> a drill location was skipped "
self.app.inform.emit("[WARNING] Excellon.create_geometry() -> a drill location was skipped "
"due of not having a tool associated.\n"
"Check the resulting GCode.")
log.debug("Excellon.create_geometry() -> a drill location was skipped "
@ -4363,11 +4370,11 @@ class CNCjob(Geometry):
def __init__(self,
units="in", kind="generic", tooldia=0.0,
z_cut=-0.002, z_move=0.1,
feedrate=3.0, feedrate_z=3.0, feedrate_rapid=3.0,
feedrate=3.0, feedrate_z=3.0, feedrate_rapid=3.0, feedrate_probe=3.0,
pp_geometry_name='default', pp_excellon_name='default',
depthpercut = 0.1,
depthpercut=0.1,z_pdepth=-0.02,
spindlespeed=None, dwell=True, dwelltime=1000,
toolchangez=0.787402,
toolchangez=0.787402, toolchange_xy=[0.0, 0.0],
endz=2.0,
segx=None,
segy=None,
@ -4392,7 +4399,8 @@ class CNCjob(Geometry):
self.tooldia = tooldia
self.toolchangez = toolchangez
self.toolchange_xy = None
self.toolchange_xy = toolchange_xy
self.toolchange_xy_type = None
self.endz = endz
self.depthpercut = depthpercut
@ -4411,6 +4419,15 @@ class CNCjob(Geometry):
self.pp_excellon_name = pp_excellon_name
self.pp_excellon = self.app.postprocessors[self.pp_excellon_name]
# Controls if the move from Z_Toolchange to Z_Move is done fast with G0 or normally with G1
self.f_plunge = None
# how much depth the probe can probe before error
self.z_pdepth = z_pdepth if z_pdepth else None
# the feedrate(speed) with which the probel travel while probing
self.feedrate_probe = feedrate_probe if feedrate_probe else None
self.spindlespeed = spindlespeed
self.dwell = dwell
self.dwelltime = dwelltime
@ -4420,6 +4437,9 @@ class CNCjob(Geometry):
self.input_geometry_bounds = None
self.oldx = None
self.oldy = None
# Attributes to be included in serialization
# Always append to it because it carries contents
# from Geometry.
@ -4490,7 +4510,7 @@ class CNCjob(Geometry):
return path
def generate_from_excellon_by_tool(self, exobj, tools="all", drillz = 3.0,
toolchange=False, toolchangez=0.1, toolchangexy="0.0, 0.0",
toolchange=False, toolchangez=0.1, toolchangexy='',
endz=2.0, startz=None,
excellon_optimization_type='B'):
"""
@ -4520,25 +4540,40 @@ class CNCjob(Geometry):
:rtype: None
"""
if drillz > 0:
self.app.inform.emit("[warning] The Cut Z parameter has positive value. "
self.app.inform.emit("[WARNING] The Cut Z parameter has positive value. "
"It is the depth value to drill into material.\n"
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
"therefore the app will convert the value to negative. "
"Check the resulting CNC code (Gcode etc).")
self.z_cut = -drillz
elif drillz == 0:
self.app.inform.emit("[warning] The Cut Z parameter is zero. "
self.app.inform.emit("[WARNING] The Cut Z parameter is zero. "
"There will be no cut, skipping %s file" % exobj.options['name'])
return
else:
self.z_cut = drillz
self.toolchangez = toolchangez
self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")]
try:
if toolchangexy == '':
self.toolchange_xy = None
else:
self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")]
if len(self.toolchange_xy) < 2:
self.app.inform.emit("[ERROR]The Toolchange X,Y field in Edit -> Preferences has to be "
"in the format (x, y) \nbut now there is only one value, not two. ")
return 'fail'
except Exception as e:
log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> %s" % str(e))
pass
self.startz = startz
self.endz = endz
self.pp_excellon = self.app.postprocessors[self.pp_excellon_name]
p = self.pp_excellon
log.debug("Creating CNC Job from Excellon...")
# Tools
@ -4576,15 +4611,19 @@ class CNCjob(Geometry):
self.gcode = []
# Basic G-Code macros
self.pp_excellon = self.app.postprocessors[self.pp_excellon_name]
p = self.pp_excellon
self.f_plunge = self.app.defaults["excellon_f_plunge"]
# Initialization
gcode = self.doformat(p.start_code)
gcode += self.doformat(p.feedrate_code)
gcode += self.doformat(p.lift_code, x=0, y=0)
gcode += self.doformat(p.startz_code, x=0, y=0)
if toolchange is False:
if self.toolchange_xy is not None:
gcode += self.doformat(p.lift_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1])
gcode += self.doformat(p.startz_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1])
else:
gcode += self.doformat(p.lift_code, x=0.0, y=0.0)
gcode += self.doformat(p.startz_code, x=0.0, y=0.0)
# Distance callback
class CreateDistanceCallback(object):
@ -4618,8 +4657,13 @@ class CNCjob(Geometry):
locations.append((point.coords.xy[0][0], point.coords.xy[1][0]))
return locations
oldx = 0
oldy = 0
if self.toolchange_xy is not None:
self.oldx = self.toolchange_xy[0]
self.oldy = self.toolchange_xy[1]
else:
self.oldx = 0.0
self.oldy = 0.0
measured_distance = 0
current_platform = platform.architecture()[0]
@ -4684,7 +4728,7 @@ class CNCjob(Geometry):
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code,toolchangexy=(oldx, oldy))
gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
@ -4702,9 +4746,9 @@ class CNCjob(Geometry):
gcode += self.doformat(p.down_code, x=locx, y=locy)
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
measured_distance += abs(distance_euclidian(locx, locy, oldx, oldy))
oldx = locx
oldy = locy
measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.oldx = locx
self.oldy = locy
log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))
elif excellon_optimization_type == 'B':
log.debug("Using OR-Tools Basic drill path optimization.")
@ -4758,7 +4802,7 @@ class CNCjob(Geometry):
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code,toolchangexy=(oldx, oldy))
gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
@ -4775,12 +4819,12 @@ class CNCjob(Geometry):
gcode += self.doformat(p.down_code, x=locx, y=locy)
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
measured_distance += abs(distance_euclidian(locx, locy, oldx, oldy))
oldx = locx
oldy = locy
measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.oldx = locx
self.oldy = locy
log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % str(measured_distance))
else:
self.app.inform.emit("[error_notcl] Wrong optimization type selected.")
self.app.inform.emit("[ERROR_NOTCL] Wrong optimization type selected.")
return
else:
log.debug("Using Travelling Salesman drill path optimization.")
@ -4792,7 +4836,7 @@ class CNCjob(Geometry):
if tool in points:
# Tool change sequence (optional)
if toolchange:
gcode += self.doformat(p.toolchange_code, toolchangexy=(oldx, oldy))
gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
gcode += self.doformat(p.spindle_code) # Spindle start)
if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time
@ -4811,15 +4855,15 @@ class CNCjob(Geometry):
gcode += self.doformat(p.down_code, x=point[0], y=point[1])
gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
measured_distance += abs(distance_euclidian(point[0], point[1], oldx, oldy))
oldx = point[0]
oldy = point[1]
measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
self.oldx = point[0]
self.oldy = point[1]
log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
gcode += self.doformat(p.spindle_stop_code) # Spindle stop
gcode += self.doformat(p.end_code, x=0, y=0)
measured_distance += abs(distance_euclidian(oldx, oldy, 0, 0))
measured_distance += abs(distance_euclidian(self.oldx, self.oldy, 0, 0))
log.debug("The total travel distance including travel to end position is: %s" %
str(measured_distance) + '\n')
self.gcode = gcode
@ -4887,19 +4931,32 @@ class CNCjob(Geometry):
self.multidepth = multidepth
self.toolchangez = toolchangez
self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")]
try:
if toolchangexy == '':
self.toolchange_xy = None
else:
self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")]
if len(self.toolchange_xy) < 2:
self.app.inform.emit("[ERROR]The Toolchange X,Y field in Edit -> Preferences has to be "
"in the format (x, y) \nbut now there is only one value, not two. ")
return 'fail'
except Exception as e:
log.debug("camlib.CNCJob.generate_from_multitool_geometry() --> %s" % str(e))
pass
self.pp_geometry_name = pp_geometry_name if pp_geometry_name else 'default'
self.f_plunge = self.app.defaults["geometry_f_plunge"]
if self.z_cut > 0:
self.app.inform.emit("[warning] The Cut Z parameter has positive value. "
self.app.inform.emit("[WARNING] The Cut Z parameter has positive value. "
"It is the depth value to cut into material.\n"
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
"therefore the app will convert the value to negative."
"Check the resulting CNC code (Gcode etc).")
self.z_cut = -self.z_cut
elif self.z_cut == 0:
self.app.inform.emit("[warning] The Cut Z parameter is zero. "
self.app.inform.emit("[WARNING] The Cut Z parameter is zero. "
"There will be no cut, skipping %s file" % self.options['name'])
## Index first and last points in paths
@ -4936,14 +4993,17 @@ class CNCjob(Geometry):
self.gcode = self.doformat(p.start_code)
self.gcode += self.doformat(p.feedrate_code) # sets the feed rate
self.gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height
self.gcode += self.doformat(p.startz_code, x=0, y=0)
if toolchange is False:
self.gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height
self.gcode += self.doformat(p.startz_code, x=0, y=0)
if toolchange:
if "line_xyz" in self.pp_geometry_name:
self.gcode += self.doformat(p.toolchange_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1])
else:
self.gcode += self.doformat(p.toolchange_code)
# if "line_xyz" in self.pp_geometry_name:
# self.gcode += self.doformat(p.toolchange_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1])
# else:
# self.gcode += self.doformat(p.toolchange_code)
self.gcode += self.doformat(p.toolchange_code)
self.gcode += self.doformat(p.spindle_code) # Spindle start
if self.dwell is True:
@ -5023,13 +5083,13 @@ class CNCjob(Geometry):
"""
if not isinstance(geometry, Geometry):
self.app.inform.emit("[error]Expected a Geometry, got %s" % type(geometry))
self.app.inform.emit("[ERROR]Expected a Geometry, got %s" % type(geometry))
return 'fail'
log.debug("Generate_from_geometry_2()")
# if solid_geometry is empty raise an exception
if not geometry.solid_geometry:
self.app.inform.emit("[error_notcl]Trying to generate a CNC Job "
self.app.inform.emit("[ERROR_NOTCL]Trying to generate a CNC Job "
"from a Geometry object without solid_geometry.")
temp_solid_geometry = []
@ -5067,19 +5127,32 @@ class CNCjob(Geometry):
self.multidepth = multidepth
self.toolchangez = toolchangez
self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")]
try:
if toolchangexy == '':
self.toolchange_xy = None
else:
self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")]
if len(self.toolchange_xy) < 2:
self.app.inform.emit("[ERROR]The Toolchange X,Y field in Edit -> Preferences has to be "
"in the format (x, y) \nbut now there is only one value, not two. ")
return 'fail'
except Exception as e:
log.debug("camlib.CNCJob.generate_from_geometry_2() --> %s" % str(e))
pass
self.pp_geometry_name = pp_geometry_name if pp_geometry_name else 'default'
self.f_plunge = self.app.defaults["geometry_f_plunge"]
if self.z_cut > 0:
self.app.inform.emit("[warning] The Cut Z parameter has positive value. "
self.app.inform.emit("[WARNING] The Cut Z parameter has positive value. "
"It is the depth value to cut into material.\n"
"The Cut Z parameter needs to have a negative value, assuming it is a typo "
"therefore the app will convert the value to negative."
"Check the resulting CNC code (Gcode etc).")
self.z_cut = -self.z_cut
elif self.z_cut == 0:
self.app.inform.emit("[warning] The Cut Z parameter is zero. "
self.app.inform.emit("[WARNING] The Cut Z parameter is zero. "
"There will be no cut, skipping %s file" % geometry.options['name'])
## Index first and last points in paths
@ -5113,18 +5186,23 @@ class CNCjob(Geometry):
self.pp_geometry = self.app.postprocessors[self.pp_geometry_name]
p = self.pp_geometry
self.oldx = 0.0
self.oldy = 0.0
self.gcode = self.doformat(p.start_code)
self.gcode += self.doformat(p.feedrate_code) # sets the feed rate
self.gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height
self.gcode += self.doformat(p.startz_code, x=0, y=0)
if toolchange is False:
self.gcode += self.doformat(p.lift_code, x=self.oldx , y=self.oldy ) # Move (up) to travel height
self.gcode += self.doformat(p.startz_code, x=self.oldx , y=self.oldy )
if toolchange:
if "line_xyz" in self.pp_geometry_name:
self.gcode += self.doformat(p.toolchange_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1])
else:
self.gcode += self.doformat(p.toolchange_code)
# if "line_xyz" in self.pp_geometry_name:
# self.gcode += self.doformat(p.toolchange_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1])
# else:
# self.gcode += self.doformat(p.toolchange_code)
self.gcode += self.doformat(p.toolchange_code)
self.gcode += self.doformat(p.spindle_code) # Spindle start
@ -5328,10 +5406,17 @@ class CNCjob(Geometry):
# Current path: temporary storage until tool is
# lifted or lowered.
if self.toolchange_xy == "excellon":
pos_xy = [float(eval(a)) for a in self.app.defaults["excellon_toolchangexy"].split(",")]
if self.toolchange_xy_type == "excellon":
if self.app.defaults["excellon_toolchangexy"] == '':
pos_xy = [0, 0]
else:
pos_xy = [float(eval(a)) for a in self.app.defaults["excellon_toolchangexy"].split(",")]
else:
pos_xy = [float(eval(a)) for a in self.app.defaults["geometry_toolchangexy"].split(",")]
if self.app.defaults["geometry_toolchangexy"] == '':
pos_xy = [0, 0]
else:
pos_xy = [float(eval(a)) for a in self.app.defaults["geometry_toolchangexy"].split(",")]
path = [pos_xy]
# path = [(0, 0)]
@ -5456,7 +5541,7 @@ class CNCjob(Geometry):
def plot2(self, tooldia=None, dpi=75, margin=0.1, gcode_parsed=None,
color={"T": ["#F0E24D4C", "#B5AB3A4C"], "C": ["#5E6CFFFF", "#4650BDFF"]},
alpha={"T": 0.3, "C": 1.0}, tool_tolerance=0.0005, obj=None, visible=False):
alpha={"T": 0.3, "C": 1.0}, tool_tolerance=0.0005, obj=None, visible=False, kind='all'):
"""
Plots the G-code job onto the given axes.
@ -5477,7 +5562,15 @@ class CNCjob(Geometry):
if tooldia == 0:
for geo in gcode_parsed:
obj.add_shape(shape=geo['geom'], color=color[geo['kind'][0]][1], visible=visible)
if kind == 'all':
obj.add_shape(shape=geo['geom'], color=color[geo['kind'][0]][1], visible=visible)
elif kind == 'travel':
if geo['kind'][0] == 'T':
obj.add_shape(shape=geo['geom'], color=color['T'][1], visible=visible)
elif kind == 'cut':
if geo['kind'][0] == 'C':
obj.add_shape(shape=geo['geom'], color=color['C'][1], visible=visible)
else:
text = []
pos = []
@ -5488,8 +5581,17 @@ class CNCjob(Geometry):
pos.append(geo['geom'].coords[0])
poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance)
obj.add_shape(shape=poly, color=color[geo['kind'][0]][1], face_color=color[geo['kind'][0]][0],
if kind == 'all':
obj.add_shape(shape=poly, color=color[geo['kind'][0]][1], face_color=color[geo['kind'][0]][0],
visible=visible, layer=1 if geo['kind'][0] == 'C' else 2)
elif kind == 'travel':
if geo['kind'][0] == 'T':
obj.add_shape(shape=poly, color=color['T'][1], face_color=color['T'][0],
visible=visible, layer=2)
elif kind == 'cut':
if geo['kind'][0] == 'C':
obj.add_shape(shape=poly, color=color['C'][1], face_color=color['C'][0],
visible=visible, layer=1)
obj.annotation.set(text=text, pos=pos, visible=obj.options['plot'])
@ -5798,6 +5900,7 @@ class CNCjob(Geometry):
else:
# it's a Shapely object, return it's bounds
return obj.bounds
if self.multitool is False:
log.debug("CNCJob->bounds()")
if self.solid_geometry is None:
@ -5806,21 +5909,30 @@ class CNCjob(Geometry):
bounds_coords = bounds_rec(self.solid_geometry)
else:
for k, v in self.cnc_tools.items():
minx = Inf
miny = Inf
maxx = -Inf
maxy = -Inf
for k in v['solid_geometry']:
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
try:
for k in v['solid_geometry']:
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
minx = min(minx, minx_)
miny = min(miny, miny_)
maxx = max(maxx, maxx_)
maxy = max(maxy, maxy_)
except TypeError:
minx_, miny_, maxx_, maxy_ = bounds_rec(v['solid_geometry'])
minx = min(minx, minx_)
miny = min(miny, miny_)
maxx = max(maxx, maxx_)
maxy = max(maxy, maxy_)
bounds_coords = minx, miny, maxx, maxy
return bounds_coords
# TODO This function should be replaced at some point with a "real" function. Until then it's an ugly hack ...
def scale(self, xfactor, yfactor=None, point=None):
"""
Scales all the geometry on the XY plane in the object by the
@ -5844,8 +5956,124 @@ class CNCjob(Geometry):
else:
px, py = point
for g in self.gcode_parsed:
g['geom'] = affinity.scale(g['geom'], xfactor, yfactor, origin=(px, py))
def scale_g(g):
"""
:param g: 'g' parameter it's a gcode string
:return: scaled gcode string
"""
temp_gcode = ''
header_start = False
header_stop = False
units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
lines = StringIO(g)
for line in lines:
# this changes the GCODE header ---- UGLY HACK
if "TOOL DIAMETER" in line or "Feedrate:" in line:
header_start = True
if "G20" in line or "G21" in line:
header_start = False
header_stop = True
if header_start is True:
header_stop = False
if "in" in line:
if units == 'MM':
line = line.replace("in", "mm")
if "mm" in line:
if units == 'IN':
line = line.replace("mm", "in")
# find any float number in header (even multiple on the same line) and convert it
numbers_in_header = re.findall(self.g_nr_re, line)
if numbers_in_header:
for nr in numbers_in_header:
new_nr = float(nr) * xfactor
# replace the updated string
line = line.replace(nr, ('%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_nr))
)
# this scales all the X and Y and Z and F values and also the Tool Dia in the toolchange message
if header_stop is True:
if "G20" in line:
if units == 'MM':
line = line.replace("G20", "G21")
if "G21" in line:
if units == 'IN':
line = line.replace("G21", "G20")
# find the X group
match_x = self.g_x_re.search(line)
if match_x:
if match_x.group(1) is not None:
new_x = float(match_x.group(1)[1:]) * xfactor
# replace the updated string
line = line.replace(
match_x.group(1),
'X%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_x)
)
# find the Y group
match_y = self.g_y_re.search(line)
if match_y:
if match_y.group(1) is not None:
new_y = float(match_y.group(1)[1:]) * yfactor
line = line.replace(
match_y.group(1),
'Y%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_y)
)
# find the Z group
match_z = self.g_z_re.search(line)
if match_z:
if match_z.group(1) is not None:
new_z = float(match_z.group(1)[1:]) * xfactor
line = line.replace(
match_z.group(1),
'Z%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_z)
)
# find the F group
match_f = self.g_f_re.search(line)
if match_f:
if match_f.group(1) is not None:
new_f = float(match_f.group(1)[1:]) * xfactor
line = line.replace(
match_f.group(1),
'F%.*f' % (self.app.defaults["cncjob_fr_decimals"], new_f)
)
# find the T group (tool dia on toolchange)
match_t = self.g_t_re.search(line)
if match_t:
if match_t.group(1) is not None:
new_t = float(match_t.group(1)[1:]) * xfactor
line = line.replace(
match_t.group(1),
'= %.*f' % (self.app.defaults["cncjob_coords_decimals"], new_t)
)
temp_gcode += line
lines.close()
header_stop = False
return temp_gcode
if self.multitool is False:
# offset Gcode
self.gcode = scale_g(self.gcode)
# offset geometry
for g in self.gcode_parsed:
g['geom'] = affinity.scale(g['geom'], xfactor, yfactor, origin=(px, py))
self.create_geometry()
else:
for k, v in self.cnc_tools.items():
# scale Gcode
v['gcode'] = scale_g(v['gcode'])
# scale gcode_parsed
for g in v['gcode_parsed']:
g['geom'] = affinity.scale(g['geom'], xfactor, yfactor, origin=(px, py))
v['solid_geometry'] = cascaded_union([geo['geom'] for geo in v['gcode_parsed']])
self.create_geometry()
@ -5875,7 +6103,7 @@ class CNCjob(Geometry):
lines = StringIO(g)
for line in lines:
# find the X group
match_x = self.g_offsetx_re.search(line)
match_x = self.g_x_re.search(line)
if match_x:
if match_x.group(1) is not None:
# get the coordinate and add X offset
@ -5885,7 +6113,7 @@ class CNCjob(Geometry):
match_x.group(1),
'X%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_x)
)
match_y = self.g_offsety_re.search(line)
match_y = self.g_y_re.search(line)
if match_y:
if match_y.group(1) is not None:
new_y = float(match_y.group(1)[1:]) + dy

View File

@ -10,6 +10,7 @@ class ToolCalculator(FlatCAMTool):
toolName = "Calculators"
v_shapeName = "V-Shape Tool Calculator"
unitsName = "Units Calculator"
eplateName = "ElectroPlating Calculator"
def __init__(self, app):
FlatCAMTool.__init__(self, app)
@ -20,7 +21,9 @@ class ToolCalculator(FlatCAMTool):
title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font>" % self.toolName)
self.layout.addWidget(title_label)
## V-shape Tool Calculator
############################
## V-shape Tool Calculator ##
############################
self.v_shape_spacer_label = QtWidgets.QLabel(" ")
self.layout.addWidget(self.v_shape_spacer_label)
@ -35,30 +38,30 @@ class ToolCalculator(FlatCAMTool):
self.tipDia_label = QtWidgets.QLabel("Tip Diameter:")
self.tipDia_entry = FCEntry()
self.tipDia_entry.setFixedWidth(70)
# self.tipDia_entry.setFixedWidth(70)
self.tipDia_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.tipDia_entry.setToolTip('This is the diameter of the tool tip.\n'
self.tipDia_label.setToolTip('This is the diameter of the tool tip.\n'
'The manufacturer specifies it.')
self.tipAngle_label = QtWidgets.QLabel("Tip Angle:")
self.tipAngle_entry = FCEntry()
self.tipAngle_entry.setFixedWidth(70)
# self.tipAngle_entry.setFixedWidth(70)
self.tipAngle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.tipAngle_entry.setToolTip("This is the angle of the tip of the tool.\n"
self.tipAngle_label.setToolTip("This is the angle of the tip of the tool.\n"
"It is specified by manufacturer.")
self.cutDepth_label = QtWidgets.QLabel("Cut Z:")
self.cutDepth_entry = FCEntry()
self.cutDepth_entry.setFixedWidth(70)
# self.cutDepth_entry.setFixedWidth(70)
self.cutDepth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.cutDepth_entry.setToolTip("This is the depth to cut into the material.\n"
self.cutDepth_label.setToolTip("This is the depth to cut into the material.\n"
"In the CNCJob is the CutZ parameter.")
self.effectiveToolDia_label = QtWidgets.QLabel("Tool Diameter:")
self.effectiveToolDia_entry = FCEntry()
self.effectiveToolDia_entry.setFixedWidth(70)
# self.effectiveToolDia_entry.setFixedWidth(70)
self.effectiveToolDia_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.effectiveToolDia_entry.setToolTip("This is the tool diameter to be entered into\n"
self.effectiveToolDia_label.setToolTip("This is the tool diameter to be entered into\n"
"FlatCAM Gerber section.\n"
"In the CNCJob section it is called >Tool dia<.")
# self.effectiveToolDia_entry.setEnabled(False)
@ -69,19 +72,21 @@ class ToolCalculator(FlatCAMTool):
form_layout.addRow(self.cutDepth_label, self.cutDepth_entry)
form_layout.addRow(self.effectiveToolDia_label, self.effectiveToolDia_entry)
## Buttons
self.calculate_button = QtWidgets.QPushButton("Calculate")
self.calculate_button.setFixedWidth(70)
self.calculate_button.setToolTip(
self.calculate_vshape_button = QtWidgets.QPushButton("Calculate")
# self.calculate_button.setFixedWidth(70)
self.calculate_vshape_button.setToolTip(
"Calculate either the Cut Z or the effective tool diameter,\n "
"depending on which is desired and which is known. "
)
self.empty_label = QtWidgets.QLabel(" ")
form_layout.addRow(self.empty_label, self.calculate_button)
form_layout.addRow(self.empty_label, self.calculate_vshape_button)
######################
## Units Calculator ##
######################
## Units Calculator
self.unists_spacer_label = QtWidgets.QLabel(" ")
self.layout.addWidget(self.unists_spacer_label)
@ -89,25 +94,109 @@ class ToolCalculator(FlatCAMTool):
units_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.unitsName)
self.layout.addWidget(units_label)
#Form Layout
form_units_layout = QtWidgets.QFormLayout()
self.layout.addLayout(form_units_layout)
#Grid Layout
grid_units_layout = QtWidgets.QGridLayout()
self.layout.addLayout(grid_units_layout)
inch_label = QtWidgets.QLabel("INCH")
mm_label = QtWidgets.QLabel("MM")
grid_units_layout.addWidget(mm_label, 0, 0)
grid_units_layout.addWidget( inch_label, 0, 1)
self.inch_entry = FCEntry()
self.inch_entry.setFixedWidth(70)
# self.inch_entry.setFixedWidth(70)
self.inch_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.inch_entry.setToolTip("Here you enter the value to be converted from INCH to MM")
self.mm_entry = FCEntry()
self.mm_entry.setFixedWidth(70)
# self.mm_entry.setFixedWidth(130)
self.mm_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.mm_entry.setToolTip("Here you enter the value to be converted from MM to INCH")
form_units_layout.addRow(mm_label, inch_label)
form_units_layout.addRow(self.mm_entry, self.inch_entry)
grid_units_layout.addWidget(self.mm_entry, 1, 0)
grid_units_layout.addWidget(self.inch_entry, 1, 1)
####################################
## ElectroPlating Tool Calculator ##
####################################
self.plate_spacer_label = QtWidgets.QLabel(" ")
self.layout.addWidget(self.plate_spacer_label)
## Title of the ElectroPlating Tools Calculator
plate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.eplateName)
plate_title_label.setToolTip(
"This calculator is useful for those who plate the via/pad/drill holes,\n"
"using a method like grahite ink or calcium hypophosphite ink or palladium chloride."
)
self.layout.addWidget(plate_title_label)
## Plate Form Layout
plate_form_layout = QtWidgets.QFormLayout()
self.layout.addLayout(plate_form_layout)
self.pcblengthlabel = QtWidgets.QLabel("Board Length:")
self.pcblength_entry = FCEntry()
# self.pcblengthlabel.setFixedWidth(70)
self.pcblength_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.pcblengthlabel.setToolTip('This is the board length. In centimeters.')
self.pcbwidthlabel = QtWidgets.QLabel("Board Width:")
self.pcbwidth_entry = FCEntry()
# self.pcbwidthlabel.setFixedWidth(70)
self.pcbwidth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.pcbwidthlabel.setToolTip('This is the board width.In centimeters.')
self.cdensity_label = QtWidgets.QLabel("Current Density:")
self.cdensity_entry = FCEntry()
# self.cdensity_entry.setFixedWidth(70)
self.cdensity_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.cdensity_label.setToolTip("Current density to pass through the board. \n"
"In Amps per Square Feet ASF.")
self.growth_label = QtWidgets.QLabel("Copper Growth:")
self.growth_entry = FCEntry()
# self.growth_entry.setFixedWidth(70)
self.growth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.growth_label.setToolTip("How thick the copper growth is intended to be.\n"
"In microns.")
# self.growth_entry.setEnabled(False)
self.cvaluelabel = QtWidgets.QLabel("Current Value:")
self.cvalue_entry = FCEntry()
# self.cvaluelabel.setFixedWidth(70)
self.cvalue_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.cvaluelabel.setToolTip('This is the current intensity value\n'
'to be set on the Power Supply. In Amps.')
self.cvalue_entry.setDisabled(True)
self.timelabel = QtWidgets.QLabel("Time:")
self.time_entry = FCEntry()
# self.timelabel.setFixedWidth(70)
self.time_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.timelabel.setToolTip('This is the calculated time required for the procedure.\n'
'In minutes.')
self.time_entry.setDisabled(True)
plate_form_layout.addRow(self.pcblengthlabel, self.pcblength_entry)
plate_form_layout.addRow(self.pcbwidthlabel, self.pcbwidth_entry)
plate_form_layout.addRow(self.cdensity_label, self.cdensity_entry)
plate_form_layout.addRow(self.growth_label, self.growth_entry)
plate_form_layout.addRow(self.cvaluelabel, self.cvalue_entry)
plate_form_layout.addRow(self.timelabel, self.time_entry)
## Buttons
self.calculate_plate_button = QtWidgets.QPushButton("Calculate")
# self.calculate_button.setFixedWidth(70)
self.calculate_plate_button.setToolTip(
"Calculate the current intensity value and the procedure time,\n "
"depending on the parameters above"
)
self.empty_label_2 = QtWidgets.QLabel(" ")
plate_form_layout.addRow(self.empty_label_2, self.calculate_plate_button)
self.layout.addStretch()
@ -116,13 +205,36 @@ class ToolCalculator(FlatCAMTool):
self.cutDepth_entry.editingFinished.connect(self.on_calculate_tool_dia)
self.tipDia_entry.editingFinished.connect(self.on_calculate_tool_dia)
self.tipAngle_entry.editingFinished.connect(self.on_calculate_tool_dia)
self.calculate_button.clicked.connect(self.on_calculate_tool_dia)
self.calculate_vshape_button.clicked.connect(self.on_calculate_tool_dia)
self.mm_entry.editingFinished.connect(self.on_calculate_inch_units)
self.inch_entry.editingFinished.connect(self.on_calculate_mm_units)
self.calculate_plate_button.clicked.connect(self.on_calculate_eplate)
def run(self):
self.app.report_usage("ToolCalculators()")
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Calc. Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+C', **kwargs)
def set_tool_ui(self):
## Initialize form
self.mm_entry.set_value('0')
self.inch_entry.set_value('0')
self.pcblength_entry.set_value('10')
self.pcbwidth_entry.set_value('10')
self.cdensity_entry.set_value('13')
self.growth_entry.set_value('10')
self.cvalue_entry.set_value(2.80)
self.time_entry.set_value(33.0)
if self.app.defaults["units"] == 'MM':
self.tipDia_entry.set_value('0.2')
self.tipAngle_entry.set_value('45')
@ -134,16 +246,6 @@ class ToolCalculator(FlatCAMTool):
self.cutDepth_entry.set_value('9.84252')
self.effectiveToolDia_entry.set_value('15.35433')
self.mm_entry.set_value('0')
self.inch_entry.set_value('0')
def run(self):
FlatCAMTool.run(self)
self.app.ui.notebook.setTabText(2, "Calc. Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+C', **kwargs)
def on_calculate_tool_dia(self):
# Calculation:
# Manufacturer gives total angle of the the tip but we need only half of it
@ -155,18 +257,117 @@ class ToolCalculator(FlatCAMTool):
try:
tip_diameter = float(self.tipDia_entry.get_value())
half_tip_angle = float(self.tipAngle_entry.get_value()) / 2
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
tip_diameter = float(self.tipDia_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
try:
half_tip_angle = float(self.tipAngle_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
half_tip_angle = float(self.tipAngle_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
half_tip_angle /= 2
try:
cut_depth = float(self.cutDepth_entry.get_value())
except:
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
cut_depth = float(self.cutDepth_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle)))
self.effectiveToolDia_entry.set_value("%.4f" % tool_diameter)
def on_calculate_inch_units(self):
self.inch_entry.set_value('%.6f' % (float(self.mm_entry.get_value()) / 25.4))
try:
mm_val = float(self.mm_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
mm_val = float(self.mm_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
self.inch_entry.set_value('%.6f' % (mm_val / 25.4))
def on_calculate_mm_units(self):
self.mm_entry.set_value('%.6f' % (float(self.inch_entry.get_value()) * 25.4))
try:
inch_val = float(self.inch_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
inch_val = float(self.inch_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
self.mm_entry.set_value('%.6f' % (inch_val * 25.4))
def on_calculate_eplate(self):
try:
length = float(self.pcblength_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
length = float(self.pcblength_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
try:
width = float(self.pcbwidth_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
width = float(self.pcbwidth_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
try:
density = float(self.cdensity_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
density = float(self.cdensity_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
try:
copper = float(self.growth_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
copper = float(self.growth_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
calculated_current = (length * width * density) * 0.0021527820833419
calculated_time = copper * 2.142857142857143 * float(20 / density)
self.cvalue_entry.set_value('%.2f' % calculated_current)
self.time_entry.set_value('%.1f' % calculated_time)
# end of file

View File

@ -7,7 +7,8 @@ from GUIElements import IntEntry, RadioSet, LengthEntry
from FlatCAMObj import FlatCAMGeometry, FlatCAMExcellon, FlatCAMGerber
class ToolCutout(FlatCAMTool):
class ToolCutOut(FlatCAMTool):
toolName = "Cutout PCB"
@ -48,6 +49,7 @@ class ToolCutout(FlatCAMTool):
self.obj_combo.setModel(self.app.collection)
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel("Object:")
self.object_label.setToolTip(
"Object to be cutout. "
@ -172,11 +174,11 @@ class ToolCutout(FlatCAMTool):
self.layout.addStretch()
## Init GUI
self.dia.set_value(1)
self.margin.set_value(0)
self.gapsize.set_value(1)
self.gaps.set_value(4)
self.gaps_rect_radio.set_value("4")
# self.dia.set_value(1)
# self.margin.set_value(0)
# self.gapsize.set_value(1)
# self.gaps.set_value(4)
# self.gaps_rect_radio.set_value("4")
## Signals
self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
@ -190,14 +192,18 @@ class ToolCutout(FlatCAMTool):
self.obj_combo.setCurrentIndex(0)
def run(self):
self.app.report_usage("ToolCutOut()")
FlatCAMTool.run(self)
self.set_ui()
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Cutout Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+U', **kwargs)
def set_ui(self):
def set_tool_ui(self):
self.reset_fields()
self.dia.set_value(float(self.app.defaults["tools_cutouttooldia"]))
self.margin.set_value(float(self.app.defaults["tools_cutoutmargin"]))
self.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"]))
@ -216,45 +222,63 @@ class ToolCutout(FlatCAMTool):
try:
cutout_obj = self.app.collection.get_by_name(str(name))
except:
self.app.inform.emit("[error_notcl]Could not retrieve object: %s" % name)
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve object: %s" % name)
return "Could not retrieve object: %s" % name
if cutout_obj is None:
self.app.inform.emit("[error_notcl]There is no object selected for Cutout.\nSelect one and try again.")
self.app.inform.emit("[ERROR_NOTCL]There is no object selected for Cutout.\nSelect one and try again.")
return
try:
dia = float(self.dia.get_value())
except TypeError:
self.app.inform.emit("[warning_notcl] Tool diameter value is missing. Add it and retry.")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
dia = float(self.dia.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
"Add it and retry.")
return
try:
margin = float(self.margin.get_value())
except TypeError:
self.app.inform.emit("[warning_notcl] Margin value is missing. Add it and retry.")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
margin = float(self.margin.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[WARNING_NOTCL] Margin value is missing or wrong format. "
"Add it and retry.")
return
try:
gapsize = float(self.gapsize.get_value())
except TypeError:
self.app.inform.emit("[warning_notcl] Gap size value is missing. Add it and retry.")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
gapsize = float(self.gapsize.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[WARNING_NOTCL] Gap size value is missing or wrong format. "
"Add it and retry.")
return
try:
gaps = self.gaps.get_value()
except TypeError:
self.app.inform.emit("[warning_notcl] Number of gaps value is missing. Add it and retry.")
self.app.inform.emit("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry.")
return
if 0 in {dia}:
self.app.inform.emit("[warning_notcl]Tool Diameter is zero value. Change it to a positive integer.")
self.app.inform.emit("[WARNING_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
return "Tool Diameter is zero value. Change it to a positive integer."
if gaps not in ['lr', 'tb', '2lr', '2tb', '4', '8']:
self.app.inform.emit("[warning_notcl] Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
self.app.inform.emit("[WARNING_NOTCL] Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
"Fill in a correct value and retry. ")
return
if cutout_obj.multigeo is True:
self.app.inform.emit("[error]Cutout operation cannot be done on a multi-geo Geometry.\n"
self.app.inform.emit("[ERROR]Cutout operation cannot be done on a multi-geo Geometry.\n"
"Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n"
"and after that perform Cutout.")
return
@ -338,39 +362,57 @@ class ToolCutout(FlatCAMTool):
try:
cutout_obj = self.app.collection.get_by_name(str(name))
except:
self.app.inform.emit("[error_notcl]Could not retrieve object: %s" % name)
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve object: %s" % name)
return "Could not retrieve object: %s" % name
if cutout_obj is None:
self.app.inform.emit("[error_notcl]Object not found: %s" % cutout_obj)
self.app.inform.emit("[ERROR_NOTCL]Object not found: %s" % cutout_obj)
try:
dia = float(self.dia.get_value())
except TypeError:
self.app.inform.emit("[warning_notcl] Tool diameter value is missing. Add it and retry.")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
dia = float(self.dia.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
"Add it and retry.")
return
try:
margin = float(self.margin.get_value())
except TypeError:
self.app.inform.emit("[warning_notcl] Margin value is missing. Add it and retry.")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
margin = float(self.margin.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[WARNING_NOTCL] Margin value is missing or wrong format. "
"Add it and retry.")
return
try:
gapsize = float(self.gapsize.get_value())
except TypeError:
self.app.inform.emit("[warning_notcl] Gap size value is missing. Add it and retry.")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
gapsize = float(self.gapsize.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[WARNING_NOTCL] Gap size value is missing or wrong format. "
"Add it and retry.")
return
try:
gaps = self.gaps_rect_radio.get_value()
except TypeError:
self.app.inform.emit("[warning_notcl] Number of gaps value is missing. Add it and retry.")
self.app.inform.emit("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry.")
return
if 0 in {dia}:
self.app.inform.emit("[error_notcl]Tool Diameter is zero value. Change it to a positive integer.")
self.app.inform.emit("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer.")
return "Tool Diameter is zero value. Change it to a positive integer."
if cutout_obj.multigeo is True:
self.app.inform.emit("[error]Cutout operation cannot be done on a multi-geo Geometry.\n"
self.app.inform.emit("[ERROR]Cutout operation cannot be done on a multi-geo Geometry.\n"
"Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n"
"and after that perform Cutout.")
return

View File

@ -6,6 +6,7 @@ from shapely.geometry import Point
from shapely import affinity
from PyQt5 import QtCore
class DblSidedTool(FlatCAMTool):
toolName = "2-Sided PCB"
@ -115,8 +116,8 @@ class DblSidedTool(FlatCAMTool):
self.axloc_label = QtWidgets.QLabel("Axis Ref:")
self.axloc_label.setToolTip(
"The axis should pass through a <b>point</b> or cut\n "
"a specified <b>box</b> (in a Geometry object) in \n"
"the middle."
"a specified <b>box</b> (in a FlatCAM object) through \n"
"the center."
)
# grid_lay.addRow("Axis Location:", self.axis_location)
grid_lay.addWidget(self.axloc_label, 8, 0)
@ -129,19 +130,18 @@ class DblSidedTool(FlatCAMTool):
self.point_box_container = QtWidgets.QVBoxLayout()
self.pb_label = QtWidgets.QLabel("<b>Point/Box:</b>")
self.pb_label.setToolTip(
"Specify the point (x, y) through which the mirror axis \n "
"passes or the Geometry object containing a rectangle \n"
"that the mirror axis cuts in half."
"If 'Point' is selected above it store the coordinates (x, y) through which\n"
"the mirroring axis passes.\n"
"If 'Box' is selected above, select here a FlatCAM object (Gerber, Exc or Geo).\n"
"Through the center of this object pass the mirroring axis selected above."
)
# grid_lay.addRow("Point/Box:", self.point_box_container)
self.add_point_button = QtWidgets.QPushButton("Add")
self.add_point_button.setToolTip(
"Add the <b>point (x, y)</b> through which the mirror axis \n "
"passes or the Object containing a rectangle \n"
"that the mirror axis cuts in half.\n"
"The point is captured by pressing SHIFT key\n"
"and left mouse clicking on canvas or you can enter them manually."
"Add the coordinates in format <b>(x, y)</b> through which the mirroring axis \n "
"selected in 'MIRROR AXIS' pass.\n"
"The (x, y) coordinates are captured by pressing SHIFT key\n"
"and left mouse button click on canvas or you can enter the coords manually."
)
self.add_point_button.setFixedWidth(40)
@ -173,9 +173,9 @@ class DblSidedTool(FlatCAMTool):
self.ah_label.setToolTip(
"Alignment holes (x1, y1), (x2, y2), ... "
"on one side of the mirror axis. For each set of (x, y) coordinates\n"
"entered here, a pair of drills will be created: one on the\n"
"coordinates entered and one in mirror position over the axis\n"
"selected above in the 'Mirror Axis'."
"entered here, a pair of drills will be created:\n\n"
"- one drill at the coordinates from the field\n"
"- one drill in mirror position over the axis selected above in the 'Mirror Axis'."
)
self.layout.addWidget(self.ah_label)
@ -186,10 +186,13 @@ class DblSidedTool(FlatCAMTool):
self.add_drill_point_button = QtWidgets.QPushButton("Add")
self.add_drill_point_button.setToolTip(
"Add alignment drill holes coords (x1, y1), (x2, y2), ... \n"
"on one side of the mirror axis.\n"
"The point(s) can be captured by pressing SHIFT key\n"
"and left mouse clicking on canvas. Or you can enter them manually."
"Add alignment drill holes coords in the format: (x1, y1), (x2, y2), ... \n"
"on one side of the mirror axis.\n\n"
"The coordinates set can be obtained:\n"
"- press SHIFT key and left mouse clicking on canvas. Then click Add.\n"
"- press SHIFT key and left mouse clicking on canvas. Then CTRL+V in the field.\n"
"- press SHIFT key and left mouse clicking on canvas. Then RMB click in the field and click Paste.\n"
"- by entering the coords manually in the format: (x1, y1), (x2, y2), ..."
)
self.add_drill_point_button.setFixedWidth(40)
@ -197,11 +200,10 @@ class DblSidedTool(FlatCAMTool):
grid_lay1.addWidget(self.add_drill_point_button, 0, 3)
## Drill diameter for alignment holes
self.dt_label = QtWidgets.QLabel("<b>Alignment Drill Creation</b>:")
self.dt_label = QtWidgets.QLabel("<b>Alignment Drill Diameter</b>:")
self.dt_label.setToolTip(
"Create a set of alignment drill holes\n"
"with the specified diameter,\n"
"at the specified coordinates."
"Diameter of the drill for the "
"alignment holes."
)
self.layout.addWidget(self.dt_label)
@ -249,20 +251,19 @@ class DblSidedTool(FlatCAMTool):
self.drill_values = ""
self.set_ui()
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+D', **kwargs)
def run(self):
self.app.report_usage("Tool2Sided()")
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "2-Sided Tool")
self.reset_fields()
self.set_ui()
def set_ui(self):
## Initialize form
def set_tool_ui(self):
self.reset_fields()
self.point_entry.set_value("")
self.alignment_holes.set_value("")
@ -283,7 +284,7 @@ class DblSidedTool(FlatCAMTool):
try:
px, py = self.point_entry.get_value()
except TypeError:
self.app.inform.emit("[warning_notcl] 'Point' reference is selected and 'Point' coordinates "
self.app.inform.emit("[WARNING_NOTCL] 'Point' reference is selected and 'Point' coordinates "
"are missing. Add them and retry.")
return
else:
@ -297,12 +298,15 @@ class DblSidedTool(FlatCAMTool):
xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis]
dia = self.drill_dia.get_value()
if dia is None:
self.app.inform.emit("[WARNING_NOTCL]No value or wrong format in Drill Dia entry. Add it and retry.")
return
tools = {"1": {"C": dia}}
# holes = self.alignment_holes.get_value()
holes = eval('[{}]'.format(self.alignment_holes.text()))
if not holes:
self.app.inform.emit("[warning_notcl] There are no Alignment Drill Coordinates to use. Add them and retry.")
self.app.inform.emit("[WARNING_NOTCL] There are no Alignment Drill Coordinates to use. Add them and retry.")
return
drills = []
@ -328,11 +332,11 @@ class DblSidedTool(FlatCAMTool):
try:
fcobj = model_index.internalPointer().obj
except Exception as e:
self.app.inform.emit("[warning_notcl] There is no Gerber object loaded ...")
self.app.inform.emit("[WARNING_NOTCL] There is no Gerber object loaded ...")
return
if not isinstance(fcobj, FlatCAMGerber):
self.app.inform.emit("[error_notcl] Only Gerber, Excellon and Geometry objects can be mirrored.")
self.app.inform.emit("[ERROR_NOTCL] Only Gerber, Excellon and Geometry objects can be mirrored.")
return
axis = self.mirror_axis.get_value()
@ -342,7 +346,7 @@ class DblSidedTool(FlatCAMTool):
try:
px, py = self.point_entry.get_value()
except TypeError:
self.app.inform.emit("[warning_notcl] 'Point' coordinates missing. "
self.app.inform.emit("[WARNING_NOTCL] 'Point' coordinates missing. "
"Using Origin (0, 0) as mirroring reference.")
px, py = (0, 0)
@ -352,7 +356,7 @@ class DblSidedTool(FlatCAMTool):
try:
bb_obj = model_index_box.internalPointer().obj
except Exception as e:
self.app.inform.emit("[warning_notcl] There is no Box object loaded ...")
self.app.inform.emit("[WARNING_NOTCL] There is no Box object loaded ...")
return
xmin, ymin, xmax, ymax = bb_obj.bounds()
@ -370,11 +374,11 @@ class DblSidedTool(FlatCAMTool):
try:
fcobj = model_index.internalPointer().obj
except Exception as e:
self.app.inform.emit("[warning_notcl] There is no Excellon object loaded ...")
self.app.inform.emit("[WARNING_NOTCL] There is no Excellon object loaded ...")
return
if not isinstance(fcobj, FlatCAMExcellon):
self.app.inform.emit("[error_notcl] Only Gerber, Excellon and Geometry objects can be mirrored.")
self.app.inform.emit("[ERROR_NOTCL] Only Gerber, Excellon and Geometry objects can be mirrored.")
return
axis = self.mirror_axis.get_value()
@ -388,7 +392,7 @@ class DblSidedTool(FlatCAMTool):
try:
bb_obj = model_index_box.internalPointer().obj
except Exception as e:
self.app.inform.emit("[warning_notcl] There is no Box object loaded ...")
self.app.inform.emit("[WARNING_NOTCL] There is no Box object loaded ...")
return
xmin, ymin, xmax, ymax = bb_obj.bounds()
@ -406,11 +410,11 @@ class DblSidedTool(FlatCAMTool):
try:
fcobj = model_index.internalPointer().obj
except Exception as e:
self.app.inform.emit("[warning_notcl] There is no Geometry object loaded ...")
self.app.inform.emit("[WARNING_NOTCL] There is no Geometry object loaded ...")
return
if not isinstance(fcobj, FlatCAMGeometry):
self.app.inform.emit("[error_notcl] Only Gerber, Excellon and Geometry objects can be mirrored.")
self.app.inform.emit("[ERROR_NOTCL] Only Gerber, Excellon and Geometry objects can be mirrored.")
return
axis = self.mirror_axis.get_value()
@ -424,7 +428,7 @@ class DblSidedTool(FlatCAMTool):
try:
bb_obj = model_index_box.internalPointer().obj
except Exception as e:
self.app.inform.emit("[warning_notcl] There is no Box object loaded ...")
self.app.inform.emit("[WARNING_NOTCL] There is no Box object loaded ...")
return
xmin, ymin, xmax, ymax = bb_obj.bounds()

View File

@ -1,6 +1,6 @@
from FlatCAMTool import FlatCAMTool
from GUIElements import RadioSet, FloatEntry
from GUIElements import RadioSet, FCEntry
from PyQt5 import QtGui, QtCore, QtWidgets
@ -44,6 +44,7 @@ class Film(FlatCAMTool):
self.tf_object_combo.setModel(self.app.collection)
self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.tf_object_combo.setCurrentIndex(1)
self.tf_object_label = QtWidgets.QLabel("Film Object:")
self.tf_object_label.setToolTip(
"Object for which to create the film."
@ -101,7 +102,7 @@ class Film(FlatCAMTool):
# Boundary for negative film generation
self.boundary_entry = FloatEntry()
self.boundary_entry = FCEntry()
self.boundary_label = QtWidgets.QLabel("Border:")
self.boundary_label.setToolTip(
"Specify a border around the object.\n"
@ -115,6 +116,15 @@ class Film(FlatCAMTool):
)
tf_form_layout.addRow(self.boundary_label, self.boundary_entry)
self.film_scale_entry = FCEntry()
self.film_scale_label = QtWidgets.QLabel("Scale Stroke:")
self.film_scale_label.setToolTip(
"Scale the line stroke thickness of each feature in the SVG file.\n"
"It means that the line that envelope each SVG feature will be thicker or thinner,\n"
"therefore the fine features may be more affected by this parameter."
)
tf_form_layout.addRow(self.film_scale_label, self.film_scale_entry)
# Buttons
hlay = QtWidgets.QHBoxLayout()
self.layout.addLayout(hlay)
@ -136,10 +146,6 @@ class Film(FlatCAMTool):
self.tf_type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
self.tf_type_box_combo.currentIndexChanged.connect(self.on_type_box_index_changed)
## Initialize form
self.film_type.set_value('neg')
self.boundary_entry.set_value(0.0)
def on_type_obj_index_changed(self, index):
obj_type = self.tf_type_obj_combo.currentIndex()
self.tf_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
@ -151,25 +157,58 @@ class Film(FlatCAMTool):
self.tf_box_combo.setCurrentIndex(0)
def run(self):
self.app.report_usage("ToolFilm()")
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Film Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+L', **kwargs)
def set_tool_ui(self):
self.reset_fields()
f_type = self.app.defaults["tools_film_type"] if self.app.defaults["tools_film_type"] else 'neg'
self.film_type.set_value(str(f_type))
b_entry = self.app.defaults[ "tools_film_boundary"] if self.app.defaults[ "tools_film_boundary"] else 0.0
self.boundary_entry.set_value(float(b_entry))
scale_stroke_width = self.app.defaults["tools_film_scale"] if self.app.defaults["tools_film_scale"] else 0.0
self.film_scale_entry.set_value(int(scale_stroke_width))
def on_film_creation(self):
try:
name = self.tf_object_combo.currentText()
except:
self.app.inform.emit("[error_notcl] No Film object selected. Load a Film object and retry.")
self.app.inform.emit("[ERROR_NOTCL] No FlatCAM object selected. Load an object for Film and retry.")
return
try:
boxname = self.tf_box_combo.currentText()
except:
self.app.inform.emit("[error_notcl] No Box object selected. Load a Box object and retry.")
self.app.inform.emit("[ERROR_NOTCL] No FlatCAM object selected. Load an object for Box and retry.")
return
try:
border = float(self.boundary_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
border = float(self.boundary_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
try:
scale_stroke_width = int(self.film_scale_entry.get_value())
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
border = float(self.boundary_entry.get_value())
if border is None:
border = 0
@ -177,32 +216,36 @@ class Film(FlatCAMTool):
if self.film_type.get_value() == "pos":
try:
filename, _ = QtWidgets.QFileDialog.getSaveFileName(caption="Export SVG positive",
directory=self.app.get_last_save_folder(), filter="*.svg")
filename, _ = QtWidgets.QFileDialog.getSaveFileName(
caption="Export SVG positive",
directory=self.app.get_last_save_folder() + '/' + name,
filter="*.svg")
except TypeError:
filename, _ = QtWidgets.QFileDialog.getSaveFileName(caption="Export SVG positive")
filename = str(filename)
if str(filename) == "":
self.app.inform.emit("Export SVG positive cancelled.")
self.app.inform.emit("[WARNING_NOTCL]Export SVG positive cancelled.")
return
else:
self.app.export_svg_black(name, boxname, filename)
self.app.export_svg_black(name, boxname, filename, scale_factor=scale_stroke_width)
else:
try:
filename, _ = QtWidgets.QFileDialog.getSaveFileName(caption="Export SVG negative",
directory=self.app.get_last_save_folder(), filter="*.svg")
filename, _ = QtWidgets.QFileDialog.getSaveFileName(
caption="Export SVG negative",
directory=self.app.get_last_save_folder() + '/' + name,
filter="*.svg")
except TypeError:
filename, _ = QtWidgets.QFileDialog.getSaveFileName(caption="Export SVG negative")
filename = str(filename)
if str(filename) == "":
self.app.inform.emit("Export SVG negative cancelled.")
self.app.inform.emit("[WARNING_NOTCL]Export SVG negative cancelled.")
return
else:
self.app.export_svg_negative(name, boxname, filename, border)
self.app.export_svg_negative(name, boxname, filename, border, scale_factor=scale_stroke_width)
def reset_fields(self):
self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))

View File

@ -124,6 +124,17 @@ class ToolImage(FlatCAMTool):
## Signals
self.import_button.clicked.connect(self.on_file_importimage)
def run(self):
self.app.report_usage("ToolImage()")
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Image Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, **kwargs)
def set_tool_ui(self):
## Initialize form
self.dpi_entry.set_value(96)
self.image_type.set_value('black')
@ -132,10 +143,6 @@ class ToolImage(FlatCAMTool):
self.mask_g_entry.set_value(250)
self.mask_b_entry.set_value(250)
def run(self):
FlatCAMTool.run(self)
self.app.ui.notebook.setTabText(2, "Image Tool")
def on_file_importimage(self):
"""
Callback for menu item File->Import IMAGE.

View File

@ -152,11 +152,19 @@ class Measurement(FlatCAMTool):
self.measure_btn.clicked.connect(self.toggle)
def run(self):
self.app.report_usage("ToolMeasurement()")
if self.app.tool_tab_locked is True:
return
self.toggle()
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Meas. Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='CTRL+M', **kwargs)
def set_tool_ui(self):
# Remove anything else in the GUI
self.app.ui.tool_scroll_area.takeWidget()
@ -167,21 +175,6 @@ class Measurement(FlatCAMTool):
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().lower()
self.show()
self.app.ui.notebook.setTabText(2, "Meas. Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='CTRL+M', **kwargs)
def on_key_release_meas(self, event):
if event.key == 'escape':
# abort the measurement action
self.toggle()
return
if event.key == 'G':
# toggle grid status
self.app.ui.grid_snap_btn.trigger()
return
def toggle(self):
# the self.active var is doing the 'toggle'
@ -264,6 +257,17 @@ class Measurement(FlatCAMTool):
self.app.inform.emit("MEASURING: Click on the Start point ...")
def on_key_release_meas(self, event):
if event.key == 'escape':
# abort the measurement action
self.toggle()
return
if event.key == 'G':
# toggle grid status
self.app.ui.grid_snap_btn.trigger()
return
def on_click_meas(self, event):
# mouse click will be accepted only if the left button is clicked
# this is necessary because right mouse click and middle mouse click

View File

@ -34,10 +34,44 @@ class ToolMove(FlatCAMTool):
FlatCAMTool.install(self, icon, separator, shortcut='M', **kwargs)
def run(self):
self.app.report_usage("ToolMove()")
if self.app.tool_tab_locked is True:
return
self.toggle()
def toggle(self):
if self.isVisible():
self.setVisible(False)
self.app.plotcanvas.vis_disconnect('mouse_move', self.on_move)
self.app.plotcanvas.vis_disconnect('mouse_press', self.on_left_click)
self.app.plotcanvas.vis_disconnect('key_release', self.on_key_press)
self.app.plotcanvas.vis_connect('key_press', self.app.on_key_over_plot)
self.clicked_move = 0
# signal that there is no command active
self.app.command_active = None
# delete the selection box
self.delete_shape()
return
else:
self.setVisible(True)
# signal that there is a command active and it is 'Move'
self.app.command_active = "Move"
if self.app.collection.get_selected():
self.app.inform.emit("MOVE: Click on the Start point ...")
# draw the selection box
self.draw_sel_bbox()
else:
self.setVisible(False)
# signal that there is no command active
self.app.command_active = None
self.app.inform.emit("[WARNING_NOTCL]MOVE action cancelled. No object(s) to move.")
def on_left_click(self, event):
# mouse click will be accepted only if the left button is clicked
# this is necessary because right mouse click and middle mouse click
@ -83,7 +117,7 @@ class ToolMove(FlatCAMTool):
try:
if not obj_list:
self.app.inform.emit("[warning_notcl] No object(s) selected.")
self.app.inform.emit("[WARNING_NOTCL] No object(s) selected.")
return "fail"
else:
for sel_obj in obj_list:
@ -99,7 +133,7 @@ class ToolMove(FlatCAMTool):
# self.app.collection.set_active(sel_obj.options['name'])
except Exception as e:
proc.done()
self.app.inform.emit('[error_notcl] '
self.app.inform.emit('[ERROR_NOTCL] '
'ToolMove.on_left_click() --> %s' % str(e))
return "fail"
proc.done()
@ -114,7 +148,7 @@ class ToolMove(FlatCAMTool):
return
except TypeError:
self.app.inform.emit('[error_notcl] '
self.app.inform.emit('[ERROR_NOTCL] '
'ToolMove.on_left_click() --> Error when mouse left click.')
return
@ -142,42 +176,10 @@ class ToolMove(FlatCAMTool):
def on_key_press(self, event):
if event.key == 'escape':
# abort the move action
self.app.inform.emit("[warning_notcl]Move action cancelled.")
self.app.inform.emit("[WARNING_NOTCL]Move action cancelled.")
self.toggle()
return
def toggle(self):
if self.isVisible():
self.setVisible(False)
self.app.plotcanvas.vis_disconnect('mouse_move', self.on_move)
self.app.plotcanvas.vis_disconnect('mouse_press', self.on_left_click)
self.app.plotcanvas.vis_disconnect('key_release', self.on_key_press)
self.app.plotcanvas.vis_connect('key_press', self.app.on_key_over_plot)
self.clicked_move = 0
# signal that there is no command active
self.app.command_active = None
# delete the selection box
self.delete_shape()
return
else:
self.setVisible(True)
# signal that there is a command active and it is 'Move'
self.app.command_active = "Move"
if self.app.collection.get_selected():
self.app.inform.emit("MOVE: Click on the Start point ...")
# draw the selection box
self.draw_sel_bbox()
else:
self.setVisible(False)
# signal that there is no command active
self.app.command_active = None
self.app.inform.emit("[warning_notcl]MOVE action cancelled. No object(s) to move.")
def draw_sel_bbox(self):
xminlist = []
yminlist = []
@ -186,7 +188,7 @@ class ToolMove(FlatCAMTool):
obj_list = self.app.collection.get_selected()
if not obj_list:
self.app.inform.emit("[warning_notcl]Object(s) not selected")
self.app.inform.emit("[WARNING_NOTCL]Object(s) not selected")
self.toggle()
else:
# if we have an object selected then we can safely activate the mouse events

View File

@ -35,6 +35,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel("Gerber:")
self.object_label.setToolTip(
"Gerber object to be cleared of excess copper. "
@ -97,7 +98,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.addtool_entry_lbl.setToolTip(
"Diameter for the new tool to add in the Tool Table"
)
self.addtool_entry = FloatEntry()
self.addtool_entry = FCEntry()
# hlay.addWidget(self.addtool_label)
# hlay.addStretch()
@ -151,7 +152,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
"due of too many paths."
)
grid3.addWidget(nccoverlabel, 1, 0)
self.ncc_overlap_entry = FloatEntry()
self.ncc_overlap_entry = FCEntry()
grid3.addWidget(self.ncc_overlap_entry, 1, 1)
nccmarginlabel = QtWidgets.QLabel('Margin:')
@ -159,7 +160,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
"Bounding box margin."
)
grid3.addWidget(nccmarginlabel, 2, 0)
self.ncc_margin_entry = FloatEntry()
self.ncc_margin_entry = FCEntry()
grid3.addWidget(self.ncc_margin_entry, 2, 1)
# Method
@ -237,13 +238,16 @@ class NonCopperClear(FlatCAMTool, Gerber):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+N', **kwargs)
def run(self):
self.app.report_usage("ToolNonCopperClear()")
FlatCAMTool.run(self)
self.tools_frame.show()
self.set_ui()
self.set_tool_ui()
self.build_ui()
self.app.ui.notebook.setTabText(2, "NCC Tool")
def set_ui(self):
def set_tool_ui(self):
self.tools_frame.show()
self.ncc_overlap_entry.set_value(self.app.defaults["tools_nccoverlap"])
self.ncc_margin_entry.set_value(self.app.defaults["tools_nccmargin"])
self.ncc_method_radio.set_value(self.app.defaults["tools_nccmethod"])
@ -408,7 +412,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.tools_table.setMinimumHeight(self.tools_table.getHeight())
self.tools_table.setMaximumHeight(self.tools_table.getHeight())
self.app.report_usage("gerber_on_ncc_button")
self.ui_connect()
def ui_connect(self):
@ -428,10 +431,19 @@ class NonCopperClear(FlatCAMTool, Gerber):
if dia:
tool_dia = dia
else:
tool_dia = self.addtool_entry.get_value()
try:
tool_dia = float(self.addtool_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
if tool_dia is None:
self.build_ui()
self.app.inform.emit("[warning_notcl] Please enter a tool diameter to add, in Float format.")
self.app.inform.emit("[WARNING_NOTCL] Please enter a tool diameter to add, in Float format.")
return
# construct a list of all 'tooluid' in the self.tools
@ -455,7 +467,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
if float('%.4f' % tool_dia) in tool_dias:
if muted is None:
self.app.inform.emit("[warning_notcl]Adding tool cancelled. Tool already in Tool Table.")
self.app.inform.emit("[WARNING_NOTCL]Adding tool cancelled. Tool already in Tool Table.")
self.tools_table.itemChanged.connect(self.on_tool_edit)
return
else:
@ -485,7 +497,18 @@ class NonCopperClear(FlatCAMTool, Gerber):
tool_dias.append(float('%.4f' % v[tool_v]))
for row in range(self.tools_table.rowCount()):
new_tool_dia = float(self.tools_table.item(row, 1).text())
try:
new_tool_dia = float(self.tools_table.item(row, 1).text())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
new_tool_dia = float(self.tools_table.item(row, 1).text().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
tooluid = int(self.tools_table.item(row, 3).text())
# identify the tool that was edited and get it's tooluid
@ -502,7 +525,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
break
restore_dia_item = self.tools_table.item(row, 1)
restore_dia_item.setText(str(old_tool_dia))
self.app.inform.emit("[warning_notcl] Edit cancelled. New diameter value is already in the Tool Table.")
self.app.inform.emit("[WARNING_NOTCL] Edit cancelled. New diameter value is already in the Tool Table.")
self.build_ui()
def on_tool_delete(self, rows_to_delete=None, all=None):
@ -541,7 +564,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.ncc_tools.pop(t, None)
except AttributeError:
self.app.inform.emit("[warning_notcl]Delete failed. Select a tool to delete.")
self.app.inform.emit("[WARNING_NOTCL]Delete failed. Select a tool to delete.")
return
except Exception as e:
log.debug(str(e))
@ -551,10 +574,28 @@ class NonCopperClear(FlatCAMTool, Gerber):
def on_ncc(self):
over = self.ncc_overlap_entry.get_value()
try:
over = float(self.ncc_overlap_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
over = float(self.ncc_overlap_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
over = over if over else self.app.defaults["tools_nccoverlap"]
margin = self.ncc_margin_entry.get_value()
try:
margin = float(self.ncc_margin_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
margin = float(self.ncc_margin_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
margin = margin if margin else self.app.defaults["tools_nccmargin"]
connect = self.ncc_connect_cb.get_value()
@ -574,7 +615,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
try:
self.ncc_obj = self.app.collection.get_by_name(self.obj_name)
except:
self.app.inform.emit("[error_notcl]Could not retrieve object: %s" % self.obj_name)
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve object: %s" % self.obj_name)
return "Could not retrieve object: %s" % self.obj_name
@ -582,7 +623,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
try:
bounding_box = self.ncc_obj.solid_geometry.envelope.buffer(distance=margin, join_style=JOIN_STYLE.mitre)
except AttributeError:
self.app.inform.emit("[error_notcl]No Gerber file available.")
self.app.inform.emit("[ERROR_NOTCL]No Gerber file available.")
return
# calculate the empty area by substracting the solid_geometry from the object bounding box geometry
@ -707,14 +748,14 @@ class NonCopperClear(FlatCAMTool, Gerber):
app_obj.new_object("geometry", name, initialize)
except Exception as e:
proc.done()
self.app.inform.emit('[error_notcl] NCCTool.clear_non_copper() --> %s' % str(e))
self.app.inform.emit('[ERROR_NOTCL] NCCTool.clear_non_copper() --> %s' % str(e))
return
proc.done()
if app_obj.poly_not_cleared is False:
self.app.inform.emit('[success] NCC Tool finished.')
else:
self.app.inform.emit('[warning_notcl] NCC Tool finished but some PCB features could not be cleared. '
self.app.inform.emit('[WARNING_NOTCL] NCC Tool finished but some PCB features could not be cleared. '
'Check the result.')
# reset the variable for next use
app_obj.poly_not_cleared = False
@ -858,7 +899,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
app_obj.new_object("geometry", name, initialize_rm)
except Exception as e:
proc.done()
self.app.inform.emit('[error_notcl] NCCTool.clear_non_copper_rest() --> %s' % str(e))
self.app.inform.emit('[ERROR_NOTCL] NCCTool.clear_non_copper_rest() --> %s' % str(e))
return
if app_obj.poly_not_cleared is True:
@ -866,7 +907,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
# focus on Selected Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
else:
self.app.inform.emit('[error_notcl] NCC Tool finished but could not clear the object '
self.app.inform.emit('[ERROR_NOTCL] NCC Tool finished but could not clear the object '
'with current settings.')
# focus on Project Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
@ -882,3 +923,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
# Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def reset_fields(self):
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))

View File

@ -33,6 +33,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel("Geometry:")
self.object_label.setToolTip(
"Geometry object to be painted. "
@ -94,7 +95,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.addtool_entry_lbl.setToolTip(
"Diameter for the new tool."
)
self.addtool_entry = FloatEntry()
self.addtool_entry = FCEntry()
# hlay.addWidget(self.addtool_label)
# hlay.addStretch()
@ -146,7 +147,7 @@ class ToolPaint(FlatCAMTool, Gerber):
"due of too many paths."
)
grid3.addWidget(ovlabel, 1, 0)
self.paintoverlap_entry = LengthEntry()
self.paintoverlap_entry = FCEntry()
grid3.addWidget(self.paintoverlap_entry, 1, 1)
# Margin
@ -157,7 +158,7 @@ class ToolPaint(FlatCAMTool, Gerber):
"be painted."
)
grid3.addWidget(marginlabel, 2, 0)
self.paintmargin_entry = LengthEntry()
self.paintmargin_entry = FCEntry()
grid3.addWidget(self.paintmargin_entry, 2, 1)
# Method
@ -294,9 +295,10 @@ class ToolPaint(FlatCAMTool, Gerber):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+P', **kwargs)
def run(self):
self.app.report_usage("ToolPaint()")
FlatCAMTool.run(self)
self.tools_frame.show()
self.set_ui()
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Paint Tool")
def on_radio_selection(self):
@ -320,7 +322,10 @@ class ToolPaint(FlatCAMTool, Gerber):
self.deltool_btn.setDisabled(False)
self.tools_table.setContextMenuPolicy(Qt.ActionsContextMenu)
def set_ui(self):
def set_tool_ui(self):
self.tools_frame.show()
self.reset_fields()
## Init the GUI interface
self.paintmargin_entry.set_value(self.default_data["paintmargin"])
self.paintmethod_combo.set_value(self.default_data["paintmethod"])
@ -484,10 +489,20 @@ class ToolPaint(FlatCAMTool, Gerber):
if dia:
tool_dia = dia
else:
tool_dia = self.addtool_entry.get_value()
try:
tool_dia = float(self.addtool_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
if tool_dia is None:
self.build_ui()
self.app.inform.emit("[warning_notcl] Please enter a tool diameter to add, in Float format.")
self.app.inform.emit("[WARNING_NOTCL] Please enter a tool diameter to add, in Float format.")
return
# construct a list of all 'tooluid' in the self.tools
@ -511,7 +526,7 @@ class ToolPaint(FlatCAMTool, Gerber):
if float('%.4f' % tool_dia) in tool_dias:
if muted is None:
self.app.inform.emit("[warning_notcl]Adding tool cancelled. Tool already in Tool Table.")
self.app.inform.emit("[WARNING_NOTCL]Adding tool cancelled. Tool already in Tool Table.")
self.tools_table.itemChanged.connect(self.on_tool_edit)
return
else:
@ -544,7 +559,16 @@ class ToolPaint(FlatCAMTool, Gerber):
tool_dias.append(float('%.4f' % v[tool_v]))
for row in range(self.tools_table.rowCount()):
new_tool_dia = float(self.tools_table.item(row, 1).text())
try:
new_tool_dia = float(self.tools_table.item(row, 1).text())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
new_tool_dia = float(self.tools_table.item(row, 1).text().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
tooluid = int(self.tools_table.item(row, 3).text())
# identify the tool that was edited and get it's tooluid
@ -561,7 +585,7 @@ class ToolPaint(FlatCAMTool, Gerber):
break
restore_dia_item = self.tools_table.item(row, 1)
restore_dia_item.setText(str(old_tool_dia))
self.app.inform.emit("[warning_notcl] Edit cancelled. New diameter value is already in the Tool Table.")
self.app.inform.emit("[WARNING_NOTCL] Edit cancelled. New diameter value is already in the Tool Table.")
self.build_ui()
# def on_tool_copy(self, all=None):
@ -594,7 +618,7 @@ class ToolPaint(FlatCAMTool, Gerber):
# print("COPIED", self.paint_tools[td])
# self.build_ui()
# except AttributeError:
# self.app.inform.emit("[warning_notcl]Failed. Select a tool to copy.")
# self.app.inform.emit("[WARNING_NOTCL]Failed. Select a tool to copy.")
# self.build_ui()
# return
# except Exception as e:
@ -602,7 +626,7 @@ class ToolPaint(FlatCAMTool, Gerber):
# # deselect the table
# # self.ui.geo_tools_table.clearSelection()
# else:
# self.app.inform.emit("[warning_notcl]Failed. Select a tool to copy.")
# self.app.inform.emit("[WARNING_NOTCL]Failed. Select a tool to copy.")
# self.build_ui()
# return
# else:
@ -658,7 +682,7 @@ class ToolPaint(FlatCAMTool, Gerber):
self.paint_tools.pop(t, None)
except AttributeError:
self.app.inform.emit("[warning_notcl]Delete failed. Select a tool to delete.")
self.app.inform.emit("[WARNING_NOTCL]Delete failed. Select a tool to delete.")
return
except Exception as e:
log.debug(str(e))
@ -669,9 +693,18 @@ class ToolPaint(FlatCAMTool, Gerber):
def on_paint_button_click(self):
self.app.report_usage("geometry_on_paint_button")
self.app.inform.emit("[warning_notcl]Click inside the desired polygon.")
self.app.inform.emit("[WARNING_NOTCL]Click inside the desired polygon.")
try:
overlap = float(self.paintoverlap_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
overlap = float(self.paintoverlap_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
overlap = self.paintoverlap_entry.get_value()
connect = self.pathconnect_cb.get_value()
contour = self.paintcontour_cb.get_value()
select_method = self.selectmethod_combo.get_value()
@ -682,11 +715,11 @@ class ToolPaint(FlatCAMTool, Gerber):
try:
self.paint_obj = self.app.collection.get_by_name(str(self.obj_name))
except:
self.app.inform.emit("[error_notcl]Could not retrieve object: %s" % self.obj_name)
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve object: %s" % self.obj_name)
return
if self.paint_obj is None:
self.app.inform.emit("[error_notcl]Object not found: %s" % self.paint_obj)
self.app.inform.emit("[ERROR_NOTCL]Object not found: %s" % self.paint_obj)
return
o_name = '%s_multitool_paint' % (self.obj_name)
@ -699,7 +732,7 @@ class ToolPaint(FlatCAMTool, Gerber):
contour=contour)
if select_method == "single":
self.app.inform.emit("[warning_notcl]Click inside the desired polygon.")
self.app.inform.emit("[WARNING_NOTCL]Click inside the desired polygon.")
# use the first tool in the tool table; get the diameter
tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
@ -742,12 +775,22 @@ class ToolPaint(FlatCAMTool, Gerber):
# poly = find_polygon(self.solid_geometry, inside_pt)
poly = obj.find_polygon(inside_pt)
paint_method = self.paintmethod_combo.get_value()
paint_margin = self.paintmargin_entry.get_value()
try:
paint_margin = float(self.paintmargin_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
# No polygon?
if poly is None:
self.app.log.warning('No polygon found.')
self.app.inform.emit('[warning] No polygon found.')
self.app.inform.emit('[WARNING] No polygon found.')
return
proc = self.app.proc_container.new("Painting polygon.")
@ -792,7 +835,7 @@ class ToolPaint(FlatCAMTool, Gerber):
geo_obj.solid_geometry += list(cp.get_objects())
return cp
else:
self.app.inform.emit('[error_notcl] Geometry could not be painted completely')
self.app.inform.emit('[ERROR_NOTCL] Geometry could not be painted completely')
return None
geo_obj.solid_geometry = []
@ -807,7 +850,7 @@ class ToolPaint(FlatCAMTool, Gerber):
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit(
"[error] Could not do Paint. Try a different combination of parameters. "
"[ERROR] Could not do Paint. Try a different combination of parameters. "
"Or a different strategy of paint\n%s" % str(e))
return
@ -838,7 +881,7 @@ class ToolPaint(FlatCAMTool, Gerber):
# self.app.inform.emit("[success] Paint single polygon Done")
# else:
# print("[WARNING] Paint single polygon done with errors")
# self.app.inform.emit("[warning] Paint single polygon done with errors. "
# self.app.inform.emit("[WARNING] Paint single polygon done with errors. "
# "%d area(s) could not be painted.\n"
# "Use different paint parameters or edit the paint geometry and correct"
# "the issue."
@ -849,7 +892,7 @@ class ToolPaint(FlatCAMTool, Gerber):
app_obj.new_object("geometry", name, gen_paintarea)
except Exception as e:
proc.done()
self.app.inform.emit('[error_notcl] PaintTool.paint_poly() --> %s' % str(e))
self.app.inform.emit('[ERROR_NOTCL] PaintTool.paint_poly() --> %s' % str(e))
return
proc.done()
# focus on Selected Tab
@ -876,10 +919,19 @@ class ToolPaint(FlatCAMTool, Gerber):
:return:
"""
paint_method = self.paintmethod_combo.get_value()
paint_margin = self.paintmargin_entry.get_value()
try:
paint_margin = float(self.paintmargin_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
proc = self.app.proc_container.new("Painting polygon.")
name = outname if outname else self.obj_name + "_paint"
over = overlap
conn = connect
@ -984,7 +1036,7 @@ class ToolPaint(FlatCAMTool, Gerber):
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit(
"[error] Could not do Paint All. Try a different combination of parameters. "
"[ERROR] Could not do Paint All. Try a different combination of parameters. "
"Or a different Method of paint\n%s" % str(e))
return
@ -1008,7 +1060,7 @@ class ToolPaint(FlatCAMTool, Gerber):
if geo_obj.tools[tooluid]['solid_geometry']:
has_solid_geo += 1
if has_solid_geo == 0:
self.app.inform.emit("[error] There is no Painting Geometry in the file.\n"
self.app.inform.emit("[ERROR] There is no Painting Geometry in the file.\n"
"Usually it means that the tool diameter is too big for the painted geometry.\n"
"Change the painting parameters and try again.")
return
@ -1063,7 +1115,7 @@ class ToolPaint(FlatCAMTool, Gerber):
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit(
"[error] Could not do Paint All. Try a different combination of parameters. "
"[ERROR] Could not do Paint All. Try a different combination of parameters. "
"Or a different Method of paint\n%s" % str(e))
return
@ -1093,7 +1145,7 @@ class ToolPaint(FlatCAMTool, Gerber):
if geo_obj.tools[tooluid]['solid_geometry']:
has_solid_geo += 1
if has_solid_geo == 0:
self.app.inform.emit("[error_notcl] There is no Painting Geometry in the file.\n"
self.app.inform.emit("[ERROR_NOTCL] There is no Painting Geometry in the file.\n"
"Usually it means that the tool diameter is too big for the painted geometry.\n"
"Change the painting parameters and try again.")
return
@ -1125,3 +1177,6 @@ class ToolPaint(FlatCAMTool, Gerber):
# Background
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
def reset_fields(self):
self.object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))

View File

@ -44,6 +44,7 @@ class Panelize(FlatCAMTool):
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
self.object_label = QtWidgets.QLabel("Object:")
self.object_label.setToolTip(
"Object to be panelized. This means that it will\n"
@ -76,6 +77,7 @@ class Panelize(FlatCAMTool):
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
self.box_combo_label = QtWidgets.QLabel("Box Object:")
self.box_combo_label.setToolTip(
"The actual object that is used a container for the\n "
@ -84,8 +86,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.box_combo_label, self.box_combo)
## Spacing Columns
self.spacing_columns = FloatEntry()
self.spacing_columns.set_value(0.0)
self.spacing_columns = FCEntry()
self.spacing_columns_label = QtWidgets.QLabel("Spacing cols:")
self.spacing_columns_label.setToolTip(
"Spacing between columns of the desired panel.\n"
@ -94,8 +95,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.spacing_columns_label, self.spacing_columns)
## Spacing Rows
self.spacing_rows = FloatEntry()
self.spacing_rows.set_value(0.0)
self.spacing_rows = FCEntry()
self.spacing_rows_label = QtWidgets.QLabel("Spacing rows:")
self.spacing_rows_label.setToolTip(
"Spacing between rows of the desired panel.\n"
@ -104,8 +104,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.spacing_rows_label, self.spacing_rows)
## Columns
self.columns = IntEntry()
self.columns.set_value(1)
self.columns = FCEntry()
self.columns_label = QtWidgets.QLabel("Columns:")
self.columns_label.setToolTip(
"Number of columns of the desired panel"
@ -113,8 +112,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.columns_label, self.columns)
## Rows
self.rows = IntEntry()
self.rows.set_value(1)
self.rows = FCEntry()
self.rows_label = QtWidgets.QLabel("Rows:")
self.rows_label.setToolTip(
"Number of rows of the desired panel"
@ -132,8 +130,7 @@ class Panelize(FlatCAMTool):
)
form_layout.addRow(self.constrain_cb)
self.x_width_entry = FloatEntry()
self.x_width_entry.set_value(0.0)
self.x_width_entry = FCEntry()
self.x_width_lbl = QtWidgets.QLabel("Width (DX):")
self.x_width_lbl.setToolTip(
"The width (DX) within which the panel must fit.\n"
@ -141,8 +138,7 @@ class Panelize(FlatCAMTool):
)
form_layout.addRow(self.x_width_lbl, self.x_width_entry)
self.y_height_entry = FloatEntry()
self.y_height_entry.set_value(0.0)
self.y_height_entry = FCEntry()
self.y_height_lbl = QtWidgets.QLabel("Height (DY):")
self.y_height_lbl.setToolTip(
"The height (DY)within which the panel must fit.\n"
@ -183,6 +179,47 @@ class Panelize(FlatCAMTool):
# flag to signal the constrain was activated
self.constrain_flag = False
def run(self):
self.app.report_usage("ToolPanelize()")
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Panel. Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+Z', **kwargs)
def set_tool_ui(self):
self.reset_fields()
sp_c = self.app.defaults["tools_panelize_spacing_columns"] if \
self.app.defaults["tools_panelize_spacing_columns"] else 0.0
self.spacing_columns.set_value(float(sp_c))
sp_r = self.app.defaults["tools_panelize_spacing_rows"] if \
self.app.defaults["tools_panelize_spacing_rows"] else 0.0
self.spacing_rows.set_value(float(sp_r))
rr = self.app.defaults["tools_panelize_rows"] if \
self.app.defaults["tools_panelize_rows"] else 0.0
self.rows.set_value(int(rr))
cc = self.app.defaults["tools_panelize_columns"] if \
self.app.defaults["tools_panelize_columns"] else 0.0
self.columns.set_value(int(cc))
c_cb = self.app.defaults["tools_panelize_constrain"] if \
self.app.defaults["tools_panelize_constrain"] else False
self.constrain_cb.set_value(c_cb)
x_w = self.app.defaults["tools_panelize_constrainx"] if \
self.app.defaults["tools_panelize_constrainx"] else 0.0
self.x_width_entry.set_value(float(x_w))
y_w = self.app.defaults["tools_panelize_constrainy"] if \
self.app.defaults["tools_panelize_constrainy"] else 0.0
self.y_height_entry.set_value(float(y_w))
def on_type_obj_index_changed(self):
obj_type = self.type_obj_combo.currentIndex()
self.object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
@ -193,13 +230,6 @@ class Panelize(FlatCAMTool):
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(0)
def run(self):
FlatCAMTool.run(self)
self.app.ui.notebook.setTabText(2, "Panel. Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+Z', **kwargs)
def on_panelize(self):
name = self.object_combo.currentText()
@ -207,13 +237,13 @@ class Panelize(FlatCAMTool):
try:
obj = self.app.collection.get_by_name(str(name))
except:
self.app.inform.emit("[error_notcl]Could not retrieve object: %s" % name)
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve object: %s" % name)
return "Could not retrieve object: %s" % name
panel_obj = obj
if panel_obj is None:
self.app.inform.emit("[error_notcl]Object not found: %s" % panel_obj)
self.app.inform.emit("[ERROR_NOTCL]Object not found: %s" % panel_obj)
return "Object not found: %s" % panel_obj
boxname = self.box_combo.currentText()
@ -221,32 +251,89 @@ class Panelize(FlatCAMTool):
try:
box = self.app.collection.get_by_name(boxname)
except:
self.app.inform.emit("[error_notcl]Could not retrieve object: %s" % boxname)
self.app.inform.emit("[ERROR_NOTCL]Could not retrieve object: %s" % boxname)
return "Could not retrieve object: %s" % boxname
if box is None:
self.app.inform.emit("[warning]No object Box. Using instead %s" % panel_obj)
self.app.inform.emit("[WARNING]No object Box. Using instead %s" % panel_obj)
box = panel_obj
self.outname = name + '_panelized'
spacing_columns = self.spacing_columns.get_value()
try:
spacing_columns = float(self.spacing_columns.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
spacing_columns = float(self.spacing_columns.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
spacing_columns = spacing_columns if spacing_columns is not None else 0
spacing_rows = self.spacing_rows.get_value()
try:
spacing_rows = float(self.spacing_rows.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
spacing_rows = float(self.spacing_rows.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
spacing_rows = spacing_rows if spacing_rows is not None else 0
rows = self.rows.get_value()
try:
rows = int(self.rows.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
rows = float(self.rows.get_value().replace(',', '.'))
rows = int(rows)
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
rows = rows if rows is not None else 1
columns = self.columns.get_value()
try:
columns = int(self.columns.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
columns = float(self.columns.get_value().replace(',', '.'))
columns = int(columns)
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
columns = columns if columns is not None else 1
constrain_dx = self.x_width_entry.get_value()
constrain_dy = self.y_height_entry.get_value()
try:
constrain_dx = float(self.x_width_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
constrain_dx = float(self.x_width_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
try:
constrain_dy = float(self.y_height_entry.get_value())
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
constrain_dy = float(self.y_height_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
"use a number.")
return
if 0 in {columns, rows}:
self.app.inform.emit("[error_notcl]Columns or Rows are zero value. Change them to a positive integer.")
self.app.inform.emit("[ERROR_NOTCL]Columns or Rows are zero value. Change them to a positive integer.")
return "Columns or Rows are zero value. Change them to a positive integer."
xmin, ymin, xmax, ymax = box.bounds()
@ -342,7 +429,7 @@ class Panelize(FlatCAMTool):
# self.app.new_object("geometry", self.outname, job_init_geometry, plot=True, autoselected=True)
#
# else:
# self.app.inform.emit("[error_notcl] Obj is None")
# self.app.inform.emit("[ERROR_NOTCL] Obj is None")
# return "ERROR: Obj is None"
# panelize()
@ -455,7 +542,7 @@ class Panelize(FlatCAMTool):
self.app.inform.emit("[success]Panel done...")
else:
self.constrain_flag = False
self.app.inform.emit("[warning] Too big for the constrain area. Final panel has %s columns and %s rows" %
self.app.inform.emit("[WARNING] Too big for the constrain area. Final panel has %s columns and %s rows" %
(columns, rows))
proc = self.app.proc_container.new("Generating panel ... Please wait.")

View File

@ -42,24 +42,26 @@ class Properties(FlatCAMTool):
self.vlay.setStretch(0,0)
def run(self):
self.app.report_usage("ToolProperties()")
if self.app.tool_tab_locked is True:
return
# this reset the TreeWidget
self.treeWidget.clear()
self.properties_frame.show()
self.set_tool_ui()
FlatCAMTool.run(self)
self.properties()
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='P', **kwargs)
def set_tool_ui(self):
# this reset the TreeWidget
self.treeWidget.clear()
self.properties_frame.show()
def properties(self):
obj_list = self.app.collection.get_selected()
if not obj_list:
self.app.inform.emit("[error_notcl] Properties Tool was not displayed. No object selected.")
self.app.inform.emit("[ERROR_NOTCL] Properties Tool was not displayed. No object selected.")
self.app.ui.notebook.setTabText(2, "Tools")
self.properties_frame.hide()
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)

View File

@ -234,7 +234,7 @@ class TermWidget(QWidget):
"""
Convert text to HTML for inserting it to browser
"""
assert style in ('in', 'out', 'err')
assert style in ('in', 'out', 'err', 'warning', 'success')
text = html.escape(text)
text = text.replace('\n', '<br/>')
@ -243,6 +243,10 @@ class TermWidget(QWidget):
text = '<span style="font-weight: bold;">%s</span>' % text
elif style == 'err':
text = '<span style="font-weight: bold; color: red;">%s</span>' % text
elif style == 'warning':
text = '<span style="font-weight: bold; color: rgb(244, 182, 66);">%s</span>' % text
elif style == 'success':
text = '<span style="font-weight: bold; color: rgb(8, 68, 0);">%s</span>' % text
else:
text = '<span>%s</span>' % text # without span <br/> is ignored!!!
@ -304,6 +308,16 @@ class TermWidget(QWidget):
"""
self._append_to_browser('out', text)
def append_success(self, text):
"""Appent text to output widget
"""
self._append_to_browser('success', text)
def append_warning(self, text):
"""Appent text to output widget
"""
self._append_to_browser('warning', text)
def append_error(self, text):
"""Appent error text to output widget. Text is drawn with red background
"""

View File

@ -355,7 +355,17 @@ class ToolTransform(FlatCAMTool):
self.offx_entry.returnPressed.connect(self.on_offx)
self.offy_entry.returnPressed.connect(self.on_offy)
def run(self):
self.app.report_usage("ToolTransform()")
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, "Transform Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+R', **kwargs)
def set_tool_ui(self):
## Initialize form
self.rotate_entry.set_value('0')
self.skewx_entry.set_value('0')
@ -366,19 +376,17 @@ class ToolTransform(FlatCAMTool):
self.offy_entry.set_value('0')
self.flip_ref_cb.setChecked(False)
def run(self):
FlatCAMTool.run(self)
self.app.ui.notebook.setTabText(2, "Transform Tool")
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+R', **kwargs)
def on_rotate(self):
try:
value = float(self.rotate_entry.get_value())
except Exception as e:
self.app.inform.emit("[error] Failed to rotate due of: %s" % str(e))
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
value = float(self.rotate_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered for Rotate, "
"use a number.")
return
self.app.worker_task.emit({'fcn': self.on_rotate_action,
'params': [value]})
# self.on_rotate_action(value)
@ -405,9 +413,15 @@ class ToolTransform(FlatCAMTool):
def on_skewx(self):
try:
value = float(self.skewx_entry.get_value())
except:
self.app.inform.emit("[warning_notcl] No value for Skew!")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
value = float(self.skewx_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered for Skew X, "
"use a number.")
return
# self.on_skew("X", value)
axis = 'X'
self.app.worker_task.emit({'fcn': self.on_skew,
@ -417,9 +431,15 @@ class ToolTransform(FlatCAMTool):
def on_skewy(self):
try:
value = float(self.skewy_entry.get_value())
except:
self.app.inform.emit("[warning_notcl] No value for Skew!")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
value = float(self.skewy_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered for Skew Y, "
"use a number.")
return
# self.on_skew("Y", value)
axis = 'Y'
self.app.worker_task.emit({'fcn': self.on_skew,
@ -429,9 +449,15 @@ class ToolTransform(FlatCAMTool):
def on_scalex(self):
try:
xvalue = float(self.scalex_entry.get_value())
except:
self.app.inform.emit("[warning_notcl] No value for Scale!")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
xvalue = float(self.scalex_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered for Scale X, "
"use a number.")
return
# scaling to zero has no sense so we remove it, because scaling with 1 does nothing
if xvalue == 0:
xvalue = 1
@ -457,9 +483,15 @@ class ToolTransform(FlatCAMTool):
xvalue = 1
try:
yvalue = float(self.scaley_entry.get_value())
except:
self.app.inform.emit("[warning_notcl] No value for Scale!")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
yvalue = float(self.scaley_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered for Scale Y, "
"use a number.")
return
# scaling to zero has no sense so we remove it, because scaling with 1 does nothing
if yvalue == 0:
yvalue = 1
@ -480,9 +512,15 @@ class ToolTransform(FlatCAMTool):
def on_offx(self):
try:
value = float(self.offx_entry.get_value())
except:
self.app.inform.emit("[warning_notcl] No value for Offset!")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
value = float(self.offx_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered for Offset X, "
"use a number.")
return
# self.on_offset("X", value)
axis = 'X'
self.app.worker_task.emit({'fcn': self.on_offset,
@ -492,9 +530,15 @@ class ToolTransform(FlatCAMTool):
def on_offy(self):
try:
value = float(self.offy_entry.get_value())
except:
self.app.inform.emit("[warning_notcl] No value for Offset!")
return
except ValueError:
# try to convert comma to decimal point. if it's still not working error message and return
try:
value = float(self.offy_entry.get_value().replace(',', '.'))
except ValueError:
self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered for Offset Y, "
"use a number.")
return
# self.on_offset("Y", value)
axis = 'Y'
self.app.worker_task.emit({'fcn': self.on_offset,
@ -509,7 +553,7 @@ class ToolTransform(FlatCAMTool):
ymaxlist = []
if not obj_list:
self.app.inform.emit("[warning_notcl] No object selected. Please Select an object to rotate!")
self.app.inform.emit("[WARNING_NOTCL] No object selected. Please Select an object to rotate!")
return
else:
with self.app.proc_container.new("Appying Rotate"):
@ -550,7 +594,7 @@ class ToolTransform(FlatCAMTool):
self.app.progress.emit(100)
except Exception as e:
self.app.inform.emit("[error_notcl] Due of %s, rotation movement was not executed." % str(e))
self.app.inform.emit("[ERROR_NOTCL] Due of %s, rotation movement was not executed." % str(e))
return
def on_flip(self, axis):
@ -561,7 +605,7 @@ class ToolTransform(FlatCAMTool):
ymaxlist = []
if not obj_list:
self.app.inform.emit("[warning_notcl] No object selected. Please Select an object to flip!")
self.app.inform.emit("[WARNING_NOTCL] No object selected. Please Select an object to flip!")
return
else:
with self.app.proc_container.new("Applying Flip"):
@ -623,7 +667,7 @@ class ToolTransform(FlatCAMTool):
self.app.progress.emit(100)
except Exception as e:
self.app.inform.emit("[error_notcl] Due of %s, Flip action was not executed." % str(e))
self.app.inform.emit("[ERROR_NOTCL] Due of %s, Flip action was not executed." % str(e))
return
def on_skew(self, axis, num):
@ -632,7 +676,7 @@ class ToolTransform(FlatCAMTool):
yminlist = []
if not obj_list:
self.app.inform.emit("[warning_notcl] No object selected. Please Select an object to shear/skew!")
self.app.inform.emit("[WARNING_NOTCL] No object selected. Please Select an object to shear/skew!")
return
else:
with self.app.proc_container.new("Applying Skew"):
@ -670,7 +714,7 @@ class ToolTransform(FlatCAMTool):
self.app.progress.emit(100)
except Exception as e:
self.app.inform.emit("[error_notcl] Due of %s, Skew action was not executed." % str(e))
self.app.inform.emit("[ERROR_NOTCL] Due of %s, Skew action was not executed." % str(e))
return
def on_scale(self, axis, xfactor, yfactor, point=None):
@ -681,7 +725,7 @@ class ToolTransform(FlatCAMTool):
ymaxlist = []
if not obj_list:
self.app.inform.emit("[warning_notcl] No object selected. Please Select an object to scale!")
self.app.inform.emit("[WARNING_NOTCL] No object selected. Please Select an object to scale!")
return
else:
with self.app.proc_container.new("Applying Scale"):
@ -725,7 +769,7 @@ class ToolTransform(FlatCAMTool):
self.app.inform.emit('Object(s) were scaled on %s axis ...' % str(axis))
self.app.progress.emit(100)
except Exception as e:
self.app.inform.emit("[error_notcl] Due of %s, Scale action was not executed." % str(e))
self.app.inform.emit("[ERROR_NOTCL] Due of %s, Scale action was not executed." % str(e))
return
def on_offset(self, axis, num):
@ -734,7 +778,7 @@ class ToolTransform(FlatCAMTool):
yminlist = []
if not obj_list:
self.app.inform.emit("[warning_notcl] No object selected. Please Select an object to offset!")
self.app.inform.emit("[WARNING_NOTCL] No object selected. Please Select an object to offset!")
return
else:
with self.app.proc_container.new("Applying Offset"):
@ -771,7 +815,7 @@ class ToolTransform(FlatCAMTool):
self.app.progress.emit(100)
except Exception as e:
self.app.inform.emit("[error_notcl] Due of %s, Offset action was not executed." % str(e))
self.app.inform.emit("[ERROR_NOTCL] Due of %s, Offset action was not executed." % str(e))
return
# end of file

View File

@ -5,7 +5,8 @@ from flatcamTools.ToolPanelize import Panelize
from flatcamTools.ToolFilm import Film
from flatcamTools.ToolMove import ToolMove
from flatcamTools.ToolDblSided import DblSidedTool
from flatcamTools.ToolCutout import ToolCutout
from flatcamTools.ToolCutOut import ToolCutOut
from flatcamTools.ToolCalculators import ToolCalculator
from flatcamTools.ToolProperties import Properties
from flatcamTools.ToolImage import ToolImage

View File

@ -0,0 +1,261 @@
from FlatCAMPostProc import *
class Toolchange_Probe_MACH3(FlatCAMPostProc):
coordinate_format = "%.*f"
feedrate_format = '%.*f'
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['toolchange_xy']
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_Z: ' + str(p['feedrate_z']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Feedrate Probe ' + str(p['feedrate_probe']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['endz']) + units + ')\n'
gcode += '(Z Probe Depth: ' + str(p['z_pdepth']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
gcode += ('G20\n' if p.units.upper() == 'IN' else 'G21\n')
gcode += 'G90\n'
gcode += 'G17\n'
gcode += 'G94\n'
return gcode
def startz_code(self, p):
return ''
def lift_code(self, p):
return 'G00 Z' + self.coordinate_format%(p.coords_decimals, p.z_move)
def down_code(self, p):
return 'G01 Z' + self.coordinate_format%(p.coords_decimals, p.z_cut)
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
f_plunge = p.f_plunge
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
no_drills = 1
if int(p.tool) == 1 and p.startz is not None:
toolchangez = p.startz
if p.units.upper() == 'MM':
toolC_formatted = format(p.toolC, '.2f')
else:
toolC_formatted = format(p.toolC, '.4f')
if str(p['options']['type']) == 'Excellon':
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
if toolchangexy is not None:
gcode = """
T{tool}
M5
M6
G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
(MSG, Change to Tool Dia = {toolC} ||| Drills for this tool = {t_drills} ||| Tool Probing MACH3)
M0
G00 Z{z_move}
F{feedrate_probe}
G31 Z{z_pdepth}
G92 Z0
G00 Z{z_move}
F{feedrate_probe_slow}
G31 Z{z_pdepth}
G92 Z0
(MSG, Remove any clips or other devices used for probing. CNC work is resuming ...)
M0
G00 Z{z_move}
""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
z_move=self.coordinate_format % (p.coords_decimals, p.z_move),
feedrate_probe=str(self.feedrate_format %(p.fr_decimals, p.feedrate_probe)),
feedrate_probe_slow=str(self.feedrate_format % (p.fr_decimals, (p.feedrate_probe / 2))),
z_pdepth=self.coordinate_format % (p.coords_decimals, p.z_pdepth),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """
T{tool}
M5
M6
G00 Z{toolchangez}
(MSG, Change to Tool Dia = {toolC} ||| Drills for this tool = {t_drills} ||| Tool Probing MACH3)
M0
G00 Z{z_move}
F{feedrate_probe}
G31 Z{z_pdepth}
G92 Z0
G00 Z{z_move}
F{feedrate_probe_slow}
G31 Z{z_pdepth}
G92 Z0
(MSG, Remove any clips or other devices used for probing. CNC work is resuming ...)
M0
G00 Z{z_move}
""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
z_move=self.coordinate_format % (p.coords_decimals, p.z_move),
feedrate_probe=str(self.feedrate_format %(p.fr_decimals, p.feedrate_probe)),
feedrate_probe_slow=str(self.feedrate_format % (p.fr_decimals, (p.feedrate_probe / 2))),
z_pdepth=self.coordinate_format % (p.coords_decimals, p.z_pdepth),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
# if f_plunge is True:
# gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
else:
if toolchangexy is not None:
gcode = """
T{tool}
M5
M6
G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
(MSG, Change to Tool Dia = {toolC} ||| Tool Probing MACH3)
M0
G00 Z{z_move}
F{feedrate_probe}
G31 Z{z_pdepth}
G92 Z0
G00 Z{z_move}
F{feedrate_probe_slow}
G31 Z{z_pdepth}
G92 Z0
(MSG, Remove any clips or other devices used for probing. CNC work is resuming ...)
M0
G00 Z{z_move}
""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
z_move=self.coordinate_format % (p.coords_decimals, p.z_move),
feedrate_probe=str(self.feedrate_format %(p.fr_decimals, p.feedrate_probe)),
feedrate_probe_slow=str(self.feedrate_format % (p.fr_decimals, (p.feedrate_probe / 2))),
z_pdepth=self.coordinate_format % (p.coords_decimals, p.z_pdepth),
tool=int(p.tool),
toolC=toolC_formatted)
else:
gcode = """
T{tool}
M5
M6
G00 Z{toolchangez}
(MSG, Change to Tool Dia = {toolC} ||| Tool Probing MACH3)
M0
G00 Z{z_move}
F{feedrate_probe}
G31 Z{z_pdepth}
G92 Z0
G00 Z{z_move}
F{feedrate_probe_slow}
G31 Z{z_pdepth}
G92 Z0
(MSG, Remove any clips or other devices used for probing. CNC work is resuming ...)
M0
G00 Z{z_move}
""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
z_move=self.coordinate_format % (p.coords_decimals, p.z_move),
feedrate_probe=str(self.feedrate_format %(p.fr_decimals, p.feedrate_probe)),
feedrate_probe_slow=str(self.feedrate_format % (p.fr_decimals, (p.feedrate_probe / 2))),
z_pdepth=self.coordinate_format % (p.coords_decimals, p.z_pdepth),
tool=int(p.tool),
toolC=toolC_formatted)
# if f_plunge is True:
# gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
def up_to_zero_code(self, p):
return 'G01 Z0'
def position_code(self, p):
return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \
(p.coords_decimals, p.x, p.coords_decimals, p.y)
def rapid_code(self, p):
return ('G00 ' + self.position_code(p)).format(**p)
def linear_code(self, p):
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate))
def feedrate_z_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate_z))
def spindle_code(self, p):
if p.spindlespeed:
return 'M03 S' + str(p.spindlespeed)
else:
return 'M03'
def dwell_code(self, p):
if p.dwelltime:
return 'G4 P' + str(p.dwelltime)
def spindle_stop_code(self,p):
return 'M05'

View File

@ -0,0 +1,191 @@
from FlatCAMPostProc import *
class Toolchange_Probe_general(FlatCAMPostProc):
coordinate_format = "%.*f"
feedrate_format = '%.*f'
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['toolchange_xy']
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_Z: ' + str(p['feedrate_z']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['endz']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
gcode += ('G20\n' if p.units.upper() == 'IN' else 'G21\n')
gcode += 'G90\n'
gcode += 'G17\n'
gcode += 'G94\n'
return gcode
def startz_code(self, p):
return ''
def lift_code(self, p):
return 'G00 Z' + self.coordinate_format%(p.coords_decimals, p.z_move)
def down_code(self, p):
return 'G01 Z' + self.coordinate_format%(p.coords_decimals, p.z_cut)
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
f_plunge = p.f_plunge
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
no_drills = 1
if int(p.tool) == 1 and p.startz is not None:
toolchangez = p.startz
if p.units.upper() == 'MM':
toolC_formatted = format(p.toolC, '.2f')
else:
toolC_formatted = format(p.toolC, '.4f')
if str(p['options']['type']) == 'Excellon':
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
if toolchangexy is not None:
gcode = """
G00 X{toolchangex} Y{toolchangey}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0
""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0
""".format(tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
else:
if toolchangexy is not None:
gcode = """
G00 X{toolchangex} Y{toolchangey}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC})
M0
""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
tool=int(p.tool),
toolC=toolC_formatted)
else:
gcode = """
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC})
M0""".format(tool=int(p.tool),
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
def up_to_zero_code(self, p):
return 'G01 Z0'
def position_code(self, p):
return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \
(p.coords_decimals, p.x, p.coords_decimals, p.y)
def rapid_code(self, p):
return ('G00 ' + self.position_code(p)).format(**p)
def linear_code(self, p):
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate))
def feedrate_z_code(self, p):
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate_z))
def spindle_code(self, p):
if p.spindlespeed:
return 'M03 S' + str(p.spindlespeed)
else:
return 'M03'
def dwell_code(self, p):
if p.dwelltime:
return 'G4 P' + str(p.dwelltime)
def spindle_stop_code(self,p):
return 'M05'

View File

@ -1,7 +1,7 @@
from FlatCAMPostProc import *
class manual_toolchange(FlatCAMPostProc):
class Toolchange_manual(FlatCAMPostProc):
coordinate_format = "%.*f"
feedrate_format = '%.*f'
@ -11,6 +11,11 @@ class manual_toolchange(FlatCAMPostProc):
coords_xy = p['toolchange_xy']
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
@ -29,20 +34,27 @@ class manual_toolchange(FlatCAMPostProc):
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['endz']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n'
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
gcode += ('G20\n' if p.units.upper() == 'IN' else 'G21\n')
gcode += 'G90\n'
gcode += 'G17\n'
gcode += 'G94\n'
return gcode
@ -62,8 +74,15 @@ class manual_toolchange(FlatCAMPostProc):
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
f_plunge = p.f_plunge
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
# else:
# toolchangex = p.oldx
# toolchangey = p.oldy
no_drills = 1
@ -79,11 +98,13 @@ class manual_toolchange(FlatCAMPostProc):
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
return """G00 Z{toolchangez}
if toolchangexy is not None:
gcode = """G00 Z{toolchangez}
T{tool}
M5
G00 X{toolchangex} Y{toolchangey}
(MSG, Change to Tool Dia = {toolC}, Total drills for current tool = {t_drills})
M5
G00 X{toolchangex} Y{toolchangey}
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0
G01 Z0
M0
@ -95,8 +116,30 @@ M0
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """G00 Z{toolchangez}
T{tool}
M5
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0
G01 Z0
M0
G00 Z{toolchangez}
M0
""".format(
toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
else:
return """G00 Z{toolchangez}
if toolchangexy is not None:
gcode = """G00 Z{toolchangez}
T{tool}
M5
G00 X{toolchangex}Y{toolchangey}
@ -111,6 +154,23 @@ M0
toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
else:
gcode = """G00 Z{toolchangez}
T{tool}
M5
(MSG, Change to Tool Dia = {toolC})
M0
G01 Z0
M0
G00 Z{toolchangez}
M0
""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
def up_to_zero_code(self, p):
return 'G01 Z0'
@ -128,7 +188,10 @@ M0
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
else:
gcode += 'G00 X0 Y0' + "\n"
return gcode
def feedrate_code(self, p):

View File

@ -11,6 +11,11 @@ class default(FlatCAMPostProc):
coords_xy = p['toolchange_xy']
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
@ -29,7 +34,12 @@ class default(FlatCAMPostProc):
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['endz']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
@ -37,7 +47,10 @@ class default(FlatCAMPostProc):
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n'
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
@ -62,8 +75,12 @@ class default(FlatCAMPostProc):
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
f_plunge = p.f_plunge
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
no_drills = 1
@ -79,17 +96,49 @@ class default(FlatCAMPostProc):
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
return """G00 Z{toolchangez}
if toolchangexy is not None:
gcode = """G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC}, Total drills for current tool = {t_drills})
M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """G00 Z{toolchangez}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
else:
return """G00 Z{toolchangez}
if toolchangexy is not None:
gcode = """G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC})
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
else:
gcode = """G00 Z{toolchangez}
T{tool}
M5
M6
@ -98,6 +147,10 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez)
tool=int(p.tool),
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
def up_to_zero_code(self, p):
return 'G01 Z0'
@ -114,7 +167,9 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez)
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):

View File

@ -11,6 +11,11 @@ class grbl_11(FlatCAMPostProc):
coords_xy = p['toolchange_xy']
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n' + '\n'
@ -29,7 +34,10 @@ class grbl_11(FlatCAMPostProc):
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['endz']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
@ -37,7 +45,10 @@ class grbl_11(FlatCAMPostProc):
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n'
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: ' + str(p['spindlespeed']) + ' RPM' + ')\n' + '\n'
@ -62,6 +73,15 @@ class grbl_11(FlatCAMPostProc):
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
f_plunge = p.f_plunge
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
no_drills = 1
if int(p.tool) == 1 and p.startz is not None:
toolchangez = p.startz
@ -75,17 +95,50 @@ class grbl_11(FlatCAMPostProc):
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
return """G00 Z{toolchangez}
if toolchangexy is not None:
gcode = """G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC}, Total drills for current tool = {t_drills})
M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """G00 Z{toolchangez}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
else:
return """G00 Z{toolchangez}
if toolchangexy is not None:
gcode = """G00 Z{toolchangez}
G00 X{toolchangex} Y{toolchangey}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC})
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
else:
gcode = """G00 Z{toolchangez}
T{tool}
M5
M6
@ -94,6 +147,10 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez)
tool=int(p.tool),
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
def up_to_zero_code(self, p):
return 'G01 Z0'
@ -110,8 +167,10 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez)
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.endz) + "\n")
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):

View File

@ -13,6 +13,11 @@ class grbl_laser(FlatCAMPostProc):
units = ' ' + str(p['units']).lower()
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
@ -22,7 +27,11 @@ class grbl_laser(FlatCAMPostProc):
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n'
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n" + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += 'G90\n'
gcode += 'G94\n'
gcode += 'G17\n'
@ -59,8 +68,11 @@ class grbl_laser(FlatCAMPostProc):
' F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate))
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
gcode += 'G00 X0Y0'
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):

View File

@ -11,6 +11,11 @@ class line_xyz(FlatCAMPostProc):
coords_xy = p['toolchange_xy']
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
@ -29,7 +34,10 @@ class line_xyz(FlatCAMPostProc):
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['endz']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
@ -37,7 +45,10 @@ class line_xyz(FlatCAMPostProc):
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
else:
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n'
gcode += '(Postprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
@ -71,8 +82,19 @@ class line_xyz(FlatCAMPostProc):
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
f_plunge = p.f_plunge
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
else:
if str(p['options']['type']) == 'Excellon':
toolchangex = p.oldx
toolchangey = p.oldy
else:
toolchangex = p.x
toolchangey = p.y
no_drills = 1
@ -88,19 +110,26 @@ class line_xyz(FlatCAMPostProc):
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
return """G00 X{toolchangex} Y{toolchangey} Z{toolchangez}
gcode = """G00 X{toolchangex} Y{toolchangey} Z{toolchangez}
T{tool}
M5
M6
(MSG, Change to Tool Dia = {toolC}, Total drills for current tool = {t_drills})
(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
M0""".format(toolchangex=self.coordinate_format%(p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
if f_plunge is True:
gcode += """\nG00 X{toolchangex} Y{toolchangey} Z{z_move}""".format(
toolchangex=self.coordinate_format%(p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
z_move=self.coordinate_format % (p.coords_decimals, p.z_move))
return gcode
else:
return """G00 X{toolchangex} Y{toolchangey} Z{toolchangez}
gcode = """G00 X{toolchangex} Y{toolchangey} Z{toolchangez}
T{tool}
M5
M6
@ -111,6 +140,13 @@ M0""".format(toolchangex=self.coordinate_format%(p.coords_decimals, toolchangex)
tool=int(p.tool),
toolC=toolC_formatted)
if f_plunge is True:
gcode += """\nG00 X{toolchangex} Y{toolchangey} Z{z_move}""".format(
toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
z_move=self.coordinate_format % (p.coords_decimals, p.z_move))
return gcode
def up_to_zero_code(self, p):
g = 'G01 ' + 'X' + self.coordinate_format % (p.coords_decimals, p.x) + \
' Y' + self.coordinate_format % (p.coords_decimals, p.y) + \
@ -132,7 +168,11 @@ M0""".format(toolchangex=self.coordinate_format%(p.coords_decimals, toolchangex)
return g
def end_code(self, p):
g = ('G00 ' + self.position_code(p)).format(**p)
coords_xy = p['toolchange_xy']
if coords_xy is not None:
g = 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
else:
g = ('G00 ' + self.position_code(p)).format(**p)
g += ' Z' + self.coordinate_format % (p.coords_decimals, p.endz)
return g

View File

@ -9,8 +9,14 @@ class marlin(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['toolchange_xy']
gcode = ''
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += ';TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n' + '\n'
@ -29,6 +35,12 @@ class marlin(FlatCAMPostProc):
gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n'
gcode += ';Z Toolchange: ' + str(p['toolchangez']) + units + '\n'
if coords_xy is not None:
gcode += ';X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + '\n'
else:
gcode += ';X,Y Toolchange: ' + "None" + units + '\n'
gcode += ';Z Start: ' + str(p['startz']) + units + '\n'
gcode += ';Z End: ' + str(p['endz']) + units + '\n'
gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n'
@ -36,7 +48,10 @@ class marlin(FlatCAMPostProc):
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += ';Postprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n'
else:
gcode += ';Postprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n'
gcode += ';Postprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n' + '\n'
gcode += ';X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n'
gcode += ';Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n\n'
gcode += ';Spindle Speed: ' + str(p['spindlespeed']) + ' RPM' + '\n' + '\n'
@ -59,6 +74,14 @@ class marlin(FlatCAMPostProc):
def toolchange_code(self, p):
toolchangez = p.toolchangez
toolchangexy = p.toolchange_xy
f_plunge = p.f_plunge
gcode = ''
if toolchangexy is not None:
toolchangex = toolchangexy[0]
toolchangey = toolchangexy[1]
no_drills = 1
if int(p.tool) == 1 and p.startz is not None:
@ -73,20 +96,61 @@ class marlin(FlatCAMPostProc):
for i in p['options']['Tools_in_use']:
if i[0] == p.tool:
no_drills = i[2]
return """G0 Z{toolchangez}
if toolchangexy is not None:
gcode = """G0 Z{toolchangez}
G0 X{toolchangex} Y{toolchangey}
T{tool}
M5
M0 Change to Tool Dia = {toolC}, Total drills for current tool = {t_drills}
""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
M6
;MSG, Change to Tool Dia = {toolC}, Total drills for tool T{tool} = {t_drills}
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """G0 Z{toolchangez}
T{tool}
M5
M6
;MSG, Change to Tool Dia = {toolC}, Total drills for tool T{tool} = {t_drills}
M0""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG0 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
else:
return """G0 Z{toolchangez}
if toolchangexy is not None:
gcode = """G0 Z{toolchangez}
G0 X{toolchangex} Y{toolchangey}
T{tool}
M5
M0 Change to Tool Dia = {toolC}
""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
M6
;MSG, Change to Tool Dia = {toolC}
M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
else:
gcode = """G0 Z{toolchangez}
T{tool}
M5
M6
;MSG, Change to Tool Dia = {toolC}
M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
tool=int(p.tool),
toolC=toolC_formatted)
if f_plunge is True:
gcode += '\nG0 Z%.*f' % (p.coords_decimals, p.z_move)
return gcode
def up_to_zero_code(self, p):
return 'G1 Z0' + " " + self.feedrate_code(p)
@ -104,7 +168,9 @@ M0 Change to Tool Dia = {toolC}
def end_code(self, p):
coords_xy = p['toolchange_xy']
gcode = ('G0 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + " " + self.feedrate_rapid_code(p) + "\n")
gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n"
if coords_xy is not None:
gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n"
return gcode

BIN
share/fscreen32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
share/plot32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

View File

@ -55,6 +55,9 @@ class TclCommandCncjob(TclCommandSignaled):
('multidepth', 'Use or not multidepth cnccut. (True or False)'),
('depthperpass', 'Height of one layer for multidepth.'),
('extracut', 'Use or not an extra cnccut over the first point in path,in the job end (example: True)'),
('toolchange', 'Enable tool changes (example: True).'),
('toolchangez', 'Z distance for toolchange (example: 30.0).'),
('toolchangexy', 'X, Y coordonates for toolchange in format (x, y) (example: (2.0, 3.1) ).'),
('endz', 'Height where the last move will park.'),
('outname', 'Name of the resulting Geometry object.'),
('ppname_g', 'Name of the Geometry postprocessor. No quotes, case sensitive')
@ -96,6 +99,10 @@ class TclCommandCncjob(TclCommandSignaled):
args["endz"]= args["endz"] if "endz" in args else obj.options["endz"]
args["ppname_g"] = args["ppname_g"] if "ppname_g" in args else obj.options["ppname_g"]
args["toolchange"] = True if "toolchange" in args and args["toolchange"] == 1 else False
args["toolchangez"] = args["toolchangez"] if "toolchangez" in args else obj.options["toolchangez"]
args["toolchangexy"] = args["toolchangexy"] if "toolchangexy" in args else obj.options["toolchangexy"]
del args['name']
# HACK !!! Should be solved elsewhere!!!

View File

@ -56,7 +56,7 @@ class TclCommandCutout(TclCommand):
name = args['name']
else:
self.app.inform.emit(
"[warning]The name of the object for which cutout is done is missing. Add it and retry.")
"[WARNING]The name of the object for which cutout is done is missing. Add it and retry.")
return
if 'margin' in args:

View File

@ -61,7 +61,7 @@ class TclCommandCutoutAny(TclCommand):
name = args['name']
else:
self.app.inform.emit(
"[warning]The name of the object for which cutout is done is missing. Add it and retry.")
"[WARNING]The name of the object for which cutout is done is missing. Add it and retry.")
return
if 'margin' in args:
@ -91,11 +91,11 @@ class TclCommandCutoutAny(TclCommand):
return "Could not retrieve object: %s" % name
if 0 in {dia}:
self.app.inform.emit("[warning]Tool Diameter is zero value. Change it to a positive integer.")
self.app.inform.emit("[WARNING]Tool Diameter is zero value. Change it to a positive integer.")
return "Tool Diameter is zero value. Change it to a positive integer."
if gaps not in ['lr', 'tb', '2lr', '2tb', 4, 8]:
self.app.inform.emit("[warning]Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
self.app.inform.emit("[WARNING]Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
"Fill in a correct value and retry. ")
return
@ -129,7 +129,7 @@ class TclCommandCutoutAny(TclCommand):
cutout_obj = self.app.collection.get_by_name(outname)
else:
self.app.inform.emit("[error]Cancelled. Object type is not supported.")
self.app.inform.emit("[ERROR]Cancelled. Object type is not supported.")
return
try:

View File

@ -47,6 +47,7 @@ class TclCommandDrillcncjob(TclCommandSignaled):
('spindlespeed', 'Speed of the spindle in rpm (example: 4000).'),
('toolchange', 'Enable tool changes (example: True).'),
('toolchangez', 'Z distance for toolchange (example: 30.0).'),
('toolchangexy', 'X, Y coordonates for toolchange in format (x, y) (example: (2.0, 3.1) ).'),
('endz', 'Z distance at job end (example: 30.0).'),
('ppname_e', 'This is the Excellon postprocessor name: case_sensitive, no_quotes'),
('outname', 'Name of the resulting Geometry object.'),
@ -85,7 +86,8 @@ class TclCommandDrillcncjob(TclCommandSignaled):
drillz = args["drillz"] if "drillz" in args else obj.options["drillz"]
job_obj.z_move = args["travelz"] if "travelz" in args else obj.options["travelz"]
job_obj.feedrate = args["feedrate"] if "feedrate" in args else obj.options["feedrate"]
job_obj.feedrate_rapid = args["feedrate_rapid"] if "feedrate_rapid" in args else obj.options["feedrate_rapid"]
job_obj.feedrate_rapid = args["feedrate_rapid"] \
if "feedrate_rapid" in args else obj.options["feedrate_rapid"]
job_obj.spindlespeed = args["spindlespeed"] if "spindlespeed" in args else None
job_obj.pp_excellon_name = args["ppname_e"] if "ppname_e" in args \
@ -93,13 +95,15 @@ class TclCommandDrillcncjob(TclCommandSignaled):
toolchange = True if "toolchange" in args and args["toolchange"] == 1 else False
toolchangez = args["toolchangez"] if "toolchangez" in args else obj.options["toolchangez"]
job_obj.toolchangexy = args["toolchangexy"] if "toolchangexy" in args else obj.options["toolchangexy"]
endz = args["endz"] if "endz" in args else obj.options["endz"]
tools = args["tools"] if "tools" in args else 'all'
opt_type = args["opt_type"] if "opt_type" in args else 'B'
job_obj.generate_from_excellon_by_tool(obj, tools, drillz=drillz, toolchangez=toolchangez, endz=endz,
job_obj.generate_from_excellon_by_tool(obj, tools, drillz=drillz, toolchangez=toolchangez,
endz=endz,
toolchange=toolchange, excellon_optimization_type=opt_type)
job_obj.gcode_parse()
job_obj.create_geometry()

View File

@ -37,7 +37,7 @@ class TclCommandListSys(TclCommand):
'args': collections.OrderedDict([
]),
'examples': ['list_sys',
'list_sys ser'
'list_sys ser',
'list_sys gerber',
'list_sys cncj']
}

View File

@ -57,12 +57,12 @@ class TclCommandOpenGerber(TclCommandSignaled):
gerber_obj.parse_file(filename, follow=follow)
except IOError:
app_obj.inform.emit("[error_notcl] Failed to open file: %s " % filename)
app_obj.inform.emit("[ERROR_NOTCL] Failed to open file: %s " % filename)
app_obj.progress.emit(0)
self.raise_tcl_error('Failed to open file: %s' % filename)
except ParseError as e:
app_obj.inform.emit("[error_notcl] Failed to parse file: %s, %s " % (filename, str(e)))
app_obj.inform.emit("[ERROR_NOTCL] Failed to parse file: %s, %s " % (filename, str(e)))
app_obj.progress.emit(0)
self.log.error(str(e))
return

65
tests/new_window_test.py Normal file
View File

@ -0,0 +1,65 @@
import sys
from PyQt5.Qt import *
from PyQt5 import QtGui, QtWidgets
class MyPopup(QWidget):
def __init__(self):
QWidget.__init__(self)
lay = QtWidgets.QVBoxLayout()
self.setLayout(lay)
lay.setContentsMargins(0, 0, 0, 0)
le = QtWidgets.QLineEdit()
le.setText("Abracadabra")
le.setReadOnly(True)
# le.setStyleSheet("QLineEdit { qproperty-frame: false }")
le.setFrame(False)
le.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
# lay.addStretch()
but = QtWidgets.QPushButton("OK")
hlay = QtWidgets.QHBoxLayout()
hlay.setContentsMargins(0, 5, 5, 5)
hlay.addStretch()
hlay.addWidget(but)
lay.addWidget(le)
lay.addLayout(hlay)
# def paintEvent(self, e):
# dc = QtGui.QPainter(self)
# dc.drawLine(0, 0, 100, 100)
# dc.drawLine(100, 0, 0, 100)
class MainWindow(QMainWindow):
def __init__(self, *args):
QtWidgets.QMainWindow.__init__(self, *args)
self.cw = QtWidgets.QWidget(self)
self.setCentralWidget(self.cw)
self.btn1 = QtWidgets.QPushButton("Click me", self.cw)
self.btn1.setGeometry(QRect(0, 0, 100, 30))
self.btn1.clicked.connect(self.doit)
self.w = None
def doit(self):
print("Opening a new popup window...")
self.w = MyPopup()
self.w.setGeometry(QRect(100, 100, 400, 200))
self.w.show()
class App(QApplication):
def __init__(self, *args):
QtWidgets.QApplication.__init__(self, *args)
self.main = MainWindow()
# self.lastWindowClosed.connect(self.byebye)
self.main.show()
def byebye(self):
self.exit(0)
def main(args):
global app
app = App(args)
app.exec_()
if __name__ == "__main__":
main(sys.argv)