diff --git a/CHANGELOG.md b/CHANGELOG.md index 7135e24b..11f0c0fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ CHANGELOG for FlatCAM beta 18.02.2022 - completed the refactoring of object `options` property to `obj_options` in preprocessors too +- made sure that the CNCJob UI building no longer have issues with table items added from an already existing table +- fixed CNCJob UI showing all tools in the Tools Table even when only some tools were selected for processing +- fixed the `drillcncjob` Tcl Command to work in the case of drilling a selection of tools instead of all, with toolchange inactive 16.02.2022 diff --git a/appObjects/FlatCAMCNCJob.py b/appObjects/FlatCAMCNCJob.py index 6e1c0b04..7e3c1f67 100644 --- a/appObjects/FlatCAMCNCJob.py +++ b/appObjects/FlatCAMCNCJob.py @@ -171,6 +171,10 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.gc_start = '' self.gc_end = '' + # it is possible that the user will process only a few tools not all in the parent object + # here we store the used tools so the UI will build only those that were generated + self.used_tools = [] + # Attributes to be included in serialization # Always append to it because it carries contents # from predecessors. @@ -214,73 +218,73 @@ class CNCJobObject(FlatCAMObj, CNCjob): if not self.tools: self.ui.cnc_tools_table.setRowCount(1) else: - n = len(self.tools) + n = len(self.used_tools) self.ui.cnc_tools_table.setRowCount(n) for dia_key, dia_value in self.tools.items(): + if dia_key in self.used_tools: + tool_idx += 1 + row_no = tool_idx - 1 - tool_idx += 1 - row_no = tool_idx - 1 + t_id = QtWidgets.QTableWidgetItem('%d' % int(tool_idx)) + # id.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) + self.ui.cnc_tools_table.setItem(row_no, 0, t_id) # Tool name/id - t_id = QtWidgets.QTableWidgetItem('%d' % int(tool_idx)) - # id.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) - self.ui.cnc_tools_table.setItem(row_no, 0, t_id) # Tool name/id + # Make sure that the tool diameter when in MM is with no more than 2 decimals. + # There are no tool bits in MM with more than 2 decimals diameter. + # For INCH the decimals should be no more than 4. There are no tools under 10mils. - # Make sure that the tool diameter when in MM is with no more than 2 decimals. - # There are no tool bits in MM with more than 2 decimals diameter. - # For INCH the decimals should be no more than 4. There are no tools under 10mils. + dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(dia_value['tooldia']))) - dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(dia_value['tooldia']))) + offset_txt = list(str(dia_value['data']['tools_mill_offset_value'])) + offset_txt[0] = offset_txt[0].upper() + offset_item = QtWidgets.QTableWidgetItem(''.join(offset_txt)) - offset_txt = list(str(dia_value['data']['tools_mill_offset_value'])) - offset_txt[0] = offset_txt[0].upper() - offset_item = QtWidgets.QTableWidgetItem(''.join(offset_txt)) + job_item_options = [_('Roughing'), _('Finishing'), _('Isolation'), _('Polishing')] + tool_shape_options = ["C1", "C2", "C3", "C4", "B", "V", "L"] - job_item_options = [_('Roughing'), _('Finishing'), _('Isolation'), _('Polishing')] - tool_shape_options = ["C1", "C2", "C3", "C4", "B", "V", "L"] + try: + job_item_txt = job_item_options[dia_value['data']['tools_mill_job_type']] + except TypeError: + job_item_txt = dia_value['data']['tools_mill_job_type'] + job_item = QtWidgets.QTableWidgetItem(job_item_txt) - try: - job_item_txt = job_item_options[dia_value['data']['tools_mill_job_type']] - except TypeError: - job_item_txt = dia_value['data']['tools_mill_job_type'] - job_item = QtWidgets.QTableWidgetItem(job_item_txt) + try: + tool_shape_item_txt = tool_shape_options[dia_value['data']['tools_mill_tool_shape']] + except TypeError: + tool_shape_item_txt = dia_value['data']['tools_mill_tool_shape'] + tool_shape_item = QtWidgets.QTableWidgetItem(tool_shape_item_txt) - try: - tool_shape_item_txt = tool_shape_options[dia_value['data']['tools_mill_tool_shape']] - except TypeError: - tool_shape_item_txt = dia_value['data']['tools_mill_tool_shape'] - tool_shape_item = QtWidgets.QTableWidgetItem(tool_shape_item_txt) + t_id.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + dia_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + offset_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + job_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + tool_shape_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - t_id.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - dia_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - offset_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - job_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - tool_shape_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + # hack so the checkbox stay centered in the table cell + # used this: + # https://stackoverflow.com/questions/32458111/pyqt-allign-checkbox-and-put-it-in-every-row + # plot_item = QtWidgets.QWidget() + # checkbox = FCCheckBox() + # checkbox.setCheckState(QtCore.Qt.Checked) + # qhboxlayout = QtWidgets.QHBoxLayout(plot_item) + # qhboxlayout.addWidget(checkbox) + # qhboxlayout.setAlignment(QtCore.Qt.AlignCenter) + # qhboxlayout.setContentsMargins(0, 0, 0, 0) + plot_item = FCCheckBox() + plot_item.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft) + tool_uid_item = QtWidgets.QTableWidgetItem(str(dia_key)) + if self.ui.plot_cb.isChecked(): + plot_item.setChecked(True) - # hack so the checkbox stay centered in the table cell - # used this: - # https://stackoverflow.com/questions/32458111/pyqt-allign-checkbox-and-put-it-in-every-row - # plot_item = QtWidgets.QWidget() - # checkbox = FCCheckBox() - # checkbox.setCheckState(QtCore.Qt.Checked) - # qhboxlayout = QtWidgets.QHBoxLayout(plot_item) - # qhboxlayout.addWidget(checkbox) - # qhboxlayout.setAlignment(QtCore.Qt.AlignCenter) - # qhboxlayout.setContentsMargins(0, 0, 0, 0) - plot_item = FCCheckBox() - plot_item.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft) - tool_uid_item = QtWidgets.QTableWidgetItem(str(dia_key)) - if self.ui.plot_cb.isChecked(): - plot_item.setChecked(True) + self.ui.cnc_tools_table.setItem(row_no, 1, dia_item) # Diameter + self.ui.cnc_tools_table.setItem(row_no, 2, offset_item) # Offset + self.ui.cnc_tools_table.setItem(row_no, 3, job_item) # Job Type + self.ui.cnc_tools_table.setItem(row_no, 4, tool_shape_item) # Tool Shape - self.ui.cnc_tools_table.setItem(row_no, 1, dia_item) # Diameter - self.ui.cnc_tools_table.setItem(row_no, 2, offset_item) # Offset - self.ui.cnc_tools_table.setItem(row_no, 3, job_item) # Job Type - self.ui.cnc_tools_table.setItem(row_no, 4, tool_shape_item) # Tool Shape - - # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ## - self.ui.cnc_tools_table.setItem(row_no, 5, tool_uid_item) # Tool unique ID) - self.ui.cnc_tools_table.setCellWidget(row_no, 6, plot_item) + # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ## + self.ui.cnc_tools_table.setItem(row_no, 5, tool_uid_item) # Tool unique ID) + self.ui.cnc_tools_table.setCellWidget(row_no, 6, plot_item) # make the diameter column editable # for row in range(tool_idx): @@ -331,47 +335,50 @@ class CNCJobObject(FlatCAMObj, CNCjob): # reset the Tools Table self.ui.exc_cnc_tools_table.setRowCount(0) - n = len(self.tools) + n = len(self.used_tools) self.ui.exc_cnc_tools_table.setRowCount(n) row_no = 0 for t_id, dia_value in self.tools.items(): - tooldia = self.tools[t_id]['tooldia'] + if t_id in self.used_tools: + tooldia = self.tools[t_id]['tooldia'] - try: - offset_val = self.app.dec_format(float(dia_value['offset']), self.decimals) + self.z_cut - except KeyError: - offset_val = self.app.dec_format(float(dia_value['offset_z']), self.decimals) + self.z_cut + try: + offset_val = self.app.dec_format(float(dia_value['offset']), self.decimals) + self.z_cut + except KeyError: + offset_val = self.app.dec_format(float(dia_value['offset_z']), self.decimals) + self.z_cut - t_id_item = QtWidgets.QTableWidgetItem('%d' % int(t_id)) - dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia))) - nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_drills'])) - nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_slots'])) - cutz_item = QtWidgets.QTableWidgetItem('%f' % offset_val) + t_id_item = QtWidgets.QTableWidgetItem('%d' % int(t_id)) + dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia))) + nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_drills'])) + nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_slots'])) + cutz_item = QtWidgets.QTableWidgetItem('%f' % offset_val) + t_id_item_2 = QtWidgets.QTableWidgetItem('%d' % int(t_id)) - t_id_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - dia_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - nr_drills_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - nr_slots_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - cutz_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + t_id_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + dia_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + nr_drills_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + nr_slots_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + t_id_item_2.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) + cutz_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) - plot_item = FCCheckBox() - plot_item.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft) + plot_cnc_exc_item = FCCheckBox() + plot_cnc_exc_item.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft) - if self.ui.plot_cb.isChecked(): - plot_item.setChecked(True) + if self.ui.plot_cb.isChecked(): + plot_cnc_exc_item.setChecked(True) - self.ui.exc_cnc_tools_table.setItem(row_no, 0, t_id_item) # Tool name/id - self.ui.exc_cnc_tools_table.setItem(row_no, 1, dia_item) # Diameter - self.ui.exc_cnc_tools_table.setItem(row_no, 2, nr_drills_item) # Nr of drills - self.ui.exc_cnc_tools_table.setItem(row_no, 3, nr_slots_item) # Nr of slots + self.ui.exc_cnc_tools_table.setItem(row_no, 0, t_id_item) # Tool name/id + self.ui.exc_cnc_tools_table.setItem(row_no, 1, dia_item) # Diameter + self.ui.exc_cnc_tools_table.setItem(row_no, 2, nr_drills_item) # Nr of drills + self.ui.exc_cnc_tools_table.setItem(row_no, 3, nr_slots_item) # Nr of slots - # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ## - self.ui.exc_cnc_tools_table.setItem(row_no, 4, t_id_item) # Tool unique ID) - self.ui.exc_cnc_tools_table.setItem(row_no, 5, cutz_item) - self.ui.exc_cnc_tools_table.setCellWidget(row_no, 6, plot_item) + # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ## + self.ui.exc_cnc_tools_table.setItem(row_no, 4, t_id_item_2) # Tool unique ID) + self.ui.exc_cnc_tools_table.setItem(row_no, 5, cutz_item) + self.ui.exc_cnc_tools_table.setCellWidget(row_no, 6, plot_cnc_exc_item) - row_no += 1 + row_no += 1 for row in range(row_no): self.ui.exc_cnc_tools_table.item(row, 0).setFlags( @@ -1332,7 +1339,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): # don't know the origin if self.obj_options['type'].lower() == "excellon": if self.tools: - for toolid_key in self.tools: + for toolid_key in self.used_tools: dia_plot = self.app.dec_format(float(self.tools[toolid_key]['tooldia']), self.decimals) gcode_parsed = self.tools[toolid_key]['gcode_parsed'] if not gcode_parsed: @@ -1344,7 +1351,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): else: # multiple tools usage if self.tools: - for tooluid_key in self.tools: + for tooluid_key in self.used_tools: dia_plot = self.app.dec_format( float(self.tools[tooluid_key]['tooldia']), self.decimals diff --git a/appPlugins/ToolDrilling.py b/appPlugins/ToolDrilling.py index 801f9c7a..d9cad1da 100644 --- a/appPlugins/ToolDrilling.py +++ b/appPlugins/ToolDrilling.py @@ -2051,8 +2051,8 @@ class ToolDrilling(AppTool, Excellon): self.app.inform.emit('%s...' % _("Starting G-Code")) # Object initialization function for app.app_obj.new_object() - def job_init(job_obj, app_obj): - assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj) + def job_init(cnc_job_obj, app_obj): + assert cnc_job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(cnc_job_obj) app_obj.inform.emit(_("Generating CNCJob...")) # ######################################################################################################### @@ -2060,7 +2060,7 @@ class ToolDrilling(AppTool, Excellon): # fill the data into the self.tools dictionary attribute of the CNCJob object # ######################################################################################################### # ######################################################################################################### - job_obj.tools = {} + cnc_job_obj.tools = {} if has_toolchange: for sel_id in sorted_tools: @@ -2069,7 +2069,7 @@ class ToolDrilling(AppTool, Excellon): selected_tooldia = sel_id[1] if selected_id == t_id: - job_obj.tools[selected_id] = deepcopy(self.excellon_tools[selected_id]) + cnc_job_obj.tools[selected_id] = deepcopy(self.excellon_tools[selected_id]) sol_geo = [] # solid geometry addition; we look into points because we may have slots converted to drills @@ -2080,7 +2080,7 @@ class ToolDrilling(AppTool, Excellon): drill_no = len(points[selected_id]) for drill in points[selected_id]: sol_geo.append( - drill.buffer((selected_tooldia / 2.0), resolution=job_obj.geo_steps_per_circle) + drill.buffer((selected_tooldia / 2.0), resolution=cnc_job_obj.geo_steps_per_circle) ) slot_no = 0 @@ -2093,7 +2093,7 @@ class ToolDrilling(AppTool, Excellon): line_geo = LineString([start, stop]) buff_distance = selected_tooldia / 2.0 sol_geo.append( - line_geo.buffer(buff_distance, resolution=job_obj.geo_steps_per_circle) + line_geo.buffer(buff_distance, resolution=cnc_job_obj.geo_steps_per_circle) ) # adjust Offset for current tool @@ -2102,16 +2102,16 @@ class ToolDrilling(AppTool, Excellon): except KeyError: z_off = 0 - job_obj.tools[selected_id]['nr_drills'] = drill_no - job_obj.tools[selected_id]['nr_slots'] = slot_no - job_obj.tools[selected_id]['offset'] = z_off - job_obj.tools[selected_id]['gcode'] = '' - job_obj.tools[selected_id]['gcode_parsed'] = [] - job_obj.tools[selected_id]['solid_geometry'] = deepcopy(sol_geo) + cnc_job_obj.tools[selected_id]['nr_drills'] = drill_no + cnc_job_obj.tools[selected_id]['nr_slots'] = slot_no + cnc_job_obj.tools[selected_id]['offset'] = z_off + cnc_job_obj.tools[selected_id]['gcode'] = '' + cnc_job_obj.tools[selected_id]['gcode_parsed'] = [] + cnc_job_obj.tools[selected_id]['solid_geometry'] = deepcopy(sol_geo) else: # use the first tool in the selection as the tool that we are going to use used_tool = sel_tools[0] - job_obj.tools[used_tool] = deepcopy(self.excellon_tools[used_tool]) + cnc_job_obj.tools[used_tool] = deepcopy(self.excellon_tools[used_tool]) sol_geo = [] drill_no = 0 @@ -2131,7 +2131,7 @@ class ToolDrilling(AppTool, Excellon): drill_no += len(points[selected_id]) for drill in points[selected_id]: sol_geo.append( - drill.buffer((selected_tooldia / 2.0), resolution=job_obj.geo_steps_per_circle) + drill.buffer((selected_tooldia / 2.0), resolution=cnc_job_obj.geo_steps_per_circle) ) convert_slots = self.excellon_tools[selected_id]['data']['tools_drill_drill_slots'] @@ -2143,7 +2143,7 @@ class ToolDrilling(AppTool, Excellon): line_geo = LineString([start, stop]) buff_distance = selected_tooldia / 2.0 sol_geo.append( - line_geo.buffer(buff_distance, resolution=job_obj.geo_steps_per_circle) + line_geo.buffer(buff_distance, resolution=cnc_job_obj.geo_steps_per_circle) ) # adjust Offset for current tool @@ -2152,12 +2152,12 @@ class ToolDrilling(AppTool, Excellon): except KeyError: z_off = 0 - job_obj.tools[used_tool]['nr_drills'] = drill_no - job_obj.tools[used_tool]['nr_slots'] = slot_no - job_obj.tools[used_tool]['offset'] = z_off - job_obj.tools[used_tool]['gcode'] = '' - job_obj.tools[used_tool]['gcode_parsed'] = [] - job_obj.tools[used_tool]['solid_geometry'] = deepcopy(sol_geo) + cnc_job_obj.tools[used_tool]['nr_drills'] = drill_no + cnc_job_obj.tools[used_tool]['nr_slots'] = slot_no + cnc_job_obj.tools[used_tool]['offset'] = z_off + cnc_job_obj.tools[used_tool]['gcode'] = '' + cnc_job_obj.tools[used_tool]['gcode_parsed'] = [] + cnc_job_obj.tools[used_tool]['solid_geometry'] = deepcopy(sol_geo) # ######################################################################################################### # ######################################################################################################### @@ -2165,48 +2165,48 @@ class ToolDrilling(AppTool, Excellon): # ######################################################################################################### # ######################################################################################################### # Preprocessor - job_obj.pp_excellon_name = self.ui.pp_excellon_name_cb.get_value() - job_obj.pp_excellon = self.app.preprocessors[job_obj.pp_excellon_name] + cnc_job_obj.pp_excellon_name = self.ui.pp_excellon_name_cb.get_value() + cnc_job_obj.pp_excellon = self.app.preprocessors[cnc_job_obj.pp_excellon_name] - job_obj.obj_options['type'] = 'Excellon' - job_obj.obj_options['ppname_e'] = obj.pp_excellon_name + cnc_job_obj.obj_options['type'] = 'Excellon' + cnc_job_obj.obj_options['ppname_e'] = obj.pp_excellon_name - job_obj.obj_options['xmin'] = xmin - job_obj.obj_options['ymin'] = ymin - job_obj.obj_options['xmax'] = xmax - job_obj.obj_options['ymax'] = ymax + cnc_job_obj.obj_options['xmin'] = xmin + cnc_job_obj.obj_options['ymin'] = ymin + cnc_job_obj.obj_options['xmax'] = xmax + cnc_job_obj.obj_options['ymax'] = ymax - job_obj.use_ui = True + cnc_job_obj.use_ui = True - job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"]) - job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"]) - job_obj.multitool = True + cnc_job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"]) + cnc_job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"]) + cnc_job_obj.multitool = True # it does not matter for the Excellon codes because we are not going to autolevel GCode out of Excellon # but it is here for uniformity between the Geometry and Excellon objects - job_obj.segx = self.app.defaults["geometry_segx"] - job_obj.segy = self.app.defaults["geometry_segy"] + cnc_job_obj.segx = self.app.defaults["geometry_segx"] + cnc_job_obj.segy = self.app.defaults["geometry_segy"] # first drill point # I can read the toolchange x,y point from any tool since it is the same for all, so I read it # from the first tool that is available first_tool_available = sel_tools[0] - job_obj.xy_toolchange = job_obj.tools[first_tool_available]['data']["tools_drill_toolchangexy"] + cnc_job_obj.xy_toolchange = cnc_job_obj.tools[first_tool_available]['data']["tools_drill_toolchangexy"] x_tc, y_tc = [0, 0] try: - if job_obj.xy_toolchange != '': - tcxy_temp = re.sub('[()\[\]]', '', str(job_obj.xy_toolchange)) + if cnc_job_obj.xy_toolchange != '': + tcxy_temp = re.sub('[()\[\]]', '', str(cnc_job_obj.xy_toolchange)) if tcxy_temp: x_tc, y_tc = [float(eval(a)) for a in tcxy_temp.split(",")] except Exception: x_tc, y_tc = [0, 0] self.app.inform.emit('[ERROR]%s' % _("The Toolchange X,Y format has to be (x, y).")) - job_obj.oldx = x_tc - job_obj.oldy = y_tc - first_drill_point = (job_obj.oldx, job_obj.oldy) + cnc_job_obj.oldx = x_tc + cnc_job_obj.oldy = y_tc + first_drill_point = (cnc_job_obj.oldx, cnc_job_obj.oldy) # ######################################################################################################### # ####################### NO TOOLCHANGE ################################################################### @@ -2222,34 +2222,36 @@ class ToolDrilling(AppTool, Excellon): used_tooldia = self.excellon_tools[used_tool]['tooldia'] # those are used by the preprocessors to display data on the toolchange line - job_obj.tool = used_tool - job_obj.postdata['toolC'] = used_tooldia + cnc_job_obj.tool = used_tool + cnc_job_obj.used_tools = [used_tool] + cnc_job_obj.postdata['toolC'] = used_tooldia # generate GCode - tool_gcode, __, start_gcode = job_obj.excellon_tool_gcode_gen(used_tool, tool_points, - job_obj.tools, - first_pt=first_drill_point, - is_first=True, - is_last=True, - opt_type=used_exc_optim_type, - toolchange=False) + tool_gcode, __, start_gcode = cnc_job_obj.excellon_tool_gcode_gen(used_tool, tool_points, + cnc_job_obj.tools, + first_pt=first_drill_point, + is_first=True, + is_last=True, + opt_type=used_exc_optim_type, + toolchange=False) # parse the Gcode - tool_gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, - start_pt=first_drill_point) + tool_gcode_parsed = cnc_job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, + start_pt=first_drill_point) # store the results in Excellon CNC tools storage - job_obj.tools[used_tool]['gcode'] = tool_gcode - job_obj.tools[used_tool]['gcode_parsed'] = tool_gcode_parsed + cnc_job_obj.tools[used_tool]['gcode'] = tool_gcode + cnc_job_obj.tools[used_tool]['gcode_parsed'] = tool_gcode_parsed if start_gcode != '': - job_obj.gc_start = start_gcode + cnc_job_obj.gc_start = start_gcode self.total_gcode = tool_gcode self.total_gcode_parsed = tool_gcode_parsed # ####################### TOOLCHANGE ACTIVE ###################################################### else: + cnc_job_obj.used_tools = deepcopy(sel_tools) for tool_id in sel_tools: tool_points = [] if tool_id in points: @@ -2257,16 +2259,16 @@ class ToolDrilling(AppTool, Excellon): used_tooldia = self.excellon_tools[tool_id]['tooldia'] # those are used by the preprocessors to display data on the toolchange line - job_obj.tool = tool_id - job_obj.postdata['toolC'] = used_tooldia + cnc_job_obj.tool = tool_id + cnc_job_obj.postdata['toolC'] = used_tooldia # if slots are converted to drill for this tool, update the number of drills and make slots nr zero convert_slots = self.excellon_tools[tool_id]['data']['tools_drill_drill_slots'] if convert_slots is True: nr_drills = len(tool_points) nr_slots = 0 - job_obj.tools[used_tooldia]['nr_drills'] = nr_drills - job_obj.tools[used_tooldia]['nr_slots'] = nr_slots + cnc_job_obj.tools[used_tooldia]['nr_drills'] = nr_drills + cnc_job_obj.tools[used_tooldia]['nr_slots'] = nr_slots # calculate if the current tool is the first one or if it is the last one # for the first tool we add some extra GCode (start Gcode, header etc) @@ -2279,75 +2281,75 @@ class ToolDrilling(AppTool, Excellon): continue # Generate Gcode for the current tool - tool_gcode, last_pt, start_gcode = job_obj.excellon_tool_gcode_gen(tool_id, tool_points, - self.excellon_tools, - first_pt=first_drill_point, - is_first=is_first_tool, - is_last=is_last_tool, - opt_type=used_exc_optim_type, - toolchange=True) + tool_gcode, last_pt, start_gcode = cnc_job_obj.excellon_tool_gcode_gen(tool_id, tool_points, + self.excellon_tools, + first_pt=first_drill_point, + is_first=is_first_tool, + is_last=is_last_tool, + opt_type=used_exc_optim_type, + toolchange=True) # parse Gcode for the current tool - tool_gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, - start_pt=first_drill_point) + tool_gcode_parsed = cnc_job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, + start_pt=first_drill_point) first_drill_point = last_pt # store the results of GCode generation and parsing - job_obj.tools[tool_id]['gcode'] = tool_gcode - job_obj.tools[tool_id]['gcode_parsed'] = tool_gcode_parsed + cnc_job_obj.tools[tool_id]['gcode'] = tool_gcode + cnc_job_obj.tools[tool_id]['gcode_parsed'] = tool_gcode_parsed if start_gcode != '': - job_obj.gc_start = start_gcode + cnc_job_obj.gc_start = start_gcode self.total_gcode += tool_gcode self.total_gcode_parsed += tool_gcode_parsed - job_obj.gcode = self.total_gcode - job_obj.source_file = self.total_gcode - job_obj.gcode_parsed = self.total_gcode_parsed - if job_obj.gcode == 'fail': + cnc_job_obj.gcode = self.total_gcode + cnc_job_obj.source_file = self.total_gcode + cnc_job_obj.gcode_parsed = self.total_gcode_parsed + if cnc_job_obj.gcode == 'fail': return 'fail' # create Geometry for plotting # FIXME is it necessary? didn't we do it previously when filling data in self.tools dictionary? - job_obj.create_geometry() + cnc_job_obj.create_geometry() if used_exc_optim_type == 'M': app_obj.log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % - str(job_obj.measured_distance)) + str(cnc_job_obj.measured_distance)) elif used_exc_optim_type == 'B': app_obj.log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % - str(job_obj.measured_distance)) + str(cnc_job_obj.measured_distance)) elif used_exc_optim_type == 'T': app_obj.log.debug( "The total travel distance with Travelling Salesman Algorithm is: %s" % - str(job_obj.measured_distance)) + str(cnc_job_obj.measured_distance)) else: app_obj.log.debug("The total travel distance with with no optimization is: %s" % - str(job_obj.measured_distance)) + str(cnc_job_obj.measured_distance)) # ######################################################################################################### # ############################# Calculate DISTANCE and ESTIMATED TIME ##################################### # ######################################################################################################### - if job_obj.xy_end is None: - job_obj.xy_end = [job_obj.oldx, job_obj.oldy] + if cnc_job_obj.xy_end is None: + cnc_job_obj.xy_end = [cnc_job_obj.oldx, cnc_job_obj.oldy] - job_obj.measured_distance += abs(distance_euclidian( - job_obj.oldx, job_obj.oldy, job_obj.xy_end[0], job_obj.xy_end[1]) + cnc_job_obj.measured_distance += abs(distance_euclidian( + cnc_job_obj.oldx, cnc_job_obj.oldy, cnc_job_obj.xy_end[0], cnc_job_obj.xy_end[1]) ) app_obj.log.debug("The total travel distance including travel to end position is: %s" % - str(job_obj.measured_distance) + '\n') - job_obj.travel_distance = job_obj.measured_distance + str(cnc_job_obj.measured_distance) + '\n') + cnc_job_obj.travel_distance = cnc_job_obj.measured_distance # I use the value of self.feedrate_rapid for the feadrate in case of the measure_lift_distance and for # traveled_time because it is not always possible to determine the feedrate that the CNC machine uses # for G0 move (the fastest speed available to the CNC router). Although self.feedrate_rapids is used only # with Marlin preprocessor and derivatives. - job_obj.routing_time = \ - (job_obj.measured_down_distance + job_obj.measured_up_to_zero_distance) / job_obj.z_feedrate - lift_time = job_obj.measured_lift_distance / job_obj.feedrate_rapid - traveled_time = job_obj.measured_distance / job_obj.feedrate_rapid - job_obj.routing_time += lift_time + traveled_time + cnc_job_obj.routing_time = \ + (cnc_job_obj.measured_down_distance + cnc_job_obj.measured_up_to_zero_distance) / cnc_job_obj.z_feedrate + lift_time = cnc_job_obj.measured_lift_distance / cnc_job_obj.feedrate_rapid + traveled_time = cnc_job_obj.measured_distance / cnc_job_obj.feedrate_rapid + cnc_job_obj.routing_time += lift_time + traveled_time # To be run in separate thread def job_thread(a_obj): diff --git a/appPlugins/ToolMilling.py b/appPlugins/ToolMilling.py index 4f0985b4..fe03fd97 100644 --- a/appPlugins/ToolMilling.py +++ b/appPlugins/ToolMilling.py @@ -3260,8 +3260,10 @@ class ToolMilling(AppTool, Excellon): new_cncjob_obj.tools.update(tools_dict) + used_tools = list(tools_dict.keys()) + new_cncjob_obj.used_tools = used_tools total_gcode = '' - for tooluid_key in list(tools_dict.keys()): + for tooluid_key in used_tools: tool_cnt += 1 dia_cnc_dict = deepcopy(tools_dict[tooluid_key]) diff --git a/camlib.py b/camlib.py index 40d51c81..683577da 100644 --- a/camlib.py +++ b/camlib.py @@ -4139,6 +4139,8 @@ class CNCjob(Geometry): selected_tools = [i[0] for i in all_tools] # we get a array of ordered tools else: selected_tools = eval(tools) + if not isinstance(selected_tools, list): + selected_tools = [selected_tools] # 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] @@ -4560,7 +4562,7 @@ class CNCjob(Geometry): self.tools[tool]['gcode'] += end_gcode else: # We are not using Toolchange therefore we need to decide which tool properties to use - one_tool = 1 + one_tool = tools[0] all_points = [] for tool in points: @@ -4799,12 +4801,21 @@ class CNCjob(Geometry): self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented')) return 'fail' self.z_cut = deepcopy(old_zcut) - self.tools[one_tool]['gcode'] = gcode + try: + self.tools[one_tool]['gcode'] = gcode + except KeyError: + # just a hack because I am lazy and I don't want to fix the Tcl command drillcncjob which needs this + self.tools[str(one_tool)]['gcode'] = gcode + # add the end_gcode end_gcode = self.doformat(p.spindle_stop_code) end_gcode += self.doformat(p.end_code, x=0, y=0) - self.tools[one_tool]['gcode'] += end_gcode + try: + self.tools[one_tool]['gcode'] += end_gcode + except KeyError: + # just a hack because I am lazy and I don't want to fix the Tcl command drillcncjob which needs this + self.tools[str(one_tool)]['gcode'] += end_gcode if used_excellon_optimization_type == 'M': self.app.log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance)) diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index 84a0b975..c409c119 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -144,8 +144,9 @@ class TclCommandDrillcncjob(TclCommandSignaled): xmax = obj.obj_options['xmax'] ymax = obj.obj_options['ymax'] - def job_init(job_obj, app_obj): + def job_init(cnc_job_obj, app_obj): # tools = args["tools"] if "tools" in args else 'all' + use_tools = [] # drilled tools diameters try: @@ -155,8 +156,8 @@ class TclCommandDrillcncjob(TclCommandSignaled): req_tools = set() for tool in obj.tools: + obj_dia_form = float('%.*f' % (obj.decimals, float(obj.tools[tool]["tooldia"]))) for req_dia in diameters: - obj_dia_form = float('%.*f' % (obj.decimals, float(obj.tools[tool]["tooldia"]))) req_dia_form = float('%.*f' % (obj.decimals, float(req_dia))) if 'diatol' in args: @@ -167,18 +168,21 @@ class TclCommandDrillcncjob(TclCommandSignaled): if math.isclose(obj_dia_form, req_dia_form, rel_tol=tolerance): req_tools.add(str(tool)) nr_diameters -= 1 + use_tools.append(tool) else: if obj_dia_form == req_dia_form: req_tools.add(str(tool)) nr_diameters -= 1 + use_tools.append(tool) if nr_diameters > 0: if muted is False: self.raise_tcl_error("One or more tool diameters of the drills to be drilled passed to the " "TclCommand are not actual tool diameters in the Excellon object.") else: - return "One or more tool diameters of the drills to be drilled passed to the "\ - "TclCommand are not actual tool diameters in the Excellon object." + app_obj.log.error("One or more tool diameters of the drills to be drilled passed to the "\ + "TclCommand are not actual tool diameters in the Excellon object.") + return "fail" # make a string of diameters separated by comma; this is what tcl_gcode_from_excellon_by_tool() is # expecting as tools parameter @@ -281,130 +285,140 @@ class TclCommandDrillcncjob(TclCommandSignaled): # ########################################################################################## # ################# Set parameters ######################################################### # ########################################################################################## - job_obj.obj_options['type'] = 'Excellon' - job_obj.multigeo = True - job_obj.multitool = True + cnc_job_obj.obj_options['type'] = 'Excellon' + cnc_job_obj.multigeo = True + cnc_job_obj.multitool = True + cnc_job_obj.used_tools = use_tools # preprocessor pp_excellon_name = args["pp"] if "pp" in args and args["pp"] else self.app.defaults["tools_drill_ppname_e"] - job_obj.pp_excellon_name = pp_excellon_name - job_obj.obj_options['ppname_e'] = pp_excellon_name + cnc_job_obj.pp_excellon_name = pp_excellon_name + cnc_job_obj.obj_options['ppname_e'] = pp_excellon_name # multidepth if 'dpp' in args: - job_obj.multidepth = True + cnc_job_obj.multidepth = True if args['dpp'] is not None: - job_obj.z_depthpercut = abs(float(args['dpp'])) + cnc_job_obj.z_depthpercut = abs(float(args['dpp'])) else: - job_obj.z_depthpercut = abs(float(obj.obj_options["dpp"])) + cnc_job_obj.z_depthpercut = abs(float(obj.obj_options["dpp"])) else: - job_obj.multidepth = self.app.defaults["tools_drill_multidepth"] - job_obj.z_depthpercut = self.app.defaults["tools_drill_depthperpass"] + cnc_job_obj.multidepth = self.app.defaults["tools_drill_multidepth"] + cnc_job_obj.z_depthpercut = self.app.defaults["tools_drill_depthperpass"] # travel Z - job_obj.z_move = float(args["travelz"]) if "travelz" in args and args["travelz"] else \ + cnc_job_obj.z_move = float(args["travelz"]) if "travelz" in args and args["travelz"] else \ self.app.defaults["tools_drill_travelz"] # Feedrate - job_obj.feedrate = float(args["feedrate_z"]) if "feedrate_z" in args and args["feedrate_z"] else \ + cnc_job_obj.feedrate = float(args["feedrate_z"]) if "feedrate_z" in args and args["feedrate_z"] else \ self.app.defaults["tools_drill_feedrate_z"] - job_obj.z_feedrate = float(args["feedrate_z"]) if "feedrate_z" in args and args["feedrate_z"] else \ + cnc_job_obj.z_feedrate = float(args["feedrate_z"]) if "feedrate_z" in args and args["feedrate_z"] else \ self.app.defaults["tools_drill_feedrate_z"] - job_obj.feedrate_rapid = float(args["feedrate_rapid"]) \ + cnc_job_obj.feedrate_rapid = float(args["feedrate_rapid"]) \ if "feedrate_rapid" in args and args["feedrate_rapid"] else \ self.app.defaults["tools_drill_feedrate_rapid"] # SpindleSpped / Laser Power if 'laser' not in pp_excellon_name: - job_obj.spindlespeed = float(args["spindlespeed"]) if "spindlespeed" in args else None + cnc_job_obj.spindlespeed = float(args["spindlespeed"]) if "spindlespeed" in args else None else: - job_obj.spindlespeed = float(args["las_power"]) if "las_power" in args else 0.0 + cnc_job_obj.spindlespeed = float(args["las_power"]) if "las_power" in args else 0.0 # laser minimum power - job_obj.laser_min_power = float(args["las_min_pwr"]) if "las_min_pwr" in args else 0.0 + cnc_job_obj.laser_min_power = float(args["las_min_pwr"]) if "las_min_pwr" in args else 0.0 # spindle direction - job_obj.spindledir = self.app.defaults["tools_drill_spindledir"] + cnc_job_obj.spindledir = self.app.defaults["tools_drill_spindledir"] # dwell and dwelltime if 'dwelltime' in args: - job_obj.dwell = True + cnc_job_obj.dwell = True if args['dwelltime'] is not None: - job_obj.dwelltime = float(args['dwelltime']) + cnc_job_obj.dwelltime = float(args['dwelltime']) else: - job_obj.dwelltime = float(self.app.defaults["tools_drill_dwelltime"]) + cnc_job_obj.dwelltime = float(self.app.defaults["tools_drill_dwelltime"]) else: - job_obj.dwell = self.app.defaults["tools_drill_dwell"] - job_obj.dwelltime = self.app.defaults["tools_drill_dwelltime"] + cnc_job_obj.dwell = self.app.defaults["tools_drill_dwell"] + cnc_job_obj.dwelltime = self.app.defaults["tools_drill_dwelltime"] - job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"]) - job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"]) + cnc_job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"]) + cnc_job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"]) - job_obj.obj_options['xmin'] = xmin - job_obj.obj_options['ymin'] = ymin - job_obj.obj_options['xmax'] = xmax - job_obj.obj_options['ymax'] = ymax + cnc_job_obj.obj_options['xmin'] = xmin + cnc_job_obj.obj_options['ymin'] = ymin + cnc_job_obj.obj_options['xmax'] = xmax + cnc_job_obj.obj_options['ymax'] = ymax # Cut Z - job_obj.z_cut = float(drillz) + cnc_job_obj.z_cut = float(drillz) # toolchange - job_obj.toolchange = toolchange + cnc_job_obj.toolchange = toolchange # toolchange X-Y location - job_obj.xy_toolchange = xy_toolchange + cnc_job_obj.xy_toolchange = xy_toolchange # toolchange Z location - job_obj.z_toolchange = float(toolchangez) + cnc_job_obj.z_toolchange = float(toolchangez) # start Z if "startz" in args and args["startz"] is not None: - job_obj.startz = float(args["startz"]) + cnc_job_obj.startz = float(args["startz"]) else: if self.app.defaults["tools_drill_startz"]: - job_obj.startz = self.app.defaults["tools_drill_startz"] + cnc_job_obj.startz = self.app.defaults["tools_drill_startz"] else: - job_obj.startz = self.app.defaults["tools_drill_travelz"] + cnc_job_obj.startz = self.app.defaults["tools_drill_travelz"] # end Z - job_obj.z_end = float(endz) + cnc_job_obj.z_end = float(endz) # end X-Y location - job_obj.xy_end = eval(str(xy_end)) + cnc_job_obj.xy_end = eval(str(xy_end)) # Excellon optimization - job_obj.excellon_optimization_type = opt_type + cnc_job_obj.excellon_optimization_type = opt_type - ret_val = job_obj.tcl_gcode_from_excellon_by_tool(obj, tools, is_first=True, use_ui=False) + ret_val = cnc_job_obj.tcl_gcode_from_excellon_by_tool(obj, tools, is_first=True, use_ui=False) if ret_val == 'fail': return 'fail' - job_obj.source_file = ret_val - job_obj.gc_start = ret_val[1] + cnc_job_obj.source_file = ret_val + cnc_job_obj.gc_start = ret_val[1] total_gcode_parsed = [] - if job_obj.toolchange is True: + if cnc_job_obj.toolchange is True: + if tools == "all": + processed_tools = list(cnc_job_obj.tools.keys()) + else: + processed_tools = use_tools + # from Excellon attribute self.tools - for t_item in job_obj.tools: - job_obj.tools[t_item]['data']['tools_drill_offset'] = \ - float(job_obj.tools[t_item]['offset_z']) + float(drillz) - job_obj.tools[t_item]['data']['tools_drill_ppname_e'] = job_obj.obj_options['ppname_e'] + for t_item in processed_tools: + cnc_job_obj.tools[t_item]['data']['tools_drill_offset'] = \ + float(cnc_job_obj.tools[t_item]['offset_z']) + float(drillz) + cnc_job_obj.tools[t_item]['data']['tools_drill_ppname_e'] = cnc_job_obj.obj_options['ppname_e'] used_tooldia = obj.tools[t_item]['tooldia'] - job_obj.tools[t_item]['tooldia'] = used_tooldia - tool_gcode = job_obj.tools[t_item]['gcode'] - first_drill_point = job_obj.tools[t_item]['last_point'] - gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, - start_pt=first_drill_point) + cnc_job_obj.tools[t_item]['tooldia'] = used_tooldia + tool_gcode = cnc_job_obj.tools[t_item]['gcode'] + first_drill_point = cnc_job_obj.tools[t_item]['last_point'] + gcode_parsed = cnc_job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, + start_pt=first_drill_point) total_gcode_parsed += gcode_parsed - job_obj.tools[t_item]['gcode_parsed'] = gcode_parsed + cnc_job_obj.tools[t_item]['gcode_parsed'] = gcode_parsed else: - first_tool = 1 - job_obj.tools[first_tool]['data']['tools_drill_offset'] = \ - float(job_obj.tools[first_tool]['offset_z']) + float(drillz) - job_obj.tools[first_tool]['data']['tools_drill_ppname_e'] = job_obj.obj_options['ppname_e'] + if tools == "all": + first_tool = 1 + else: + first_tool = use_tools[0] + + cnc_job_obj.tools[first_tool]['data']['tools_drill_offset'] = \ + float(cnc_job_obj.tools[first_tool]['offset_z']) + float(drillz) + cnc_job_obj.tools[first_tool]['data']['tools_drill_ppname_e'] = cnc_job_obj.obj_options['ppname_e'] used_tooldia = obj.tools[first_tool]['tooldia'] - job_obj.tools[first_tool]['tooldia'] = used_tooldia - tool_gcode = job_obj.tools[first_tool]['gcode'] - first_drill_point = job_obj.tools[first_tool]['last_point'] - gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, - start_pt=first_drill_point) + cnc_job_obj.tools[first_tool]['tooldia'] = used_tooldia + tool_gcode = cnc_job_obj.tools[first_tool]['gcode'] + first_drill_point = cnc_job_obj.tools[first_tool]['last_point'] + gcode_parsed = cnc_job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode, + start_pt=first_drill_point) total_gcode_parsed += gcode_parsed - job_obj.tools[first_tool]['gcode_parsed'] = gcode_parsed + cnc_job_obj.tools[first_tool]['gcode_parsed'] = gcode_parsed - job_obj.gcode_parsed = total_gcode_parsed - # job_obj.gcode_parse() - job_obj.create_geometry() + cnc_job_obj.gcode_parsed = total_gcode_parsed + # cnc_job_obj.gcode_parse() + cnc_job_obj.create_geometry() self.app.app_obj.new_object("cncjob", args['outname'], job_init, plot=False)