commit
13cae5d0be
|
@ -6,6 +6,7 @@ from FlatCAMApp import App
|
|||
from multiprocessing import freeze_support
|
||||
import VisPyPatches
|
||||
|
||||
|
||||
if sys.platform == "win32":
|
||||
# cx_freeze 'module win32' workaround
|
||||
import OpenGL.platform.win32
|
||||
|
|
1101
FlatCAMApp.py
1101
FlatCAMApp.py
File diff suppressed because it is too large
Load Diff
134
FlatCAMEditor.py
134
FlatCAMEditor.py
|
@ -291,7 +291,7 @@ class TextInputTool(FlatCAMTool):
|
|||
font_name=self.font_name,
|
||||
font_size=font_to_geo_size,
|
||||
font_type=font_to_geo_type,
|
||||
units=self.app.general_options_form.general_group.units_radio.get_value().upper())
|
||||
units=self.app.general_options_form.general_app_group.units_radio.get_value().upper())
|
||||
|
||||
def font_family(self, font):
|
||||
self.text_input_entry.selectAll()
|
||||
|
@ -613,6 +613,7 @@ class FCCircle(FCShapeTool):
|
|||
radius = distance(p1, p2)
|
||||
self.geometry = DrawToolShape(Point(p1).buffer(radius, int(self.steps_per_circ / 4)))
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Adding Circle completed.")
|
||||
|
||||
|
||||
class FCArc(FCShapeTool):
|
||||
|
@ -794,6 +795,7 @@ class FCArc(FCShapeTool):
|
|||
self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
|
||||
self.direction, self.steps_per_circ)))
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Arc completed.")
|
||||
|
||||
|
||||
class FCRectangle(FCShapeTool):
|
||||
|
@ -831,6 +833,7 @@ class FCRectangle(FCShapeTool):
|
|||
# self.geometry = LinearRing([p1, (p2[0], p1[1]), p2, (p1[0], p2[1])])
|
||||
self.geometry = DrawToolShape(Polygon([p1, (p2[0], p1[1]), p2, (p1[0], p2[1])]))
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Rectangle completed.")
|
||||
|
||||
|
||||
class FCPolygon(FCShapeTool):
|
||||
|
@ -869,6 +872,7 @@ class FCPolygon(FCShapeTool):
|
|||
self.geometry = DrawToolShape(Polygon(self.points))
|
||||
self.draw_app.in_action = False
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Polygon completed.")
|
||||
|
||||
def on_key(self, key):
|
||||
if key == 'backspace':
|
||||
|
@ -885,6 +889,7 @@ class FCPath(FCPolygon):
|
|||
self.geometry = DrawToolShape(LineString(self.points))
|
||||
self.draw_app.in_action = False
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Path completed.")
|
||||
|
||||
def utility_geometry(self, data=None):
|
||||
if len(self.points) > 0:
|
||||
|
@ -1143,6 +1148,7 @@ class FCMove(FCShapeTool):
|
|||
self.start_msg = "Click on reference point."
|
||||
|
||||
def set_origin(self, origin):
|
||||
self.draw_app.app.inform.emit("Click on destination point.")
|
||||
self.origin = origin
|
||||
|
||||
def click(self, point):
|
||||
|
@ -1173,6 +1179,7 @@ class FCMove(FCShapeTool):
|
|||
# self.draw_app.set_selected(g)
|
||||
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Geometry(s) Move completed.")
|
||||
|
||||
def utility_geometry(self, data=None):
|
||||
"""
|
||||
|
@ -1208,6 +1215,7 @@ class FCCopy(FCMove):
|
|||
self.geometry = [DrawToolShape(affinity.translate(geom.geo, xoff=dx, yoff=dy))
|
||||
for geom in self.draw_app.get_selected()]
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Geometry(s) Copy completed.")
|
||||
|
||||
|
||||
class FCText(FCShapeTool):
|
||||
|
@ -1241,6 +1249,7 @@ class FCText(FCShapeTool):
|
|||
self.text_gui.text_path = []
|
||||
self.text_gui.hide_tool()
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Adding Text completed.")
|
||||
|
||||
def utility_geometry(self, data=None):
|
||||
"""
|
||||
|
@ -1281,6 +1290,7 @@ class FCBuffer(FCShapeTool):
|
|||
self.draw_app.buffer(buffer_distance, join_style)
|
||||
self.app.ui.notebook.setTabText(2, "Tools")
|
||||
self.disactivate()
|
||||
self.draw_app.app.inform.emit("[success]Done. Buffer Tool completed.")
|
||||
|
||||
def on_buffer_int(self):
|
||||
buffer_distance = self.buff_tool.buffer_distance_entry.get_value()
|
||||
|
@ -1290,6 +1300,7 @@ class FCBuffer(FCShapeTool):
|
|||
self.draw_app.buffer_int(buffer_distance, join_style)
|
||||
self.app.ui.notebook.setTabText(2, "Tools")
|
||||
self.disactivate()
|
||||
self.draw_app.app.inform.emit("[success]Done. Buffer Int Tool completed.")
|
||||
|
||||
def on_buffer_ext(self):
|
||||
buffer_distance = self.buff_tool.buffer_distance_entry.get_value()
|
||||
|
@ -1299,6 +1310,7 @@ class FCBuffer(FCShapeTool):
|
|||
self.draw_app.buffer_ext(buffer_distance, join_style)
|
||||
self.app.ui.notebook.setTabText(2, "Tools")
|
||||
self.disactivate()
|
||||
self.draw_app.app.inform.emit("[success]Done. Buffer Ext Tool completed.")
|
||||
|
||||
def activate(self):
|
||||
self.buff_tool.buffer_button.clicked.disconnect()
|
||||
|
@ -1360,6 +1372,7 @@ class FCRotate(FCShapeTool):
|
|||
# Delete old
|
||||
self.draw_app.delete_selected()
|
||||
self.complete = True
|
||||
self.draw_app.app.inform.emit("[success]Done. Geometry rotate completed.")
|
||||
|
||||
# MS: automatically select the Select Tool after finishing the action but is not working yet :(
|
||||
#self.draw_app.select_tool("select")
|
||||
|
@ -1901,6 +1914,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
self.app.ui.geo_cutpath_btn.triggered.connect(self.cutpath)
|
||||
self.app.ui.geo_delete_btn.triggered.connect(self.on_delete_btn)
|
||||
|
||||
self.app.ui.geo_move_menuitem.triggered.connect(self.on_move)
|
||||
self.app.ui.geo_cornersnap_menuitem.triggered.connect(self.on_corner_snap)
|
||||
|
||||
## Toolbar events and properties
|
||||
self.tools = {
|
||||
"select": {"button": self.app.ui.geo_select_btn,
|
||||
|
@ -2002,7 +2018,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
try:
|
||||
self.options[option] = float(entry.text())
|
||||
except Exception as e:
|
||||
log.debug(str(e))
|
||||
log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e))
|
||||
return
|
||||
|
||||
self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator())
|
||||
self.app.ui.grid_gap_x_entry.textChanged.connect(
|
||||
|
@ -2565,6 +2582,15 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
self.on_tool_select('rotate')
|
||||
self.active_tool.set_origin(self.snap(self.x, self.y))
|
||||
|
||||
if event.key == '1':
|
||||
self.app.on_zoom_fit(None)
|
||||
|
||||
if event.key == '2':
|
||||
self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y])
|
||||
|
||||
if event.key == '3':
|
||||
self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y])
|
||||
|
||||
# Arc Tool
|
||||
if event.key.name == 'A':
|
||||
self.select_tool('arc')
|
||||
|
@ -2580,6 +2606,22 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
self.active_tool.set_origin(self.snap(self.x, self.y))
|
||||
self.app.inform.emit("Click on target point.")
|
||||
|
||||
# Substract Tool
|
||||
if event.key.name == 'E':
|
||||
if self.get_selected() is not None:
|
||||
self.intersection()
|
||||
else:
|
||||
msg = "Please select geometry items \n" \
|
||||
"on which to perform Intersection Tool."
|
||||
|
||||
messagebox =QtWidgets.QMessageBox()
|
||||
messagebox.setText(msg)
|
||||
messagebox.setWindowTitle("Warning")
|
||||
messagebox.setWindowIcon(QtGui.QIcon('share/warning.png'))
|
||||
messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
messagebox.exec_()
|
||||
|
||||
# Grid Snap
|
||||
if event.key.name == 'G':
|
||||
self.app.ui.grid_snap_btn.trigger()
|
||||
|
@ -2596,14 +2638,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
|
||||
# Corner Snap
|
||||
if event.key.name == 'K':
|
||||
self.app.ui.corner_snap_btn.trigger()
|
||||
self.on_corner_snap()
|
||||
|
||||
# Move
|
||||
if event.key.name == 'M':
|
||||
self.app.ui.geo_move_btn.setChecked(True)
|
||||
self.on_tool_select('move')
|
||||
self.active_tool.set_origin(self.snap(self.x, self.y))
|
||||
self.app.inform.emit("Click on target point.")
|
||||
self.on_move_click()
|
||||
|
||||
# Polygon Tool
|
||||
if event.key.name == 'N':
|
||||
|
@ -2621,14 +2660,42 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
if event.key.name == 'R':
|
||||
self.select_tool('rectangle')
|
||||
|
||||
# Select Tool
|
||||
# Substract Tool
|
||||
if event.key.name == 'S':
|
||||
self.select_tool('select')
|
||||
if self.get_selected() is not None:
|
||||
self.subtract()
|
||||
else:
|
||||
msg = "Please select geometry items \n" \
|
||||
"on which to perform Substraction Tool."
|
||||
|
||||
messagebox =QtWidgets.QMessageBox()
|
||||
messagebox.setText(msg)
|
||||
messagebox.setWindowTitle("Warning")
|
||||
messagebox.setWindowIcon(QtGui.QIcon('share/warning.png'))
|
||||
messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
messagebox.exec_()
|
||||
|
||||
# Add Text Tool
|
||||
if event.key.name == 'T':
|
||||
self.select_tool('text')
|
||||
|
||||
# Substract Tool
|
||||
if event.key.name == 'U':
|
||||
if self.get_selected() is not None:
|
||||
self.union()
|
||||
else:
|
||||
msg = "Please select geometry items \n" \
|
||||
"on which to perform union."
|
||||
|
||||
messagebox =QtWidgets.QMessageBox()
|
||||
messagebox.setText(msg)
|
||||
messagebox.setWindowTitle("Warning")
|
||||
messagebox.setWindowIcon(QtGui.QIcon('share/warning.png'))
|
||||
messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok)
|
||||
messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok)
|
||||
messagebox.exec_()
|
||||
|
||||
# Cut Action Tool
|
||||
if event.key.name == 'X':
|
||||
if self.get_selected() is not None:
|
||||
|
@ -2661,11 +2728,15 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
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>G:</b> Paint Tool<br>
|
||||
<b>I:</b> Paint Tool<br>
|
||||
<b>K:</b> Corner Snap On/Off<br>
|
||||
<b>M:</b> Move Geo Item<br>
|
||||
<br>
|
||||
|
@ -2673,8 +2744,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
<b>O:</b> Add a 'Circle'<br>
|
||||
<b>P:</b> Add a 'Path'<br>
|
||||
<b>R:</b> Add an 'Rectangle'<br>
|
||||
<b>S:</b> Select Tool Active<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>
|
||||
|
@ -2682,7 +2754,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
<br>
|
||||
<b>Space:</b> Rotate selected Geometry<br>
|
||||
<b>Enter:</b> Finish Current Action<br>
|
||||
<b>Escape:</b> Abort Current Action<br>
|
||||
<b>Escape:</b> Select Tool (Exit any other Tool)<br>
|
||||
<b>Delete:</b> Delete Obj'''
|
||||
|
||||
helpbox =QtWidgets.QMessageBox()
|
||||
|
@ -2718,6 +2790,17 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
if shape in self.selected:
|
||||
self.selected.remove(shape) # TODO: Check performance
|
||||
|
||||
def on_move(self):
|
||||
self.app.ui.geo_move_btn.setChecked(True)
|
||||
self.on_tool_select('move')
|
||||
|
||||
def on_move_click(self):
|
||||
self.on_move()
|
||||
self.active_tool.set_origin(self.snap(self.x, self.y))
|
||||
|
||||
def on_corner_snap(self):
|
||||
self.app.ui.corner_snap_btn.trigger()
|
||||
|
||||
def get_selected(self):
|
||||
"""
|
||||
Returns list of shapes that are selected in the editor.
|
||||
|
@ -2950,7 +3033,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
|
|||
|
||||
shapes = self.get_selected()
|
||||
|
||||
try:
|
||||
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.select_tool('select')
|
||||
return
|
||||
|
||||
for shape in shapes[1:]:
|
||||
results = results.intersection(shape.geo)
|
||||
|
@ -3638,7 +3727,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.move_timer.setSingleShot(True)
|
||||
|
||||
## Current application units in Upper Case
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
self.key = None # Currently pressed key
|
||||
self.modifiers = None
|
||||
|
@ -3712,7 +3801,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
|
||||
def set_ui(self):
|
||||
# updated units
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
self.olddia_newdia.clear()
|
||||
self.tool2tooldia.clear()
|
||||
|
@ -3752,7 +3841,7 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
pass
|
||||
|
||||
# updated units
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
# make a new name for the new Excellon object (the one with edited content)
|
||||
self.edited_obj_name = self.exc_obj.options['name']
|
||||
|
@ -4744,6 +4833,18 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
self.app.inform.emit("[warning_notcl]Cancelled. Nothing selected to delete.")
|
||||
return
|
||||
|
||||
if event.key == '1':
|
||||
self.launched_from_shortcuts = True
|
||||
self.app.on_zoom_fit(None)
|
||||
|
||||
if event.key == '2':
|
||||
self.launched_from_shortcuts = True
|
||||
self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y])
|
||||
|
||||
if event.key == '3':
|
||||
self.launched_from_shortcuts = True
|
||||
self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'], [self.snap_x, self.snap_y])
|
||||
|
||||
# Add Array of Drill Hole Tool
|
||||
if event.key.name == 'A':
|
||||
self.launched_from_shortcuts = True
|
||||
|
@ -4828,6 +4929,9 @@ class FlatCAMExcEditor(QtCore.QObject):
|
|||
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>
|
||||
|
|
1302
FlatCAMGUI.py
1302
FlatCAMGUI.py
File diff suppressed because it is too large
Load Diff
214
FlatCAMObj.py
214
FlatCAMObj.py
|
@ -366,6 +366,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
|
||||
if grb_final.solid_geometry is None:
|
||||
grb_final.solid_geometry = []
|
||||
|
||||
if type(grb_final.solid_geometry) is not list:
|
||||
grb_final.solid_geometry = [grb_final.solid_geometry]
|
||||
|
||||
|
@ -380,10 +381,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
# Expand lists
|
||||
if type(grb) is list:
|
||||
FlatCAMGerber.merge(grb, grb_final)
|
||||
else: # If not list, just append
|
||||
for geos in grb.solid_geometry:
|
||||
grb_final.solid_geometry.append(geos)
|
||||
|
||||
# If not list, just append
|
||||
else:
|
||||
grb_final.solid_geometry.append(grb.solid_geometry)
|
||||
grb_final.solid_geometry = MultiPolygon(grb_final.solid_geometry)
|
||||
|
||||
def __init__(self, name):
|
||||
Gerber.__init__(self, steps_per_circle=self.app.defaults["gerber_circle_steps"])
|
||||
|
@ -402,9 +404,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
"isooverlap": 0.15,
|
||||
"milling_type": "cl",
|
||||
"combine_passes": True,
|
||||
"ncctools": "1.0, 0.5",
|
||||
"nccoverlap": 0.4,
|
||||
"nccmargin": 1,
|
||||
"noncoppermargin": 0.0,
|
||||
"noncopperrounded": False,
|
||||
"bboxmargin": 0.0,
|
||||
|
@ -755,9 +754,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
|
|||
factor = Gerber.convert_units(self, units)
|
||||
|
||||
self.options['isotooldia'] *= factor
|
||||
self.options['cutoutmargin'] *= factor
|
||||
self.options['cutoutgapsize'] *= factor
|
||||
self.options['noncoppermargin'] *= factor
|
||||
self.options['bboxmargin'] *= factor
|
||||
|
||||
def plot(self, **kwargs):
|
||||
|
@ -899,14 +895,21 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
:return: None
|
||||
"""
|
||||
|
||||
# flag to signal that we need to reorder the tools dictionary and drills and slots lists
|
||||
flag_order = False
|
||||
|
||||
try:
|
||||
flattened_list = list(itertools.chain(*exc_list))
|
||||
except TypeError:
|
||||
flattened_list = exc_list
|
||||
|
||||
# this dict will hold the unique tool diameters found in the exc_list objects as the dict keys and the dict
|
||||
# values will be list of Shapely Points
|
||||
custom_dict = {}
|
||||
# values will be list of Shapely Points; for drills
|
||||
custom_dict_drills = {}
|
||||
|
||||
# this dict will hold the unique tool diameters found in the exc_list objects as the dict keys and the dict
|
||||
# values will be list of Shapely Points; for slots
|
||||
custom_dict_slots = {}
|
||||
|
||||
for exc in flattened_list:
|
||||
# copy options of the current excellon obj to the final excellon obj
|
||||
|
@ -920,10 +923,18 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
for drill in exc.drills:
|
||||
exc_tool_dia = float('%.3f' % exc.tools[drill['tool']]['C'])
|
||||
|
||||
if exc_tool_dia not in custom_dict:
|
||||
custom_dict[exc_tool_dia] = [drill['point']]
|
||||
if exc_tool_dia not in custom_dict_drills:
|
||||
custom_dict_drills[exc_tool_dia] = [drill['point']]
|
||||
else:
|
||||
custom_dict[exc_tool_dia].append(drill['point'])
|
||||
custom_dict_drills[exc_tool_dia].append(drill['point'])
|
||||
|
||||
for slot in exc.slots:
|
||||
exc_tool_dia = float('%.3f' % exc.tools[slot['tool']]['C'])
|
||||
|
||||
if exc_tool_dia not in custom_dict_slots:
|
||||
custom_dict_slots[exc_tool_dia] = [[slot['start'], slot['stop']]]
|
||||
else:
|
||||
custom_dict_slots[exc_tool_dia].append([slot['start'], slot['stop']])
|
||||
|
||||
# add the zeros and units to the exc_final object
|
||||
exc_final.zeros = exc.zeros
|
||||
|
@ -931,10 +942,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
|
||||
# variable to make tool_name for the tools
|
||||
current_tool = 0
|
||||
|
||||
# Here we add data to the exc_final object
|
||||
# the tools diameter are now the keys in the drill_dia dict and the values are the Shapely Points
|
||||
for tool_dia in custom_dict:
|
||||
# the tools diameter are now the keys in the drill_dia dict and the values are the Shapely Points in case of
|
||||
# drills
|
||||
for tool_dia in custom_dict_drills:
|
||||
# we create a tool name for each key in the drill_dia dict (the key is a unique drill diameter)
|
||||
current_tool += 1
|
||||
|
||||
|
@ -943,7 +954,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
exc_final.tools[tool_name] = spec
|
||||
|
||||
# rebuild the drills list of dict's that belong to the exc_final object
|
||||
for point in custom_dict[tool_dia]:
|
||||
for point in custom_dict_drills[tool_dia]:
|
||||
exc_final.drills.append(
|
||||
{
|
||||
"point": point,
|
||||
|
@ -951,6 +962,94 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
}
|
||||
)
|
||||
|
||||
# Here we add data to the exc_final object
|
||||
# the tools diameter are now the keys in the drill_dia dict and the values are a list ([start, stop])
|
||||
# of two Shapely Points in case of slots
|
||||
for tool_dia in custom_dict_slots:
|
||||
# we create a tool name for each key in the slot_dia dict (the key is a unique slot diameter)
|
||||
# but only if there are no drills
|
||||
if not exc_final.tools:
|
||||
current_tool += 1
|
||||
tool_name = str(current_tool)
|
||||
spec = {"C": float(tool_dia)}
|
||||
exc_final.tools[tool_name] = spec
|
||||
else:
|
||||
dia_list = []
|
||||
for v in exc_final.tools.values():
|
||||
dia_list.append(float(v["C"]))
|
||||
|
||||
if tool_dia not in dia_list:
|
||||
flag_order = True
|
||||
|
||||
current_tool = len(dia_list) + 1
|
||||
tool_name = str(current_tool)
|
||||
spec = {"C": float(tool_dia)}
|
||||
exc_final.tools[tool_name] = spec
|
||||
|
||||
else:
|
||||
for k, v in exc_final.tools.items():
|
||||
if v["C"] == tool_dia:
|
||||
current_tool = int(k)
|
||||
break
|
||||
|
||||
# rebuild the slots list of dict's that belong to the exc_final object
|
||||
for point in custom_dict_slots[tool_dia]:
|
||||
exc_final.slots.append(
|
||||
{
|
||||
"start": point[0],
|
||||
"stop": point[1],
|
||||
"tool": str(current_tool)
|
||||
}
|
||||
)
|
||||
|
||||
# flag_order == True means that there was an slot diameter not in the tools and we also have drills
|
||||
# and the new tool was added to self.tools therefore we need to reorder the tools and drills and slots
|
||||
current_tool = 0
|
||||
if flag_order is True:
|
||||
dia_list = []
|
||||
temp_drills = []
|
||||
temp_slots = []
|
||||
temp_tools = {}
|
||||
for v in exc_final.tools.values():
|
||||
dia_list.append(float(v["C"]))
|
||||
dia_list.sort()
|
||||
for ordered_dia in dia_list:
|
||||
current_tool += 1
|
||||
tool_name_temp = str(current_tool)
|
||||
spec_temp = {"C": float(ordered_dia)}
|
||||
temp_tools[tool_name_temp] = spec_temp
|
||||
|
||||
for drill in exc_final.drills:
|
||||
exc_tool_dia = float('%.3f' % exc_final.tools[drill['tool']]['C'])
|
||||
if exc_tool_dia == ordered_dia:
|
||||
temp_drills.append(
|
||||
{
|
||||
"point": drill["point"],
|
||||
"tool": str(current_tool)
|
||||
}
|
||||
)
|
||||
|
||||
for slot in exc_final.slots:
|
||||
slot_tool_dia = float('%.3f' % exc_final.tools[slot['tool']]['C'])
|
||||
if slot_tool_dia == ordered_dia:
|
||||
temp_slots.append(
|
||||
{
|
||||
"start": slot["start"],
|
||||
"stop": slot["stop"],
|
||||
"tool": str(current_tool)
|
||||
}
|
||||
)
|
||||
|
||||
# delete the exc_final tools, drills and slots
|
||||
exc_final.tools = dict()
|
||||
exc_final.drills[:] = []
|
||||
exc_final.slots[:] = []
|
||||
|
||||
# update the exc_final tools, drills and slots with the ordered values
|
||||
exc_final.tools = temp_tools
|
||||
exc_final.drills[:] = temp_drills
|
||||
exc_final.slots[:] = temp_slots
|
||||
|
||||
# create the geometry for the exc_final object
|
||||
exc_final.create_geometry()
|
||||
|
||||
|
@ -1192,7 +1291,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
"""
|
||||
|
||||
excellon_code = ''
|
||||
units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
# store here if the file has slots, return 1 if any slots, 0 if only drills
|
||||
has_slots = 0
|
||||
|
@ -1252,7 +1351,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
|
|||
"""
|
||||
|
||||
excellon_code = ''
|
||||
units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
# store here if the file has slots, return 1 if any slots, 0 if only drills
|
||||
has_slots = 0
|
||||
|
@ -2759,7 +2858,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.ui.geo_tools_table.setCurrentItem(self.ui.geo_tools_table.item(row, 0))
|
||||
|
||||
def export_dxf(self):
|
||||
units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
dwg = None
|
||||
try:
|
||||
dwg = ezdxf.new('R2010')
|
||||
|
@ -2859,7 +2958,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
else:
|
||||
self.app.inform.emit("[error_notcl] Failed. No tool selected in the tool table ...")
|
||||
|
||||
def mtool_gen_cncjob(self, use_thread=True):
|
||||
def mtool_gen_cncjob(self, segx=None, segy=None, use_thread=True):
|
||||
"""
|
||||
Creates a multi-tool CNCJob out of this Geometry object.
|
||||
The actual work is done by the target FlatCAMCNCjob object's
|
||||
|
@ -2883,6 +2982,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia
|
||||
outname = "%s_%s" % (self.options["name"], 'cnc')
|
||||
|
||||
segx = segx if segx is not None else float(self.app.defaults['geometry_segx'])
|
||||
segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
|
||||
|
||||
# Object initialization function for app.new_object()
|
||||
# RUNNING ON SEPARATE THREAD!
|
||||
def job_init_single_geometry(job_obj, app_obj):
|
||||
|
@ -2901,6 +3003,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
# job_obj.create_geometry()
|
||||
|
||||
job_obj.options['Tools_in_use'] = self.get_selected_tools_table_items()
|
||||
job_obj.segx = segx
|
||||
job_obj.segy = segy
|
||||
|
||||
for tooluid_key in self.sel_tools:
|
||||
tool_cnt += 1
|
||||
|
@ -3242,6 +3346,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
toolchange=None, toolchangez=None, toolchangexy=None,
|
||||
extracut=None, startz=None, endz=None,
|
||||
ppname_g=None,
|
||||
segx=None,
|
||||
segy=None,
|
||||
use_thread=True):
|
||||
"""
|
||||
Only used for TCL Command.
|
||||
|
@ -3273,6 +3379,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
multidepth = multidepth if multidepth is not None else self.options["multidepth"]
|
||||
depthperpass = depthperpass if depthperpass is not None else self.options["depthperpass"]
|
||||
|
||||
segx = segx if segx is not None else float(self.app.defaults['geometry_segx'])
|
||||
segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
|
||||
|
||||
extracut = extracut if extracut is not None else self.options["extracut"]
|
||||
startz = startz if startz is not None else self.options["startz"]
|
||||
endz = endz if endz is not None else self.options["endz"]
|
||||
|
@ -3307,6 +3416,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
job_obj.options['type'] = 'Geometry'
|
||||
job_obj.options['tool_dia'] = tooldia
|
||||
|
||||
job_obj.segx = segx
|
||||
job_obj.segy = segy
|
||||
|
||||
# TODO: The tolerance should not be hard coded. Just for testing.
|
||||
job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=0.0005,
|
||||
z_cut=z_cut, z_move=z_move,
|
||||
|
@ -3358,8 +3470,20 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
:rtype: None
|
||||
"""
|
||||
|
||||
try:
|
||||
xfactor = float(xfactor)
|
||||
except:
|
||||
self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.")
|
||||
return
|
||||
|
||||
if yfactor is None:
|
||||
yfactor = xfactor
|
||||
else:
|
||||
try:
|
||||
yfactor = float(yfactor)
|
||||
except:
|
||||
self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.")
|
||||
return
|
||||
|
||||
if point is None:
|
||||
px = 0
|
||||
|
@ -3367,16 +3491,33 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
else:
|
||||
px, py = point
|
||||
|
||||
if type(self.solid_geometry) == list:
|
||||
geo_list = self.flatten(self.solid_geometry)
|
||||
self.solid_geometry = []
|
||||
# for g in geo_list:
|
||||
# self.solid_geometry.append(affinity.scale(g, xfactor, yfactor, origin=(px, py)))
|
||||
self.solid_geometry = [affinity.scale(g, xfactor, yfactor, origin=(px, py))
|
||||
for g in geo_list]
|
||||
# if type(self.solid_geometry) == list:
|
||||
# geo_list = self.flatten(self.solid_geometry)
|
||||
# self.solid_geometry = []
|
||||
# # for g in geo_list:
|
||||
# # self.solid_geometry.append(affinity.scale(g, xfactor, yfactor, origin=(px, py)))
|
||||
# self.solid_geometry = [affinity.scale(g, xfactor, yfactor, origin=(px, py))
|
||||
# for g in geo_list]
|
||||
# else:
|
||||
# self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor,
|
||||
# origin=(px, py))
|
||||
# self.app.inform.emit("[success]Geometry Scale done.")
|
||||
|
||||
def scale_recursion(geom):
|
||||
if type(geom) == list:
|
||||
geoms=list()
|
||||
for local_geom in geom:
|
||||
geoms.append(scale_recursion(local_geom))
|
||||
return geoms
|
||||
else:
|
||||
self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor,
|
||||
origin=(px, py))
|
||||
return affinity.scale(geom, xfactor, yfactor, origin=(px, py))
|
||||
|
||||
if self.multigeo is True:
|
||||
for tool in self.tools:
|
||||
self.tools[tool]['solid_geometry'] = scale_recursion(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
self.solid_geometry=scale_recursion(self.solid_geometry)
|
||||
self.app.inform.emit("[success]Geometry Scale done.")
|
||||
|
||||
def offset(self, vect):
|
||||
"""
|
||||
|
@ -3388,7 +3529,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
:rtype: None
|
||||
"""
|
||||
|
||||
try:
|
||||
dx, dy = vect
|
||||
except TypeError:
|
||||
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
|
||||
|
||||
def translate_recursion(geom):
|
||||
if type(geom) == list:
|
||||
|
@ -3404,6 +3550,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
|
|||
self.tools[tool]['solid_geometry'] = translate_recursion(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
self.solid_geometry=translate_recursion(self.solid_geometry)
|
||||
self.app.inform.emit("[success]Geometry Offset done.")
|
||||
|
||||
def convert_units(self, units):
|
||||
self.ui_disconnect()
|
||||
|
@ -4042,12 +4189,15 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
|
|||
cw_row = cw_index.row()
|
||||
|
||||
self.shapes.clear(update=True)
|
||||
|
||||
for tooluid_key in self.cnc_tools:
|
||||
tooldia = float('%.4f' % float(self.cnc_tools[tooluid_key]['tooldia']))
|
||||
gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed']
|
||||
# tool_uid = int(self.ui.cnc_tools_table.item(cw_row, 3).text())
|
||||
|
||||
if self.ui.cnc_tools_table.cellWidget((tooluid_key - 1), 6).isChecked():
|
||||
for r in range(self.ui.cnc_tools_table.rowCount()):
|
||||
if int(self.ui.cnc_tools_table.item(r, 5).text()) == int(tooluid_key):
|
||||
if self.ui.cnc_tools_table.cellWidget(r, 6).isChecked():
|
||||
self.plot2(tooldia=tooldia, obj=self, visible=True, gcode_parsed=gcode_parsed)
|
||||
|
||||
self.shapes.redraw()
|
||||
|
|
|
@ -33,7 +33,7 @@ class FlatCAMTool(QtWidgets.QWidget):
|
|||
|
||||
self.menuAction = None
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
def install(self, icon=None, separator=None, shortcut=None, **kwargs):
|
||||
before = None
|
||||
|
||||
# 'pos' is the menu where the Action has to be installed
|
||||
|
@ -54,8 +54,13 @@ class FlatCAMTool(QtWidgets.QWidget):
|
|||
# if provided, add an icon to this Action
|
||||
if icon is not None:
|
||||
self.menuAction.setIcon(icon)
|
||||
|
||||
# set the text name of the Action, which will be displayed in the menu
|
||||
if shortcut is None:
|
||||
self.menuAction.setText(self.toolName)
|
||||
else:
|
||||
self.menuAction.setText(self.toolName + '\t%s' % shortcut)
|
||||
|
||||
# add a ToolTip to the new Action
|
||||
# self.menuAction.setToolTip(self.toolTip) # currently not available
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import inspect # TODO: Remove
|
|||
import FlatCAMApp
|
||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
||||
from PyQt5.QtCore import Qt
|
||||
import webbrowser
|
||||
|
||||
|
||||
class KeySensitiveListView(QtWidgets.QTreeView):
|
||||
|
@ -262,6 +263,9 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
if key == QtCore.Qt.Key_G:
|
||||
self.app.on_fileopengerber()
|
||||
|
||||
if key == QtCore.Qt.Key_N:
|
||||
self.app.on_file_new_click()
|
||||
|
||||
if key == QtCore.Qt.Key_M:
|
||||
self.app.measurement_tool.run()
|
||||
if key == QtCore.Qt.Key_O:
|
||||
|
@ -272,6 +276,11 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
return
|
||||
elif modifiers == QtCore.Qt.ShiftModifier:
|
||||
|
||||
# Copy Object Name
|
||||
# Copy Object Name
|
||||
if key == QtCore.Qt.Key_C:
|
||||
self.app.on_copy_name()
|
||||
|
||||
# Toggle axis
|
||||
if key == QtCore.Qt.Key_G:
|
||||
if self.toggle_axis is False:
|
||||
|
@ -286,12 +295,65 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
self.appplotcanvas.redraw()
|
||||
self.app.toggle_axis = False
|
||||
|
||||
# Open Preferences Window
|
||||
if key == QtCore.Qt.Key_P:
|
||||
self.app.on_preferences()
|
||||
return
|
||||
|
||||
# Rotate Object by 90 degree CCW
|
||||
if key == QtCore.Qt.Key_R:
|
||||
self.app.on_rotate(silent=True, preset=-90)
|
||||
return
|
||||
|
||||
# Run a Script
|
||||
if key == QtCore.Qt.Key_S:
|
||||
self.app.on_filerunscript()
|
||||
return
|
||||
|
||||
# Toggle Workspace
|
||||
if key == QtCore.Qt.Key_W:
|
||||
self.app.on_workspace_menu()
|
||||
return
|
||||
|
||||
# Skew on X axis
|
||||
if key == QtCore.Qt.Key_X:
|
||||
self.app.on_skewx()
|
||||
return
|
||||
|
||||
# Skew on Y axis
|
||||
if key == QtCore.Qt.Key_Y:
|
||||
self.app.on_skewy()
|
||||
return
|
||||
elif modifiers == QtCore.Qt.AltModifier:
|
||||
# 2-Sided PCB Tool
|
||||
if key == QtCore.Qt.Key_D:
|
||||
self.app.dblsidedtool.run()
|
||||
return
|
||||
|
||||
# Non-Copper Clear Tool
|
||||
if key == QtCore.Qt.Key_N:
|
||||
self.app.ncclear_tool.run()
|
||||
return
|
||||
|
||||
# Transformation Tool
|
||||
if key == QtCore.Qt.Key_R:
|
||||
self.app.transform_tool.run()
|
||||
return
|
||||
|
||||
# Cutout Tool
|
||||
if key == QtCore.Qt.Key_U:
|
||||
self.app.cutout_tool.run()
|
||||
return
|
||||
|
||||
else:
|
||||
# Open Manual
|
||||
if key == QtCore.Qt.Key_F1:
|
||||
webbrowser.open(self.app.manual_url)
|
||||
|
||||
# Open Video Help
|
||||
if key == QtCore.Qt.Key_F2:
|
||||
webbrowser.open(self.app.video_url)
|
||||
|
||||
# Zoom Fit
|
||||
if key == QtCore.Qt.Key_1:
|
||||
self.app.on_zoom_fit(None)
|
||||
|
@ -316,10 +378,6 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
select.ui.plot_cb.toggle()
|
||||
self.app.delete_selection_shape()
|
||||
|
||||
# Copy Object Name
|
||||
if key == QtCore.Qt.Key_C:
|
||||
self.app.on_copy_name()
|
||||
|
||||
# Copy Object Name
|
||||
if key == QtCore.Qt.Key_E:
|
||||
self.app.object2editor()
|
||||
|
@ -332,6 +390,10 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
if key == QtCore.Qt.Key_J:
|
||||
self.app.on_jump_to()
|
||||
|
||||
# New Excellon
|
||||
if key == QtCore.Qt.Key_L:
|
||||
self.app.new_excellon_object()
|
||||
|
||||
# Move tool toggle
|
||||
if key == QtCore.Qt.Key_M:
|
||||
self.app.move_tool.toggle()
|
||||
|
@ -340,12 +402,22 @@ class ObjectCollection(QtCore.QAbstractItemModel):
|
|||
if key == QtCore.Qt.Key_N:
|
||||
self.app.on_new_geometry()
|
||||
|
||||
# Set Origin
|
||||
if key == QtCore.Qt.Key_O:
|
||||
self.app.on_set_origin()
|
||||
return
|
||||
|
||||
# Set Origin
|
||||
if key == QtCore.Qt.Key_P:
|
||||
self.app.properties_tool.run()
|
||||
return
|
||||
|
||||
# Change Units
|
||||
if key == QtCore.Qt.Key_Q:
|
||||
if self.app.options["units"] == 'MM':
|
||||
self.app.general_options_form.general_group.units_radio.set_value("IN")
|
||||
self.app.general_options_form.general_app_group.units_radio.set_value("IN")
|
||||
else:
|
||||
self.app.general_options_form.general_group.units_radio.set_value("MM")
|
||||
self.app.general_options_form.general_app_group.units_radio.set_value("MM")
|
||||
self.app.on_toggle_units()
|
||||
|
||||
# Rotate Object by 90 degree CW
|
||||
|
|
|
@ -970,8 +970,9 @@ class GeometryObjectUI(ObjectUI):
|
|||
# Spindlespeed
|
||||
spdlabel = QtWidgets.QLabel('Spindle speed:')
|
||||
spdlabel.setToolTip(
|
||||
"Speed of the spindle\n"
|
||||
"in RPM (optional)"
|
||||
"Speed of the spindle in RPM (optional).\n"
|
||||
"If LASER postprocessor is used,\n"
|
||||
"this value is the power of laser."
|
||||
)
|
||||
self.grid3.addWidget(spdlabel, 14, 0)
|
||||
self.cncspindlespeed_entry = IntEntry(allow_empty=True)
|
||||
|
|
|
@ -65,7 +65,7 @@ class PlotCanvas(QtCore.QObject):
|
|||
self.draw_workspace()
|
||||
|
||||
# if self.app.defaults['global_workspace'] is True:
|
||||
# if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM':
|
||||
# if self.app.general_options_form.general_app_group.units_radio.get_value().upper() == 'MM':
|
||||
# self.wkspace_t = Line(pos=)
|
||||
|
||||
self.shape_collections = []
|
||||
|
@ -92,7 +92,7 @@ class PlotCanvas(QtCore.QObject):
|
|||
a3p_mm = np.array([(0, 0), (297, 0), (297, 420), (0, 420)])
|
||||
a3l_mm = np.array([(0, 0), (420, 0), (420, 297), (0, 297)])
|
||||
|
||||
if self.app.general_options_form.general_group.units_radio.get_value().upper() == 'MM':
|
||||
if self.app.general_options_form.general_app_group.units_radio.get_value().upper() == 'MM':
|
||||
if self.app.defaults['global_workspaceT'] == 'A4P':
|
||||
a = a4p_mm
|
||||
elif self.app.defaults['global_workspaceT'] == 'A4L':
|
||||
|
|
43
README.md
43
README.md
|
@ -9,6 +9,49 @@ CAD program, and create G-Code for Isolation routing.
|
|||
|
||||
=================================================
|
||||
|
||||
29.01.2019
|
||||
|
||||
- fixed issue in Tool Calculators when a float value was entered starting only with the dot.
|
||||
- added protection for entering incorrect values in Offset and Scale fields for Gerber and Geometry objects (in Selected Tab)
|
||||
- added more shortcut keys in the Geometry Editor and in Excellon Editor; activated also the zoom (fit, in, out) shortcut keys ('1' , '2', '3') for the editors
|
||||
- disabled the context menu in tools table on Paint Tool in case that the painting method is single.
|
||||
- added protection when trying to do Intersection in Geometry Editor without having selected Geometry items.
|
||||
- fixed the scale, mirror, rotate, skew functions to work with Geometry Objects of multi-geometry type.
|
||||
- added a GUI for Excellon Search time for OR-TOOLS path optimization in Edit -> Preferences -> Excellon General -> Optimization Time
|
||||
- more changes in Edit -> Preferences -> Geometry, Gerber and in CNCJob
|
||||
- added new option for Cutout Tool Freeform Gaps in Edit -> Preferences -> Tools
|
||||
- fixed Freeform Cutout gaps issue (it was double than the value set)
|
||||
- added protection so the Cutout (either Freeform or Rectangular) cannot be done on a multigeo Geometry
|
||||
- added 2Sided Tool default values in Edit -> Preferences -> Tools
|
||||
- optimized the FlatCAMCNCJob.on_plot_cb_click_table() plot function and solved a bug regarding having tools numbers not in sync with the cnc tool table
|
||||
|
||||
28.01.2018
|
||||
|
||||
- fixed the FlatCAMGerber.merge() function
|
||||
- added a new menu entry for the Gerber Join function: Edit -> Conversions -> "Join Gerber(s) to Gerber" allowing joining Gerber objects into a final Gerber object
|
||||
- moved Paint Tool defaults from Geometry section to the Tools section in Edit -> Preferences
|
||||
- added key shortcuts for Open Manual = F1 and for Open Online VideoHelp = F2
|
||||
|
||||
27.01.2018
|
||||
|
||||
- added more key shortcuts into the application; they are now displayed in the GUI menu's
|
||||
- reorganized the Edit -> Preferences -> Global
|
||||
- redesigned the messagebox that is showed when quiting ot creating a New Project: now it has an option ('Cancel') to abort the process returning to the app
|
||||
- added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ )
|
||||
- added shortcut key 'L' for creating 'New Excellon'
|
||||
- added shortcut key combo 'SHIFT+S' for Running a Script.
|
||||
- modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M03 (laser active) whenever a value is enter in the Spindlespeed entry field
|
||||
- remade the EDIT -> PREFERENCES window, the Excellon and Gerber sections. Created a new section named TOOLS
|
||||
|
||||
26.01.2019
|
||||
|
||||
- fixed grbl_11 postprocessor in linear_code() function
|
||||
- added icons to the Project Tab context menu
|
||||
- added new entries to the Canvas context menu (Copy, Delete, Edit/Save, Move, New Excellon, New Geometry, New Project)
|
||||
- fixed grbl_laser postprocessor file
|
||||
- updated function for copy of an Excellon object for the case when the object has slots
|
||||
- updated FlatCAMExcellon.merge() function to work in case some (or all) of the merged objects have slots
|
||||
|
||||
25.01.2019
|
||||
|
||||
- deleted junk folders
|
||||
|
|
105
camlib.py
105
camlib.py
|
@ -1354,11 +1354,17 @@ class Geometry(object):
|
|||
return affinity.scale(obj, xscale, yscale, origin=(px,py))
|
||||
|
||||
try:
|
||||
if self.multigeo is True:
|
||||
for tool in self.tools:
|
||||
self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
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")
|
||||
|
||||
|
||||
|
||||
def rotate(self, angle, point):
|
||||
"""
|
||||
Rotate an object by an angle (in degrees) around the provided coordinates.
|
||||
|
@ -1388,6 +1394,10 @@ class Geometry(object):
|
|||
return affinity.rotate(obj, angle, origin=(px, py))
|
||||
|
||||
try:
|
||||
if self.multigeo is True:
|
||||
for tool in self.tools:
|
||||
self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
self.solid_geometry = rotate_geom(self.solid_geometry)
|
||||
self.app.inform.emit('[success]Object was rotated ...')
|
||||
except AttributeError:
|
||||
|
@ -1420,6 +1430,10 @@ class Geometry(object):
|
|||
return affinity.skew(obj, angle_x, angle_y, origin=(px, py))
|
||||
|
||||
try:
|
||||
if self.multigeo is True:
|
||||
for tool in self.tools:
|
||||
self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
|
||||
else:
|
||||
self.solid_geometry = skew_geom(self.solid_geometry)
|
||||
self.app.inform.emit('[success]Object was skewed ...')
|
||||
except AttributeError:
|
||||
|
@ -2965,7 +2979,7 @@ class Gerber (Geometry):
|
|||
return 0, 0, 0, 0
|
||||
|
||||
def bounds_rec(obj):
|
||||
if type(obj) is list:
|
||||
if type(obj) is list and type(obj) is not MultiPolygon:
|
||||
minx = Inf
|
||||
miny = Inf
|
||||
maxx = -Inf
|
||||
|
@ -2980,7 +2994,12 @@ class Gerber (Geometry):
|
|||
maxx = max(maxx, maxx_)
|
||||
maxy = max(maxy, maxy_)
|
||||
else:
|
||||
try:
|
||||
minx_, miny_, maxx_, maxy_ = bounds_rec(k)
|
||||
except Exception as e:
|
||||
log.debug("camlib.Geometry.bounds() --> %s" % str(e))
|
||||
return
|
||||
|
||||
minx = min(minx, minx_)
|
||||
miny = min(miny, miny_)
|
||||
maxx = max(maxx, maxx_)
|
||||
|
@ -3013,8 +3032,20 @@ class Gerber (Geometry):
|
|||
:rtype : None
|
||||
"""
|
||||
|
||||
try:
|
||||
xfactor = float(xfactor)
|
||||
except:
|
||||
self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.")
|
||||
return
|
||||
|
||||
if yfactor is None:
|
||||
yfactor = xfactor
|
||||
else:
|
||||
try:
|
||||
yfactor = float(yfactor)
|
||||
except:
|
||||
self.app.inform.emit("[error_notcl] Scale factor has to be a number: integer or float.")
|
||||
return
|
||||
|
||||
if point is None:
|
||||
px = 0
|
||||
|
@ -3033,6 +3064,7 @@ class Gerber (Geometry):
|
|||
yfactor, origin=(px, py))
|
||||
|
||||
self.solid_geometry = scale_geom(self.solid_geometry)
|
||||
self.app.inform.emit("[success]Gerber Scale done.")
|
||||
|
||||
## solid_geometry ???
|
||||
# It's a cascaded union of objects.
|
||||
|
@ -3061,8 +3093,12 @@ class Gerber (Geometry):
|
|||
:type vect: tuple
|
||||
:return: None
|
||||
"""
|
||||
|
||||
try:
|
||||
dx, dy = vect
|
||||
except TypeError:
|
||||
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
|
||||
|
||||
def offset_geom(obj):
|
||||
if type(obj) is list:
|
||||
|
@ -3076,6 +3112,7 @@ class Gerber (Geometry):
|
|||
## Solid geometry
|
||||
# self.solid_geometry = affinity.translate(self.solid_geometry, xoff=dx, yoff=dy)
|
||||
self.solid_geometry = offset_geom(self.solid_geometry)
|
||||
self.app.inform.emit("[success]Gerber Offset done.")
|
||||
|
||||
def mirror(self, axis, point):
|
||||
"""
|
||||
|
@ -4332,6 +4369,8 @@ class CNCjob(Geometry):
|
|||
spindlespeed=None, dwell=True, dwelltime=1000,
|
||||
toolchangez=0.787402,
|
||||
endz=2.0,
|
||||
segx=None,
|
||||
segy=None,
|
||||
steps_per_circle=None):
|
||||
|
||||
# Used when parsing G-code arcs
|
||||
|
@ -4376,6 +4415,9 @@ class CNCjob(Geometry):
|
|||
self.dwell = dwell
|
||||
self.dwelltime = dwelltime
|
||||
|
||||
self.segx = float(segx) if segx is not None else 0.0
|
||||
self.segy = float(segy) if segy is not None else 0.0
|
||||
|
||||
self.input_geometry_bounds = None
|
||||
|
||||
# Attributes to be included in serialization
|
||||
|
@ -5456,6 +5498,61 @@ class CNCjob(Geometry):
|
|||
self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed])
|
||||
return self.solid_geometry
|
||||
|
||||
# code snippet added by Lei Zheng in a rejected pull request on FlatCAM https://bitbucket.org/realthunder/
|
||||
def segment(self, coords):
|
||||
"""
|
||||
break long linear lines to make it more auto level friendly
|
||||
"""
|
||||
|
||||
if len(coords) < 2 or self.segx <= 0 and self.segy <= 0:
|
||||
return list(coords)
|
||||
|
||||
path = [coords[0]]
|
||||
|
||||
# break the line in either x or y dimension only
|
||||
def linebreak_single(line, dim, dmax):
|
||||
if dmax <= 0:
|
||||
return None
|
||||
|
||||
if line[1][dim] > line[0][dim]:
|
||||
sign = 1.0
|
||||
d = line[1][dim] - line[0][dim]
|
||||
else:
|
||||
sign = -1.0
|
||||
d = line[0][dim] - line[1][dim]
|
||||
if d > dmax:
|
||||
# make sure we don't make any new lines too short
|
||||
if d > dmax * 2:
|
||||
dd = dmax
|
||||
else:
|
||||
dd = d / 2
|
||||
other = dim ^ 1
|
||||
return (line[0][dim] + dd * sign, line[0][other] + \
|
||||
dd * (line[1][other] - line[0][other]) / d)
|
||||
return None
|
||||
|
||||
# recursively breaks down a given line until it is within the
|
||||
# required step size
|
||||
def linebreak(line):
|
||||
pt_new = linebreak_single(line, 0, self.segx)
|
||||
if pt_new is None:
|
||||
pt_new2 = linebreak_single(line, 1, self.segy)
|
||||
else:
|
||||
pt_new2 = linebreak_single((line[0], pt_new), 1, self.segy)
|
||||
if pt_new2 is not None:
|
||||
pt_new = pt_new2[::-1]
|
||||
|
||||
if pt_new is None:
|
||||
path.append(line[1])
|
||||
else:
|
||||
path.append(pt_new)
|
||||
linebreak((pt_new, line[1]))
|
||||
|
||||
for pt in coords[1:]:
|
||||
linebreak((path[-1], pt))
|
||||
|
||||
return path
|
||||
|
||||
def linear2gcode(self, linear, tolerance=0, down=True, up=True,
|
||||
z_cut=None, z_move=None, zdownrate=None,
|
||||
feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False):
|
||||
|
@ -5500,7 +5597,9 @@ class CNCjob(Geometry):
|
|||
|
||||
gcode = ""
|
||||
|
||||
path = list(target_linear.coords)
|
||||
# path = list(target_linear.coords)
|
||||
path = self.segment(target_linear.coords)
|
||||
|
||||
p = self.pp_geometry
|
||||
|
||||
# Move fast to 1st point
|
||||
|
|
|
@ -141,6 +141,9 @@ class ToolCalculator(FlatCAMTool):
|
|||
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
|
||||
|
@ -154,7 +157,7 @@ class ToolCalculator(FlatCAMTool):
|
|||
tip_diameter = float(self.tipDia_entry.get_value())
|
||||
half_tip_angle = float(self.tipAngle_entry.get_value()) / 2
|
||||
cut_depth = float(self.cutDepth_entry.get_value())
|
||||
except TypeError:
|
||||
except:
|
||||
return
|
||||
|
||||
tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle)))
|
||||
|
|
|
@ -9,7 +9,7 @@ from FlatCAMObj import FlatCAMGeometry, FlatCAMExcellon, FlatCAMGerber
|
|||
|
||||
class ToolCutout(FlatCAMTool):
|
||||
|
||||
toolName = "Cutout PCB Tool"
|
||||
toolName = "Cutout PCB"
|
||||
|
||||
def __init__(self, app):
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
@ -101,10 +101,9 @@ class ToolCutout(FlatCAMTool):
|
|||
# 8 - 2*left + 2*right +2*top + 2*bottom
|
||||
|
||||
# Gaps
|
||||
self.gaps = FCEntry()
|
||||
self.gaps_label = QtWidgets.QLabel("Type of gaps: ")
|
||||
self.gaps_label.setToolTip(
|
||||
"Number of gaps used for the cutout.\n"
|
||||
gaps_ff_label = QtWidgets.QLabel('Gaps FF: ')
|
||||
gaps_ff_label.setToolTip(
|
||||
"Number of gaps used for the FreeForm cutout.\n"
|
||||
"There can be maximum 8 bridges/gaps.\n"
|
||||
"The choices are:\n"
|
||||
"- lr - left + right\n"
|
||||
|
@ -114,7 +113,13 @@ class ToolCutout(FlatCAMTool):
|
|||
"- 2tb - 2*top + 2*bottom\n"
|
||||
"- 8 - 2*left + 2*right +2*top + 2*bottom"
|
||||
)
|
||||
form_layout_2.addRow(self.gaps_label, self.gaps)
|
||||
|
||||
self.gaps = FCComboBox()
|
||||
gaps_items = ['LR', 'TB', '4', '2LR', '2TB', '8']
|
||||
for it in gaps_items:
|
||||
self.gaps.addItem(it)
|
||||
self.gaps.setStyleSheet('background-color: rgb(255,255,255)')
|
||||
form_layout_2.addRow(gaps_ff_label, self.gaps)
|
||||
|
||||
## Buttons
|
||||
hlay = QtWidgets.QHBoxLayout()
|
||||
|
@ -146,8 +151,8 @@ class ToolCutout(FlatCAMTool):
|
|||
"- one gap Left / one gap Right\n"
|
||||
"- one gap on each of the 4 sides."
|
||||
)
|
||||
self.gaps_rect_radio = RadioSet([{'label': 'T/B', 'value': 'tb'},
|
||||
{'label': 'L/R', 'value': 'lr'},
|
||||
self.gaps_rect_radio = RadioSet([{'label': '2(T/B)', 'value': 'tb'},
|
||||
{'label': '2(L/R)', 'value': 'lr'},
|
||||
{'label': '4', 'value': '4'}])
|
||||
form_layout_3.addRow(gapslabel_rect, self.gaps_rect_radio)
|
||||
|
||||
|
@ -186,8 +191,19 @@ class ToolCutout(FlatCAMTool):
|
|||
|
||||
def run(self):
|
||||
FlatCAMTool.run(self)
|
||||
self.set_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):
|
||||
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"]))
|
||||
self.gaps.set_value(4)
|
||||
self.gaps_rect_radio.set_value(str(self.app.defaults["tools_gaps_rect"]))
|
||||
|
||||
def on_freeform_cutout(self):
|
||||
|
||||
def subtract_rectangle(obj_, x0, y0, x1, y1):
|
||||
|
@ -204,7 +220,8 @@ class ToolCutout(FlatCAMTool):
|
|||
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]There is no object selected for Cutout.\nSelect one and try again.")
|
||||
return
|
||||
|
||||
try:
|
||||
dia = float(self.dia.get_value())
|
||||
|
@ -236,6 +253,12 @@ class ToolCutout(FlatCAMTool):
|
|||
"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"
|
||||
"Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n"
|
||||
"and after that perform Cutout.")
|
||||
return
|
||||
|
||||
# Get min and max data for each object as we just cut rectangles across X or Y
|
||||
xmin, ymin, xmax, ymax = cutout_obj.bounds()
|
||||
px = 0.5 * (xmin + xmax) + margin
|
||||
|
@ -243,7 +266,7 @@ class ToolCutout(FlatCAMTool):
|
|||
lenghtx = (xmax - xmin) + (margin * 2)
|
||||
lenghty = (ymax - ymin) + (margin * 2)
|
||||
|
||||
gapsize = gapsize + (dia / 2)
|
||||
gapsize = gapsize / 2 + (dia / 2)
|
||||
|
||||
if isinstance(cutout_obj,FlatCAMGeometry):
|
||||
# rename the obj name so it can be identified as cutout
|
||||
|
@ -346,6 +369,12 @@ class ToolCutout(FlatCAMTool):
|
|||
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"
|
||||
"Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n"
|
||||
"and after that perform Cutout.")
|
||||
return
|
||||
|
||||
def geo_init(geo_obj, app_obj):
|
||||
real_margin = margin + (dia / 2)
|
||||
real_gap_size = gapsize + dia
|
||||
|
|
|
@ -8,7 +8,7 @@ from PyQt5 import QtCore
|
|||
|
||||
class DblSidedTool(FlatCAMTool):
|
||||
|
||||
toolName = "Double-Sided PCB Tool"
|
||||
toolName = "2-Sided PCB"
|
||||
|
||||
def __init__(self, app):
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
@ -249,10 +249,26 @@ 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):
|
||||
FlatCAMTool.run(self)
|
||||
|
||||
self.app.ui.notebook.setTabText(2, "2-Sided Tool")
|
||||
self.reset_fields()
|
||||
self.set_ui()
|
||||
|
||||
def set_ui(self):
|
||||
## Initialize form
|
||||
self.mirror_axis.set_value('X')
|
||||
self.axis_location.set_value('point')
|
||||
self.drill_dia.set_value(1)
|
||||
self.point_entry.set_value("")
|
||||
self.alignment_holes.set_value("")
|
||||
|
||||
self.mirror_axis.set_value(self.app.defaults["tools_2sided_mirror_axis"])
|
||||
self.axis_location.set_value(self.app.defaults["tools_2sided_axis_loc"])
|
||||
self.drill_dia.set_value(self.app.defaults["tools_2sided_drilldia"])
|
||||
|
||||
def on_combo_box_type(self):
|
||||
obj_type = self.box_combo_type.currentIndex()
|
||||
|
@ -454,14 +470,6 @@ class DblSidedTool(FlatCAMTool):
|
|||
|
||||
|
||||
self.drill_values = ""
|
||||
self.point_entry.set_value("")
|
||||
self.alignment_holes.set_value("")
|
||||
## Initialize form
|
||||
self.mirror_axis.set_value('X')
|
||||
self.axis_location.set_value('point')
|
||||
self.drill_dia.set_value(1)
|
||||
|
||||
def run(self):
|
||||
FlatCAMTool.run(self)
|
||||
self.app.ui.notebook.setTabText(2, "2-Sided Tool")
|
||||
self.reset_fields()
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from PyQt5 import QtGui, QtCore, QtWidgets
|
|||
|
||||
class Film(FlatCAMTool):
|
||||
|
||||
toolName = "Film PCB Tool"
|
||||
toolName = "Film PCB"
|
||||
|
||||
def __init__(self, app):
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
@ -154,6 +154,9 @@ class Film(FlatCAMTool):
|
|||
FlatCAMTool.run(self)
|
||||
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 on_film_creation(self):
|
||||
try:
|
||||
name = self.tf_object_combo.currentText()
|
||||
|
|
|
@ -7,12 +7,12 @@ from math import sqrt
|
|||
|
||||
class Measurement(FlatCAMTool):
|
||||
|
||||
toolName = "Measurement Tool"
|
||||
toolName = "Measurement"
|
||||
|
||||
def __init__(self, app):
|
||||
FlatCAMTool.__init__(self, app)
|
||||
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().lower()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().lower()
|
||||
|
||||
## Title
|
||||
title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
|
||||
|
@ -165,10 +165,13 @@ class Measurement(FlatCAMTool):
|
|||
|
||||
# Switch notebook to tool page
|
||||
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().lower()
|
||||
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
|
||||
|
@ -217,7 +220,7 @@ class Measurement(FlatCAMTool):
|
|||
else:
|
||||
# ENABLE the Measuring TOOL
|
||||
self.active = True
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().lower()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().lower()
|
||||
|
||||
# we disconnect the mouse/key handlers from wherever the measurement tool was called
|
||||
if self.app.call_source == 'app':
|
||||
|
|
|
@ -31,7 +31,7 @@ class ToolMove(FlatCAMTool):
|
|||
self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene, layers=1)
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
FlatCAMTool.install(self, icon, separator, **kwargs)
|
||||
FlatCAMTool.install(self, icon, separator, shortcut='M', **kwargs)
|
||||
|
||||
def run(self):
|
||||
if self.app.tool_tab_locked is True:
|
||||
|
|
|
@ -7,7 +7,7 @@ import time
|
|||
|
||||
class NonCopperClear(FlatCAMTool, Gerber):
|
||||
|
||||
toolName = "Non-Copper Clearing Tool"
|
||||
toolName = "Non-Copper Clearing"
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
@ -234,7 +234,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
self.generate_ncc_button.clicked.connect(self.on_ncc)
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
FlatCAMTool.install(self, icon, separator, **kwargs)
|
||||
FlatCAMTool.install(self, icon, separator, shortcut='ALT+N', **kwargs)
|
||||
|
||||
def run(self):
|
||||
FlatCAMTool.run(self)
|
||||
|
@ -244,12 +244,12 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
self.app.ui.notebook.setTabText(2, "NCC Tool")
|
||||
|
||||
def set_ui(self):
|
||||
self.ncc_overlap_entry.set_value(self.app.defaults["gerber_nccoverlap"])
|
||||
self.ncc_margin_entry.set_value(self.app.defaults["gerber_nccmargin"])
|
||||
self.ncc_method_radio.set_value(self.app.defaults["gerber_nccmethod"])
|
||||
self.ncc_connect_cb.set_value(self.app.defaults["gerber_nccconnect"])
|
||||
self.ncc_contour_cb.set_value(self.app.defaults["gerber_ncccontour"])
|
||||
self.ncc_rest_cb.set_value(self.app.defaults["gerber_nccrest"])
|
||||
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"])
|
||||
self.ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"])
|
||||
self.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"])
|
||||
self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
|
||||
|
||||
self.tools_table.setupContextMenu()
|
||||
self.tools_table.addContextMenu(
|
||||
|
@ -263,7 +263,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
self.default_data.update({
|
||||
"name": '_ncc',
|
||||
"plot": self.app.defaults["geometry_plot"],
|
||||
"tooldia": self.app.defaults["geometry_painttooldia"],
|
||||
"cutz": self.app.defaults["geometry_cutz"],
|
||||
"vtipdia": 0.1,
|
||||
"vtipangle": 30,
|
||||
|
@ -283,24 +282,27 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
|
||||
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
|
||||
"startz": self.app.defaults["geometry_startz"],
|
||||
"paintmargin": self.app.defaults["geometry_paintmargin"],
|
||||
"paintmethod": self.app.defaults["geometry_paintmethod"],
|
||||
"selectmethod": self.app.defaults["geometry_selectmethod"],
|
||||
"pathconnect": self.app.defaults["geometry_pathconnect"],
|
||||
"paintcontour": self.app.defaults["geometry_paintcontour"],
|
||||
"paintoverlap": self.app.defaults["geometry_paintoverlap"],
|
||||
"nccoverlap": self.app.defaults["gerber_nccoverlap"],
|
||||
"nccmargin": self.app.defaults["gerber_nccmargin"],
|
||||
"nccmethod": self.app.defaults["gerber_nccmethod"],
|
||||
"nccconnect": self.app.defaults["gerber_nccconnect"],
|
||||
"ncccontour": self.app.defaults["gerber_ncccontour"],
|
||||
"nccrest": self.app.defaults["gerber_nccrest"]
|
||||
|
||||
"tooldia": self.app.defaults["tools_painttooldia"],
|
||||
"paintmargin": self.app.defaults["tools_paintmargin"],
|
||||
"paintmethod": self.app.defaults["tools_paintmethod"],
|
||||
"selectmethod": self.app.defaults["tools_selectmethod"],
|
||||
"pathconnect": self.app.defaults["tools_pathconnect"],
|
||||
"paintcontour": self.app.defaults["tools_paintcontour"],
|
||||
"paintoverlap": self.app.defaults["tools_paintoverlap"],
|
||||
|
||||
"nccoverlap": self.app.defaults["tools_nccoverlap"],
|
||||
"nccmargin": self.app.defaults["tools_nccmargin"],
|
||||
"nccmethod": self.app.defaults["tools_nccmethod"],
|
||||
"nccconnect": self.app.defaults["tools_nccconnect"],
|
||||
"ncccontour": self.app.defaults["tools_ncccontour"],
|
||||
"nccrest": self.app.defaults["tools_nccrest"]
|
||||
})
|
||||
|
||||
try:
|
||||
dias = [float(eval(dia)) for dia in self.app.defaults["gerber_ncctools"].split(",")]
|
||||
dias = [float(eval(dia)) for dia in self.app.defaults["tools_ncctools"].split(",")]
|
||||
except:
|
||||
log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> Gerber Object -> NCC Tools.")
|
||||
log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> TOOLS -> NCC Tools.")
|
||||
return
|
||||
|
||||
self.tooluid = 0
|
||||
|
@ -322,13 +324,13 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
self.obj_name = ""
|
||||
self.ncc_obj = None
|
||||
self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
def build_ui(self):
|
||||
self.ui_disconnect()
|
||||
|
||||
# updated units
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
if self.units == "IN":
|
||||
self.addtool_entry.set_value(0.039)
|
||||
|
@ -550,22 +552,22 @@ class NonCopperClear(FlatCAMTool, Gerber):
|
|||
def on_ncc(self):
|
||||
|
||||
over = self.ncc_overlap_entry.get_value()
|
||||
over = over if over else self.app.defaults["gerber_nccoverlap"]
|
||||
over = over if over else self.app.defaults["tools_nccoverlap"]
|
||||
|
||||
margin = self.ncc_margin_entry.get_value()
|
||||
margin = margin if margin else self.app.defaults["gerber_nccmargin"]
|
||||
margin = margin if margin else self.app.defaults["tools_nccmargin"]
|
||||
|
||||
connect = self.ncc_connect_cb.get_value()
|
||||
connect = connect if connect else self.app.defaults["gerber_nccconnect"]
|
||||
connect = connect if connect else self.app.defaults["tools_nccconnect"]
|
||||
|
||||
contour = self.ncc_contour_cb.get_value()
|
||||
contour = contour if contour else self.app.defaults["gerber_ncccontour"]
|
||||
contour = contour if contour else self.app.defaults["tools_ncccontour"]
|
||||
|
||||
clearing_method = self.ncc_rest_cb.get_value()
|
||||
clearing_method = clearing_method if clearing_method else self.app.defaults["gerber_nccrest"]
|
||||
clearing_method = clearing_method if clearing_method else self.app.defaults["tools_nccrest"]
|
||||
|
||||
pol_method = self.ncc_method_radio.get_value()
|
||||
pol_method = pol_method if pol_method else self.app.defaults["gerber_nccmethod"]
|
||||
pol_method = pol_method if pol_method else self.app.defaults["tools_nccmethod"]
|
||||
|
||||
self.obj_name = self.object_combo.currentText()
|
||||
# Get source object.
|
||||
|
|
|
@ -5,7 +5,7 @@ from ObjectCollection import *
|
|||
|
||||
class ToolPaint(FlatCAMTool, Gerber):
|
||||
|
||||
toolName = "Paint Area Tool"
|
||||
toolName = "Paint Area"
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
@ -250,7 +250,6 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
self.default_data.update({
|
||||
"name": '_paint',
|
||||
"plot": self.app.defaults["geometry_plot"],
|
||||
"tooldia": self.app.defaults["geometry_painttooldia"],
|
||||
"cutz": self.app.defaults["geometry_cutz"],
|
||||
"vtipdia": 0.1,
|
||||
"vtipangle": 30,
|
||||
|
@ -270,12 +269,14 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
|
||||
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
|
||||
"startz": self.app.defaults["geometry_startz"],
|
||||
"paintmargin": self.app.defaults["geometry_paintmargin"],
|
||||
"paintmethod": self.app.defaults["geometry_paintmethod"],
|
||||
"selectmethod": self.app.defaults["geometry_selectmethod"],
|
||||
"pathconnect": self.app.defaults["geometry_pathconnect"],
|
||||
"paintcontour": self.app.defaults["geometry_paintcontour"],
|
||||
"paintoverlap": self.app.defaults["geometry_paintoverlap"]
|
||||
|
||||
"tooldia": self.app.defaults["tools_painttooldia"],
|
||||
"paintmargin": self.app.defaults["tools_paintmargin"],
|
||||
"paintmethod": self.app.defaults["tools_paintmethod"],
|
||||
"selectmethod": self.app.defaults["tools_selectmethod"],
|
||||
"pathconnect": self.app.defaults["tools_pathconnect"],
|
||||
"paintcontour": self.app.defaults["tools_paintcontour"],
|
||||
"paintoverlap": self.app.defaults["tools_paintoverlap"]
|
||||
})
|
||||
|
||||
self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
|
||||
|
@ -290,7 +291,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
FlatCAMTool.install(self, icon, separator, **kwargs)
|
||||
FlatCAMTool.install(self, icon, separator, shortcut='ALT+P', **kwargs)
|
||||
|
||||
def run(self):
|
||||
FlatCAMTool.run(self)
|
||||
|
@ -311,11 +312,13 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
self.addtool_entry.setDisabled(True)
|
||||
self.addtool_btn.setDisabled(True)
|
||||
self.deltool_btn.setDisabled(True)
|
||||
self.tools_table.setContextMenuPolicy(Qt.NoContextMenu)
|
||||
else:
|
||||
self.rest_cb.setDisabled(False)
|
||||
self.addtool_entry.setDisabled(False)
|
||||
self.addtool_btn.setDisabled(False)
|
||||
self.deltool_btn.setDisabled(False)
|
||||
self.tools_table.setContextMenuPolicy(Qt.ActionsContextMenu)
|
||||
|
||||
def set_ui(self):
|
||||
## Init the GUI interface
|
||||
|
@ -327,7 +330,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
self.paintoverlap_entry.set_value(self.default_data["paintoverlap"])
|
||||
|
||||
# updated units
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
if self.units == "IN":
|
||||
self.addtool_entry.set_value(0.039)
|
||||
|
@ -349,7 +352,6 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
self.default_data.update({
|
||||
"name": '_paint',
|
||||
"plot": self.app.defaults["geometry_plot"],
|
||||
"tooldia": self.app.defaults["geometry_painttooldia"],
|
||||
"cutz": self.app.defaults["geometry_cutz"],
|
||||
"vtipdia": 0.1,
|
||||
"vtipangle": 30,
|
||||
|
@ -369,17 +371,23 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
|
||||
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
|
||||
"startz": self.app.defaults["geometry_startz"],
|
||||
"paintmargin": self.app.defaults["geometry_paintmargin"],
|
||||
"paintmethod": self.app.defaults["geometry_paintmethod"],
|
||||
"selectmethod": self.app.defaults["geometry_selectmethod"],
|
||||
"pathconnect": self.app.defaults["geometry_pathconnect"],
|
||||
"paintcontour": self.app.defaults["geometry_paintcontour"],
|
||||
"paintoverlap": self.app.defaults["geometry_paintoverlap"]
|
||||
|
||||
"tooldia": self.app.defaults["tools_painttooldia"],
|
||||
"paintmargin": self.app.defaults["tools_paintmargin"],
|
||||
"paintmethod": self.app.defaults["tools_paintmethod"],
|
||||
"selectmethod": self.app.defaults["tools_selectmethod"],
|
||||
"pathconnect": self.app.defaults["tools_pathconnect"],
|
||||
"paintcontour": self.app.defaults["tools_paintcontour"],
|
||||
"paintoverlap": self.app.defaults["tools_paintoverlap"]
|
||||
})
|
||||
|
||||
# call on self.on_tool_add() counts as an call to self.build_ui()
|
||||
# through this, we add a initial row / tool in the tool_table
|
||||
self.on_tool_add(self.app.defaults["geometry_painttooldia"], muted=True)
|
||||
self.on_tool_add(self.app.defaults["tools_painttooldia"], muted=True)
|
||||
|
||||
# if the Paint Method is "Single" disable the tool table context menu
|
||||
if self.default_data["selectmethod"] == "single":
|
||||
self.tools_table.setContextMenuPolicy(Qt.NoContextMenu)
|
||||
|
||||
def build_ui(self):
|
||||
|
||||
|
@ -390,7 +398,7 @@ class ToolPaint(FlatCAMTool, Gerber):
|
|||
pass
|
||||
|
||||
# updated units
|
||||
self.units = self.app.general_options_form.general_group.units_radio.get_value().upper()
|
||||
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
|
||||
|
||||
sorted_tools = []
|
||||
for k, v in self.paint_tools.items():
|
||||
|
|
|
@ -6,7 +6,7 @@ import time
|
|||
|
||||
class Panelize(FlatCAMTool):
|
||||
|
||||
toolName = "Panelize PCB Tool"
|
||||
toolName = "Panelize PCB"
|
||||
|
||||
def __init__(self, app):
|
||||
super(Panelize, self).__init__(self)
|
||||
|
@ -197,6 +197,9 @@ class Panelize(FlatCAMTool):
|
|||
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()
|
||||
|
||||
|
|
|
@ -53,6 +53,9 @@ class Properties(FlatCAMTool):
|
|||
FlatCAMTool.run(self)
|
||||
self.properties()
|
||||
|
||||
def install(self, icon=None, separator=None, **kwargs):
|
||||
FlatCAMTool.install(self, icon, separator, shortcut='P', **kwargs)
|
||||
|
||||
def properties(self):
|
||||
obj_list = self.app.collection.get_selected()
|
||||
if not obj_list:
|
||||
|
@ -86,10 +89,10 @@ class Properties(FlatCAMTool):
|
|||
width = abs(ymax - ymin)
|
||||
|
||||
self.addChild(dims, ['Length:', '%.4f %s' % (
|
||||
length, self.app.general_options_form.general_group.units_radio.get_value().lower())], True)
|
||||
length, self.app.general_options_form.general_app_group.units_radio.get_value().lower())], True)
|
||||
self.addChild(dims, ['Width:', '%.4f %s' % (
|
||||
width, self.app.general_options_form.general_group.units_radio.get_value().lower())], True)
|
||||
if self.app.general_options_form.general_group.units_radio.get_value().lower() == 'mm':
|
||||
width, self.app.general_options_form.general_app_group.units_radio.get_value().lower())], True)
|
||||
if self.app.general_options_form.general_app_group.units_radio.get_value().lower() == 'mm':
|
||||
area = (length * width) / 100
|
||||
self.addChild(dims, ['Box Area:', '%.4f %s' % (area, 'cm2')], True)
|
||||
else:
|
||||
|
|
|
@ -370,6 +370,9 @@ class ToolTransform(FlatCAMTool):
|
|||
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())
|
||||
|
|
|
@ -105,7 +105,8 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez)
|
|||
return ('G00 ' + self.position_code(p)).format(**p)
|
||||
|
||||
def linear_code(self, p):
|
||||
return ('G01 ' + self.position_code(p)).format(**p) + " " + self.feedrate_code(p)
|
||||
return ('G01 ' + self.position_code(p)).format(**p) + \
|
||||
' F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate))
|
||||
|
||||
def end_code(self, p):
|
||||
coords_xy = p['toolchange_xy']
|
||||
|
|
|
@ -36,6 +36,9 @@ class grbl_laser(FlatCAMPostProc):
|
|||
return 'M05'
|
||||
|
||||
def down_code(self, p):
|
||||
if p.spindlespeed:
|
||||
return 'M03 S%d' % p.spindlespeed
|
||||
else:
|
||||
return 'M03'
|
||||
|
||||
def toolchange_code(self, p):
|
||||
|
@ -52,7 +55,8 @@ class grbl_laser(FlatCAMPostProc):
|
|||
return ('G00 ' + self.position_code(p)).format(**p)
|
||||
|
||||
def linear_code(self, p):
|
||||
return ('G01 ' + self.position_code(p)).format(**p) + " " + self.feedrate_code(p)
|
||||
return ('G01 ' + self.position_code(p)).format(**p) + \
|
||||
' F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate))
|
||||
|
||||
def end_code(self, p):
|
||||
gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
|
||||
|
@ -66,9 +70,6 @@ class grbl_laser(FlatCAMPostProc):
|
|||
return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate_z))
|
||||
|
||||
def spindle_code(self, p):
|
||||
if p.spindlespeed:
|
||||
return 'S%d' % p.spindlespeed
|
||||
else:
|
||||
return ''
|
||||
|
||||
def dwell_code(self, p):
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 317 B |
Binary file not shown.
After Width: | Height: | Size: 518 B |
Loading…
Reference in New Issue