commit
c14f28d35e
951
FlatCAMApp.py
951
FlatCAMApp.py
File diff suppressed because it is too large
Load Diff
336
FlatCAMEditor.py
336
FlatCAMEditor.py
|
@ -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)
|
||||
|
||||
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)
|
||||
# never deactivate the snap toolbar - MS
|
||||
# self.app.ui.snap_toolbar.setDisabled(True) # TODO: Combine and move into tool
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
|
|
1123
FlatCAMGUI.py
1123
FlatCAMGUI.py
File diff suppressed because it is too large
Load Diff
543
FlatCAMObj.py
543
FlatCAMObj.py
File diff suppressed because it is too large
Load Diff
476
GUIElements.py
476
GUIElements.py
|
@ -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
|
||||
|
|
|
@ -46,18 +46,24 @@ 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())
|
||||
|
||||
# file drop from outside application
|
||||
if drop_indicator == QtWidgets.QAbstractItemView.OnItem:
|
||||
if self.filename == "":
|
||||
self.app.inform.emit("Open cancelled.")
|
||||
else:
|
||||
|
@ -94,9 +100,12 @@ class KeySensitiveListView(QtWidgets.QTreeView):
|
|||
self.app.open_project(self.filename)
|
||||
else:
|
||||
event.ignore()
|
||||
else:
|
||||
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:
|
||||
|
@ -573,35 +614,23 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
|
||||
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
|
||||
|
|
91
ObjectUI.py
91
ObjectUI.py
|
@ -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')
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
86
README.md
86
README.md
|
@ -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
|
||||
|
|
362
camlib.py
362
camlib.py
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
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,13 +4993,16 @@ class CNCjob(Geometry):
|
|||
self.gcode = self.doformat(p.start_code)
|
||||
|
||||
self.gcode += self.doformat(p.feedrate_code) # sets the feed rate
|
||||
|
||||
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:
|
||||
# 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
|
||||
|
@ -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
|
||||
|
||||
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,17 +5186,22 @@ 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:
|
||||
# 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":
|
||||
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:
|
||||
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:
|
||||
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)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
|
|
@ -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:
|
||||
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
|
|
@ -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.")
|
||||
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.")
|
||||
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.")
|
||||
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.")
|
||||
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.")
|
||||
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.")
|
||||
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
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
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()))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()):
|
||||
|
||||
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()))
|
||||
|
||||
|
|
|
@ -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()):
|
||||
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()))
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
"""
|
||||
|
|
|
@ -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,18 +376,16 @@ 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))
|
||||
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]})
|
||||
|
@ -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!")
|
||||
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!")
|
||||
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!")
|
||||
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!")
|
||||
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!")
|
||||
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!")
|
||||
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
|
|
@ -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
|
||||
|
|
|
@ -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'
|
|
@ -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'
|
|
@ -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'
|
||||
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
|
||||
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})
|
||||
(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:
|
||||
return """G00 Z{toolchangez}
|
||||
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:
|
||||
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")
|
||||
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):
|
|
@ -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'
|
||||
|
||||
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,6 +75,10 @@ class default(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]
|
||||
|
||||
|
@ -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:
|
||||
return """G00 Z{toolchangez}
|
||||
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:
|
||||
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,6 +167,8 @@ 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")
|
||||
|
||||
if coords_xy is not None:
|
||||
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
|
||||
return gcode
|
||||
|
||||
|
|
|
@ -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'
|
||||
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:
|
||||
return """G00 Z{toolchangez}
|
||||
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:
|
||||
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,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 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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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'
|
||||
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
|
||||
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,6 +168,10 @@ M0""".format(toolchangex=self.coordinate_format%(p.coords_decimals, toolchangex)
|
|||
return g
|
||||
|
||||
def end_code(self, 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
|
||||
|
|
|
@ -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),
|
||||
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:
|
||||
return """G0 Z{toolchangez}
|
||||
gcode = """G0 Z{toolchangez}
|
||||
T{tool}
|
||||
M5
|
||||
M0 Change to Tool Dia = {toolC}
|
||||
""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
|
||||
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:
|
||||
if toolchangexy is not None:
|
||||
gcode = """G0 Z{toolchangez}
|
||||
G0 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 = """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,6 +168,8 @@ 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")
|
||||
|
||||
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
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 267 B |
|
@ -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!!!
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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']
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue