- changed the data structure for the Excellon object; modified the Excellon parser and the Excellon object class

- fixed partially the Excellon Editor to work with the new data structure
- fixed Excellon export to work with the new data structure
- fixed all transformations in the Excellon object attributes; still need to fix the App Tools that creates or use Exellon objects
This commit is contained in:
Marius Stanciu 2020-06-16 03:47:26 +03:00 committed by Marius
parent bb24a45f5a
commit 949c265378
24 changed files with 691 additions and 683 deletions

View File

@ -7,6 +7,13 @@ CHANGELOG for FlatCAM beta
================================================= =================================================
16.06.2020
- changed the data structure for the Excellon object; modified the Excellon parser and the Excellon object class
- fixed partially the Excellon Editor to work with the new data structure
- fixed Excellon export to work with the new data structure
- fixed all transformations in the Excellon object attributes; still need to fix the App Tools that creates or use Exellon objects
15.06.2020 15.06.2020
- in Paint Tool and NCC Tool updated the way the selected tools were processed and made sure that the Tools Table rows are counted only once in the processing - in Paint Tool and NCC Tool updated the way the selected tools were processed and made sure that the Tools Table rows are counted only once in the processing

View File

@ -390,8 +390,7 @@ class FCSlot(FCShapeTool):
item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1) item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
self.draw_app.tools_table_exc.setCurrentItem(item) self.draw_app.tools_table_exc.setCurrentItem(item)
except KeyError: except KeyError:
self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % _("To add a slot first select a tool"))
_("To add a slot first select a tool"))
self.draw_app.select_tool("drill_select") self.draw_app.select_tool("drill_select")
return return
@ -2059,9 +2058,7 @@ class AppExcEditor(QtCore.QObject):
self.sorted_diameters = [] self.sorted_diameters = []
self.new_drills = []
self.new_tools = {} self.new_tools = {}
self.new_slots = []
# dictionary to store the tool_row and diameters in Tool_table # dictionary to store the tool_row and diameters in Tool_table
# it will be updated everytime self.build_ui() is called # it will be updated everytime self.build_ui() is called
@ -2261,30 +2258,33 @@ class AppExcEditor(QtCore.QObject):
self.tool2tooldia.clear() self.tool2tooldia.clear()
# build the self.points_edit dict {dimaters: [point_list]} # build the self.points_edit dict {dimaters: [point_list]}
for drill in self.exc_obj.drills: for tool, tool_dict in self.exc_obj.tools.items():
if drill['tool'] in self.exc_obj.tools: tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[tool]['tooldia']))
tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[drill['tool']]['C']))
try: if 'drills' in tool_dict and tool_dict['drills']:
self.points_edit[tool_dia].append(drill['point']) for drill in tool_dict['drills']:
except KeyError: try:
self.points_edit[tool_dia] = [drill['point']] self.points_edit[tool_dia].append(drill)
except KeyError:
self.points_edit[tool_dia] = [drill]
# build the self.slot_points_edit dict {dimaters: {"start": Point, "stop": Point}} # build the self.slot_points_edit dict {dimaters: {"start": Point, "stop": Point}}
for slot in self.exc_obj.slots: for tool, tool_dict in self.exc_obj.tools.items():
if slot['tool'] in self.exc_obj.tools: tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[tool]['tooldia']))
tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[slot['tool']]['C']))
if 'slots' in tool_dict and tool_dict['slots']:
for slot in tool_dict['slots']:
try:
self.slot_points_edit[tool_dia].append({
"start": slot[0],
"stop": slot[1]
})
except KeyError:
self.slot_points_edit[tool_dia] = [{
"start": slot[0],
"stop": slot[1]
}]
try:
self.slot_points_edit[tool_dia].append({
"start": slot["start"],
"stop": slot["stop"]
})
except KeyError:
self.slot_points_edit[tool_dia] = [{
"start": slot["start"],
"stop": slot["stop"]
}]
# update the olddia_newdia dict to make sure we have an updated state of the tool_table # update the olddia_newdia dict to make sure we have an updated state of the tool_table
for key in self.points_edit: for key in self.points_edit:
@ -2309,7 +2309,7 @@ class AppExcEditor(QtCore.QObject):
# Excellon file has no tool diameter information. In this case do not order the diameter in the table # Excellon file has no tool diameter information. In this case do not order the diameter in the table
# but use the real order found in the exc_obj.tools # but use the real order found in the exc_obj.tools
for k, v in self.exc_obj.tools.items(): for k, v in self.exc_obj.tools.items():
tool_dia = float('%.*f' % (self.decimals, v['C'])) tool_dia = float('%.*f' % (self.decimals, v['tooldia']))
self.tool2tooldia[int(k)] = tool_dia self.tool2tooldia[int(k)] = tool_dia
# Init appGUI # Init appGUI
@ -2385,21 +2385,27 @@ class AppExcEditor(QtCore.QObject):
self.tot_drill_cnt += drill_cnt self.tot_drill_cnt += drill_cnt
try: # try:
# Find no of slots for the current tool # # Find no of slots for the current tool
for slot in self.slot_points_edit: # for slot in self.slot_points_edit:
if slot['tool'] == tool_no: # if float(slot) == tool_no:
slot_cnt += 1 # slot_cnt += 1
#
# self.tot_slot_cnt += slot_cnt
# except AttributeError:
# # log.debug("No slots in the Excellon file")
# # Find no of slots for the current tool
# for tool_dia in self.slot_points_edit:
# if float(tool_dia) == tool_no:
# slot_cnt = len(self.slot_points_edit[tool_dia])
#
# self.tot_slot_cnt += slot_cnt
self.tot_slot_cnt += slot_cnt for tool_dia in self.slot_points_edit:
except AttributeError: if float(tool_dia) == tool_no:
# log.debug("No slots in the Excellon file") slot_cnt = len(self.slot_points_edit[tool_dia])
# Find no of slots for the current tool
for tool_dia in self.slot_points_edit:
if float(tool_dia) == tool_no:
slot_cnt = len(self.slot_points_edit[tool_dia])
self.tot_slot_cnt += slot_cnt self.tot_slot_cnt += slot_cnt
idd = QtWidgets.QTableWidgetItem('%d' % int(tool_id)) idd = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
idd.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) idd.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
@ -3141,6 +3147,10 @@ class AppExcEditor(QtCore.QObject):
poly = x.geo poly = x.geo
poly = poly.buffer(-radius) poly = poly.buffer(-radius)
if not poly.is_valid or poly.is_empty:
print("Polygon not valid: %s" % str(poly.wkt))
continue
xmin, ymin, xmax, ymax = poly.bounds xmin, ymin, xmax, ymax = poly.bounds
line_one = LineString([(xmin, ymin), (xmax, ymax)]).intersection(poly).length line_one = LineString([(xmin, ymin), (xmax, ymax)]).intersection(poly).length
line_two = LineString([(xmin, ymax), (xmax, ymin)]).intersection(poly).length line_two = LineString([(xmin, ymax), (xmax, ymin)]).intersection(poly).length
@ -3197,59 +3207,61 @@ class AppExcEditor(QtCore.QObject):
current_tool += 1 current_tool += 1
# create the self.tools for the new Excellon object (the one with edited content) # create the self.tools for the new Excellon object (the one with edited content)
name = str(current_tool) if current_tool not in self.new_tools:
spec = {"C": float(tool_dia[0])} self.new_tools[current_tool] = {}
self.new_tools[name] = spec self.new_tools[current_tool]['tooldia'] = float(tool_dia[0])
# add in self.tools the 'solid_geometry' key, the value (a list) is populated below # add in self.tools the 'solid_geometry' key, the value (a list) is populated below
self.new_tools[name]['solid_geometry'] = [] self.new_tools[current_tool]['solid_geometry'] = []
# create the self.drills for the new Excellon object (the one with edited content) # create the self.drills for the new Excellon object (the one with edited content)
for point in tool_dia[1]: for point in tool_dia[1]:
self.new_drills.append( try:
{ self.new_tools[current_tool]['drills'].append(Point(point))
'point': Point(point), except KeyError:
'tool': str(current_tool) self.new_tools[current_tool]['drills'] = [Point(point)]
}
)
# repopulate the 'solid_geometry' for each tool # repopulate the 'solid_geometry' for each tool
poly = Point(point).buffer(float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4)) poly = Point(point).buffer(float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4))
self.new_tools[name]['solid_geometry'].append(poly) self.new_tools[current_tool]['solid_geometry'].append(poly)
ordered_edited_slot_points = sorted(zip(edited_slot_points.keys(), edited_slot_points.values())) ordered_edited_slot_points = sorted(zip(edited_slot_points.keys(), edited_slot_points.values()))
for tool_dia in ordered_edited_slot_points: for tool_dia in ordered_edited_slot_points:
tool_exist_flag = False tool_exist_flag = False
for tool in self.new_tools: for tool in self.new_tools:
if tool_dia[0] == self.new_tools[tool]["C"]: if tool_dia[0] == self.new_tools[tool]["tooldia"]:
current_tool = tool current_tool = tool
tool_exist_flag = True tool_exist_flag = True
break break
if tool_exist_flag is False: if tool_exist_flag is False:
current_tool += 1 current_tool += 1
# create the self.tools for the new Excellon object (the one with edited content) # create the self.tools for the new Excellon object (the one with edited content)
name = str(current_tool) if current_tool not in self.new_tools:
spec = {"C": float(tool_dia[0])} self.new_tools[current_tool] = {}
self.new_tools[name] = spec self.new_tools[current_tool]['tooldia'] = float(tool_dia[0])
# add in self.tools the 'solid_geometry' key, the value (a list) is populated below # add in self.tools the 'solid_geometry' key, the value (a list) is populated below
self.new_tools[name]['solid_geometry'] = [] self.new_tools[current_tool]['solid_geometry'] = []
# create the self.slots for the new Excellon object (the one with edited content) # create the self.slots for the new Excellon object (the one with edited content)
for coord_dict in tool_dia[1]: for coord_dict in tool_dia[1]:
self.new_slots.append( slot = (
{ Point(coord_dict['start']),
'start': Point(coord_dict['start']), Point(coord_dict['stop'])
'stop': Point(coord_dict['stop']),
'tool': str(current_tool)
}
) )
try:
self.new_tools[current_tool]['slots'].append(slot)
except KeyError:
self.new_tools[current_tool]['slots'] = [slot]
# repopulate the 'solid_geometry' for each tool # repopulate the 'solid_geometry' for each tool
poly = LineString([coord_dict['start'], coord_dict['stop']]).buffer( poly = LineString([coord_dict['start'], coord_dict['stop']]).buffer(
float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4) float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4)
) )
self.new_tools[str(current_tool)]['solid_geometry'].append(poly) self.new_tools[current_tool]['solid_geometry'].append(poly)
if self.is_modified is True: if self.is_modified is True:
if "_edit" in self.edited_obj_name: if "_edit" in self.edited_obj_name:

View File

@ -682,7 +682,7 @@ class NumericalEvalEntry(EvalEntry):
return evaled return evaled
class NumericalEvalTupleEntry(FCEntry): class NumericalEvalTupleEntry(EvalEntry):
""" """
Will return a text value. Accepts only float numbers and formulas using the operators: /,*,+,-,% Will return a text value. Accepts only float numbers and formulas using the operators: /,*,+,-,%
""" """

View File

@ -138,7 +138,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
self.ser_attrs += ['options', 'kind'] self.ser_attrs += ['options', 'kind']
@staticmethod @staticmethod
def merge(exc_list, exc_final, decimals=None): def merge(exc_list, exc_final, decimals=None, fuse_tools=True):
""" """
Merge Excellon objects found in exc_list parameter into exc_final object. Merge Excellon objects found in exc_list parameter into exc_final object.
Options are always copied from source . Options are always copied from source .
@ -153,31 +153,31 @@ class ExcellonObject(FlatCAMObj, Excellon):
:type exc_list: list :type exc_list: list
:param exc_final: Destination ExcellonObject object. :param exc_final: Destination ExcellonObject object.
:type exc_final: class :type exc_final: class
:param decimals: The number of decimals to be used for diameters
:type decimals: int
:param fuse_tools: If True will try to fuse tools of the same diameter for the Excellon objects
:type fuse_tools: bool
:return: None :return: None
""" """
if exc_final.tools is None:
exc_final.tools = {}
if decimals is None: if decimals is None:
decimals = 4 decimals = 4
decimals_exc = decimals decimals_exc = decimals
# flag to signal that we need to reorder the tools dictionary and drills and slots lists
flag_order = False
try: try:
flattened_list = list(itertools.chain(*exc_list)) flattened_list = list(itertools.chain(*exc_list))
except TypeError: except TypeError:
flattened_list = exc_list 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 new_tools = {}
# values will be list of Shapely Points; for drills total_geo = []
custom_dict_drills = defaultdict(list) toolid = 0
# 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 = defaultdict(list)
for exc in flattened_list: for exc in flattened_list:
# copy options of the current excellon obj to the final excellon obj # copy options of the current excellon obj to the final excellon obj
# only the last object options will survive
for option in exc.options: for option in exc.options:
if option != 'name': if option != 'name':
try: try:
@ -185,129 +185,47 @@ class ExcellonObject(FlatCAMObj, Excellon):
except Exception: except Exception:
exc.app.log.warning("Failed to copy option.", option) exc.app.log.warning("Failed to copy option.", option)
for drill in exc.drills: for tool in exc.tools:
exc_tool_dia = float('%.*f' % (decimals_exc, exc.tools[drill['tool']]['C'])) toolid += 1
custom_dict_drills[exc_tool_dia].append(drill['point']) new_tools[toolid] = exc.tools[tool]
for slot in exc.slots:
exc_tool_dia = float('%.*f' % (decimals_exc, exc.tools[slot['tool']]['C']))
custom_dict_slots[exc_tool_dia].append([slot['start'], slot['stop']])
exc_final.tools = deepcopy(new_tools)
# add the zeros and units to the exc_final object # add the zeros and units to the exc_final object
exc_final.zeros = exc.zeros exc_final.zeros = exc.zeros
exc_final.units = exc.units exc_final.units = exc.units
total_geo += exc.solid_geometry
# ########################################## exc_final.solid_geometry = total_geo
# Here we add data to the exc_final object #
# ##########################################
# variable to make tool_name for the tools fused_tools_dict = {}
current_tool = 0 if exc_final.tools and fuse_tools:
# The tools diameter are now the keys in the drill_dia dict and the values are the Shapely Points in case of toolid = 0
# drills for tool, tool_dict in exc_final.tools.items():
for tool_dia in custom_dict_drills: current_tooldia = float('%.*f' % (decimals_exc, tool_dict['tooldia']))
# we create a tool name for each key in the drill_dia dict (the key is a unique drill diameter) toolid += 1
current_tool += 1
tool_name = str(current_tool) # calculate all diameters in fused_tools_dict
spec = {"C": float(tool_dia)} all_dia = []
exc_final.tools[tool_name] = spec if fused_tools_dict:
for f_tool in fused_tools_dict:
all_dia.append(float('%.*f' % (decimals_exc, fused_tools_dict[f_tool]['tooldia'])))
# rebuild the drills list of dict's that belong to the exc_final object if current_tooldia in all_dia:
for point in custom_dict_drills[tool_dia]: # find tool for current_tooldia in fuse_tools
exc_final.drills.append( t = None
{ for f_tool in fused_tools_dict:
"point": point, if fused_tools_dict[f_tool]['tooldia'] == current_tooldia:
"tool": str(current_tool) t = f_tool
}
)
# 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 break
if t:
fused_tools_dict[t]['drills'] += tool_dict['drills']
fused_tools_dict[t]['slots'] += tool_dict['slots']
fused_tools_dict[t]['solid_geometry'] += tool_dict['solid_geometry']
else:
fused_tools_dict[toolid] = tool_dict
fused_tools_dict[toolid]['tooldia'] = current_tooldia
# rebuild the slots list of dict's that belong to the exc_final object exc_final.tools = fused_tools_dict
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('%.*f' % (decimals_exc, 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('%.*f' % (decimals_exc, 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 = {}
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 # create the geometry for the exc_final object
exc_final.create_geometry() exc_final.create_geometry()
@ -351,7 +269,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
sort = [] sort = []
for k, v in list(self.tools.items()): for k, v in list(self.tools.items()):
sort.append((k, v.get('C'))) sort.append((k, v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1]) sorted_tools = sorted(sort, key=lambda t1: t1[1])
tools = [i[0] for i in sorted_tools] tools = [i[0] for i in sorted_tools]
@ -370,23 +288,23 @@ class ExcellonObject(FlatCAMObj, Excellon):
slot_cnt = 0 # variable to store the nr of slots per tool slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool # Find no of drills for the current tool
for drill in self.drills: try:
if drill['tool'] == tool_no: drill_cnt = len(self.tools[tool_no]['drills'])
drill_cnt += 1 except KeyError:
drill_cnt = 0
self.tot_drill_cnt += drill_cnt self.tot_drill_cnt += drill_cnt
# Find no of slots for the current tool # Find no of slots for the current tool
for slot in self.slots: try:
if slot['tool'] == tool_no: slot_cnt = len(self.tools[tool_no]['slots'])
slot_cnt += 1 except KeyError:
slot_cnt = 0
self.tot_slot_cnt += slot_cnt self.tot_slot_cnt += slot_cnt
exc_id_item = QtWidgets.QTableWidgetItem('%d' % int(tool_no)) exc_id_item = QtWidgets.QTableWidgetItem('%d' % int(tool_no))
exc_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) exc_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.tools[tool_no]['C'])) dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.tools[tool_no]['tooldia']))
dia_item.setFlags(QtCore.Qt.ItemIsEnabled) dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
drill_count_item = QtWidgets.QTableWidgetItem('%d' % drill_cnt) drill_count_item = QtWidgets.QTableWidgetItem('%d' % drill_cnt)
@ -508,14 +426,26 @@ class ExcellonObject(FlatCAMObj, Excellon):
self.ui.tools_table.setMinimumHeight(self.ui.tools_table.getHeight()) self.ui.tools_table.setMinimumHeight(self.ui.tools_table.getHeight())
self.ui.tools_table.setMaximumHeight(self.ui.tools_table.getHeight()) self.ui.tools_table.setMaximumHeight(self.ui.tools_table.getHeight())
if not self.drills: # find if we have drills:
has_drills = None
for tt in self.tools:
if 'drills' in self.tools[tt] and self.tools[tt]['drills']:
has_drills = True
break
if has_drills is None:
self.ui.tooldia_entry.hide() self.ui.tooldia_entry.hide()
self.ui.generate_milling_button.hide() self.ui.generate_milling_button.hide()
else: else:
self.ui.tooldia_entry.show() self.ui.tooldia_entry.show()
self.ui.generate_milling_button.show() self.ui.generate_milling_button.show()
if not self.slots: # find if we have slots
has_slots = None
for tt in self.tools:
if 'slots' in self.tools[tt] and self.tools[tt]['slots']:
has_slots = True
break
if has_slots is None:
self.ui.slot_tooldia_entry.hide() self.ui.slot_tooldia_entry.hide()
self.ui.generate_milling_slots_button.hide() self.ui.generate_milling_slots_button.hide()
else: else:
@ -962,7 +892,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
:rtype: list :rtype: list
""" """
return [str(x.text()) for x in self.ui.tools_table.selectedItems()] return [x.text() for x in self.ui.tools_table.selectedItems()]
def get_selected_tools_table_items(self): def get_selected_tools_table_items(self):
""" """
@ -1017,23 +947,37 @@ class ExcellonObject(FlatCAMObj, Excellon):
excellon_code = '' excellon_code = ''
# store here if the file has slots, return 1 if any slots, 0 if only drills # store here if the file has slots, return 1 if any slots, 0 if only drills
has_slots = 0 slots_in_file = 0
# find if we have drills:
has_drills = None
for tt in self.tools:
if 'drills' in self.tools[tt] and self.tools[tt]['drills']:
has_drills = True
break
# find if we have slots:
has_slots = None
for tt in self.tools:
if 'slots' in self.tools[tt] and self.tools[tt]['slots']:
has_slots = True
slots_in_file = 1
break
# drills processing # drills processing
try: try:
if self.drills: if has_drills:
length = whole + fract length = whole + fract
for tool in self.tools: for tool in self.tools:
excellon_code += 'T0%s\n' % str(tool) if int(tool) < 10 else 'T%s\n' % str(tool) excellon_code += 'T0%s\n' % str(tool) if int(tool) < 10 else 'T%s\n' % str(tool)
for drill in self.drills: for drill in self.tools[tool]['drills']:
if form == 'dec' and tool == drill['tool']: if form == 'dec':
drill_x = drill['point'].x * factor drill_x = drill.x * factor
drill_y = drill['point'].y * factor drill_y = drill.y * factor
excellon_code += "X{:.{dec}f}Y{:.{dec}f}\n".format(drill_x, drill_y, dec=fract) excellon_code += "X{:.{dec}f}Y{:.{dec}f}\n".format(drill_x, drill_y, dec=fract)
elif e_zeros == 'LZ' and tool == drill['tool']: elif e_zeros == 'LZ':
drill_x = drill['point'].x * factor drill_x = drill.x * factor
drill_y = drill['point'].y * factor drill_y = drill.y * factor
exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract) exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract)
exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract) exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract)
@ -1053,9 +997,9 @@ class ExcellonObject(FlatCAMObj, Excellon):
excellon_code += "X{xform}Y{yform}\n".format(xform=exc_x_formatted, excellon_code += "X{xform}Y{yform}\n".format(xform=exc_x_formatted,
yform=exc_y_formatted) yform=exc_y_formatted)
elif tool == drill['tool']: else:
drill_x = drill['point'].x * factor drill_x = drill.x * factor
drill_y = drill['point'].y * factor drill_y = drill.y * factor
exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract).replace('.', '') exc_x_formatted = "{:.{dec}f}".format(drill_x, dec=fract).replace('.', '')
exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract).replace('.', '') exc_y_formatted = "{:.{dec}f}".format(drill_y, dec=fract).replace('.', '')
@ -1071,8 +1015,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
# slots processing # slots processing
try: try:
if self.slots: if has_slots:
has_slots = 1
for tool in self.tools: for tool in self.tools:
excellon_code += 'G05\n' excellon_code += 'G05\n'
@ -1081,12 +1024,12 @@ class ExcellonObject(FlatCAMObj, Excellon):
else: else:
excellon_code += 'T' + str(tool) + '\n' excellon_code += 'T' + str(tool) + '\n'
for slot in self.slots: for slot in self.tools[tool]['slots']:
if form == 'dec' and tool == slot['tool']: if form == 'dec':
start_slot_x = slot['start'].x * factor start_slot_x = slot.x * factor
start_slot_y = slot['start'].y * factor start_slot_y = slot.y * factor
stop_slot_x = slot['stop'].x * factor stop_slot_x = slot.x * factor
stop_slot_y = slot['stop'].y * factor stop_slot_y = slot.y * factor
if slot_type == 'routing': if slot_type == 'routing':
excellon_code += "G00X{:.{dec}f}Y{:.{dec}f}\nM15\n".format(start_slot_x, excellon_code += "G00X{:.{dec}f}Y{:.{dec}f}\nM15\n".format(start_slot_x,
start_slot_y, start_slot_y,
@ -1099,11 +1042,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
start_slot_x, start_slot_y, stop_slot_x, stop_slot_y, dec=fract start_slot_x, start_slot_y, stop_slot_x, stop_slot_y, dec=fract
) )
elif e_zeros == 'LZ' and tool == slot['tool']: elif e_zeros == 'LZ':
start_slot_x = slot['start'].x * factor start_slot_x = slot.x * factor
start_slot_y = slot['start'].y * factor start_slot_y = slot.y * factor
stop_slot_x = slot['stop'].x * factor stop_slot_x = slot.x * factor
stop_slot_y = slot['stop'].y * factor stop_slot_y = slot.y * factor
start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '') start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '')
start_slot_y_formatted = "{:.{dec}f}".format(start_slot_y, dec=fract).replace('.', '') start_slot_y_formatted = "{:.{dec}f}".format(start_slot_y, dec=fract).replace('.', '')
@ -1139,11 +1082,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
xstart=start_slot_x_formatted, ystart=start_slot_y_formatted, xstart=start_slot_x_formatted, ystart=start_slot_y_formatted,
xstop=stop_slot_x_formatted, ystop=stop_slot_y_formatted xstop=stop_slot_x_formatted, ystop=stop_slot_y_formatted
) )
elif tool == slot['tool']: else:
start_slot_x = slot['start'].x * factor start_slot_x = slot.x * factor
start_slot_y = slot['start'].y * factor start_slot_y = slot.y * factor
stop_slot_x = slot['stop'].x * factor stop_slot_x = slot.x * factor
stop_slot_y = slot['stop'].y * factor stop_slot_y = slot.y * factor
length = whole + fract length = whole + fract
start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '') start_slot_x_formatted = "{:.{dec}f}".format(start_slot_x, dec=fract).replace('.', '')
@ -1170,11 +1113,11 @@ class ExcellonObject(FlatCAMObj, Excellon):
except Exception as e: except Exception as e:
log.debug(str(e)) log.debug(str(e))
if not self.drills and not self.slots: if not has_drills and not has_slots:
log.debug("FlatCAMObj.ExcellonObject.export_excellon() --> Excellon Object is empty: no drills, no slots.") log.debug("FlatCAMObj.ExcellonObject.export_excellon() --> Excellon Object is empty: no drills, no slots.")
return 'fail' return 'fail'
return has_slots, excellon_code return slots_in_file, excellon_code
def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False): def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False):
""" """
@ -1215,7 +1158,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
sort = [] sort = []
for k, v in self.tools.items(): for k, v in self.tools.items():
sort.append((k, v.get('C'))) sort.append((k, v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1]) sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all": if tools == "all":
@ -1223,19 +1166,15 @@ class ExcellonObject(FlatCAMObj, Excellon):
log.debug("Tools 'all' and sorted are: %s" % str(tools)) log.debug("Tools 'all' and sorted are: %s" % str(tools))
if len(tools) == 0: if len(tools) == 0:
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' % _("Please select one or more tools from the list and try again."))
_("Please select one or more tools from the list and try again."))
return False, "Error: No tools." return False, "Error: No tools."
for tool in tools: for tool in tools:
if tooldia > self.tools[tool]["C"]: if tooldia > self.tools[tool]["tooldia"]:
self.app.inform.emit( mseg = '[ERROR_NOTCL] %s %s: %s' % (_("Milling tool for DRILLS is larger than hole size. Cancelled."),
'[ERROR_NOTCL] %s %s: %s' % ( _("Tool"),
_("Milling tool for DRILLS is larger than hole size. Cancelled."), str(tool))
_("Tool"), self.app.inform.emit(mseg)
str(tool)
)
)
return False, "Error: Milling tool is larger than hole." return False, "Error: Milling tool is larger than hole."
def geo_init(geo_obj, app_obj): def geo_init(geo_obj, app_obj):
@ -1266,15 +1205,13 @@ class ExcellonObject(FlatCAMObj, Excellon):
# in case that the tool used has the same diameter with the hole, and since the maximum resolution # in case that the tool used has the same diameter with the hole, and since the maximum resolution
# for FlatCAM is 6 decimals, # for FlatCAM is 6 decimals,
# we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero" # we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero"
for hole in self.drills: for tool in tools:
if hole['tool'] in tools: for drill in self.tools[tool]['drills']:
buffer_value = self.tools[hole['tool']]["C"] / 2 - tooldia / 2 buffer_value = self.tools[tool]['tooldia'] / 2 - tooldia / 2
if buffer_value == 0: if buffer_value == 0:
geo_obj.solid_geometry.append( geo_obj.solid_geometry.append(drill.buffer(0.0000001).exterior)
Point(hole['point']).buffer(0.0000001).exterior)
else: else:
geo_obj.solid_geometry.append( geo_obj.solid_geometry.append(drill.buffer(buffer_value).exterior)
Point(hole['point']).buffer(buffer_value).exterior)
if use_thread: if use_thread:
def geo_thread(a_obj): def geo_thread(a_obj):
@ -1329,7 +1266,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
sort = [] sort = []
for k, v in self.tools.items(): for k, v in self.tools.items():
sort.append((k, v.get('C'))) sort.append((k, v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1]) sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all": if tools == "all":
@ -1337,14 +1274,13 @@ class ExcellonObject(FlatCAMObj, Excellon):
log.debug("Tools 'all' and sorted are: %s" % str(tools)) log.debug("Tools 'all' and sorted are: %s" % str(tools))
if len(tools) == 0: if len(tools) == 0:
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' % _("Please select one or more tools from the list and try again."))
_("Please select one or more tools from the list and try again."))
return False, "Error: No tools." return False, "Error: No tools."
for tool in tools: for tool in tools:
# I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
adj_toolstable_tooldia = float('%.*f' % (self.decimals, float(tooldia))) adj_toolstable_tooldia = float('%.*f' % (self.decimals, float(tooldia)))
adj_file_tooldia = float('%.*f' % (self.decimals, float(self.tools[tool]["C"]))) adj_file_tooldia = float('%.*f' % (self.decimals, float(self.tools[tool]["tooldia"])))
if adj_toolstable_tooldia > adj_file_tooldia + 0.0001: if adj_toolstable_tooldia > adj_file_tooldia + 0.0001:
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Milling tool for SLOTS is larger than hole size. Cancelled.")) _("Milling tool for SLOTS is larger than hole size. Cancelled."))
@ -1369,24 +1305,24 @@ class ExcellonObject(FlatCAMObj, Excellon):
# in case that the tool used has the same diameter with the hole, and since the maximum resolution # in case that the tool used has the same diameter with the hole, and since the maximum resolution
# for FlatCAM is 6 decimals, # for FlatCAM is 6 decimals,
# we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero" # we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero"
for slot in self.slots: for tool in tools:
if slot['tool'] in tools: for slot in self.tools[tool]['slots']:
toolstable_tool = float('%.*f' % (self.decimals, float(tooldia))) toolstable_tool = float('%.*f' % (self.decimals, float(tooldia)))
file_tool = float('%.*f' % (self.decimals, float(self.tools[tool]["C"]))) file_tool = float('%.*f' % (self.decimals, float(self.tools[tool]["tooldia"])))
# I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
# for the file_tool (tooldia actually) # for the file_tool (tooldia actually)
buffer_value = float(file_tool / 2) - float(toolstable_tool / 2) + 0.0001 buffer_value = float(file_tool / 2) - float(toolstable_tool / 2) + 0.0001
if buffer_value == 0: if buffer_value == 0:
start = slot['start'] start = slot[0]
stop = slot['stop'] stop = slot[1]
lines_string = LineString([start, stop]) lines_string = LineString([start, stop])
poly = lines_string.buffer(0.0000001, int(self.geo_steps_per_circle)).exterior poly = lines_string.buffer(0.0000001, int(self.geo_steps_per_circle)).exterior
geo_obj.solid_geometry.append(poly) geo_obj.solid_geometry.append(poly)
else: else:
start = slot['start'] start = slot[0]
stop = slot['stop'] stop = slot[1]
lines_string = LineString([start, stop]) lines_string = LineString([start, stop])
poly = lines_string.buffer(buffer_value, int(self.geo_steps_per_circle)).exterior poly = lines_string.buffer(buffer_value, int(self.geo_steps_per_circle)).exterior
@ -1522,7 +1458,7 @@ class ExcellonObject(FlatCAMObj, Excellon):
# tool number) it means that there are 3 rows (1 tool and 2 totals). # tool number) it means that there are 3 rows (1 tool and 2 totals).
# in this case regardless of the selection status of that tool, use it. # in this case regardless of the selection status of that tool, use it.
if self.ui.tools_table.rowCount() == 3: if self.ui.tools_table.rowCount() == 3:
tools.append(self.ui.tools_table.item(0, 0).text()) tools.append(int(self.ui.tools_table.item(0, 0).text()))
else: else:
self.app.inform.emit('[ERROR_NOTCL] %s' % self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Please select one or more tools from the list and try again.")) _("Please select one or more tools from the list and try again."))

View File

@ -40,30 +40,13 @@ class Excellon(Geometry):
================ ==================================== ================ ====================================
Key Value Key Value
================ ==================================== ================ ====================================
C Diameter of the tool tooldia Diameter of the tool
solid_geometry Geometry list for each tool drills List that store the Shapely Points for drill points
slots List that store the Shapely Points for slots. Each is a tuple: (start_point, stop_point
data dictionary which holds the options for each tool data dictionary which holds the options for each tool
Others Not supported (Ignored). solid_geometry Geometry list for each tool
================ ==================================== ================ ====================================
* ``drills`` (list): Each is a dictionary:
================ ====================================
Key Value
================ ====================================
point (Shapely.Point) Where to drill
tool (str) A key in ``tools``
================ ====================================
* ``slots`` (list): Each is a dictionary
================ ====================================
Key Value
================ ====================================
start (Shapely.Point) Start point of the slot
stop (Shapely.Point) Stop point of the slot
tool (str) A key in ``tools``
================ ====================================
""" """
defaults = { defaults = {
@ -96,10 +79,6 @@ class Excellon(Geometry):
# dictionary to store tools, see above for description # dictionary to store tools, see above for description
self.tools = {} self.tools = {}
# list to store the drills, see above for description
self.drills = []
# self.slots (list) to store the slots; each is a dictionary
self.slots = []
self.source_file = '' self.source_file = ''
@ -110,9 +89,6 @@ class Excellon(Geometry):
self.match_routing_start = None self.match_routing_start = None
self.match_routing_stop = None self.match_routing_stop = None
self.num_tools = [] # List for keeping the tools sorted
self.index_per_tool = {} # Dictionary to store the indexed points for each tool
# ## IN|MM -> Units are inherited from Geometry # ## IN|MM -> Units are inherited from Geometry
self.units = self.app.defaults['units'] self.units = self.app.defaults['units']
self.units_found = self.app.defaults['units'] self.units_found = self.app.defaults['units']
@ -142,9 +118,8 @@ class Excellon(Geometry):
# Attributes to be included in serialization # Attributes to be included in serialization
# Always append to it because it carries contents # Always append to it because it carries contents
# from Geometry. # from Geometry.
self.ser_attrs += ['tools', 'drills', 'zeros', 'excellon_format_upper_mm', 'excellon_format_lower_mm', self.ser_attrs += ['tools', 'zeros', 'excellon_format_upper_mm', 'excellon_format_lower_mm',
'excellon_format_upper_in', 'excellon_format_lower_in', 'excellon_units', 'slots', 'excellon_format_upper_in', 'excellon_format_lower_in', 'excellon_units', 'source_file']
'source_file']
# ### Patterns #### # ### Patterns ####
# Regex basics: # Regex basics:
@ -346,14 +321,22 @@ class Excellon(Geometry):
if match.group(2): if match.group(2):
name_tool += 1 name_tool += 1
# ---------- add a TOOL ------------ #
if name_tool not in self.tools:
self.tools[name_tool] = {}
if line_units == 'MILS': if line_units == 'MILS':
spec = {"C": (float(match.group(2)) / 1000)} spec = {
self.tools[str(name_tool)] = spec 'tooldia': (float(match.group(2)) / 1000)
log.debug("Tool definition: %s %s" % (name_tool, spec)) }
self.tools[name_tool]['tooldia'] = (float(match.group(2)) / 1000)
log.debug("Tool definition: %d %s" % (name_tool, spec))
else: else:
spec = {"C": float(match.group(2))} spec = {
self.tools[str(name_tool)] = spec 'tooldia': float(match.group(2))
log.debug("Tool definition: %s %s" % (name_tool, spec)) }
self.tools[name_tool]['tooldia'] = float(match.group(2))
log.debug("Tool definition: %d %s" % (name_tool, spec))
spec['solid_geometry'] = [] spec['solid_geometry'] = []
continue continue
# search for Altium Excellon Format / Sprint Layout who is included as a comment # search for Altium Excellon Format / Sprint Layout who is included as a comment
@ -400,7 +383,7 @@ class Excellon(Geometry):
lower_tools = set() lower_tools = set()
if not self.excellon_units_found and self.tools: if not self.excellon_units_found and self.tools:
for tool in self.tools: for tool in self.tools:
tool_dia = float(self.tools[tool]['C']) tool_dia = float(self.tools[tool]['tooldia'])
lower_tools.add(tool_dia) if tool_dia <= 0.1 else greater_tools.add(tool_dia) lower_tools.add(tool_dia) if tool_dia <= 0.1 else greater_tools.add(tool_dia)
assumed_units = "IN" if len(lower_tools) > len(greater_tools) else "MM" assumed_units = "IN" if len(lower_tools) > len(greater_tools) else "MM"
@ -434,12 +417,12 @@ class Excellon(Geometry):
# ## Tool change ### # ## Tool change ###
match = self.toolsel_re.search(eline) match = self.toolsel_re.search(eline)
if match: if match:
current_tool = str(int(match.group(1))) current_tool = int(match.group(1))
log.debug("Tool change: %s" % current_tool) log.debug("Tool change: %s" % current_tool)
if bool(headerless): if bool(headerless):
match = self.toolset_hl_re.search(eline) match = self.toolset_hl_re.search(eline)
if match: if match:
name = str(int(match.group(1))) name = int(match.group(1))
try: try:
diam = float(match.group(2)) diam = float(match.group(2))
except Exception: except Exception:
@ -467,8 +450,13 @@ class Excellon(Geometry):
else: else:
diam = (self.toolless_diam + (int(current_tool) - 1) / 100) / 25.4 diam = (self.toolless_diam + (int(current_tool) - 1) / 100) / 25.4
spec = {"C": diam, 'solid_geometry': []} # ---------- add a TOOL ------------ #
self.tools[name] = spec spec = {"tooldia": diam, 'solid_geometry': []}
if name not in self.tools:
self.tools[name] = {}
self.tools[name]['tooldia'] = diam
self.tools[name]['solid_geometry'] = []
log.debug("Tool definition out of header: %s %s" % (name, spec)) log.debug("Tool definition out of header: %s %s" % (name, spec))
continue continue
@ -479,8 +467,8 @@ class Excellon(Geometry):
match1 = self.stop_re.search(eline) match1 = self.stop_re.search(eline)
if match or match1: if match or match1:
name_tool += 1 name_tool += 1
current_tool = str(name_tool) current_tool = name_tool
log.debug("Tool change for Allegro type of Excellon: %s" % current_tool) log.debug("Tool change for Allegro type of Excellon: %d" % current_tool)
continue continue
# ## Slots parsing for drilled slots (contain G85) # ## Slots parsing for drilled slots (contain G85)
@ -546,7 +534,7 @@ class Excellon(Geometry):
# store current tool diameter as slot diameter # store current tool diameter as slot diameter
slot_dia = 0.05 slot_dia = 0.05
try: try:
slot_dia = float(self.tools[current_tool]['C']) slot_dia = float(self.tools[current_tool]['tooldia'])
except Exception: except Exception:
pass pass
log.debug( log.debug(
@ -556,13 +544,17 @@ class Excellon(Geometry):
) )
) )
self.slots.append( # ---------- add a slot ------------ #
{ slot = (
'start': Point(slot_start_x, slot_start_y), Point(slot_start_x, slot_start_y),
'stop': Point(slot_stop_x, slot_stop_y), Point(slot_stop_x, slot_stop_y)
'tool': current_tool
}
) )
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue continue
# Slot coordinates with period: Use literally. ### # Slot coordinates with period: Use literally. ###
@ -616,7 +608,7 @@ class Excellon(Geometry):
# store current tool diameter as slot diameter # store current tool diameter as slot diameter
slot_dia = 0.05 slot_dia = 0.05
try: try:
slot_dia = float(self.tools[current_tool]['C']) slot_dia = float(self.tools[current_tool]['tooldia'])
except Exception: except Exception:
pass pass
log.debug( log.debug(
@ -626,13 +618,17 @@ class Excellon(Geometry):
) )
) )
self.slots.append( # ---------- add a Slot ------------ #
{ slot = (
'start': Point(slot_start_x, slot_start_y), Point(slot_start_x, slot_start_y),
'stop': Point(slot_stop_x, slot_stop_y), Point(slot_stop_x, slot_stop_y)
'tool': current_tool
}
) )
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue continue
# ## Coordinates without period # ## # ## Coordinates without period # ##
@ -660,7 +656,14 @@ class Excellon(Geometry):
coordx += repeating_x coordx += repeating_x
if repeating_y: if repeating_y:
coordy += repeating_y coordy += repeating_y
self.drills.append({'point': Point((coordx, coordy)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((coordx, coordy)))
else:
self.tools[current_tool]['drills'] = [Point((coordx, coordy))]
repeat -= 1 repeat -= 1
current_x = coordx current_x = coordx
@ -710,19 +713,31 @@ class Excellon(Geometry):
self.routing_flag = 1 self.routing_flag = 1
slot_stop_x = x slot_stop_x = x
slot_stop_y = y slot_stop_y = y
self.slots.append(
{ # ---------- add a Slot ------------ #
'start': Point(slot_start_x, slot_start_y), slot = (
'stop': Point(slot_stop_x, slot_stop_y), Point(slot_start_x, slot_start_y),
'tool': current_tool Point(slot_stop_x, slot_stop_y)
}
) )
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue continue
if self.match_routing_start is None and self.match_routing_stop is None: if self.match_routing_start is None and self.match_routing_stop is None:
# signal that there are drill operations # signal that there are drill operations
self.defaults['excellon_drills'] = True self.defaults['excellon_drills'] = True
self.drills.append({'point': Point((x, y)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((x, y)))
else:
self.tools[current_tool]['drills'] = [Point((x, y))]
# log.debug("{:15} {:8} {:8}".format(eline, x, y)) # log.debug("{:15} {:8} {:8}".format(eline, x, y))
continue continue
@ -778,13 +793,18 @@ class Excellon(Geometry):
self.routing_flag = 1 self.routing_flag = 1
slot_stop_x = x slot_stop_x = x
slot_stop_y = y slot_stop_y = y
self.slots.append(
{ # ---------- add a Slot ------------ #
'start': Point(slot_start_x, slot_start_y), slot = (
'stop': Point(slot_stop_x, slot_stop_y), Point(slot_start_x, slot_start_y),
'tool': current_tool Point(slot_stop_x, slot_stop_y)
}
) )
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'slots' in self.tools[current_tool]:
self.tools[current_tool]['slots'].apend(slot)
else:
self.tools[current_tool]['slots'] = [slot]
continue continue
if self.match_routing_start is None and self.match_routing_stop is None: if self.match_routing_start is None and self.match_routing_stop is None:
@ -792,7 +812,14 @@ class Excellon(Geometry):
if repeat == 0: if repeat == 0:
# signal that there are drill operations # signal that there are drill operations
self.defaults['excellon_drills'] = True self.defaults['excellon_drills'] = True
self.drills.append({'point': Point((x, y)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((x, y)))
else:
self.tools[current_tool]['drills'] = [Point((x, y))]
else: else:
coordx = x coordx = x
coordy = y coordy = y
@ -801,7 +828,15 @@ class Excellon(Geometry):
coordx = (repeat * x) + repeating_x coordx = (repeat * x) + repeating_x
if repeating_y: if repeating_y:
coordy = (repeat * y) + repeating_y coordy = (repeat * y) + repeating_y
self.drills.append({'point': Point((coordx, coordy)), 'tool': current_tool})
# ---------- add a Drill ------------ #
if current_tool not in self.tools:
self.tools[current_tool] = {}
if 'drills' in self.tools[current_tool]:
self.tools[current_tool]['drills'].append(Point((coordx, coordy)))
else:
self.tools[current_tool]['drills'] = [Point((coordx, coordy))]
repeat -= 1 repeat -= 1
repeating_x = repeating_y = 0 repeating_x = repeating_y = 0
# log.debug("{:15} {:8} {:8}".format(eline, x, y)) # log.debug("{:15} {:8} {:8}".format(eline, x, y))
@ -813,9 +848,14 @@ class Excellon(Geometry):
# ## Tool definitions # ## # ## Tool definitions # ##
match = self.toolset_re.search(eline) match = self.toolset_re.search(eline)
if match: if match:
name = str(int(match.group(1))) # ---------- add a TOOL ------------ #
name = int(match.group(1))
spec = {"C": float(match.group(2)), 'solid_geometry': []} spec = {"C": float(match.group(2)), 'solid_geometry': []}
self.tools[name] = spec if name not in self.tools:
self.tools[name] = {}
self.tools[name]['tooldia'] = float(match.group(2))
self.tools[name]['solid_geometry'] = []
log.debug("Tool definition: %s %s" % (name, spec)) log.debug("Tool definition: %s %s" % (name, spec))
continue continue
@ -977,8 +1017,9 @@ class Excellon(Geometry):
def create_geometry(self): def create_geometry(self):
""" """
Creates circles of the tool diameter at every point Creates circles of the tool diameter at every point
specified in ``self.drills``. Also creates geometries (polygons) specified in self.tools[tool]['drills'].
for the slots as specified in ``self.slots`` Also creates geometries (polygons)
for the slots as specified in self.tools[tool]['slots']
All the resulting geometry is stored into self.solid_geometry list. All the resulting geometry is stored into self.solid_geometry list.
The list self.solid_geometry has 2 elements: first is a dict with the drills geometry, The list self.solid_geometry has 2 elements: first is a dict with the drills geometry,
and second element is another similar dict that contain the slots geometry. and second element is another similar dict that contain the slots geometry.
@ -1001,36 +1042,35 @@ class Excellon(Geometry):
self.tools[tool]['solid_geometry'] = [] self.tools[tool]['solid_geometry'] = []
self.tools[tool]['data'] = {} self.tools[tool]['data'] = {}
for drill in self.drills: for tool in self.tools:
# poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0) tooldia = self.tools[tool]['tooldia']
if drill['tool'] == '':
self.app.inform.emit('[WARNING] %s' %
_("Excellon.create_geometry() -> a drill location was skipped "
"due of not having a tool associated.\n"
"Check the resulting GCode."))
log.debug("appParsers.ParseExcellon.Excellon.create_geometry() -> a drill location was skipped "
"due of not having a tool associated")
continue
tooldia = self.tools[drill['tool']]['C']
poly = drill['point'].buffer(tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
self.solid_geometry.append(poly)
tool_in_drills = drill['tool'] if 'drills' in self.tools[tool]:
self.tools[tool_in_drills]['solid_geometry'].append(poly) for drill in self.tools[tool]['drills']:
self.tools[tool_in_drills]['data'] = deepcopy(self.default_data) poly = drill.buffer(tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
for slot in self.slots: # add poly in the tools geometry
slot_tooldia = self.tools[slot['tool']]['C'] self.tools[tool]['solid_geometry'].append(poly)
start = slot['start'] self.tools[tool]['data'] = deepcopy(self.default_data)
stop = slot['stop']
lines_string = LineString([start, stop]) # add poly to the total solid geometry
poly = lines_string.buffer(slot_tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4)) self.solid_geometry.append(poly)
self.solid_geometry.append(poly)
if 'slots' in self.tools[tool]:
for slot in self.tools[tool]['slots']:
start = slot[0]
stop = slot[1]
lines_string = LineString([start, stop])
poly = lines_string.buffer(tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
# add poly in the tools geometry
self.tools[tool]['solid_geometry'].append(poly)
self.tools[tool]['data'] = deepcopy(self.default_data)
# add poly to the total solid geometry
self.solid_geometry.append(poly)
tool_in_slots = slot['tool']
self.tools[tool_in_slots]['solid_geometry'].append(poly)
self.tools[tool_in_slots]['data'] = deepcopy(self.default_data)
except Exception as e: except Exception as e:
log.debug("appParsers.ParseExcellon.Excellon.create_geometry() -> " log.debug("appParsers.ParseExcellon.Excellon.create_geometry() -> "
"Excellon geometry creation failed due of ERROR: %s" % str(e)) "Excellon geometry creation failed due of ERROR: %s" % str(e))
@ -1126,7 +1166,7 @@ class Excellon(Geometry):
# Tools # Tools
for tname in self.tools: for tname in self.tools:
self.tools[tname]["C"] *= factor self.tools[tname]["tooldia"] *= factor
self.create_geometry() self.create_geometry()
return factor return factor
@ -1173,31 +1213,39 @@ class Excellon(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
self.geo_len = len(self.drills) self.geo_len = len(self.tools)
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
self.old_disp_number = 0 self.old_disp_number = 0
self.el_count = 0 self.el_count = 0
# Drills for tool in self.tools:
for drill in self.drills: # Scale Drills
drill['point'] = affinity.scale(drill['point'], xfactor, yfactor, origin=(px, py)) if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.scale(drill, xfactor, yfactor, origin=(px, py)))
self.tools[tool]['drills'] = new_drills
# Scale Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.scale(slot[0], xfactor, yfactor, origin=(px, py))
new_stop = affinity.scale(slot[1], xfactor, yfactor, origin=(px, py))
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Scale solid_geometry
self.tools[tool]['solid_geometry'] = scale_geom(self.tools[tool]['solid_geometry'])
# update status display
self.el_count += 1 self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100])) disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100: if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number) self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number self.old_disp_number = disp_number
# scale solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = scale_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.scale(slot['stop'], xfactor, yfactor, origin=(px, py))
slot['start'] = affinity.scale(slot['start'], xfactor, yfactor, origin=(px, py))
self.create_geometry() self.create_geometry()
self.app.proc_container.new_text = '' self.app.proc_container.new_text = ''
@ -1231,31 +1279,39 @@ class Excellon(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
self.geo_len = len(self.drills) self.geo_len = len(self.tools)
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
self.old_disp_number = 0 self.old_disp_number = 0
self.el_count = 0 self.el_count = 0
# Drills for tool in self.tools:
for drill in self.drills: # Offset Drills
drill['point'] = affinity.translate(drill['point'], xoff=dx, yoff=dy) if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.translate(drill, xoff=dx, yoff=dy))
self.tools[tool]['drills'] = new_drills
# Offset Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.translate(slot[0], xoff=dx, yoff=dy)
new_stop = affinity.translate(slot[1], xoff=dx, yoff=dy)
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Offset solid_geometry
self.tools[tool]['solid_geometry'] = offset_geom(self.tools[tool]['solid_geometry'])
# update status display
self.el_count += 1 self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100])) disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100: if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number) self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number self.old_disp_number = disp_number
# offset solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = offset_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.translate(slot['stop'], xoff=dx, yoff=dy)
slot['start'] = affinity.translate(slot['start'], xoff=dx, yoff=dy)
# Recreate geometry # Recreate geometry
self.create_geometry() self.create_geometry()
self.app.proc_container.new_text = '' self.app.proc_container.new_text = ''
@ -1291,31 +1347,39 @@ class Excellon(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
self.geo_len = len(self.drills) self.geo_len = len(self.tools)
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
self.old_disp_number = 0 self.old_disp_number = 0
self.el_count = 0 self.el_count = 0
# Drills for tool in self.tools:
for drill in self.drills: # Offset Drills
drill['point'] = affinity.scale(drill['point'], xscale, yscale, origin=(px, py)) if 'drills' in self.tools[tool]:
new_drills = []
for drill in self.tools[tool]['drills']:
new_drills.append(affinity.scale(drill, xscale, yscale, origin=(px, py)))
self.tools[tool]['drills'] = new_drills
# Offset Slots
if 'slots' in self.tools[tool]:
new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.scale(slot[0], xscale, yscale, origin=(px, py))
new_stop = affinity.scale(slot[1], xscale, yscale, origin=(px, py))
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Offset solid_geometry
self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry'])
# update status display
self.el_count += 1 self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100])) disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100: if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number) self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number self.old_disp_number = disp_number
# mirror solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.scale(slot['stop'], xscale, yscale, origin=(px, py))
slot['start'] = affinity.scale(slot['start'], xscale, yscale, origin=(px, py))
# Recreate geometry # Recreate geometry
self.create_geometry() self.create_geometry()
self.app.proc_container.new_text = '' self.app.proc_container.new_text = ''
@ -1361,7 +1425,7 @@ class Excellon(Geometry):
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
self.geo_len = len(self.drills) self.geo_len = len(self.tools)
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
self.old_disp_number = 0 self.old_disp_number = 0
@ -1369,47 +1433,35 @@ class Excellon(Geometry):
if point is None: if point is None:
px, py = 0, 0 px, py = 0, 0
# Drills
for drill in self.drills:
drill['point'] = affinity.skew(drill['point'], angle_x, angle_y,
origin=(px, py))
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
# skew solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
# Slots
for slot in self.slots:
slot['stop'] = affinity.skew(slot['stop'], angle_x, angle_y, origin=(px, py))
slot['start'] = affinity.skew(slot['start'], angle_x, angle_y, origin=(px, py))
else: else:
px, py = point px, py = point
# Drills
for drill in self.drills:
drill['point'] = affinity.skew(drill['point'], angle_x, angle_y,
origin=(px, py))
self.el_count += 1 for tool in self.tools:
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100])) # Offset Drills
if self.old_disp_number < disp_number <= 100: if 'drills' in self.tools[tool]:
self.app.proc_container.update_view_text(' %d%%' % disp_number) new_drills = []
self.old_disp_number = disp_number for drill in self.tools[tool]['drills']:
new_drills.append(affinity.skew(drill, angle_x, angle_y, origin=(px, py)))
self.tools[tool]['drills'] = new_drills
# skew solid_geometry # Offset Slots
for tool in self.tools: if 'slots' in self.tools[tool]:
self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry']) new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.skew(slot[0], angle_x, angle_y, origin=(px, py))
new_stop = affinity.skew(slot[1], angle_x, angle_y, origin=(px, py))
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Slots # Offset solid_geometry
for slot in self.slots: self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
slot['stop'] = affinity.skew(slot['stop'], angle_x, angle_y, origin=(px, py))
slot['start'] = affinity.skew(slot['start'], angle_x, angle_y, origin=(px, py)) # update status display
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
self.create_geometry() self.create_geometry()
self.app.proc_container.new_text = '' self.app.proc_container.new_text = ''
@ -1441,58 +1493,50 @@ class Excellon(Geometry):
return obj return obj
else: else:
try: try:
return affinity.rotate(obj, angle, origin=(px, py)) return affinity.rotate(obj, angle, origin=orig)
except AttributeError: except AttributeError:
return obj return obj
# variables to display the percentage of work done # variables to display the percentage of work done
self.geo_len = 0 self.geo_len = 0
try: try:
self.geo_len = len(self.drills) self.geo_len = len(self.tools)
except TypeError: except TypeError:
self.geo_len = 1 self.geo_len = 1
self.old_disp_number = 0 self.old_disp_number = 0
self.el_count = 0 self.el_count = 0
if point is None: if point is None:
# Drills orig = 'center'
for drill in self.drills:
drill['point'] = affinity.rotate(drill['point'], angle, origin='center')
# rotate solid_geometry
for tool in self.tools:
self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'], origin='center')
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
# Slots
for slot in self.slots:
slot['stop'] = affinity.rotate(slot['stop'], angle, origin='center')
slot['start'] = affinity.rotate(slot['start'], angle, origin='center')
else: else:
px, py = point orig = point
# Drills
for drill in self.drills:
drill['point'] = affinity.rotate(drill['point'], angle, origin=(px, py))
self.el_count += 1 for tool in self.tools:
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100])) # Offset Drills
if self.old_disp_number < disp_number <= 100: if 'drills' in self.tools[tool]:
self.app.proc_container.update_view_text(' %d%%' % disp_number) new_drills = []
self.old_disp_number = disp_number for drill in self.tools[tool]['drills']:
new_drills.append(affinity.rotate(drill, angle, origin=orig))
self.tools[tool]['drills'] = new_drills
# rotate solid_geometry # Offset Slots
for tool in self.tools: if 'slots' in self.tools[tool]:
self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry']) new_slots = []
for slot in self.tools[tool]['slots']:
new_start = affinity.rotate(slot[0], angle, origin=orig)
new_stop = affinity.rotate(slot[1], angle, origin=orig)
new_slot = (new_start, new_stop)
new_slots.append(new_slot)
# Slots # Offset solid_geometry
for slot in self.slots: self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'], origin=orig)
slot['stop'] = affinity.rotate(slot['stop'], angle, origin=(px, py))
slot['start'] = affinity.rotate(slot['start'], angle, origin=(px, py)) # update status display
self.el_count += 1
disp_number = int(np.interp(self.el_count, [0, self.geo_len], [0, 100]))
if self.old_disp_number < disp_number <= 100:
self.app.proc_container.update_view_text(' %d%%' % disp_number)
self.old_disp_number = disp_number
self.create_geometry() self.create_geometry()
self.app.proc_container.new_text = '' self.app.proc_container.new_text = ''
@ -1534,8 +1578,8 @@ class Excellon(Geometry):
except TypeError: except TypeError:
self.tools[tool]['solid_geometry'] = [res] self.tools[tool]['solid_geometry'] = [res]
if factor is None: if factor is None:
self.tools[tool]['C'] += distance self.tools[tool]['tooldia'] += distance
else: else:
self.tools[tool]['C'] *= distance self.tools[tool]['tooldia'] *= distance
self.create_geometry() self.create_geometry()

View File

@ -613,9 +613,9 @@ class DblSidedTool(AppTool):
return return
tools = {} tools = {}
tools["1"] = {} tools[1] = {}
tools["1"]["C"] = dia tools[1]["tooldia"] = dia
tools["1"]['solid_geometry'] = [] tools[1]['solid_geometry'] = []
# holes = self.alignment_holes.get_value() # holes = self.alignment_holes.get_value()
holes = eval('[{}]'.format(self.alignment_holes.text())) holes = eval('[{}]'.format(self.alignment_holes.text()))
@ -624,21 +624,16 @@ class DblSidedTool(AppTool):
"Add them and retry.")) "Add them and retry."))
return return
drills = []
for hole in holes: for hole in holes:
point = Point(hole) point = Point(hole)
point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py)) point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
drills.append({"point": point, "tool": "1"}) tools[1]['drills'] = [point, point_mirror]
drills.append({"point": point_mirror, "tool": "1"}) tools[1]['solid_geometry'].append(point)
tools[1]['solid_geometry'].append(point_mirror)
tools["1"]['solid_geometry'].append(point)
tools["1"]['solid_geometry'].append(point_mirror)
def obj_init(obj_inst, app_inst): def obj_init(obj_inst, app_inst):
obj_inst.tools = tools obj_inst.tools = tools
obj_inst.drills = drills
obj_inst.create_geometry() obj_inst.create_geometry()
obj_inst.source_file = app_inst.export_excellon(obj_name=obj_inst.options['name'], local_use=obj_inst, obj_inst.source_file = app_inst.export_excellon(obj_name=obj_inst.options['name'], local_use=obj_inst,
filename=None, use_thread=False) filename=None, use_thread=False)

View File

@ -172,7 +172,6 @@ class ToolDrilling(AppTool, Excellon):
# ############################ SIGNALS ######################################## # ############################ SIGNALS ########################################
# ############################################################################# # #############################################################################
self.ui.object_combo.currentIndexChanged.connect(self.on_object_changed)
self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked) self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
self.ui.generate_cnc_button.clicked.connect(self.on_cnc_button_click) self.ui.generate_cnc_button.clicked.connect(self.on_cnc_button_click)
self.ui.tools_table.drag_drop_sig.connect(self.rebuild_ui) self.ui.tools_table.drag_drop_sig.connect(self.rebuild_ui)
@ -365,8 +364,6 @@ class ToolDrilling(AppTool, Excellon):
if opt_key.find('geometry_') == 0: if opt_key.find('geometry_') == 0:
self.default_data[opt_key] = deepcopy(opt_val) self.default_data[opt_key] = deepcopy(opt_val)
self.on_object_changed()
self.obj_name = "" self.obj_name = ""
self.excellon_obj = None self.excellon_obj = None
@ -382,6 +379,16 @@ class ToolDrilling(AppTool, Excellon):
self.ui.operation_radio.set_value("drill") self.ui.operation_radio.set_value("drill")
self.ui.operation_radio.setEnabled(False) self.ui.operation_radio.setEnabled(False)
self.on_object_changed()
if self.excellon_obj:
self.build_ui()
try:
self.ui.object_combo.currentIndexChanged.disconnect()
except (AttributeError, TypeError):
pass
self.ui.object_combo.currentIndexChanged.connect(self.on_object_changed)
def rebuild_ui(self): def rebuild_ui(self):
# read the table tools uid # read the table tools uid
current_uid_list = [] current_uid_list = []
@ -419,17 +426,8 @@ class ToolDrilling(AppTool, Excellon):
if self.excellon_obj: if self.excellon_obj:
self.ui.exc_param_frame.setDisabled(False) self.ui.exc_param_frame.setDisabled(False)
sort = [] tools = [k for k in self.excellon_tools]
for k, v in list(self.excellon_obj.tools.items()):
sort.append((k, float('%.*f' % (self.decimals, float(v.get('C'))))))
order = self.ui.order_radio.get_value()
if order == 'fwd':
sorted_tools = sorted(sort, key=lambda t1: t1[1])
elif order == 'rev':
sorted_tools = sorted(sort, key=lambda t1: t1[1], reverse=True)
else:
sorted_tools = sort
tools = [i[0] for i in sorted_tools]
else: else:
tools = [] tools = []
@ -438,30 +436,23 @@ class ToolDrilling(AppTool, Excellon):
self.ui.tools_table.setRowCount(n + 2) self.ui.tools_table.setRowCount(n + 2)
self.tool_row = 0 self.tool_row = 0
new_options = {}
if self.excellon_obj:
for opt in self.excellon_obj.options:
new_options[opt] = self.excellon_obj.options[opt]
for tool_no in tools: for tool_no in tools:
# add the data dictionary for each tool with the default values
self.tools[tool_no]['data'] = deepcopy(new_options)
drill_cnt = 0 # variable to store the nr of drills per tool drill_cnt = 0 # variable to store the nr of drills per tool
slot_cnt = 0 # variable to store the nr of slots per tool slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool # Find no of drills for the current tool
for drill in self.drills: try:
if drill['tool'] == tool_no: drill_cnt = len(self.excellon_tools[tool_no]["drills"])
drill_cnt += 1 except KeyError:
drill_cnt = 0
self.tot_drill_cnt += drill_cnt self.tot_drill_cnt += drill_cnt
# Find no of slots for the current tool # Find no of slots for the current tool
for slot in self.slots: try:
if slot['tool'] == tool_no: slot_cnt = len(self.excellon_tools[tool_no]["slots"])
slot_cnt += 1 except KeyError:
slot_cnt = 0
self.tot_slot_cnt += slot_cnt self.tot_slot_cnt += slot_cnt
# Tool name/id # Tool name/id
@ -470,7 +461,7 @@ class ToolDrilling(AppTool, Excellon):
self.ui.tools_table.setItem(self.tool_row, 0, exc_id_item) self.ui.tools_table.setItem(self.tool_row, 0, exc_id_item)
# Tool Diameter # Tool Diameter
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.tools[tool_no]['C'])) dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.excellon_tools[tool_no]['tooldia']))
dia_item.setFlags(QtCore.Qt.ItemIsEnabled) dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
self.ui.tools_table.setItem(self.tool_row, 1, dia_item) self.ui.tools_table.setItem(self.tool_row, 1, dia_item)
@ -496,6 +487,8 @@ class ToolDrilling(AppTool, Excellon):
# add a last row with the Total number of drills # add a last row with the Total number of drills
empty_1 = QtWidgets.QTableWidgetItem('') empty_1 = QtWidgets.QTableWidgetItem('')
empty_1.setFlags(~QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) empty_1.setFlags(~QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
empty_1_1 = QtWidgets.QTableWidgetItem('')
empty_1_1.setFlags(~QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
label_tot_drill_count = QtWidgets.QTableWidgetItem(_('Total Drills')) label_tot_drill_count = QtWidgets.QTableWidgetItem(_('Total Drills'))
label_tot_drill_count.setFlags(QtCore.Qt.ItemIsEnabled) label_tot_drill_count.setFlags(QtCore.Qt.ItemIsEnabled)
@ -506,6 +499,7 @@ class ToolDrilling(AppTool, Excellon):
self.ui.tools_table.setItem(self.tool_row, 0, empty_1) self.ui.tools_table.setItem(self.tool_row, 0, empty_1)
self.ui.tools_table.setItem(self.tool_row, 1, label_tot_drill_count) self.ui.tools_table.setItem(self.tool_row, 1, label_tot_drill_count)
self.ui.tools_table.setItem(self.tool_row, 2, tot_drill_count) # Total number of drills self.ui.tools_table.setItem(self.tool_row, 2, tot_drill_count) # Total number of drills
self.ui.tools_table.setItem(self.tool_row, 4, empty_1_1)
font = QtGui.QFont() font = QtGui.QFont()
font.setBold(True) font.setBold(True)
@ -642,35 +636,15 @@ class ToolDrilling(AppTool, Excellon):
self.excellon_obj = self.app.collection.get_by_name(self.obj_name) self.excellon_obj = self.app.collection.get_by_name(self.obj_name)
except Exception as e: except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name))) self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name)))
return "Could not retrieve object: %s with error: %s" % (self.obj_name, str(e)) return
if self.excellon_obj is None: if self.excellon_obj is None:
self.ui.exc_param_frame.setDisabled(True) self.ui.exc_param_frame.setDisabled(True)
else: else:
self.ui.exc_param_frame.setDisabled(False) self.ui.exc_param_frame.setDisabled(False)
sort = [] self.excellon_tools = self.excellon_obj.tools
for k, v in list(self.excellon_obj.tools.items()):
sort.append((k, float('%.*f' % (self.decimals, float(v.get('C'))))))
dias = [i[0] for i in sort]
if not dias: self.build_ui()
log.error("At least one tool diameter needed. Excellon object might be empty.")
return
tooluid = 0
self.excellon_tools.clear()
for tool_dia in dias:
tooluid += 1
self.excellon_tools.update({
int(tooluid): {
'tooldia': float('%.*f' % (self.decimals, tool_dia)),
'data': deepcopy(self.default_data),
'solid_geometry': []
}
})
self.build_ui()
def ui_connect(self): def ui_connect(self):
@ -809,7 +783,7 @@ class ToolDrilling(AppTool, Excellon):
item = self.ui.tools_table.item(c_row, 3) item = self.ui.tools_table.item(c_row, 3)
if type(item) is not None: if type(item) is not None:
tooluid = item.text() tooluid = item.text()
self.storage_to_form(self.drilling_tools[str(tooluid)]['data']) self.storage_to_form(self.excellon_tools[str(tooluid)]['data'])
else: else:
self.blockSignals(False) self.blockSignals(False)
return return
@ -845,8 +819,9 @@ class ToolDrilling(AppTool, Excellon):
:return: None :return: None
:rtype: :rtype:
""" """
if self.ui.tools_table.rowCount() == 0: if self.ui.tools_table.rowCount() == 2:
# there is no tool in tool table so we can't save the GUI elements values to storage # there is no tool in tool table so we can't save the GUI elements values to storage
# Excellon Tool Table has 2 rows by default
return return
self.blockSignals(True) self.blockSignals(True)
@ -862,7 +837,7 @@ class ToolDrilling(AppTool, Excellon):
row = 0 row = 0
tooluid_item = int(self.ui.tools_table.item(row, 3).text()) tooluid_item = int(self.ui.tools_table.item(row, 3).text())
for tooluid_key, tooluid_val in self.iso_tools.items(): for tooluid_key, tooluid_val in self.excellon_tools.items():
if int(tooluid_key) == tooluid_item: if int(tooluid_key) == tooluid_item:
new_option_value = self.form_fields[option_changed].get_value() new_option_value = self.form_fields[option_changed].get_value()
if option_changed in tooluid_val: if option_changed in tooluid_val:
@ -1034,7 +1009,7 @@ class ToolDrilling(AppTool, Excellon):
sort = [] sort = []
for k, v in self.tools.items(): for k, v in self.tools.items():
sort.append((k, v.get('C'))) sort.append((k, v.get('tooldia')))
sorted_tools = sorted(sort, key=lambda t1: t1[1]) sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all": if tools == "all":
@ -1150,7 +1125,7 @@ class ToolDrilling(AppTool, Excellon):
sort = [] sort = []
for k, v in self.tools.items(): for k, v in self.tools.items():
sort.append((k, v.get('C'))) sort.append((k, v.get('tooldia')))
sorted_tools = sorted(sort, key=lambda t1: t1[1]) sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all": if tools == "all":

View File

@ -530,7 +530,7 @@ class ToolExtractDrills(AppTool):
tool_in_drills = False tool_in_drills = False
for tool, tool_val in tools.items(): for tool, tool_val in tools.items():
if abs(float('%.*f' % (self.decimals, tool_val["C"])) - float('%.*f' % (self.decimals, dia))) < \ if abs(float('%.*f' % (self.decimals, tool_val["tooldia"])) - float('%.*f' % (self.decimals, dia))) < \
(10 ** -self.decimals): (10 ** -self.decimals):
tool_in_drills = tool tool_in_drills = tool
@ -615,7 +615,7 @@ class ToolExtractDrills(AppTool):
tool_in_drills = False tool_in_drills = False
for tool, tool_val in tools.items(): for tool, tool_val in tools.items():
if abs(float('%.*f' % (self.decimals, tool_val["C"])) - float('%.*f' % (self.decimals, dia))) < \ if abs(float('%.*f' % (self.decimals, tool_val["tooldia"])) - float('%.*f' % (self.decimals, dia))) < \
(10 ** -self.decimals): (10 ** -self.decimals):
tool_in_drills = tool tool_in_drills = tool

View File

@ -397,16 +397,14 @@ class Properties(AppTool):
slot_cnt = 0 # variable to store the nr of slots per tool slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool # Find no of drills for the current tool
for drill in obj.drills: if 'drills' in value and value['drills']:
if drill['tool'] == tool: drill_cnt = len(value['drills'])
drill_cnt += 1
tot_drill_cnt += drill_cnt tot_drill_cnt += drill_cnt
# Find no of slots for the current tool # Find no of slots for the current tool
for slot in obj.slots: if 'slots' in value and value['slots']:
if slot['tool'] == tool: slot_cnt = len(value['slots'])
slot_cnt += 1
tot_slot_cnt += slot_cnt tot_slot_cnt += slot_cnt
@ -414,7 +412,7 @@ class Properties(AppTool):
toolid, toolid,
[ [
_('Diameter'), _('Diameter'),
'%.*f %s' % (self.decimals, value['C'], self.app.defaults['units'].lower()) '%.*f %s' % (self.decimals, value['tooldia'], self.app.defaults['units'].lower())
], ],
True True
) )

View File

@ -558,7 +558,7 @@ class ToolPunchGerber(AppTool):
# make it work only for Gerber Flashes who are Points in 'follow' # make it work only for Gerber Flashes who are Points in 'follow'
if 'solid' in elem and isinstance(elem['follow'], Point): if 'solid' in elem and isinstance(elem['follow'], Point):
for drill in exc_obj.drills: for drill in exc_obj.drills:
clear_apid_size = exc_obj.tools[drill['tool']]['C'] clear_apid_size = exc_obj.tools[drill['tool']]['tooldia']
# since there may be drills that do not drill into a pad we test only for drills in a pad # since there may be drills that do not drill into a pad we test only for drills in a pad
if drill['point'].within(elem['solid']): if drill['point'].within(elem['solid']):

View File

@ -2314,7 +2314,20 @@ class App(QtCore.QObject):
self.ui.tool_scroll_area.takeWidget() self.ui.tool_scroll_area.takeWidget()
# delete the old object (the source object) if it was an empty one # delete the old object (the source object) if it was an empty one
if len(edited_obj.drills) == 0 and len(edited_obj.slots) == 0: # find if we have drills:
has_drills = None
for tt in edited_obj.tools:
if 'drills' in edited_obj.tools[tt] and edited_obj.tools[tt]['drills']:
has_drills = True
break
# find if we have slots:
has_slots = None
for tt in edited_obj.tools:
if 'slots' in edited_obj.tools[tt] and edited_obj.tools[tt]['slots']:
has_slots = True
slots_in_file = 1
break
if has_drills is None and has_slots is None:
old_name = edited_obj.options['name'] old_name = edited_obj.options['name']
self.collection.delete_by_name(name=old_name) self.collection.delete_by_name(name=old_name)
self.inform.emit('[success] %s' % _("Editor exited. Editor content saved.")) self.inform.emit('[success] %s' % _("Editor exited. Editor content saved."))
@ -8230,11 +8243,11 @@ class App(QtCore.QObject):
for tool in obj.tools: for tool in obj.tools:
if eunits == 'METRIC': if eunits == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool), tool=str(tool),
dec=2) dec=2)
else: else:
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['tooldia']) * factor,
tool=str(tool), tool=str(tool),
dec=4) dec=4)
else: else:
@ -8247,13 +8260,15 @@ class App(QtCore.QObject):
for tool in obj.tools: for tool in obj.tools:
if eunits == 'METRIC': if eunits == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, header += "T{tool}F00S00C{:.{dec}f}\n".format(
tool=str(tool), float(obj.tools[tool]['tooldia']) * factor,
dec=2) tool=str(tool),
dec=2)
else: else:
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, header += "T{tool}F00S00C{:.{dec}f}\n".format(
tool=str(tool), float(obj.tools[tool]['tooldia']) * factor,
dec=4) tool=str(tool),
dec=4)
else: else:
has_slots, excellon_code = obj.export_excellon(ewhole, efract, has_slots, excellon_code = obj.export_excellon(ewhole, efract,
form='ndec', e_zeros='TZ', factor=factor, form='ndec', e_zeros='TZ', factor=factor,
@ -8263,13 +8278,15 @@ class App(QtCore.QObject):
for tool in obj.tools: for tool in obj.tools:
if eunits == 'METRIC': if eunits == 'METRIC':
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, header += "T{tool}F00S00C{:.{dec}f}\n".format(
tool=str(tool), float(obj.tools[tool]['tooldia']) * factor,
dec=2) tool=str(tool),
dec=2)
else: else:
header += "T{tool}F00S00C{:.{dec}f}\n".format(float(obj.tools[tool]['C']) * factor, header += "T{tool}F00S00C{:.{dec}f}\n".format(
tool=str(tool), float(obj.tools[tool]['tooldia']) * factor,
dec=4) tool=str(tool),
dec=4)
header += '%\n' header += '%\n'
footer = 'M30\n' footer = 'M30\n'

204
camlib.py
View File

@ -2555,8 +2555,6 @@ class CNCjob(Geometry):
# here store the routing time # here store the routing time
self.routing_time = 0.0 self.routing_time = 0.0
# used for creating drill CCode geometry; will be updated in the generate_from_excellon_by_tool()
self.exc_drills = None
# store here the Excellon source object tools to be accessible locally # store here the Excellon source object tools to be accessible locally
self.exc_tools = None self.exc_tools = None
@ -2710,8 +2708,7 @@ class CNCjob(Geometry):
:rtype: None :rtype: None
""" """
# create a local copy of the exobj.drills so it can be used for creating drill CCode geometry # create a local copy of the exobj.tools so it can be used for creating drill CCode geometry
self.exc_drills = deepcopy(exobj.drills)
self.exc_tools = deepcopy(exobj.tools) self.exc_tools = deepcopy(exobj.tools)
# the Excellon GCode preprocessor will use this info in the start_code() method # the Excellon GCode preprocessor will use this info in the start_code() method
@ -2774,8 +2771,8 @@ class CNCjob(Geometry):
# sorted_tools = sorted(exobj.tools.items(), key=lambda t1: t1['C']) # sorted_tools = sorted(exobj.tools.items(), key=lambda t1: t1['C'])
sort = [] sort = []
for k, v in list(exobj.tools.items()): for k, v in list(self.exc_tools.items()):
sort.append((k, v.get('C'))) sort.append((int(k), v['tooldia']))
sorted_tools = sorted(sort, key=lambda t1: t1[1]) sorted_tools = sorted(sort, key=lambda t1: t1[1])
if tools == "all": if tools == "all":
@ -2783,7 +2780,7 @@ class CNCjob(Geometry):
log.debug("Tools 'all' and sorted are: %s" % str(tools)) log.debug("Tools 'all' and sorted are: %s" % str(tools))
else: else:
selected_tools = [x.strip() for x in tools.split(",")] # we strip spaces and also separate the tools by ',' selected_tools = [x.strip() for x in tools.split(",")] # we strip spaces and also separate the tools by ','
selected_tools = [t1 for t1 in selected_tools if t1 in selected_tools] selected_tools = [int(t1) for t1 in selected_tools if t1 in selected_tools]
# Create a sorted list of selected tools from the sorted_tools list # Create a sorted list of selected tools from the sorted_tools list
tools = [i for i, j in sorted_tools for k in selected_tools if i == k] tools = [i for i, j in sorted_tools for k in selected_tools if i == k]
@ -2803,18 +2800,20 @@ class CNCjob(Geometry):
for it in sorted_tools: for it in sorted_tools:
for to_ol in tools: for to_ol in tools:
if to_ol == it[0]: if to_ol == it[0]:
drill_no = 0
sol_geo = [] sol_geo = []
for dr in exobj.drills:
if dr['tool'] == it[0]: drill_no = 0
drill_no += 1 if 'drills' in exobj.tools[to_ol]:
sol_geo.append(dr['point']) drill_no = len(exobj.tools[to_ol]['drills'])
for drill in exobj.tools[to_ol]['drills']:
sol_geo.append(drill.buffer((it[1] / 2.0), resolution=self.geo_steps_per_circle))
slot_no = 0 slot_no = 0
for dr in exobj.slots: if 'slots' in exobj.tools[to_ol]:
if dr['tool'] == it[0]: slot_no = len(exobj.tools[to_ol]['slots'])
slot_no += 1 for slot in exobj.tools[to_ol]['slots']:
start = (dr['start'].x, dr['start'].y) start = (slot[0].x, slot[0].y)
stop = (dr['stop'].x, dr['stop'].y) stop = (slot[1].x, slot[1].y)
sol_geo.append( sol_geo.append(
LineString([start, stop]).buffer((it[1] / 2.0), resolution=self.geo_steps_per_circle) LineString([start, stop]).buffer((it[1] / 2.0), resolution=self.geo_steps_per_circle)
) )
@ -2850,25 +2849,26 @@ class CNCjob(Geometry):
# Points (Group by tool): a dictionary of shapely Point geo elements grouped by tool number # Points (Group by tool): a dictionary of shapely Point geo elements grouped by tool number
points = {} points = {}
for drill in exobj.drills: for tool, tool_dict in self.exc_tools.items():
if self.app.abort_flag: if self.app.abort_flag:
# graceful abort requested by the user # graceful abort requested by the user
raise grace raise grace
if drill['tool'] in tools: if 'drills' in tool_dict and tool_dict['drills']:
try: for drill_pt in tool_dict['drills']:
points[drill['tool']].append(drill['point']) try:
except KeyError: points[tool].append(drill_pt)
points[drill['tool']] = [drill['point']] except KeyError:
points[tool] = [drill_pt]
# log.debug("Found %d drills." % len(points)) log.debug("Found %d TOOLS." % len(points))
# check if there are drill points in the exclusion areas. # check if there are drill points in the exclusion areas.
# If we find any within the exclusion areas return 'fail' # If we find any within the exclusion areas return 'fail'
for tool in points: for tool in points:
for pt in points[tool]: for pt in points[tool]:
for area in self.app.exc_areas.exclusion_areas_storage: for area in self.app.exc_areas.exclusion_areas_storage:
pt_buf = pt.buffer(exobj.tools[tool]['C'] / 2.0) pt_buf = pt.buffer(self.exc_tools[tool]['tooldia'] / 2.0)
if pt_buf.within(area['shape']) or pt_buf.intersects(area['shape']): if pt_buf.within(area['shape']) or pt_buf.intersects(area['shape']):
self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed. Drill points inside the exclusion zones.")) self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed. Drill points inside the exclusion zones."))
return 'fail' return 'fail'
@ -2954,22 +2954,28 @@ class CNCjob(Geometry):
used_excellon_optimization_type = self.excellon_optimization_type used_excellon_optimization_type = self.excellon_optimization_type
if used_excellon_optimization_type == 'M': if used_excellon_optimization_type == 'M':
log.debug("Using OR-Tools Metaheuristic Guided Local Search drill path optimization.") log.debug("Using OR-Tools Metaheuristic Guided Local Search drill path optimization.")
if exobj.drills:
has_drills = None
for tool, tool_dict in self.exc_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
if has_drills:
for tool in tools: for tool in tools:
if self.app.abort_flag: if self.app.abort_flag:
# graceful abort requested by the user # graceful abort requested by the user
raise grace raise grace
self.tool = tool self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"] self.tooldia = self.exc_tools[tool]["tooldia"]
self.tooldia = exobj.tools[tool]["C"] self.postdata['toolC'] = self.tooldia
if self.use_ui: if self.use_ui:
self.z_feedrate = exobj.tools[tool]['data']['feedrate_z'] self.z_feedrate = self.exc_tools[tool]['data']['feedrate_z']
self.feedrate = exobj.tools[tool]['data']['feedrate'] self.feedrate = self.exc_tools[tool]['data']['feedrate']
gcode += self.doformat(p.z_feedrate_code) gcode += self.doformat(p.z_feedrate_code)
self.z_cut = exobj.tools[tool]['data']['cutz'] self.z_cut = self.exc_tools[tool]['data']['cutz']
if self.machinist_setting == 0: if self.machinist_setting == 0:
if self.z_cut > 0: if self.z_cut > 0:
@ -2991,12 +2997,12 @@ class CNCjob(Geometry):
old_zcut = deepcopy(self.z_cut) old_zcut = deepcopy(self.z_cut)
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
self.spindlespeed = exobj.tools[tool]['data']['spindlespeed'] self.spindlespeed = self.exc_tools[tool]['data']['spindlespeed']
self.dwell = exobj.tools[tool]['data']['dwell'] self.dwell = self.exc_tools[tool]['data']['dwell']
self.dwelltime = exobj.tools[tool]['data']['dwelltime'] self.dwelltime = self.exc_tools[tool]['data']['dwelltime']
self.multidepth = exobj.tools[tool]['data']['multidepth'] self.multidepth = self.exc_tools[tool]['data']['multidepth']
self.z_depthpercut = exobj.tools[tool]['data']['depthperpass'] self.z_depthpercut = self.exc_tools[tool]['data']['depthperpass']
else: else:
old_zcut = deepcopy(self.z_cut) old_zcut = deepcopy(self.z_cut)
@ -3085,7 +3091,7 @@ class CNCjob(Geometry):
if self.dwell is True: if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time gcode += self.doformat(p.dwell_code) # Dwell time
current_tooldia = float('%.*f' % (self.decimals, float(exobj.tools[tool]["C"]))) current_tooldia = float('%.*f' % (self.decimals, float(self.exc_tools[tool]["tooldia"])))
self.app.inform.emit( self.app.inform.emit(
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"), '%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
@ -3098,7 +3104,7 @@ class CNCjob(Geometry):
# because the values for Z offset are created in build_ui() # because the values for Z offset are created in build_ui()
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
try: try:
z_offset = float(exobj.tools[tool]['data']['offset']) * (-1) z_offset = float(self.exc_tools[tool]['data']['offset']) * (-1)
except KeyError: except KeyError:
z_offset = 0 z_offset = 0
self.z_cut = z_offset + old_zcut self.z_cut = z_offset + old_zcut
@ -3138,7 +3144,7 @@ class CNCjob(Geometry):
gcode += self.doformat(p.lift_code, x=locx, y=locy) gcode += self.doformat(p.lift_code, x=locx, y=locy)
# restore z_move # restore z_move
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
else: else:
if prev_z is not None: if prev_z is not None:
# move to next point # move to next point
@ -3146,7 +3152,7 @@ class CNCjob(Geometry):
# we assume that previously the z_move was altered therefore raise to # we assume that previously the z_move was altered therefore raise to
# the travel_z (z_move) # the travel_z (z_move)
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
gcode += self.doformat(p.lift_code, x=locx, y=locy) gcode += self.doformat(p.lift_code, x=locx, y=locy)
else: else:
# move to next point # move to next point
@ -3218,21 +3224,28 @@ class CNCjob(Geometry):
if used_excellon_optimization_type == 'B': if used_excellon_optimization_type == 'B':
log.debug("Using OR-Tools Basic drill path optimization.") log.debug("Using OR-Tools Basic drill path optimization.")
if exobj.drills:
has_drills = None
for tool, tool_dict in self.exc_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
if has_drills:
for tool in tools: for tool in tools:
if self.app.abort_flag: if self.app.abort_flag:
# graceful abort requested by the user # graceful abort requested by the user
raise grace raise grace
self.tool = tool self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"] self.tooldia = self.exc_tools[tool]["tooldia"]
self.tooldia = exobj.tools[tool]["C"] self.postdata['toolC'] = self.tooldia
if self.use_ui: if self.use_ui:
self.z_feedrate = exobj.tools[tool]['data']['feedrate_z'] self.z_feedrate = self.exc_tools[tool]['data']['feedrate_z']
self.feedrate = exobj.tools[tool]['data']['feedrate'] self.feedrate = self.exc_tools[tool]['data']['feedrate']
gcode += self.doformat(p.z_feedrate_code) gcode += self.doformat(p.z_feedrate_code)
self.z_cut = exobj.tools[tool]['data']['cutz'] self.z_cut = self.exc_tools[tool]['data']['cutz']
if self.machinist_setting == 0: if self.machinist_setting == 0:
if self.z_cut > 0: if self.z_cut > 0:
@ -3254,13 +3267,13 @@ class CNCjob(Geometry):
old_zcut = deepcopy(self.z_cut) old_zcut = deepcopy(self.z_cut)
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
self.spindlespeed = exobj.tools[tool]['data']['spindlespeed'] self.spindlespeed = self.exc_tools[tool]['data']['spindlespeed']
self.dwell = exobj.tools[tool]['data']['dwell'] self.dwell = self.exc_tools[tool]['data']['dwell']
self.dwelltime = exobj.tools[tool]['data']['dwelltime'] self.dwelltime = self.exc_tools[tool]['data']['dwelltime']
self.multidepth = exobj.tools[tool]['data']['multidepth'] self.multidepth = self.exc_tools[tool]['data']['multidepth']
self.z_depthpercut = exobj.tools[tool]['data']['depthperpass'] self.z_depthpercut = self.exc_tools[tool]['data']['depthperpass']
else: else:
old_zcut = deepcopy(self.z_cut) old_zcut = deepcopy(self.z_cut)
@ -3338,7 +3351,7 @@ class CNCjob(Geometry):
if self.dwell is True: if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time gcode += self.doformat(p.dwell_code) # Dwell time
current_tooldia = float('%.*f' % (self.decimals, float(exobj.tools[tool]["C"]))) current_tooldia = float('%.*f' % (self.decimals, float(self.exc_tools[tool]["tooldia"])))
self.app.inform.emit( self.app.inform.emit(
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"), '%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
@ -3351,7 +3364,7 @@ class CNCjob(Geometry):
# because the values for Z offset are created in build_ui() # because the values for Z offset are created in build_ui()
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
try: try:
z_offset = float(exobj.tools[tool]['data']['offset']) * (-1) z_offset = float(self.exc_tools[tool]['data']['offset']) * (-1)
except KeyError: except KeyError:
z_offset = 0 z_offset = 0
self.z_cut = z_offset + old_zcut self.z_cut = z_offset + old_zcut
@ -3390,7 +3403,7 @@ class CNCjob(Geometry):
gcode += self.doformat(p.lift_code, x=locx, y=locy) gcode += self.doformat(p.lift_code, x=locx, y=locy)
# restore z_move # restore z_move
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
else: else:
if prev_z is not None: if prev_z is not None:
# move to next point # move to next point
@ -3398,7 +3411,7 @@ class CNCjob(Geometry):
# we assume that previously the z_move was altered therefore raise to # we assume that previously the z_move was altered therefore raise to
# the travel_z (z_move) # the travel_z (z_move)
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
gcode += self.doformat(p.lift_code, x=locx, y=locy) gcode += self.doformat(p.lift_code, x=locx, y=locy)
else: else:
# move to next point # move to next point
@ -3473,22 +3486,29 @@ class CNCjob(Geometry):
if used_excellon_optimization_type == 'T': if used_excellon_optimization_type == 'T':
log.debug("Using Travelling Salesman drill path optimization.") log.debug("Using Travelling Salesman drill path optimization.")
has_drills = None
for tool, tool_dict in self.exc_tools.items():
if 'drills' in tool_dict and tool_dict['drills']:
has_drills = True
break
for tool in tools: for tool in tools:
if self.app.abort_flag: if self.app.abort_flag:
# graceful abort requested by the user # graceful abort requested by the user
raise grace raise grace
if exobj.drills: if has_drills:
self.tool = tool self.tool = tool
self.postdata['toolC'] = exobj.tools[tool]["C"] self.tooldia = self.exc_tools[tool]["tooldia"]
self.tooldia = exobj.tools[tool]["C"] self.postdata['toolC'] = self.tooldia
if self.use_ui: if self.use_ui:
self.z_feedrate = exobj.tools[tool]['data']['feedrate_z'] self.z_feedrate = self.exc_tools[tool]['data']['feedrate_z']
self.feedrate = exobj.tools[tool]['data']['feedrate'] self.feedrate = self.exc_tools[tool]['data']['feedrate']
gcode += self.doformat(p.z_feedrate_code) gcode += self.doformat(p.z_feedrate_code)
self.z_cut = exobj.tools[tool]['data']['cutz'] self.z_cut = self.exc_tools[tool]['data']['cutz']
if self.machinist_setting == 0: if self.machinist_setting == 0:
if self.z_cut > 0: if self.z_cut > 0:
@ -3510,12 +3530,12 @@ class CNCjob(Geometry):
old_zcut = deepcopy(self.z_cut) old_zcut = deepcopy(self.z_cut)
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
self.spindlespeed = exobj.tools[tool]['data']['spindlespeed'] self.spindlespeed = self.exc_tools[tool]['data']['spindlespeed']
self.dwell = exobj.tools[tool]['data']['dwell'] self.dwell = self.exc_tools[tool]['data']['dwell']
self.dwelltime = exobj.tools[tool]['data']['dwelltime'] self.dwelltime = self.exc_tools[tool]['data']['dwelltime']
self.multidepth = exobj.tools[tool]['data']['multidepth'] self.multidepth = self.exc_tools[tool]['data']['multidepth']
self.z_depthpercut = exobj.tools[tool]['data']['depthperpass'] self.z_depthpercut = self.exc_tools[tool]['data']['depthperpass']
else: else:
old_zcut = deepcopy(self.z_cut) old_zcut = deepcopy(self.z_cut)
@ -3536,7 +3556,7 @@ class CNCjob(Geometry):
if self.dwell is True: if self.dwell is True:
gcode += self.doformat(p.dwell_code) # Dwell time gcode += self.doformat(p.dwell_code) # Dwell time
current_tooldia = float('%.*f' % (self.decimals, float(exobj.tools[tool]["C"]))) current_tooldia = float('%.*f' % (self.decimals, float(self.exc_tools[tool]["tooldia"])))
self.app.inform.emit( self.app.inform.emit(
'%s: %s%s.' % (_("Starting G-Code for tool with diameter"), '%s: %s%s.' % (_("Starting G-Code for tool with diameter"),
@ -3549,7 +3569,7 @@ class CNCjob(Geometry):
# because the values for Z offset are created in build_ui() # because the values for Z offset are created in build_ui()
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
try: try:
z_offset = float(exobj.tools[tool]['data']['offset']) * (-1) z_offset = float(self.exc_tools[tool]['data']['offset']) * (-1)
except KeyError: except KeyError:
z_offset = 0 z_offset = 0
self.z_cut = z_offset + old_zcut self.z_cut = z_offset + old_zcut
@ -3593,7 +3613,7 @@ class CNCjob(Geometry):
gcode += self.doformat(p.lift_code, x=locx, y=locy) gcode += self.doformat(p.lift_code, x=locx, y=locy)
# restore z_move # restore z_move
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
else: else:
if prev_z is not None: if prev_z is not None:
# move to next point # move to next point
@ -3601,7 +3621,7 @@ class CNCjob(Geometry):
# we assume that previously the z_move was altered therefore raise to # we assume that previously the z_move was altered therefore raise to
# the travel_z (z_move) # the travel_z (z_move)
self.z_move = exobj.tools[tool]['data']['travelz'] self.z_move = self.exc_tools[tool]['data']['travelz']
gcode += self.doformat(p.lift_code, x=locx, y=locy) gcode += self.doformat(p.lift_code, x=locx, y=locy)
else: else:
# move to next point # move to next point
@ -3687,6 +3707,7 @@ class CNCjob(Geometry):
self.routing_time += lift_time + traveled_time self.routing_time += lift_time + traveled_time
self.gcode = gcode self.gcode = gcode
self.app.inform.emit(_("Finished G-Code generation...")) self.app.inform.emit(_("Finished G-Code generation..."))
return 'OK' return 'OK'
@ -4914,22 +4935,27 @@ class CNCjob(Geometry):
) )
# find the drill diameter knowing the drill coordinates # find the drill diameter knowing the drill coordinates
for pt_dict in self.exc_drills: break_loop = False
point_in_dict_coords = ( for tool, tool_dict in self.exc_tools.items():
float('%.*f' % (self.decimals, pt_dict['point'].x)), if 'drills' in tool_dict:
float('%.*f' % (self.decimals, pt_dict['point'].y)) for drill_pt in tool_dict['drills']:
) point_in_dict_coords = (
if point_in_dict_coords == current_drill_point_coords: float('%.*f' % (self.decimals, drill_pt.x)),
tool = pt_dict['tool'] float('%.*f' % (self.decimals, drill_pt.y))
dia = self.exc_tools[tool]['C'] )
kind = ['C', 'F'] if point_in_dict_coords == current_drill_point_coords:
geometry.append( dia = self.exc_tools[tool]['tooldia']
{ kind = ['C', 'F']
"geom": Point(current_drill_point_coords).buffer(dia / 2.0).exterior, geometry.append(
"kind": kind {
} "geom": Point(current_drill_point_coords).buffer(dia / 2.0).exterior,
) "kind": kind
break }
)
break_loop = True
break
if break_loop:
break
if 'G' in gobj: if 'G' in gobj:
current['G'] = int(gobj['G']) current['G'] = int(gobj['G'])

View File

@ -48,7 +48,7 @@ class Berta_CNC(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -38,7 +38,7 @@ class ISEL_CNC(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -38,7 +38,7 @@ class ISEL_ICP_CNC(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n' gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n' gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + '\n'
gcode += '\n;FEEDRATE Z: \n' gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class Marlin(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n' gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n' gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + '\n'
gcode += '\n;FEEDRATE Z: \n' gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class Repetier(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n' gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n' gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + '\n'
gcode += '\n;FEEDRATE Z: \n' gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class Toolchange_Custom(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class Toolchange_Manual(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class Toolchange_Probe_MACH3(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class default(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -40,7 +40,7 @@ class grbl_11(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -39,7 +39,7 @@ class line_xyz(PreProc):
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True: elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n' gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n' gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["tooldia"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n' gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items(): for tool, val in p['exc_tools'].items():

View File

@ -198,25 +198,23 @@ class TclCommandDrillcncjob(TclCommandSignaled):
if tools == 'all': if tools == 'all':
sort = [] sort = []
for k, v in list(obj.tools.items()): for k, v in list(obj.tools.items()):
sort.append((k, v.get('C'))) sort.append((k, v.get('tooldia')))
sorted_tools = sorted(sort, key=lambda t1: t1[1]) sorted_tools = sorted(sort, key=lambda t1: t1[1])
use_tools = [i[0] for i in sorted_tools] use_tools = [i[0] for i in sorted_tools]
for tool_no in use_tools: for tool_no in use_tools:
tool_dia_used = obj.tools[tool_no]['C'] tool_dia_used = obj.tools[tool_no]['tooldia']
drill_cnt = 0 # variable to store the nr of drills per tool drill_cnt = 0 # variable to store the nr of drills per tool
slot_cnt = 0 # variable to store the nr of slots per tool slot_cnt = 0 # variable to store the nr of slots per tool
# Find no of drills for the current tool # Find no of drills for the current tool
for drill in obj.drills: if 'drills' in obj.tools[tool_no] and obj.tools[tool_no]['drills']:
if drill['tool'] == tool_no: drill_cnt = len(obj.tools[tool_no]['drills'])
drill_cnt += 1
# Find no of slots for the current tool # Find no of slots for the current tool
for slot in obj.slots: if 'slots' in obj.tools[tool_no] and obj.tools[tool_no]['slots']:
if slot['tool'] == tool_no: slot_cnt = len(obj.tools[tool_no]['slots'])
slot_cnt += 1
used_tools_info.append([str(tool_no), str(tool_dia_used), str(drill_cnt), str(slot_cnt)]) used_tools_info.append([str(tool_no), str(tool_dia_used), str(drill_cnt), str(slot_cnt)])