diff --git a/CHANGELOG.md b/CHANGELOG.md index 16178d09..69479713 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ CHANGELOG for FlatCAM beta - refactored the Isolation Plugin class - some more work on the Isolation plugin - fixed more issues in the `ncc` and `paint` Tcl commands (missing `segx` and `segy` keys) +- fixed not adding the feedrate code in `drillcncjob` Tcl command +- fixed crash when trying to do a `select all` and there are app Scripts present +- updated the `drillcncjob` Tcl command to make a script exit in case of an error 29.01.2022 diff --git a/appObjects/FlatCAMCNCJob.py b/appObjects/FlatCAMCNCJob.py index 440d7ce1..f3652df3 100644 --- a/appObjects/FlatCAMCNCJob.py +++ b/appObjects/FlatCAMCNCJob.py @@ -117,7 +117,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): ... } It is populated in the ExcellonObject.on_create_cncjob_click() but actually - it's done in camlib.CNCJob.generate_from_excellon_by_tool() + it's done in camlib.CNCJob.tcl_gcode_from_excellon_by_tool() BEWARE: I rely on the ordered nature of the Python 3.7 dictionary. Things might change ... ''' self.tools = {} diff --git a/appObjects/FlatCAMDocument.py b/appObjects/FlatCAMDocument.py index 6f8b8000..7e438bdb 100644 --- a/appObjects/FlatCAMDocument.py +++ b/appObjects/FlatCAMDocument.py @@ -164,14 +164,14 @@ class DocumentObject(FlatCAMObj): # try to not add too many times a tab that it is already installed for idx in range(self.app.ui.plot_tab_area.count()): - if self.app.ui.plot_tab_area.widget(idx).objectName() == self.options['name']: + if self.app.ui.plot_tab_area.widget(idx).objectName() == self.options['name'] + "_editor_tab": tab_here = True break # add the tab if it is not already added if tab_here is False: self.app.ui.plot_tab_area.addTab(self.document_editor_tab, '%s' % _("Document Editor")) - self.document_editor_tab.setObjectName(self.options['name']) + self.document_editor_tab.setObjectName(self.options['name'] + "_editor_tab") # Switch plot_area to CNCJob tab self.app.ui.plot_tab_area.setCurrentWidget(self.document_editor_tab) diff --git a/appObjects/FlatCAMScript.py b/appObjects/FlatCAMScript.py index 29064ff8..5923ca85 100644 --- a/appObjects/FlatCAMScript.py +++ b/appObjects/FlatCAMScript.py @@ -148,14 +148,14 @@ class ScriptObject(FlatCAMObj): tab_here = False # try to not add too many times a tab that it is already installed for idx in range(self.app.ui.plot_tab_area.count()): - if self.app.ui.plot_tab_area.widget(idx).objectName() == self.options['name']: + if self.app.ui.plot_tab_area.widget(idx).objectName() == (self.options['name'] + "_editor_tab"): tab_here = True break # add the tab if it is not already added if tab_here is False: self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor")) - self.script_editor_tab.setObjectName(self.options['name']) + self.script_editor_tab.setObjectName(self.options['name'] + "_editor_tab") self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab) def change_level(self, level): diff --git a/appObjects/ObjectCollection.py b/appObjects/ObjectCollection.py index effe4019..fed8008d 100644 --- a/appObjects/ObjectCollection.py +++ b/appObjects/ObjectCollection.py @@ -700,7 +700,8 @@ class ObjectCollection(QtCore.QAbstractItemModel): # some objects add a Tab on creation, close it here for idx in range(self.app.ui.plot_tab_area.count()): - if self.app.ui.plot_tab_area.widget(idx).objectName() == active.obj.options['name']: + widget_name = self.app.ui.plot_tab_area.widget(idx).objectName() + if widget_name == active.obj.options['name'] or widget_name == (active.obj.options['name'] + "_editor_tab"): self.app.ui.plot_tab_area.removeTab(idx) break @@ -753,7 +754,8 @@ class ObjectCollection(QtCore.QAbstractItemModel): # some objects add a Tab on creation, close it here for idx in range(self.app.ui.plot_tab_area.count()): - if self.app.ui.plot_tab_area.widget(idx).objectName() == deleted.obj.options['name']: + wdg_name = self.app.ui.plot_tab_area.widget(idx).objectName() + if wdg_name == deleted.obj.options['name'] or wdg_name == (deleted.obj.options['name'] + "_editor_tab"): self.app.ui.plot_tab_area.removeTab(idx) break diff --git a/appPlugins/ToolDrilling.py b/appPlugins/ToolDrilling.py index 359f9696..eff5841c 100644 --- a/appPlugins/ToolDrilling.py +++ b/appPlugins/ToolDrilling.py @@ -1932,8 +1932,8 @@ class ToolDrilling(AppTool, Excellon): obj.pp_excellon_name = self.ui.pp_excellon_name_cb.get_value() if self.is_valid_excellon() is False: - log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> " - "The loaded Excellon file has no drills ...") + self.app.log.debug("ToolDrilling.on_cnc_button_click() --> " + "The loaded Excellon file has no drills ...") self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills')) return diff --git a/app_Main.py b/app_Main.py index 3fdf049a..bbabc341 100644 --- a/app_Main.py +++ b/app_Main.py @@ -6460,7 +6460,6 @@ class App(QtCore.QObject): :return: """ - self.defaults.report_usage("on_selectall()") # delete the possible selection box around a possible selected object self.delete_selection_shape() @@ -6469,7 +6468,11 @@ class App(QtCore.QObject): curr_sel_obj = self.collection.get_by_name(name) # create the selection box around the selected object if self.defaults['global_selection_shape'] is True: - self.draw_selection_shape(curr_sel_obj) + try: + self.draw_selection_shape(curr_sel_obj) + except Exception as err: + self.log.error( + "App.on_select_all(). Object %s can't be selected on canvas. Error: %s" % (name, str(err))) def on_toggle_preferences(self): pref_open = False diff --git a/camlib.py b/camlib.py index 10cb39b9..260fa0a8 100644 --- a/camlib.py +++ b/camlib.py @@ -2751,7 +2751,7 @@ class CNCjob(Geometry): self.tolerance = self.drawing_tolerance - # used by the self.generate_from_excellon_by_tool() method + # used by the self.tcl_gcode_from_excellon_by_tool() method # but set directly before the actual usage of the method with obj.excellon_optimization_type = value self.excellon_optimization_type = 'No' @@ -3349,7 +3349,7 @@ class CNCjob(Geometry): self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y format has to be (x, y).")) return 'fail' except Exception as e: - self.app.log.error("camlib.CNCJob.generate_from_excellon_by_tool() xy_toolchange --> %s" % str(e)) + self.app.log.error("camlib.CNCJob.tcl_gcode_from_excellon_by_tool() xy_toolchange --> %s" % str(e)) self.xy_toolchange = [0, 0] # End position parameters @@ -3374,7 +3374,7 @@ class CNCjob(Geometry): self.app.inform.emit('[ERROR] %s' % _("The End X,Y format has to be (x, y).")) return 'fail' except Exception as e: - log.error("camlib.CNCJob.generate_from_excellon_by_tool() xy_end --> %s" % str(e)) + log.error("camlib.CNCJob.tcl_gcode_from_excellon_by_tool() xy_end --> %s" % str(e)) self.xy_end = [0, 0] # Probe parameters @@ -4019,7 +4019,7 @@ class CNCjob(Geometry): self.gcode = t_gcode return self.gcode, start_gcode - def generate_from_excellon_by_tool(self, exobj, tools="all", order='fwd', is_first=False, use_ui=False): + def tcl_gcode_from_excellon_by_tool(self, exobj, tools="all", order='fwd', is_first=False, use_ui=False): """ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Used by the Tcl command Drillcncjob @@ -4057,8 +4057,6 @@ class CNCjob(Geometry): self.z_cut = self.check_zcut(zcut=self.z_cut) if self.z_cut == 'fail': return 'fail' - # multidepth use this - # old_zcut = deepcopy(self.z_cut) # XY_toolchange parameter try: @@ -4076,7 +4074,7 @@ class CNCjob(Geometry): "in the format (x, y) \nbut now there is only one value, not two. ")) return 'fail' except Exception as e: - self.app.log.error("camlib.CNCJob.generate_from_excellon_by_tool() --> %s" % str(e)) + self.app.log.error("camlib.CNCJob.tcl_gcode_from_excellon_by_tool() --> %s" % str(e)) pass # XY_end parameter @@ -4193,7 +4191,7 @@ class CNCjob(Geometry): points[tool].append(drill_pt) except KeyError: points[tool] = [drill_pt] - self.app.log.debug("Found %d TOOLS with drills." % len(points)) + self.app.log.debug("Found %d TOOLS with drill points." % len(points)) # check if there are drill points in the exclusion areas. # If we find any within the exclusion areas return 'fail' @@ -4206,7 +4204,7 @@ class CNCjob(Geometry): return 'fail' # this holds the resulting GCode - self.gcode = [] + self.gcode = '' # ############################################################################################################# # ############################################################################################################# # Initialization @@ -4216,17 +4214,15 @@ class CNCjob(Geometry): start_gcode = '' if is_first: start_gcode = self.doformat(p.start_code) + start_gcode += self.doformat(p.z_feedrate_code) - if use_ui is False: - gcode += self.doformat(p.z_feedrate_code) - - if self.toolchange is False: - if self.xy_toolchange is not None: - gcode += self.doformat(p.lift_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1]) - gcode += self.doformat(p.startz_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1]) - else: - gcode += self.doformat(p.lift_code, x=0.0, y=0.0) - gcode += self.doformat(p.startz_code, x=0.0, y=0.0) + if self.toolchange is False: + if self.xy_toolchange is not None: + start_gcode += self.doformat(p.lift_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1]) + start_gcode += self.doformat(p.startz_code, x=self.xy_toolchange[0], y=self.xy_toolchange[1]) + else: + start_gcode += self.doformat(p.lift_code, x=0.0, y=0.0) + start_gcode += self.doformat(p.startz_code, x=0.0, y=0.0) if self.xy_toolchange is not None: self.oldx = self.xy_toolchange[0] @@ -4253,7 +4249,7 @@ class CNCjob(Geometry): has_drills = True break if not has_drills: - log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> " + log.debug("camlib.CNCJob.tcl_gcode_from_excellon_by_tool() --> " "The loaded Excellon file has no drills ...") self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills')) return 'fail' @@ -4301,7 +4297,6 @@ class CNCjob(Geometry): self.z_feedrate = self.exc_tools[tool]['data']['tools_drill_feedrate_z'] self.feedrate = self.exc_tools[tool]['data']['tools_drill_feedrate_z'] self.z_cut = self.exc_tools[tool]['data']['tools_drill_cutz'] - tool_gcode += self.doformat(p.z_feedrate_code) if self.z_cut > 0: self.app.inform.emit('[WARNING] %s' % @@ -4357,10 +4352,16 @@ class CNCjob(Geometry): for point in points[tool]: altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0])) optimized_path = self.optimized_travelling_salesman(altPoints) + elif used_excellon_optimization_type == 'R': + for point in points[tool]: + altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0])) + optimized_path = self.exc_optimized_rtree(altPoints) + if optimized_path == 'fail': + return 'fail' else: # it's actually not optimized path but here we build a list of (x,y) coordinates # out of the tool's drills - for drill in self.exc_tools[tool]['drills']: + for drill in exobj[tool]['drills']: unoptimized_coords = ( drill.x, drill.y @@ -4380,6 +4381,9 @@ class CNCjob(Geometry): # Tool change sequence (optional) if self.toolchange: tool_gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy)) + + tool_gcode += self.doformat(p.z_feedrate_code) + # Spindle start tool_gcode += self.doformat(p.spindle_code) # Dwell time @@ -4535,11 +4539,12 @@ class CNCjob(Geometry): self.tooldia = self.exc_tools[one_tool]["tooldia"] self.postdata['toolC'] = self.tooldia + gcode += self.doformat(p.z_feedrate_code) + if self.use_ui: self.z_feedrate = self.exc_tools[one_tool]['data']['tools_drill_feedrate_z'] self.feedrate = self.exc_tools[one_tool]['data']['tools_drill_feedrate_z'] self.z_cut = self.exc_tools[one_tool]['data']['tools_drill_cutz'] - gcode += self.doformat(p.z_feedrate_code) if self.z_cut > 0: self.app.inform.emit('[WARNING] %s' % @@ -4955,7 +4960,7 @@ class CNCjob(Geometry): # return 'fail' # self.z_cut = deepcopy(old_zcut) # else: - # log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> " + # log.debug("camlib.CNCJob.tcl_gcode_from_excellon_by_tool() --> " # "The loaded Excellon file has no drills ...") # self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills')) # return 'fail' @@ -5160,7 +5165,7 @@ class CNCjob(Geometry): # return 'fail' # self.z_cut = deepcopy(old_zcut) # else: - # log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> " + # log.debug("camlib.CNCJob.tcl_gcode_from_excellon_by_tool() --> " # "The loaded Excellon file has no drills ...") # self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills')) # return 'fail' @@ -5362,7 +5367,7 @@ class CNCjob(Geometry): # self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented')) # return 'fail' # else: - # log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> " + # log.debug("camlib.CNCJob.tcl_gcode_from_excellon_by_tool() --> " # "The loaded Excellon file has no drills ...") # self.app.inform.emit('[ERROR_NOTCL] %s...' % _('The loaded Excellon file has no drills')) # return 'fail' @@ -5370,7 +5375,7 @@ class CNCjob(Geometry): # log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance)) # # else: - # log.debug("camlib.CNCJob.generate_from_excellon_by_tool(): Chosen drill optimization doesn't exist.") + # log.debug("camlib.CNCJob.tcl_gcode_from_excellon_by_tool(): Chosen drill optimization doesn't exist.") # return 'fail' # Spindle stop @@ -5382,7 +5387,7 @@ class CNCjob(Geometry): # ############################# Calculate DISTANCE and ESTIMATED TIME ######################################### # ############################################################################################################# measured_distance += abs(distance_euclidian(self.oldx, self.oldy, 0, 0)) - log.debug("The total travel distance including travel to end position is: %s" % + self.app.log.debug("The total travel distance including travel to end position is: %s" % str(measured_distance) + '\n') self.travel_distance = measured_distance diff --git a/preprocessors/GRBL_11.py b/preprocessors/GRBL_11.py index e5ec52c1..53b61c3e 100644 --- a/preprocessors/GRBL_11.py +++ b/preprocessors/GRBL_11.py @@ -90,7 +90,6 @@ class GRBL_11(PreProc): else: gcode += '(X,Y End: ' + "None" + units + ')\n' gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n' - gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n' if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry': gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n' @@ -258,8 +257,10 @@ G00 Z{z_toolchange} return sdir def dwell_code(self, p): + gcode = '' if p.dwelltime: - return 'G4 P' + str(p.dwelltime) + gcode += 'G4 P' + str(p.dwelltime) + return gcode def spindle_stop_code(self, p): return 'M05' diff --git a/tclCommands/TclCommandDrillcncjob.py b/tclCommands/TclCommandDrillcncjob.py index e9bdd746..64f11e96 100644 --- a/tclCommands/TclCommandDrillcncjob.py +++ b/tclCommands/TclCommandDrillcncjob.py @@ -126,14 +126,14 @@ class TclCommandDrillcncjob(TclCommandSignaled): if obj is None: if muted is False: self.raise_tcl_error("Object not found: %s" % name) - - return "Object not found: %s" % name + self.app.log.error("Object not found: %s" % name) + return "fail" if obj.kind != 'excellon': if muted is False: self.raise_tcl_error('Expected ExcellonObject, got %s %s.' % (name, type(obj))) - - return 'Expected ExcellonObject, got %s %s.' % (name, type(obj)) + self.app.log.error('Expected ExcellonObject, got %s %s.' % (name, type(obj))) + return "fail" xmin = obj.options['xmin'] ymin = obj.options['ymin'] @@ -143,6 +143,7 @@ class TclCommandDrillcncjob(TclCommandSignaled): def job_init(job_obj, app_obj): # tools = args["tools"] if "tools" in args else 'all' + # drilled tools diameters try: if 'drilled_dias' in args and args['drilled_dias'] != 'all': diameters = [x.strip() for x in args['drilled_dias'].split(",") if x != ''] @@ -175,7 +176,7 @@ class TclCommandDrillcncjob(TclCommandSignaled): 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." - # make a string of diameters separated by comma; this is what generate_from_excellon_by_tool() is + # make a string of diameters separated by comma; this is what tcl_gcode_from_excellon_by_tool() is # expecting as tools parameter tools = ','.join(req_tools) @@ -194,7 +195,8 @@ class TclCommandDrillcncjob(TclCommandSignaled): if muted is False: self.raise_tcl_error("Bad tools: %s" % str(e)) - return "Bad tools: %s" % str(e) + self.app.log.error("Bad tools: %s" % str(e)) + return "fail" used_tools_info = [] used_tools_info.insert(0, [_("Tool_nr"), _("Diameter"), _("Drills_Nr"), _("Slots_Nr")]) @@ -246,8 +248,9 @@ class TclCommandDrillcncjob(TclCommandSignaled): if len(eval(xy_toolchange)) != 2: self.raise_tcl_error("The entered value for 'toolchangexy' needs to have the format x,y or " "in format (x, y) - no spaces allowed. But always two comma separated values.") - return "The entered value for 'toolchangexy' needs to have the format x,y or "\ - "in format (x, y) - no spaces allowed. But always two comma separated values." + self.app.log.error("The entered value for 'toolchangexy' needs to have the format x,y or " + "in format (x, y) - no spaces allowed. But always two comma separated values.") + return "fail" endz = args["endz"] if "endz" in args and args["endz"] is not None else \ self.app.defaults["tools_drill_endz"] @@ -263,8 +266,9 @@ class TclCommandDrillcncjob(TclCommandSignaled): if len(eval(xy_end)) != 2: self.raise_tcl_error("The entered value for 'xy_end' needs to have the format x,y or " "in format (x, y) - no spaces allowed. But always two comma separated values.") - return "The entered value for 'xy_end' needs to have the format x,y or "\ - "in format (x, y) - no spaces allowed. But always two comma separated values." + self.app.log.error("The entered value for 'xy_end' needs to have the format x,y or " + "in format (x, y) - no spaces allowed. But always two comma separated values.") + return "fail" opt_type = args["opt_type"] if "opt_type" in args and args["opt_type"] else 'B' @@ -275,10 +279,11 @@ class TclCommandDrillcncjob(TclCommandSignaled): job_obj.multigeo = True job_obj.multitool = True + # 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.options['ppname_e'] = pp_excellon_name - + # multidepth if 'dpp' in args: job_obj.multidepth = True if args['dpp'] is not None: @@ -288,21 +293,22 @@ class TclCommandDrillcncjob(TclCommandSignaled): else: job_obj.multidepth = self.app.defaults["tools_drill_multidepth"] 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 \ self.app.defaults["tools_drill_travelz"] - + # Feedrate 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 \ self.app.defaults["tools_drill_feedrate_z"] - 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 job_obj.spindlespeed = float(args["spindlespeed"]) if "spindlespeed" in args else None - job_obj.spindledir = self.app.defaults['tools_drill_spindlespeed'] + # spindle direction + job_obj.spindledir = self.app.defaults["tools_drill_spindledir"] + # dwell and dwelltime if 'dwelltime' in args: job_obj.dwell = True if args['dwelltime'] is not None: @@ -321,11 +327,15 @@ class TclCommandDrillcncjob(TclCommandSignaled): job_obj.options['xmax'] = xmax job_obj.options['ymax'] = ymax + # Cut Z job_obj.z_cut = float(drillz) + # toolchange job_obj.toolchange = toolchange + # toolchange X-Y location job_obj.xy_toolchange = xy_toolchange + # toolchange Z location job_obj.z_toolchange = float(toolchangez) - + # start Z if "startz" in args and args["startz"] is not None: job_obj.startz = float(args["startz"]) else: @@ -333,18 +343,18 @@ class TclCommandDrillcncjob(TclCommandSignaled): job_obj.startz = self.app.defaults["tools_drill_startz"] else: job_obj.startz = self.app.defaults["tools_drill_travelz"] - + # end Z job_obj.endz = float(endz) + # end X-Y location job_obj.xy_end = xy_end + # Excellon optimization job_obj.excellon_optimization_type = opt_type - job_obj.spindledir = self.app.defaults["tools_drill_spindledir"] - ret_val = job_obj.generate_from_excellon_by_tool(obj, tools, is_first=True, use_ui=False) + ret_val = 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] total_gcode_parsed = []