Просмотр исходного кода

- fixed the name self-insert in save dialog file for GCode; added protection in case the save path is None
- fixed FlatCAM crash when trying to make drills GCode out of a file that have only slots.
- made the shell toggle shortcut key work when focused on Selected Tab; toggle units shortcut also
- changed the messages for Units COnversion

Marius Stanciu 7 лет назад
Родитель
Сommit
dedf8c09de
5 измененных файлов с 250 добавлено и 191 удалено
  1. 11 5
      FlatCAMApp.py
  2. 6 0
      FlatCAMGUI.py
  3. 40 18
      FlatCAMObj.py
  4. 4 0
      README.md
  5. 189 168
      camlib.py

+ 11 - 5
FlatCAMApp.py

@@ -1659,7 +1659,10 @@ class App(QtCore.QObject):
         return self.defaults["global_last_folder"]
         return self.defaults["global_last_folder"]
 
 
     def get_last_save_folder(self):
     def get_last_save_folder(self):
-        return self.defaults["global_last_save_folder"]
+        loc = self.defaults["global_last_save_folder"]
+        if loc is None:
+            loc = self.defaults["global_last_folder"]
+        return loc
 
 
     def report_usage(self, resource):
     def report_usage(self, resource):
         """
         """
@@ -2070,6 +2073,8 @@ class App(QtCore.QObject):
             except:
             except:
                 self.inform.emit("[ERROR_NOTCL] Failed to write defaults to file.")
                 self.inform.emit("[ERROR_NOTCL] Failed to write defaults to file.")
                 return
                 return
+
+        self.file_saved.emit("preferences", filename)
         self.inform.emit("[success]Exported Defaults to %s" % filename)
         self.inform.emit("[success]Exported Defaults to %s" % filename)
 
 
     def on_preferences_open_folder(self):
     def on_preferences_open_folder(self):
@@ -2825,6 +2830,9 @@ class App(QtCore.QObject):
                     current.to_form()
                     current.to_form()
 
 
             self.plot_all()
             self.plot_all()
+            self.inform.emit("[success]Converted units to %s" % self.options["units"])
+            # self.ui.units_label.setText("[" + self.options["units"] + "]")
+            self.set_screen_units(self.options["units"])
         else:
         else:
             # Undo toggling
             # Undo toggling
             self.toggle_units_ignore = True
             self.toggle_units_ignore = True
@@ -2833,11 +2841,9 @@ class App(QtCore.QObject):
             else:
             else:
                 self.general_options_form.general_app_group.units_radio.set_value('MM')
                 self.general_options_form.general_app_group.units_radio.set_value('MM')
             self.toggle_units_ignore = False
             self.toggle_units_ignore = False
+            self.inform.emit("[WARNING_NOTCL]Units conversion cancelled.")
 
 
         self.options_read_form()
         self.options_read_form()
-        self.inform.emit("Converted units to %s" % self.options["units"])
-        #self.ui.units_label.setText("[" + self.options["units"] + "]")
-        self.set_screen_units(self.options["units"])
 
 
     def on_toggle_units_click(self):
     def on_toggle_units_click(self):
         if self.options["units"] == 'MM':
         if self.options["units"] == 'MM':
@@ -6044,7 +6050,7 @@ class App(QtCore.QObject):
         self.defaults["global_last_folder"] = os.path.split(str(filename))[0]
         self.defaults["global_last_folder"] = os.path.split(str(filename))[0]
 
 
     def register_save_folder(self, filename):
     def register_save_folder(self, filename):
-        self.defaults['global_last_save_folder'] = os.path.split(str(filename))[0]
+        self.defaults["global_last_save_folder"] = os.path.split(str(filename))[0]
 
 
     def set_progress_bar(self, percentage, text=""):
     def set_progress_bar(self, percentage, text=""):
         self.ui.progress_bar.setValue(int(percentage))
         self.ui.progress_bar.setValue(int(percentage))

+ 6 - 0
FlatCAMGUI.py

@@ -1508,6 +1508,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         if event.key() == QtCore.Qt.Key_3:
         if event.key() == QtCore.Qt.Key_3:
             self.app.on_select_tab('tool')
             self.app.on_select_tab('tool')
 
 
+        if event.key == QtCore.Qt.Key_Q:
+            self.app.on_toggle_units_click()
+
+        if event.key() == QtCore.Qt.Key_S:
+            self.app.on_toggle_shell()
+
         # Show shortcut list
         # Show shortcut list
         if event.key() == QtCore.Qt.Key_Ampersand:
         if event.key() == QtCore.Qt.Key_Ampersand:
             self.app.on_shortcut_list()
             self.app.on_shortcut_list()

+ 40 - 18
FlatCAMObj.py

@@ -1564,7 +1564,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             return False, "Error: No tools."
             return False, "Error: No tools."
 
 
         for tool in tools:
         for tool in tools:
-            if tooldia > self.tools[tool]["C"]:
+            # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
+            adj_toolstable_tooldia = float('%.4f' % float(tooldia))
+            adj_file_tooldia = float('%.4f' % float(self.tools[tool]["C"]))
+            if adj_toolstable_tooldia > adj_file_tooldia + 0.0001:
                 self.app.inform.emit("[ERROR_NOTCL] Milling tool for SLOTS is larger than hole size. Cancelled.")
                 self.app.inform.emit("[ERROR_NOTCL] Milling tool for SLOTS is larger than hole size. Cancelled.")
                 return False, "Error: Milling tool is larger than hole."
                 return False, "Error: Milling tool is larger than hole."
 
 
@@ -1590,7 +1593,12 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             # 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 slot in self.slots:
                 if slot['tool'] in tools:
                 if slot['tool'] in tools:
-                    buffer_value = (float(self.tools[slot['tool']]["C"]) / 2) - float(tooldia / 2)
+                    toolstable_tool = float('%.4f' % float(tooldia))
+                    file_tool = float('%.4f' % float(self.tools[tool]["C"]))
+
+                    # 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)
+                    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['start']
                         stop = slot['stop']
                         stop = slot['stop']
@@ -1729,14 +1737,16 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             # job_obj.options["tooldia"] =
             # job_obj.options["tooldia"] =
 
 
             tools_csv = ','.join(tools)
             tools_csv = ','.join(tools)
-            job_obj.generate_from_excellon_by_tool(self, tools_csv,
-                                                   drillz=self.options['drillz'],
-                                                   toolchange=self.options["toolchange"],
-                                                   toolchangez=self.options["toolchangez"],
-                                                   startz=self.options["startz"],
-                                                   endz=self.options["endz"],
-                                                   excellon_optimization_type=self.options["optimization_type"])
-
+            ret_val = job_obj.generate_from_excellon_by_tool(self, tools_csv,
+                                                             drillz=self.options['drillz'],
+                                                             toolchange=self.options["toolchange"],
+                                                             toolchangez=self.options["toolchangez"],
+                                                             startz=self.options["startz"],
+                                                             endz=self.options["endz"],
+                                                             excellon_optimization_type=self.app.defaults[
+                                                                 "excellon_optimization_type"])
+            if ret_val == 'fail':
+                return 'fail'
             app_obj.progress.emit(50)
             app_obj.progress.emit(50)
             job_obj.gcode_parse()
             job_obj.gcode_parse()
 
 
@@ -3125,10 +3135,18 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         segx = segx if segx is not None else float(self.app.defaults['geometry_segx'])
         segx = segx if segx is not None else float(self.app.defaults['geometry_segx'])
         segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
         segy = segy if segy is not None else float(self.app.defaults['geometry_segy'])
 
 
-        xmin = self.options['xmin']
-        ymin = self.options['ymin']
-        xmax = self.options['xmax']
-        ymax = self.options['ymax']
+        try:
+            xmin = self.options['xmin']
+            ymin = self.options['ymin']
+            xmax = self.options['xmax']
+            ymax = self.options['ymax']
+        except Exception as e:
+            log.debug("FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s\n" % str(e))
+            msg = "[ERROR] An internal error has ocurred. See shell.\n"
+            msg += 'FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s' % str(e)
+            msg += traceback.format_exc()
+            self.app.inform.emit(msg)
+            return
 
 
         # Object initialization function for app.new_object()
         # Object initialization function for app.new_object()
         # RUNNING ON SEPARATE THREAD!
         # RUNNING ON SEPARATE THREAD!
@@ -4267,14 +4285,17 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
             _filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.cnc);;" \
             _filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.cnc);;" \
                        "G-Code Files (*.g-code);;All Files (*.*)"
                        "G-Code Files (*.g-code);;All Files (*.*)"
 
 
+        dir_file_to_save = self.app.get_last_save_folder() + '/' + str(name)
         try:
         try:
-            filename = str(QtWidgets.QFileDialog.getSaveFileName(
+            filename, _ = QtWidgets.QFileDialog.getSaveFileName(
                 caption="Export Machine Code ...",
                 caption="Export Machine Code ...",
-                directory=self.app.get_last_save_folder() + '/' + name,
+                directory=dir_file_to_save,
                 filter=_filter_
                 filter=_filter_
-            )[0])
+            )
         except TypeError:
         except TypeError:
-            filename = str(QtWidgets.QFileDialog.getSaveFileName(caption="Export Machine Code ...", filter=_filter_)[0])
+            filename, _ = QtWidgets.QFileDialog.getSaveFileName(caption="Export Machine Code ...", filter=_filter_)
+
+        filename = str(filename)
 
 
         if filename == '':
         if filename == '':
             self.app.inform.emit("[WARNING_NOTCL]Export Machine Code cancelled ...")
             self.app.inform.emit("[WARNING_NOTCL]Export Machine Code cancelled ...")
@@ -4482,6 +4503,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         elif to_file is False:
         elif to_file is False:
             # Just for adding it to the recent files list.
             # Just for adding it to the recent files list.
             self.app.file_opened.emit("cncjob", filename)
             self.app.file_opened.emit("cncjob", filename)
+            self.app.file_saved.emit("cncjob", filename)
 
 
             self.app.inform.emit("[success] Saved to: " + filename)
             self.app.inform.emit("[success] Saved to: " + filename)
         else:
         else:

+ 4 - 0
README.md

@@ -16,6 +16,10 @@ CAD program, and create G-Code for Isolation routing.
 - fixed bug in multigeometry geometry not having the bounds in self.options and crashing the GCode generation
 - fixed bug in multigeometry geometry not having the bounds in self.options and crashing the GCode generation
 - fixed bug that crashed whole application in case that the GCode editor is activated on a Tool gcode that is defective. 
 - fixed bug that crashed whole application in case that the GCode editor is activated on a Tool gcode that is defective. 
 - fixed bug in Excellon Slots milling: a value of a dict key was a string instead to be an int. A cast to integer solved it.
 - fixed bug in Excellon Slots milling: a value of a dict key was a string instead to be an int. A cast to integer solved it.
+- fixed the name self-insert in save dialog file for GCode; added protection in case the save path is None
+- fixed FlatCAM crash when trying to make drills GCode out of a file that have only slots.
+- made the shell toggle shortcut key work when focused on Selected Tab; toggle units shortcut also
+- changed the messages for Units COnversion
 
 
 5.02.3019
 5.02.3019
 
 

+ 189 - 168
camlib.py

@@ -4549,7 +4549,7 @@ class CNCjob(Geometry):
         elif drillz == 0:
         elif drillz == 0:
             self.app.inform.emit("[WARNING] The Cut Z parameter is zero. "
             self.app.inform.emit("[WARNING] The Cut Z parameter is zero. "
                                  "There will be no cut, skipping %s file" % exobj.options['name'])
                                  "There will be no cut, skipping %s file" % exobj.options['name'])
-            return
+            return 'fail'
         else:
         else:
             self.z_cut = drillz
             self.z_cut = drillz
 
 
@@ -4670,139 +4670,188 @@ class CNCjob(Geometry):
         if current_platform == '64bit':
         if current_platform == '64bit':
             if excellon_optimization_type == 'M':
             if 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.")
-                for tool in tools:
-                    self.tool=tool
-                    self.postdata['toolC']=exobj.tools[tool]["C"]
-
-                    ################################################
-                    # Create the data.
-                    node_list = []
-                    locations = create_data_array()
-                    tsp_size = len(locations)
-                    num_routes = 1  # The number of routes, which is 1 in the TSP.
-                    # Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
-                    depot = 0
-                    # Create routing model.
-                    if tsp_size > 0:
-                        routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
-                        search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
-                        search_parameters.local_search_metaheuristic = (
-                            routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
-
-                        # Set search time limit in milliseconds.
-                        if float(self.app.defaults["excellon_search_time"]) != 0:
-                            search_parameters.time_limit_ms = int(
-                                float(self.app.defaults["excellon_search_time"]) * 1000)
-                        else:
-                            search_parameters.time_limit_ms = 3000
-
-                        # Callback to the distance function. The callback takes two
-                        # arguments (the from and to node indices) and returns the distance between them.
-                        dist_between_locations = CreateDistanceCallback()
-                        dist_callback = dist_between_locations.Distance
-                        routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
-
-                        # Solve, returns a solution if any.
-                        assignment = routing.SolveWithParameters(search_parameters)
-
-                        if assignment:
-                            # Solution cost.
-                            log.info("Total distance: " + str(assignment.ObjectiveValue()))
-
-                            # Inspect solution.
-                            # Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
-                            route_number = 0
-                            node = routing.Start(route_number)
-                            start_node = node
-
-                            while not routing.IsEnd(node):
-                                node_list.append(node)
-                                node = assignment.Value(routing.NextVar(node))
-                        else:
-                            log.warning('No solution found.')
-                    else:
-                        log.warning('Specify an instance greater than 0.')
-                    ################################################
-
-                    # Only if tool has points.
-                    if tool in points:
-                        # Tool change sequence (optional)
-                        if toolchange:
-                            gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
-                            gcode += self.doformat(p.spindle_code)  # Spindle start
-                            if self.dwell is True:
-                                gcode += self.doformat(p.dwell_code)  # Dwell time
+                if exobj.drills:
+                    for tool in tools:
+                        self.tool=tool
+                        self.postdata['toolC']=exobj.tools[tool]["C"]
+
+                        ################################################
+                        # Create the data.
+                        node_list = []
+                        locations = create_data_array()
+                        tsp_size = len(locations)
+                        num_routes = 1  # The number of routes, which is 1 in the TSP.
+                        # Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
+                        depot = 0
+                        # Create routing model.
+                        if tsp_size > 0:
+                            routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
+                            search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
+                            search_parameters.local_search_metaheuristic = (
+                                routing_enums_pb2.LocalSearchMetaheuristic.GUIDED_LOCAL_SEARCH)
+
+                            # Set search time limit in milliseconds.
+                            if float(self.app.defaults["excellon_search_time"]) != 0:
+                                search_parameters.time_limit_ms = int(
+                                    float(self.app.defaults["excellon_search_time"]) * 1000)
+                            else:
+                                search_parameters.time_limit_ms = 3000
+
+                            # Callback to the distance function. The callback takes two
+                            # arguments (the from and to node indices) and returns the distance between them.
+                            dist_between_locations = CreateDistanceCallback()
+                            dist_callback = dist_between_locations.Distance
+                            routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
+
+                            # Solve, returns a solution if any.
+                            assignment = routing.SolveWithParameters(search_parameters)
+
+                            if assignment:
+                                # Solution cost.
+                                log.info("Total distance: " + str(assignment.ObjectiveValue()))
+
+                                # Inspect solution.
+                                # Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
+                                route_number = 0
+                                node = routing.Start(route_number)
+                                start_node = node
+
+                                while not routing.IsEnd(node):
+                                    node_list.append(node)
+                                    node = assignment.Value(routing.NextVar(node))
+                            else:
+                                log.warning('No solution found.')
                         else:
                         else:
-                            gcode += self.doformat(p.spindle_code)
-                            if self.dwell is True:
-                                gcode += self.doformat(p.dwell_code)  # Dwell time
+                            log.warning('Specify an instance greater than 0.')
+                        ################################################
+
+                        # Only if tool has points.
+                        if tool in points:
+                            # Tool change sequence (optional)
+                            if toolchange:
+                                gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
+                                gcode += self.doformat(p.spindle_code)  # Spindle start
+                                if self.dwell is True:
+                                    gcode += self.doformat(p.dwell_code)  # Dwell time
+                            else:
+                                gcode += self.doformat(p.spindle_code)
+                                if self.dwell is True:
+                                    gcode += self.doformat(p.dwell_code)  # Dwell time
+
+                            # Drillling!
+                            for k in node_list:
+                                locx = locations[k][0]
+                                locy = locations[k][1]
+
+                                gcode += self.doformat(p.rapid_code, x=locx, y=locy)
+                                gcode += self.doformat(p.down_code, x=locx, y=locy)
+                                gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
+                                gcode += self.doformat(p.lift_code, x=locx, y=locy)
+                                measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
+                                self.oldx = locx
+                                self.oldy = locy
+                else:
+                    log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
+                              "The loaded Excellon file has no drills ...")
+                    self.app.inform.emit('[ERROR_NOTCL]The loaded Excellon file has no drills ...')
+                    return 'fail'
 
 
-                        # Drillling!
-                        for k in node_list:
-                            locx = locations[k][0]
-                            locy = locations[k][1]
-
-                            gcode += self.doformat(p.rapid_code, x=locx, y=locy)
-                            gcode += self.doformat(p.down_code, x=locx, y=locy)
-                            gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
-                            gcode += self.doformat(p.lift_code, x=locx, y=locy)
-                            measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
-                            self.oldx = locx
-                            self.oldy = locy
                 log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))
                 log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))
             elif excellon_optimization_type == 'B':
             elif excellon_optimization_type == 'B':
                 log.debug("Using OR-Tools Basic drill path optimization.")
                 log.debug("Using OR-Tools Basic drill path optimization.")
-                for tool in tools:
-                    self.tool=tool
-                    self.postdata['toolC']=exobj.tools[tool]["C"]
-
-                    ################################################
-                    node_list = []
-                    locations = create_data_array()
-                    tsp_size = len(locations)
-                    num_routes = 1  # The number of routes, which is 1 in the TSP.
-
-                    # Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
-                    depot = 0
-
-                    # Create routing model.
-                    if tsp_size > 0:
-                        routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
-                        search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
-
-                        # Callback to the distance function. The callback takes two
-                        # arguments (the from and to node indices) and returns the distance between them.
-                        dist_between_locations = CreateDistanceCallback()
-                        dist_callback = dist_between_locations.Distance
-                        routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
-
-                        # Solve, returns a solution if any.
-                        assignment = routing.SolveWithParameters(search_parameters)
-
-                        if assignment:
-                            # Solution cost.
-                            log.info("Total distance: " + str(assignment.ObjectiveValue()))
-
-                            # Inspect solution.
-                            # Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
-                            route_number = 0
-                            node = routing.Start(route_number)
-                            start_node = node
-
-                            while not routing.IsEnd(node):
-                                node_list.append(node)
-                                node = assignment.Value(routing.NextVar(node))
+                if exobj.drills:
+                    for tool in tools:
+                        self.tool=tool
+                        self.postdata['toolC']=exobj.tools[tool]["C"]
+
+                        ################################################
+                        node_list = []
+                        locations = create_data_array()
+                        tsp_size = len(locations)
+                        num_routes = 1  # The number of routes, which is 1 in the TSP.
+
+                        # Nodes are indexed from 0 to tsp_size - 1. The depot is the starting node of the route.
+                        depot = 0
+
+                        # Create routing model.
+                        if tsp_size > 0:
+                            routing = pywrapcp.RoutingModel(tsp_size, num_routes, depot)
+                            search_parameters = pywrapcp.RoutingModel.DefaultSearchParameters()
+
+                            # Callback to the distance function. The callback takes two
+                            # arguments (the from and to node indices) and returns the distance between them.
+                            dist_between_locations = CreateDistanceCallback()
+                            dist_callback = dist_between_locations.Distance
+                            routing.SetArcCostEvaluatorOfAllVehicles(dist_callback)
+
+                            # Solve, returns a solution if any.
+                            assignment = routing.SolveWithParameters(search_parameters)
+
+                            if assignment:
+                                # Solution cost.
+                                log.info("Total distance: " + str(assignment.ObjectiveValue()))
+
+                                # Inspect solution.
+                                # Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
+                                route_number = 0
+                                node = routing.Start(route_number)
+                                start_node = node
+
+                                while not routing.IsEnd(node):
+                                    node_list.append(node)
+                                    node = assignment.Value(routing.NextVar(node))
+                            else:
+                                log.warning('No solution found.')
                         else:
                         else:
-                            log.warning('No solution found.')
-                    else:
-                        log.warning('Specify an instance greater than 0.')
-                    ################################################
+                            log.warning('Specify an instance greater than 0.')
+                        ################################################
+
+                        # Only if tool has points.
+                        if tool in points:
+                            # Tool change sequence (optional)
+                            if toolchange:
+                                gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
+                                gcode += self.doformat(p.spindle_code)  # Spindle start)
+                                if self.dwell is True:
+                                    gcode += self.doformat(p.dwell_code)  # Dwell time
+                            else:
+                                gcode += self.doformat(p.spindle_code)
+                                if self.dwell is True:
+                                    gcode += self.doformat(p.dwell_code)  # Dwell time
+
+                            # Drillling!
+                            for k in node_list:
+                                locx = locations[k][0]
+                                locy = locations[k][1]
+                                gcode += self.doformat(p.rapid_code, x=locx, y=locy)
+                                gcode += self.doformat(p.down_code, x=locx, y=locy)
+                                gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
+                                gcode += self.doformat(p.lift_code, x=locx, y=locy)
+                                measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
+                                self.oldx = locx
+                                self.oldy = locy
+                else:
+                    log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
+                              "The loaded Excellon file has no drills ...")
+                    self.app.inform.emit('[ERROR_NOTCL]The loaded Excellon file has no drills ...')
+                    return 'fail'
+
+                log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % str(measured_distance))
+            else:
+                self.app.inform.emit("[ERROR_NOTCL] Wrong optimization type selected.")
+                return 'fail'
+        else:
+            log.debug("Using Travelling Salesman drill path optimization.")
+            for tool in tools:
+                if exobj.drills:
+                    self.tool = tool
+                    self.postdata['toolC'] = exobj.tools[tool]["C"]
 
 
                     # Only if tool has points.
                     # Only if tool has points.
                     if tool in points:
                     if tool in points:
                         # Tool change sequence (optional)
                         # Tool change sequence (optional)
                         if toolchange:
                         if toolchange:
-                            gcode += self.doformat(p.toolchange_code,toolchangexy=(self.oldx, self.oldy))
+                            gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
                             gcode += self.doformat(p.spindle_code)  # Spindle start)
                             gcode += self.doformat(p.spindle_code)  # Spindle start)
                             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
@@ -4812,52 +4861,23 @@ class CNCjob(Geometry):
                                 gcode += self.doformat(p.dwell_code)  # Dwell time
                                 gcode += self.doformat(p.dwell_code)  # Dwell time
 
 
                         # Drillling!
                         # Drillling!
-                        for k in node_list:
-                            locx = locations[k][0]
-                            locy = locations[k][1]
-                            gcode += self.doformat(p.rapid_code, x=locx, y=locy)
-                            gcode += self.doformat(p.down_code, x=locx, y=locy)
-                            gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
-                            gcode += self.doformat(p.lift_code, x=locx, y=locy)
-                            measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
-                            self.oldx = locx
-                            self.oldy = locy
-                log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % str(measured_distance))
-            else:
-                self.app.inform.emit("[ERROR_NOTCL] Wrong optimization type selected.")
-                return
-        else:
-            log.debug("Using Travelling Salesman drill path optimization.")
-            for tool in tools:
-                self.tool = tool
-                self.postdata['toolC'] = exobj.tools[tool]["C"]
-
-                # Only if tool has points.
-                if tool in points:
-                    # Tool change sequence (optional)
-                    if toolchange:
-                        gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
-                        gcode += self.doformat(p.spindle_code)  # Spindle start)
-                        if self.dwell is True:
-                            gcode += self.doformat(p.dwell_code)  # Dwell time
+                        altPoints = []
+                        for point in points[tool]:
+                            altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
+
+                        for point in self.optimized_travelling_salesman(altPoints):
+                            gcode += self.doformat(p.rapid_code, x=point[0], y=point[1])
+                            gcode += self.doformat(p.down_code, x=point[0], y=point[1])
+                            gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
+                            gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
+                            measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
+                            self.oldx = point[0]
+                            self.oldy = point[1]
                     else:
                     else:
-                        gcode += self.doformat(p.spindle_code)
-                        if self.dwell is True:
-                            gcode += self.doformat(p.dwell_code)  # Dwell time
-
-                    # Drillling!
-                    altPoints = []
-                    for point in points[tool]:
-                        altPoints.append((point.coords.xy[0][0], point.coords.xy[1][0]))
-
-                    for point in self.optimized_travelling_salesman(altPoints):
-                        gcode += self.doformat(p.rapid_code, x=point[0], y=point[1])
-                        gcode += self.doformat(p.down_code, x=point[0], y=point[1])
-                        gcode += self.doformat(p.up_to_zero_code, x=point[0], y=point[1])
-                        gcode += self.doformat(p.lift_code, x=point[0], y=point[1])
-                        measured_distance += abs(distance_euclidian(point[0], point[1], self.oldx, self.oldy))
-                        self.oldx = point[0]
-                        self.oldy = point[1]
+                        log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
+                                  "The loaded Excellon file has no drills ...")
+                        self.app.inform.emit('[ERROR_NOTCL]The loaded Excellon file has no drills ...')
+                        return 'fail'
             log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
             log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
 
 
         gcode += self.doformat(p.spindle_stop_code)  # Spindle stop
         gcode += self.doformat(p.spindle_stop_code)  # Spindle stop
@@ -4867,6 +4887,7 @@ class CNCjob(Geometry):
         log.debug("The total travel distance including travel to end position is: %s" %
         log.debug("The total travel distance including travel to end position is: %s" %
                   str(measured_distance) + '\n')
                   str(measured_distance) + '\n')
         self.gcode = gcode
         self.gcode = gcode
+        return 'OK'
 
 
     def generate_from_multitool_geometry(self, geometry, append=True,
     def generate_from_multitool_geometry(self, geometry, append=True,
                                          tooldia=None, offset=0.0, tolerance=0, z_cut=1.0, z_move=2.0,
                                          tooldia=None, offset=0.0, tolerance=0, z_cut=1.0, z_move=2.0,