Przeglądaj źródła

jpcgt/flatcam/Beta слито с Beta

Camellan 6 lat temu
rodzic
commit
0c364c9540

+ 77 - 20
FlatCAMApp.py

@@ -1631,7 +1631,7 @@ class App(QtCore.QObject):
         self.toggle_units_ignore = False
 
         # #############################################################################
-        # ########################## LOAD POSTPROCESSORS ##############################
+        # ########################## LOAD PREPROCESSORS ###############################
         # #############################################################################
 
         # a dictionary that have as keys the name of the preprocessor files and the value is the class from
@@ -1846,7 +1846,7 @@ class App(QtCore.QObject):
         # signal to be called when the app is quiting
         self.app_quit.connect(self.quit_application, type=Qt.QueuedConnection)
         self.message.connect(self.message_dialog)
-        self.progress.connect(self.set_progress_bar)
+        # self.progress.connect(self.set_progress_bar)
 
         # signals that are emitted when object state changes
         self.object_created.connect(self.on_object_created)
@@ -2464,7 +2464,10 @@ class App(QtCore.QObject):
         self.fiducial_tool = None
 
         # always install tools only after the shell is initialized because the self.inform.emit() depends on shell
-        self.install_tools()
+        try:
+            self.install_tools()
+        except AttributeError:
+            pass
 
         # ##################################################################################
         # ########################### SETUP RECENT ITEMS ###################################
@@ -2628,7 +2631,10 @@ class App(QtCore.QObject):
         # Storage for shapes, storage that can be used by FlatCAm tools for utility geometry
         # VisPy visuals
         if self.is_legacy is False:
-            self.tool_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1)
+            try:
+                self.tool_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1)
+            except AttributeError:
+                self.tool_shapes = None
         else:
             from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
             self.tool_shapes = ShapeCollectionLegacy(obj=self, app=self, name="tool")
@@ -2639,9 +2645,20 @@ class App(QtCore.QObject):
 
         # watch out for the position of the editors instantiation ... if it is done before a save of the default values
         # at the first launch of the App , the editors will not be functional.
-        self.geo_editor = FlatCAMGeoEditor(self, disabled=True)
-        self.exc_editor = FlatCAMExcEditor(self)
-        self.grb_editor = FlatCAMGrbEditor(self)
+        try:
+            self.geo_editor = FlatCAMGeoEditor(self, disabled=True)
+        except AttributeError:
+            pass
+
+        try:
+            self.exc_editor = FlatCAMExcEditor(self)
+        except AttributeError:
+            pass
+
+        try:
+            self.grb_editor = FlatCAMGrbEditor(self)
+        except AttributeError:
+            pass
         self.log.debug("Finished adding FlatCAM Editor's.")
 
         self.set_ui_title(name=_("New Project - Not saved"))
@@ -3136,7 +3153,11 @@ class App(QtCore.QObject):
         self.ui.menutoolshell.triggered.connect(self.on_toggle_shell)
 
         # third install all of them
-        self.install_tools()
+        try:
+            self.install_tools()
+        except AttributeError:
+            pass
+
         self.log.debug("Tools are initialized.")
 
     # def parse_system_fonts(self):
@@ -10874,13 +10895,11 @@ class App(QtCore.QObject):
             name = outname or filename.split('/')[-1].split('\\')[-1]
 
             self.new_object(obj_type, name, obj_init, autoselected=False)
-            self.progress.emit(20)
             # Register recent file
             self.file_opened.emit("dxf", filename)
 
             # GUI feedback
             self.inform.emit('[success] %s: %s' % (_("Opened"), filename))
-            self.progress.emit(100)
 
     def open_gerber(self, filename, outname=None):
         """
@@ -10956,7 +10975,6 @@ class App(QtCore.QObject):
 
         # How the object should be initialized
         def obj_init(excellon_obj, app_obj):
-            # self.progress.emit(20)
 
             try:
                 ret = excellon_obj.parse_file(filename=filename)
@@ -10969,7 +10987,6 @@ class App(QtCore.QObject):
                 app_obj.inform.emit('[ERROR_NOTCL] %s: %s' %
                                     (_("Cannot open file"), filename))
                 log.debug("Could not open Excellon object.")
-                self.progress.emit(0)  # TODO: self and app_bjj mixed
                 return "fail"
             except Exception:
                 msg = '[ERROR_NOTCL] %s' % \
@@ -11611,6 +11628,11 @@ class App(QtCore.QObject):
 
         }
 
+        try:
+            image_opener = self.image_tool.import_image
+        except AttributeError:
+            image_opener = None
+
         openers = {
             'gerber': lambda fname: self.worker_task.emit({'fcn': self.open_gerber, 'params': [fname]}),
             'excellon': lambda fname: self.worker_task.emit({'fcn': self.open_excellon, 'params': [fname]}),
@@ -11621,7 +11643,7 @@ class App(QtCore.QObject):
             'project': self.open_project,
             'svg': self.import_svg,
             'dxf': self.import_dxf,
-            'image': self.image_tool.import_image,
+            'image': image_opener,
             'pdf': lambda fname: self.worker_task.emit({'fcn': self.pdf_tool.open_pdf, 'params': [fname]})
         }
 
@@ -11950,7 +11972,7 @@ class App(QtCore.QObject):
 
     def on_plotcanvas_setup(self, container=None):
         """
-        This is doing the setup for the plot area (VisPy canvas)
+        This is doing the setup for the plot area (canvas)
 
         :param container: widget where to install the canvas
         :return: None
@@ -11961,7 +11983,18 @@ class App(QtCore.QObject):
             plot_container = self.ui.right_layout
 
         if self.is_legacy is False:
-            self.plotcanvas = PlotCanvas(plot_container, self)
+            try:
+                self.plotcanvas = PlotCanvas(plot_container, self)
+            except Exception as er:
+                msg_txt = traceback.format_exc()
+                log.debug("App.on_plotcanvas_setup() failed -> %s" % str(er))
+                log.debug("OpenGL canvas initialization failed with the following error.\n" + msg_txt)
+                msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n")
+                msg += _("OpenGL canvas initialization failed. HW or HW configuration not supported."
+                         "Change the graphic engine to Legacy(2D) in Edit -> Preferences -> General tab.\n\n")
+                msg += msg_txt
+                self.inform.emit(msg)
+                return 'fail'
         else:
             self.plotcanvas = PlotCanvasLegacy(plot_container, self)
 
@@ -12044,8 +12077,7 @@ class App(QtCore.QObject):
         log.debug("App.on_enable_sel_plot()")
         object_list = self.collection.get_selected()
         self.enable_plots(objects=object_list)
-        self.inform.emit('[success] %s' %
-                         _("Selected plots enabled..."))
+        self.inform.emit('[success] %s' % _("Selected plots enabled..."))
 
     def on_disable_sel_plots(self):
         log.debug("App.on_disable_sel_plot()")
@@ -12053,8 +12085,7 @@ class App(QtCore.QObject):
         # self.inform.emit(_("Disabling plots ..."))
         object_list = self.collection.get_selected()
         self.disable_plots(objects=object_list)
-        self.inform.emit('[success] %s' %
-                         _("Selected plots disabled..."))
+        self.inform.emit('[success] %s' % _("Selected plots disabled..."))
 
     def enable_plots(self, objects):
         """
@@ -12070,6 +12101,20 @@ class App(QtCore.QObject):
             if obj.options['plot'] is False:
                 obj.options.set_change_callback(lambda x: None)
                 obj.options['plot'] = True
+                try:
+                    # only the Gerber obj has on_plot_cb_click() method
+                    obj.ui.plot_cb.stateChanged.disconnect(obj.on_plot_cb_click)
+                    # disable this cb while disconnected,
+                    # in case the operation takes time the user is not allowed to change it
+                    obj.ui.plot_cb.setDisabled(True)
+                except AttributeError:
+                    pass
+                obj.set_form_item("plot")
+                try:
+                    obj.ui.plot_cb.stateChanged.connect(obj.on_plot_cb_click)
+                    obj.ui.plot_cb.setDisabled(False)
+                except AttributeError:
+                    pass
                 obj.options.set_change_callback(obj.on_options_change)
 
         def worker_task(objs):
@@ -12104,6 +12149,18 @@ class App(QtCore.QObject):
             if obj.options['plot'] is True:
                 obj.options.set_change_callback(lambda x: None)
                 obj.options['plot'] = False
+                try:
+                    # only the Gerber obj has on_plot_cb_click() method
+                    obj.ui.plot_cb.stateChanged.disconnect(obj.on_plot_cb_click)
+                    obj.ui.plot_cb.setDisabled(True)
+                except AttributeError:
+                    pass
+                obj.set_form_item("plot")
+                try:
+                    obj.ui.plot_cb.stateChanged.connect(obj.on_plot_cb_click)
+                    obj.ui.plot_cb.setDisabled(False)
+                except AttributeError:
+                    pass
                 obj.options.set_change_callback(obj.on_options_change)
 
         try:
@@ -12242,7 +12299,7 @@ class App(QtCore.QObject):
             try:
                 self.collection.get_active().read_form()
             except Exception as e:
-                self.log.debug("There was no active object. %s" % str(e))
+                self.log.debug("save_project() --> There was no active object. Skipping read_form. %s" % str(e))
                 pass
 
             # Serialize the whole project

+ 2 - 17
FlatCAMObj.py

@@ -1811,7 +1811,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             visibility = kwargs['visible']
 
         with self.app.proc_container.new(_("Plotting Apertures")):
-            self.app.progress.emit(30)
 
             def job_thread(app_obj):
                 try:
@@ -3080,7 +3079,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         def geo_init(geo_obj, app_obj):
             assert isinstance(geo_obj, FlatCAMGeometry), \
                 "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
-            app_obj.progress.emit(20)
 
             # ## Add properties to the object
 
@@ -3110,7 +3108,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         if use_thread:
             def geo_thread(app_obj):
                 app_obj.new_object("geometry", outname, geo_init, plot=plot)
-                app_obj.progress.emit(100)
 
             # Create a promise with the new name
             self.app.collection.promise(outname)
@@ -3173,7 +3170,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         def geo_init(geo_obj, app_obj):
             assert isinstance(geo_obj, FlatCAMGeometry), \
                 "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
-            app_obj.progress.emit(20)
 
             # ## Add properties to the object
 
@@ -3217,7 +3213,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         if use_thread:
             def geo_thread(app_obj):
                 app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot)
-                app_obj.progress.emit(100)
 
             # Create a promise with the new name
             self.app.collection.promise(outname)
@@ -3358,7 +3353,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         def job_thread(app_obj):
             with self.app.proc_container.new(_("Generating CNC Code")):
                 app_obj.new_object("cncjob", job_name, job_init)
-                app_obj.progress.emit(100)
 
         # Create promise for the new name.
         self.app.collection.promise(job_name)
@@ -5425,11 +5419,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             # Propagate options
             job_obj.options["tooldia"] = tooldia
 
-            app_obj.progress.emit(20)
-
             job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"]
             job_obj.fr_decimals = self.app.defaults["cncjob_fr_decimals"]
-            app_obj.progress.emit(40)
 
             job_obj.options['type'] = 'Geometry'
             job_obj.options['tool_dia'] = tooldia
@@ -5459,24 +5450,18 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 pp_geometry_name=ppname_g
             )
 
-            app_obj.progress.emit(50)
             # tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
             # source of gcode
             job_obj.toolchange_xy_type = "geometry"
             job_obj.gcode_parse()
-            self.app.inform.emit('[success] %s' %
-                                 _("Finished G-Code processing..."))
-
-            app_obj.progress.emit(80)
+            self.app.inform.emit('[success] %s' % _("Finished G-Code processing..."))
 
         if use_thread:
             # To be run in separate thread
             def job_thread(app_obj):
                 with self.app.proc_container.new(_("Generating CNC Code")):
                     app_obj.new_object("cncjob", outname, job_init, plot=plot)
-                    app_obj.inform.emit('[success] %s: %s' %
-                                        (_("CNCjob created")), outname)
-                    app_obj.progress.emit(100)
+                    app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created")), outname)
 
             # Create a promise with the name
             self.app.collection.promise(outname)

+ 18 - 0
README.md

@@ -9,11 +9,29 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+8.01.2019
+
+- working in NCC Tool
+- selected rows in the Tools Tables will stay colored in blue after loosing focus instead of the default gray
+- in NCC Tool the Tool name in the Parameters section will be the Tool ID in the Tool Table
+- added an exception catch in case the plotcanvas init failed for the OpenGL graphic engine and warn user about what happened
+
+7.01.2019
+
+- solved issue #368 - when using the Enable/Disable prj context menu entries the plotted status is not updated in the object properties
+- updates in NCC Tool
+
+6.01.2019
+
+- working on new NCC Tool
+
 2.01.2020
 
 - started to rework the NCC Tool GUI in preparation for adding a Tool DB feature
 - for auto-completer, now clicking an entry in the completer popup will select that entry and insert it
 - made available only for Linux and Windows (not OSX) the starting of the thread that checks if another instance of FlatCAM is already running at the launch of FLatCAM
+- modified Toggle Workspace function to work in the new Preferences UI configuration
+- cleaned the app from progress signal usage since it is not used anymore
 
 1.01.2020
 

+ 1 - 5
flatcamEditors/FlatCAMExcEditor.py

@@ -3305,7 +3305,6 @@ class FlatCAMExcEditor(QtCore.QObject):
         # How the object should be initialized
         def obj_init(excellon_obj, app_obj):
 
-            # self.progress.emit(20)
             excellon_obj.drills = deepcopy(new_drills)
             excellon_obj.tools = deepcopy(new_tools)
             excellon_obj.slots = deepcopy(new_slots)
@@ -3335,12 +3334,9 @@ class FlatCAMExcEditor(QtCore.QObject):
                                                                   use_thread=False)
             except Exception as e:
                 log.error("Error on Edited object creation: %s" % str(e))
-                self.app.progress.emit(100)
                 return
 
-            self.app.inform.emit('[success] %s' %
-                                 _("Excellon editing finished."))
-            # self.progress.emit(100)
+            self.app.inform.emit('[success] %s' % _("Excellon editing finished."))
 
     def on_tool_select(self, tool):
         """

+ 8 - 34
flatcamEditors/FlatCAMGeoEditor.py

@@ -1272,8 +1272,6 @@ class TransformEditorTool(FlatCAMTool):
                     xmaximal = max(xmaxlist)
                     ymaximal = max(ymaxlist)
 
-                    self.app.progress.emit(20)
-
                     for sel_sha in shape_list:
                         px = 0.5 * (xminimal + xmaximal)
                         py = 0.5 * (yminimal + ymaximal)
@@ -1284,11 +1282,7 @@ class TransformEditorTool(FlatCAMTool):
 
                     # self.draw_app.transform_complete.emit()
 
-                    self.app.inform.emit('[success] %s' %
-                                         _("Done. Rotate completed."))
-
-                    self.app.progress.emit(100)
-
+                    self.app.inform.emit('[success] %s' % _("Done. Rotate completed."))
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Rotation action was not executed"), str(e)))
                     return
@@ -1329,8 +1323,6 @@ class TransformEditorTool(FlatCAMTool):
                         px = 0.5 * (xminimal + xmaximal)
                         py = 0.5 * (yminimal + ymaximal)
 
-                    self.app.progress.emit(20)
-
                     # execute mirroring
                     for sha in shape_list:
                         if axis is 'X':
@@ -1347,8 +1339,6 @@ class TransformEditorTool(FlatCAMTool):
                     #
                     # self.draw_app.transform_complete.emit()
 
-                    self.app.progress.emit(100)
-
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Flip action was not executed"), str(e)))
                     return
@@ -1375,8 +1365,6 @@ class TransformEditorTool(FlatCAMTool):
                     xminimal = min(xminlist)
                     yminimal = min(yminlist)
 
-                    self.app.progress.emit(20)
-
                     for sha in shape_list:
                         if axis is 'X':
                             sha.skew(num, 0, point=(xminimal, yminimal))
@@ -1388,12 +1376,9 @@ class TransformEditorTool(FlatCAMTool):
                     #
                     # self.draw_app.transform_complete.emit()
                     if axis == 'X':
-                        self.app.inform.emit('[success] %s...' %
-                                             _('Skew on the X axis done'))
+                        self.app.inform.emit('[success] %s...' % _('Skew on the X axis done'))
                     else:
-                        self.app.inform.emit('[success] %s...' %
-                                             _('Skew on the Y axis done'))
-                    self.app.progress.emit(100)
+                        self.app.inform.emit('[success] %s...' % _('Skew on the Y axis done'))
 
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Skew action was not executed"), str(e)))
@@ -1407,8 +1392,7 @@ class TransformEditorTool(FlatCAMTool):
         ymaxlist = []
 
         if not shape_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("No shape selected. Please Select a shape to scale!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No shape selected. Please Select a shape to scale!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Scale")):
@@ -1427,8 +1411,6 @@ class TransformEditorTool(FlatCAMTool):
                     xmaximal = max(xmaxlist)
                     ymaximal = max(ymaxlist)
 
-                    self.app.progress.emit(20)
-
                     if point is None:
                         px = 0.5 * (xminimal + xmaximal)
                         py = 0.5 * (yminimal + ymaximal)
@@ -1445,12 +1427,9 @@ class TransformEditorTool(FlatCAMTool):
                     # self.draw_app.transform_complete.emit()
 
                     if str(axis) == 'X':
-                        self.app.inform.emit('[success] %s...' %
-                                             _('Scale on the X axis done'))
+                        self.app.inform.emit('[success] %s...' % _('Scale on the X axis done'))
                     else:
-                        self.app.inform.emit('[success] %s...' %
-                                             _('Scale on the Y axis done'))
-                    self.app.progress.emit(100)
+                        self.app.inform.emit('[success] %s...' % _('Scale on the Y axis done'))
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Scale action was not executed"), str(e)))
                     return
@@ -1467,8 +1446,6 @@ class TransformEditorTool(FlatCAMTool):
         else:
             with self.app.proc_container.new(_("Applying Offset")):
                 try:
-                    self.app.progress.emit(20)
-
                     for sha in shape_list:
                         if axis is 'X':
                             sha.offset((num, 0))
@@ -1477,12 +1454,9 @@ class TransformEditorTool(FlatCAMTool):
                         self.draw_app.replot()
 
                     if axis == 'X':
-                        self.app.inform.emit('[success] %s...' %
-                                             _('Offset on the X axis done'))
+                        self.app.inform.emit('[success] %s...' % _('Offset on the X axis done'))
                     else:
-                        self.app.inform.emit('[success] %s...' %
-                                             _('Offset on the Y axis done'))
-                    self.app.progress.emit(100)
+                        self.app.inform.emit('[success] %s...' % _('Offset on the Y axis done'))
 
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Offset action was not executed"), str(e)))

+ 2 - 16
flatcamEditors/FlatCAMGrbEditor.py

@@ -5761,7 +5761,6 @@ class TransformEditorTool(FlatCAMTool):
                 xmaximal = max(xmaxlist)
                 ymaximal = max(ymaxlist)
 
-                self.app.progress.emit(20)
                 px = 0.5 * (xminimal + xmaximal)
                 py = 0.5 * (yminimal + ymaximal)
 
@@ -5775,12 +5774,9 @@ class TransformEditorTool(FlatCAMTool):
                         sel_el['clear'] = affinity.rotate(sel_el['clear'], angle=-num, origin=(px, py))
                 self.draw_app.plot_all()
 
-                self.app.inform.emit('[success] %s' %
-                                     _("Done. Rotate completed."))
-                self.app.progress.emit(100)
+                self.app.inform.emit('[success] %s' % _("Done. Rotate completed."))
             except Exception as e:
-                self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
-                                     (_("Rotation action was not executed."), str(e)))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Rotation action was not executed."), str(e)))
                 return
 
     def on_flip(self, axis):
@@ -5827,8 +5823,6 @@ class TransformEditorTool(FlatCAMTool):
                     px = 0.5 * (xminimal + xmaximal)
                     py = 0.5 * (yminimal + ymaximal)
 
-                self.app.progress.emit(20)
-
                 # execute mirroring
                 for sel_el_shape in elem_list:
                     sel_el = sel_el_shape.geo
@@ -5851,8 +5845,6 @@ class TransformEditorTool(FlatCAMTool):
                         self.app.inform.emit('[success] %s...' %
                                              _('Flip on the X axis done'))
                 self.draw_app.plot_all()
-                self.app.progress.emit(100)
-
             except Exception as e:
                 self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
                                      (_("Flip action was not executed."), str(e)))
@@ -5889,8 +5881,6 @@ class TransformEditorTool(FlatCAMTool):
                     xminimal = min(xminlist)
                     yminimal = min(yminlist)
 
-                    self.app.progress.emit(20)
-
                     for sel_el_shape in elem_list:
                         sel_el = sel_el_shape.geo
                         if axis is 'X':
@@ -5913,8 +5903,6 @@ class TransformEditorTool(FlatCAMTool):
                         self.app.inform.emit('[success] %s...' % _('Skew on the X axis done'))
                     else:
                         self.app.inform.emit('[success] %s...' % _('Skew on the Y axis done'))
-                    self.app.progress.emit(100)
-
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Skew action was not executed."), str(e)))
                     return
@@ -5958,8 +5946,6 @@ class TransformEditorTool(FlatCAMTool):
                     xmaximal = max(xmaxlist)
                     ymaximal = max(ymaxlist)
 
-                    self.app.progress.emit(20)
-
                     if point is None:
                         px = 0.5 * (xminimal + xmaximal)
                         py = 0.5 * (yminimal + ymaximal)

+ 8 - 4
flatcamGUI/FlatCAMGUI.py

@@ -2221,9 +2221,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.infobar.addWidget(self.units_label)
 
         # disabled
-        self.progress_bar = QtWidgets.QProgressBar()
-        self.progress_bar.setMinimum(0)
-        self.progress_bar.setMaximum(100)
+        # self.progress_bar = QtWidgets.QProgressBar()
+        # self.progress_bar.setMinimum(0)
+        # self.progress_bar.setMaximum(100)
         # infobar.addWidget(self.progress_bar)
 
         # ########################################################################
@@ -2262,6 +2262,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.grb_editor_cmenu.menuAction().setVisible(False)
         self.e_editor_cmenu.menuAction().setVisible(False)
 
+        # ########################################################################
+        # ######################## BUILD PREFERENCES #############################
+        # ########################################################################
+
         self.general_defaults_form = GeneralPreferencesUI(decimals=self.decimals)
         self.gerber_defaults_form = GerberPreferencesUI(decimals=self.decimals)
         self.excellon_defaults_form = ExcellonPreferencesUI(decimals=self.decimals)
@@ -2358,7 +2362,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         :param event: QT event to filter
         :return:
         """
-        if self.general_defaults_form.general_app_set_group.toggle_tooltips_cb.get_value() is False:
+        if self.app.defaults["global_toggle_tooltips"] is False:
             if event.type() == QtCore.QEvent.ToolTip:
                 return True
             else:

+ 5 - 0
flatcamGUI/GUIElements.py

@@ -2006,6 +2006,11 @@ class FCTable(QtWidgets.QTableWidget):
     def __init__(self, drag_drop=False, protected_rows=None, parent=None):
         super(FCTable, self).__init__(parent)
 
+        palette = QtGui.QPalette()
+        palette.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Highlight,
+                         palette.color(QtGui.QPalette.Active, QtGui.QPalette.Highlight))
+        self.setPalette(palette)
+
         if drag_drop:
             self.setDragEnabled(True)
             self.setAcceptDrops(True)

+ 10 - 15
flatcamGUI/PreferencesUI.py

@@ -1661,18 +1661,13 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        self.layout.addWidget(separator_line)
-
-        grid1 = QtWidgets.QGridLayout()
-        self.layout.addLayout(grid1)
-        grid1.setColumnStretch(0, 0)
-        grid1.setColumnStretch(1, 1)
+        grid0.addWidget(separator_line, 31, 0, 1, 2)
 
         self.pdf_param_label = QtWidgets.QLabel('<B>%s:</b>' % _("Text to PDF parameters"))
         self.pdf_param_label.setToolTip(
             _("Used when saving text in Code Editor or in FlatCAM Document objects.")
         )
-        grid1.addWidget(self.pdf_param_label, 0, 0, 1, 2)
+        grid0.addWidget(self.pdf_param_label, 32, 0, 1, 2)
 
         # Top Margin value
         self.tmargin_entry = FCDoubleSpinner()
@@ -1684,8 +1679,8 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
             _("Distance between text body and the top of the PDF file.")
         )
 
-        grid1.addWidget(self.tmargin_label, 1, 0)
-        grid1.addWidget(self.tmargin_entry, 1, 1)
+        grid0.addWidget(self.tmargin_label, 33, 0)
+        grid0.addWidget(self.tmargin_entry, 33, 1)
 
         # Bottom Margin value
         self.bmargin_entry = FCDoubleSpinner()
@@ -1697,8 +1692,8 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
             _("Distance between text body and the bottom of the PDF file.")
         )
 
-        grid1.addWidget(self.bmargin_label, 2, 0)
-        grid1.addWidget(self.bmargin_entry, 2, 1)
+        grid0.addWidget(self.bmargin_label, 34, 0)
+        grid0.addWidget(self.bmargin_entry, 34, 1)
 
         # Left Margin value
         self.lmargin_entry = FCDoubleSpinner()
@@ -1710,8 +1705,8 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
             _("Distance between text body and the left of the PDF file.")
         )
 
-        grid1.addWidget(self.lmargin_label, 3, 0)
-        grid1.addWidget(self.lmargin_entry, 3, 1)
+        grid0.addWidget(self.lmargin_label, 35, 0)
+        grid0.addWidget(self.lmargin_entry, 35, 1)
 
         # Right Margin value
         self.rmargin_entry = FCDoubleSpinner()
@@ -1723,8 +1718,8 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
             _("Distance between text body and the right of the PDF file.")
         )
 
-        grid1.addWidget(self.rmargin_label, 4, 0)
-        grid1.addWidget(self.rmargin_entry, 4, 1)
+        grid0.addWidget(self.rmargin_label, 36, 0)
+        grid0.addWidget(self.rmargin_entry, 36, 1)
 
         self.layout.addStretch()
 

+ 1212 - 571
flatcamTools/ToolNonCopperClear.py

@@ -6,10 +6,11 @@
 # ##########################################################
 
 from PyQt5 import QtWidgets, QtCore, QtGui
+
 from FlatCAMTool import FlatCAMTool
 from flatcamGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog, FCButton
 from flatcamParsers.ParseGerber import Gerber
-from FlatCAMObj import FlatCAMGeometry, FlatCAMGerber
+
 import FlatCAMApp
 
 from copy import deepcopy
@@ -175,6 +176,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
         )
+        self.milling_type_radio.setObjectName(_("Milling Type"))
 
         grid1.addWidget(self.milling_type_label, 0, 0)
         grid1.addWidget(self.milling_type_radio, 0, 1)
@@ -218,7 +220,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.grid3.setColumnStretch(0, 0)
         self.grid3.setColumnStretch(1, 1)
 
-        self.tool_sel_label = QtWidgets.QLabel('<b>%s</b>' % _("Tool Selection"))
+        self.tool_sel_label = QtWidgets.QLabel('<b>%s</b>' % _("New Tool"))
         self.grid3.addWidget(self.tool_sel_label, 1, 0, 1, 2)
 
         # Tool Type Radio Button
@@ -236,6 +238,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
               "- 'V-shape'\n"
               "- Circular")
         )
+        self.tool_type_radio.setObjectName(_("Tool Type"))
+
         self.grid3.addWidget(self.tool_type_label, 2, 0)
         self.grid3.addWidget(self.tool_type_radio, 2, 1)
 
@@ -245,7 +249,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
             _("The tip diameter for V-Shape Tool"))
         self.tipdia_entry = FCDoubleSpinner()
         self.tipdia_entry.set_precision(self.decimals)
+        self.tipdia_entry.set_range(0.0000, 9999.9999)
         self.tipdia_entry.setSingleStep(0.1)
+        self.tipdia_entry.setObjectName(_("V-Tip Dia"))
 
         self.grid3.addWidget(self.tipdialabel, 3, 0)
         self.grid3.addWidget(self.tipdia_entry, 3, 1)
@@ -257,7 +263,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
               "In degree."))
         self.tipangle_entry = FCDoubleSpinner()
         self.tipangle_entry.set_precision(self.decimals)
+        self.tipangle_entry.set_range(0.0000, 180.0000)
         self.tipangle_entry.setSingleStep(5)
+        self.tipangle_entry.setObjectName(_("V-Tip Angle"))
 
         self.grid3.addWidget(self.tipanglelabel, 4, 0)
         self.grid3.addWidget(self.tipangle_entry, 4, 1)
@@ -271,6 +279,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_range(-99999.9999, 0.0000)
+        self.cutz_entry.setObjectName(_("Cut Z"))
 
         self.cutz_entry.setToolTip(
            _("Depth of cut into material. Negative value.\n"
@@ -288,17 +297,12 @@ class NonCopperClear(FlatCAMTool, Gerber):
         )
         self.addtool_entry = FCDoubleSpinner()
         self.addtool_entry.set_precision(self.decimals)
+        self.addtool_entry.set_range(0.000, 9999.9999)
+        self.addtool_entry.setObjectName(_("Tool Dia"))
 
         self.grid3.addWidget(self.addtool_entry_lbl, 6, 0)
         self.grid3.addWidget(self.addtool_entry, 6, 1)
 
-        self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add Tool from DataBase'))
-        self.addtool_from_db_btn.setToolTip(
-            _("Add a new tool to the Tool Table\n"
-              "from the Tool DataBase.")
-        )
-        self.grid3.addWidget(self.addtool_from_db_btn, 7, 0, 1, 2)
-
         hlay = QtWidgets.QHBoxLayout()
 
         self.addtool_btn = QtWidgets.QPushButton(_('Add'))
@@ -316,7 +320,14 @@ class NonCopperClear(FlatCAMTool, Gerber):
         hlay.addWidget(self.addtool_btn)
         hlay.addWidget(self.deltool_btn)
 
-        self.grid3.addLayout(hlay, 8, 0, 1, 2)
+        self.grid3.addLayout(hlay, 7, 0, 1, 2)
+
+        self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add Tool from DataBase'))
+        self.addtool_from_db_btn.setToolTip(
+            _("Add a new tool to the Tool Table\n"
+              "from the Tool DataBase.")
+        )
+        self.grid3.addWidget(self.addtool_from_db_btn, 8, 0, 1, 2)
 
         self.grid3.addWidget(QtWidgets.QLabel(''), 9, 0, 1, 2)
 
@@ -351,16 +362,20 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.ncc_overlap_entry.setWrapping(True)
         self.ncc_overlap_entry.setRange(0.000, 99.9999)
         self.ncc_overlap_entry.setSingleStep(0.1)
+        self.ncc_overlap_entry.setObjectName(_("Overlap Rate"))
 
         self.grid3.addWidget(nccoverlabel, 12, 0)
         self.grid3.addWidget(self.ncc_overlap_entry, 12, 1)
 
+        # Margin
         nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
         nccmarginlabel.setToolTip(
             _("Bounding box margin.")
         )
         self.ncc_margin_entry = FCDoubleSpinner()
         self.ncc_margin_entry.set_precision(self.decimals)
+        self.ncc_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.ncc_margin_entry.setObjectName(_("Margin"))
 
         self.grid3.addWidget(nccmarginlabel, 13, 0)
         self.grid3.addWidget(self.ncc_margin_entry, 13, 1)
@@ -378,19 +393,25 @@ class NonCopperClear(FlatCAMTool, Gerber):
             {"label": _("Seed-based"), "value": "seed"},
             {"label": _("Straight lines"), "value": "lines"}
         ], orientation='vertical', stretch=False)
+        self.ncc_method_radio.setObjectName(_("Method"))
 
         self.grid3.addWidget(methodlabel, 14, 0)
         self.grid3.addWidget(self.ncc_method_radio, 14, 1)
 
         # Connect lines
         self.ncc_connect_cb = FCCheckBox('%s' % _("Connect"))
+        self.ncc_connect_cb.setObjectName(_("Connect"))
+
         self.ncc_connect_cb.setToolTip(
             _("Draw lines between resulting\n"
               "segments to minimize tool lifts.")
         )
         self.grid3.addWidget(self.ncc_connect_cb, 15, 0, 1, 2)
 
+        # Contour
         self.ncc_contour_cb = FCCheckBox('%s' % _("Contour"))
+        self.ncc_contour_cb.setObjectName(_("Contour"))
+
         self.ncc_contour_cb.setToolTip(
             _("Cut around the perimeter of the polygon\n"
               "to trim rough edges.")
@@ -399,6 +420,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         # Rest Machining
         self.ncc_rest_cb = FCCheckBox('%s' % _("Rest Machining"))
+        self.ncc_rest_cb.setObjectName(_("Rest Machining"))
+
         self.ncc_rest_cb.setToolTip(
             _("If checked, use 'rest machining'.\n"
               "Basically it will clear copper outside PCB features,\n"
@@ -413,6 +436,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         # ## NCC Offset choice
         self.ncc_choice_offset_cb = FCCheckBox('%s' % _("Offset"))
+        self.ncc_choice_offset_cb.setObjectName(_("Offset"))
+
         self.ncc_choice_offset_cb.setToolTip(
             _("If used, it will add an offset to the copper features.\n"
               "The copper clearing will finish to a distance\n"
@@ -433,6 +458,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.ncc_offset_spinner.set_range(0.00, 10.00)
         self.ncc_offset_spinner.set_precision(4)
         self.ncc_offset_spinner.setWrapping(True)
+        self.ncc_offset_spinner.setObjectName(_("Offset value"))
 
         units = self.app.defaults['units'].upper()
         if units == 'MM':
@@ -452,7 +478,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
             {"label": _("Area Selection"), "value": "area"},
             {'label':  _("Reference Object"), 'value': 'box'}
         ], orientation='vertical', stretch=False)
-        self.reference_label = QtWidgets.QLabel(_("Reference:"))
+        self.reference_radio.setObjectName(_("Reference"))
+
+        self.reference_label = QtWidgets.QLabel('%s:' % _("Reference"))
         self.reference_label.setToolTip(
             _("- 'Itself' - the non copper clearing extent is based on the object that is copper cleared.\n "
               "- 'Area Selection' - left mouse click to start selection of the area to be painted.\n"
@@ -586,6 +614,32 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         self.tooldia = None
 
+        self.form_fields = {
+            "nccoverlap": self.ncc_overlap_entry,
+            "nccmargin": self.ncc_margin_entry,
+            "nccmethod": self.ncc_method_radio,
+            "nccconnect": self.ncc_connect_cb,
+            "ncccontour": self.ncc_contour_cb,
+            "nccrest": self.ncc_rest_cb,
+            "nccoffset": self.ncc_choice_offset_cb,
+            "nccoffset_value": self.ncc_offset_spinner,
+            "nccref":  self.reference_radio,
+            "milling_type": self.milling_type_radio
+        }
+
+        self.name2option = {
+            _('Overlap Rate'): "nccoverlap",
+            _('Margin'): "nccmargin",
+            _('Method'): "nccmethod",
+            _("Connect"): "nccconnect",
+            _("Contour"): "ncccontour",
+            _("Rest Machining"): "nccrest",
+            _("Offset"): "nccoffset",
+            _("Offset value"): "nccoffset_value",
+            _("Reference"): "nccref",
+            _('Milling Type'): "milling_type",
+        }
+
         # #############################################################################
         # ############################ SGINALS ########################################
         # #############################################################################
@@ -618,7 +672,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.update_ui()
 
     def update_ui(self, row=None):
-        self.ui_disconnect()
+        self.blockSignals(True)
 
         if row is None:
             try:
@@ -634,7 +688,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         # populate the form with the data from the tool associated with the row parameter
         try:
             item = self.tools_table.item(current_row, 3)
-            if type(item) is not None:
+            if item is not None:
                 tooluid = int(item.text())
             else:
                 return
@@ -644,7 +698,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         # update the QLabel that shows for which Tool we have the parameters in the UI form
         self.tool_data_label.setText(
-            "<b>%s: <font color='#0000FF'>%s %d</font></b>" % (_('Parameters for'), _("Tool"), tooluid)
+            "<b>%s: <font color='#0000FF'>%s %d</font></b>" % (_('Parameters for'), _("Tool"), (current_row + 1))
         )
 
         try:
@@ -654,70 +708,46 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     for key, value in tooluid_value.items():
                         if key == 'data':
                             form_value_storage = tooluid_value[key]
-                            self.update_form(form_value_storage)
-                        if key == 'offset_value':
-                            # update the offset value in the entry even if the entry is hidden
-                            self.ncc_offset_spinner.set_value(tooluid_value[key])
-
-                        if key == 'tool_type' and value == 'V':
-                            self.update_cutz()
+                            self.storage_to_form(form_value_storage)
         except Exception as e:
             log.debug("FlatCAMObj ---> update_ui() " + str(e))
-        self.ui_connect()
 
-    def update_cutz(self):
-        vdia = float(self.tipdia_entry.get_value())
-        half_vangle = float(self.tipangle_entry.get_value()) / 2
+        self.blockSignals(False)
 
-        row = self.tools_table.currentRow()
-        tool_uid_item = self.tools_table.item(row, 3)
-        if tool_uid_item is None:
-            return
-        tool_uid = int(tool_uid_item.text())
+    def storage_to_form(self, dict_storage):
+        for form_key in self.form_fields:
+            for storage_key in dict_storage:
+                if form_key == storage_key:
+                    try:
+                        self.form_fields[form_key].set_value(dict_storage[form_key])
+                    except Exception:
+                        pass
 
-        tool_dia_item = self.tools_table.item(row, 1)
-        if tool_dia_item is None:
+    def form_to_storage(self):
+        if self.tools_table.rowCount() == 0:
+            # there is no tool in tool table so we can't save the GUI elements values to storage
             return
-        tooldia = float(tool_dia_item.text())
-
-        new_cutz = (tooldia - vdia) / (2 * math.tan(math.radians(half_vangle)))
-        new_cutz = float('%.*f' % (self.decimals, new_cutz)) * -1.0   # this value has to be negative
-
-        self.cutz_entry.set_value(new_cutz)
 
-        # store the new CutZ value into storage (self.ncc_tools)
-        for tooluid_key, tooluid_value in self.ncc_tools.items():
-            if int(tooluid_key) == tool_uid:
-                tooluid_value['data']['cutz'] = new_cutz
+        self.blockSignals(True)
 
-    def on_tooltable_cellwidget_change(self):
-        cw = self.sender()
-        cw_index = self.tools_table.indexAt(cw.pos())
-        cw_row = cw_index.row()
-        cw_col = cw_index.column()
-        current_uid = int(self.tools_table.item(cw_row, 3).text())
+        widget_changed = self.sender()
+        wdg_objname = widget_changed.objectName()
+        option_changed = self.name2option[wdg_objname]
 
-        # store the text of the cellWidget that changed it's index in the self.tools
-        for tooluid_key, tooluid_value in self.ncc_tools.items():
-            if int(tooluid_key) == current_uid:
-                cb_txt = cw.currentText()
-                if cw_col == 2:
-                    tooluid_value['tool_type'] = cb_txt
+        row = self.tools_table.currentRow()
+        if row < 0:
+            row = 0
+        tooluid_item = int(self.tools_table.item(row, 3).text())
 
-    def update_form(self, dict_storage):
-        for form_key in self.form_fields:
-            for storage_key in dict_storage:
-                if form_key == storage_key:
-                    try:
-                        self.form_fields[form_key].set_value(dict_storage[form_key])
-                    except Exception as e:
-                        log.debug(str(e))
+        for tooluid_key, tooluid_val in self.ncc_tools.items():
+            if int(tooluid_key) == tooluid_item:
+                new_option_value = self.form_fields[option_changed].get_value()
+                if option_changed in tooluid_val:
+                    tooluid_val[option_changed] = new_option_value
+                if option_changed in tooluid_val['data']:
+                    tooluid_val['data'][option_changed] = new_option_value
 
-        # this is done here because those buttons control through OptionalInputSelection if some entry's are Enabled
-        # or not. But due of using the ui_disconnect() status is no longer updated and I had to do it here
-        self.ui.ois_dwell_geo.on_cb_change()
-        self.ui.ois_mpass_geo.on_cb_change()
-        self.ui.ois_tcz_geo.on_cb_change()
+        self.blockSignals(False)
 
     def on_apply_param_to_all_clicked(self):
         if self.tools_table.rowCount() == 0:
@@ -725,7 +755,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
             log.debug("NonCopperClear.on_apply_param_to_all_clicked() --> no tool in Tools Table, aborting.")
             return
 
-        self.ui_disconnect()
+        self.blockSignals(True)
 
         row = self.tools_table.currentRow()
         if row < 0:
@@ -736,8 +766,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
         type_item = self.tools_table.cellWidget(row, 2).currentText()
         operation_type_item = self.ui.geo_tools_table.cellWidget(row, 4).currentText()
 
-        offset_item = self.ncc_choice_offset_cb.get_value()
-        offset_value_item = float(self.ncc_offset_spinner.get_value())
+        nccoffset_item = self.ncc_choice_offset_cb.get_value()
+        nccoffset_value_item = float(self.ncc_offset_spinner.get_value())
 
         # this new dict will hold the actual useful data, another dict that is the value of key 'data'
         temp_tools = {}
@@ -746,16 +776,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         for tooluid_key, tooluid_value in self.ncc_tools.items():
             for key, value in tooluid_value.items():
-                if key == 'tooldia':
-                    temp_dia[key] = tooldia_item
-                # update the 'offset', 'type' and 'tool_type' sections
-                if key == 'offset':
-                    temp_dia[key] = offset_item
-                if key == 'type':
-                    temp_dia[key] = type_item
-                if key == 'offset_value':
-                    temp_dia[key] = offset_value_item
-
                 if key == 'data':
                     # update the 'data' section
                     for data_key in tooluid_value[key].keys():
@@ -769,8 +789,10 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     temp_dia[key] = deepcopy(temp_data)
                     temp_data.clear()
 
-                if key == 'solid_geometry':
+                elif key == 'solid_geometry':
                     temp_dia[key] = deepcopy(self.tools[tooluid_key]['solid_geometry'])
+                else:
+                    temp_dia[key] = deepcopy(value)
 
                 temp_tools[tooluid_key] = deepcopy(temp_dia)
 
@@ -778,93 +800,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.ncc_tools = deepcopy(temp_tools)
         temp_tools.clear()
 
-        self.ui_connect()
-
-    def gui_form_to_storage(self):
-        if self.tools_table.rowCount() == 0:
-            # there is no tool in tool table so we can't save the GUI elements values to storage
-            log.debug("NonCopperClear.gui_form_to_storage() --> no tool in Tools Table, aborting.")
-            return
-
-        self.ui_disconnect()
-        widget_changed = self.sender()
-        try:
-            widget_idx = self.grid3.indexOf(widget_changed)
-        except Exception as e:
-            return
-
-        # those are the indexes for the V-Tip Dia and V-Tip Angle, if edited calculate the new Cut Z
-        if widget_idx == 1 or widget_idx == 3:
-            self.update_cutz()
-
-        # the original connect() function of the OptionalInputSelection is no longer working because of the
-        # ui_diconnect() so I use this 'hack'
-        # if isinstance(widget_changed, FCCheckBox):
-        #     if widget_changed.text() == 'Multi-Depth:':
-        #         self.ui.ois_mpass_geo.on_cb_change()
-        #
-        #     if widget_changed.text() == 'Tool change':
-        #         self.ui.ois_tcz_geo.on_cb_change()
-        #
-        #     if widget_changed.text() == 'Dwell:':
-        #         self.ui.ois_dwell_geo.on_cb_change()
-
-        row = self.tools_table.currentRow()
-        if row < 0:
-            row = 0
-
-        # store all the data associated with the row parameter to the self.tools storage
-        tooldia_item = float(self.tools_table.item(row, 1).text())
-        tool_type_item = self.tools_table.cellWidget(row, 2).currentText()
-        operation_type_item = self.ui.geo_tools_table.cellWidget(row, 4).currentText()
-
-        offset_item = self.ncc_choice_offset_cb.get_value()
-        offset_value_item = float(self.ncc_offset_spinner.get_value())
-
-        tooluid_item = int(self.ui.geo_tools_table.item(row, 3).text())
-
-        # this new dict will hold the actual useful data, another dict that is the value of key 'data'
-        temp_tools = {}
-        temp_dia = {}
-        temp_data = {}
-
-        for tooluid_key, tooluid_value in self.tools.items():
-            if int(tooluid_key) == tooluid_item:
-                for key, value in tooluid_value.items():
-                    if key == 'tooldia':
-                        temp_dia[key] = tooldia_item
-                    # update the 'offset', 'type' and 'tool_type' sections
-                    if key == 'offset':
-                        temp_dia[key] = offset_item
-                    if key == 'tool_type':
-                        temp_dia[key] = tool_type_item
-                    if key == 'offset_value':
-                        temp_dia[key] = offset_value_item
-
-                    if key == 'data':
-                        # update the 'data' section
-                        for data_key in tooluid_value[key].keys():
-                            for form_key, form_value in self.form_fields.items():
-                                if form_key == data_key:
-                                    temp_data[data_key] = form_value.get_value()
-                            # make sure we make a copy of the keys not in the form (we may use 'data' keys that are
-                            # updated from self.app.defaults
-                            if data_key not in self.form_fields:
-                                temp_data[data_key] = value[data_key]
-                        temp_dia[key] = deepcopy(temp_data)
-                        temp_data.clear()
-
-                    if key == 'solid_geometry':
-                        temp_dia[key] = deepcopy(self.ncc_tools[tooluid_key]['solid_geometry'])
-
-                    temp_tools[tooluid_key] = deepcopy(temp_dia)
-            else:
-                temp_tools[tooluid_key] = deepcopy(tooluid_value)
-
-        self.ncc_tools.clear()
-        self.ncc_tools = deepcopy(temp_tools)
-        temp_tools.clear()
-        self.ui_connect()
+        self.blockSignals(False)
 
     def on_add_tool_by_key(self):
         tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"),
@@ -931,6 +867,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"])
         self.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"])
         self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
+        self.ncc_choice_offset_cb.set_value(self.app.defaults["tools_ncc_offset_choice"])
+        self.ncc_offset_spinner.set_value(self.app.defaults["tools_ncc_offset_value"])
+
         self.reference_radio.set_value(self.app.defaults["tools_nccref"])
         self.milling_type_radio.set_value(self.app.defaults["tools_nccmilling_type"])
         self.cutz_entry.set_value(self.app.defaults["tools_ncccutz"])
@@ -943,7 +882,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         # init the working variables
         self.default_data.clear()
-        self.default_data.update({
+        self.default_data = {
             "name": '_ncc',
             "plot": self.app.defaults["geometry_plot"],
             "cutz": float(self.cutz_entry.get_value()),
@@ -967,21 +906,17 @@ class NonCopperClear(FlatCAMTool, Gerber):
             "toolchangexy": self.app.defaults["geometry_toolchangexy"],
             "startz": self.app.defaults["geometry_startz"],
 
-            "tooldia": self.app.defaults["tools_painttooldia"],
-            "paintmargin": self.app.defaults["tools_paintmargin"],
-            "paintmethod": self.app.defaults["tools_paintmethod"],
-            "selectmethod": self.app.defaults["tools_selectmethod"],
-            "pathconnect": self.app.defaults["tools_pathconnect"],
-            "paintcontour": self.app.defaults["tools_paintcontour"],
-            "paintoverlap": self.app.defaults["tools_paintoverlap"],
-
             "nccmargin": self.app.defaults["tools_nccmargin"],
             "nccmethod": self.app.defaults["tools_nccmethod"],
             "nccconnect": self.app.defaults["tools_nccconnect"],
             "ncccontour": self.app.defaults["tools_ncccontour"],
             "nccoverlap": self.app.defaults["tools_nccoverlap"],
-            "nccrest": self.app.defaults["tools_nccrest"]
-        })
+            "nccrest": self.app.defaults["tools_nccrest"],
+            "nccref": self.app.defaults["tools_nccref"],
+            "nccoffset": self.app.defaults["tools_ncc_offset_choice"],
+            "nccoffset_value": self.app.defaults["tools_ncc_offset_value"],
+
+        }
 
         try:
             dias = [float(self.app.defaults["tools_ncctools"])]
@@ -1118,21 +1053,56 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.tools_table.itemChanged.connect(self.on_tool_edit)
 
         for row in range(self.tools_table.rowCount()):
-            for col in [2, 4]:
-                self.tools_table.cellWidget(row, col).currentIndexChanged.connect(self.on_tooltable_cellwidget_change)
+            try:
+                self.tools_table.cellWidget(row, 2).currentIndexChanged.connect(self.on_tooltable_cellwidget_change)
+            except AttributeError:
+                pass
+
+            try:
+                self.tools_table.cellWidget(row, 4).currentIndexChanged.connect(self.on_tooltable_cellwidget_change)
+            except AttributeError:
+                pass
 
         self.tool_type_radio.activated_custom.connect(self.on_tool_type)
 
+        # first disconnect
+        for opt in self.form_fields:
+            current_widget = self.form_fields[opt]
+            if isinstance(current_widget, FCCheckBox):
+                try:
+                    current_widget.stateChanged.disconnect(self.form_to_storage)
+                except (TypeError, ValueError):
+                    pass
+            if isinstance(current_widget, RadioSet):
+                try:
+                    current_widget.activated_custom.disconnect(self.form_to_storage)
+                except (TypeError, ValueError):
+                    pass
+            elif isinstance(current_widget, FCDoubleSpinner):
+                try:
+                    current_widget.returnPressed.disconnect(self.form_to_storage)
+                except (TypeError, ValueError):
+                    pass
+
+        for opt in self.form_fields:
+            current_widget = self.form_fields[opt]
+            if isinstance(current_widget, FCCheckBox):
+                current_widget.stateChanged.connect(self.form_to_storage)
+            if isinstance(current_widget, RadioSet):
+                current_widget.activated_custom.connect(self.form_to_storage)
+            elif isinstance(current_widget, FCDoubleSpinner):
+                current_widget.returnPressed.connect(self.form_to_storage)
+
     def ui_disconnect(self):
         try:
             # if connected, disconnect the signal from the slot on item_changed as it creates issues
-            self.tools_table.itemChanged.disconnect(self.on_tool_edit)
+            self.tools_table.itemChanged.disconnect()
         except (TypeError, AttributeError):
             pass
 
         try:
             # if connected, disconnect the signal from the slot on item_changed as it creates issues
-            self.tool_type_radio.activated_custom.disconnect(self.on_tool_type)
+            self.tool_type_radio.activated_custom.disconnect()
         except (TypeError, AttributeError):
             pass
 
@@ -1143,6 +1113,24 @@ class NonCopperClear(FlatCAMTool, Gerber):
                 except (TypeError, AttributeError):
                     pass
 
+        for opt in self.form_fields:
+            current_widget = self.form_fields[opt]
+            if isinstance(current_widget, FCCheckBox):
+                try:
+                    current_widget.stateChanged.disconnect()
+                except (TypeError, ValueError):
+                    pass
+            if isinstance(current_widget, RadioSet):
+                try:
+                    current_widget.activated_custom.disconnect()
+                except (TypeError, ValueError):
+                    pass
+            elif isinstance(current_widget, FCDoubleSpinner):
+                try:
+                    current_widget.returnPressed.disconnect()
+                except (TypeError, ValueError):
+                    pass
+
     def on_combo_box_type(self):
         obj_type = self.box_combo_type.currentIndex()
         self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
@@ -1189,32 +1177,30 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         current_uid = int(self.tools_table.item(cw_row, 3).text())
 
-        hide_iso_type = True
-        for row in range(self.tools_table.rowCount()):
-            if self.tools_table.cellWidget(row, 4).currentText() == 'iso_op':
-                hide_iso_type = False
-                break
-
-        if hide_iso_type is False:
-            self.milling_type_label.show()
-            self.milling_type_radio.show()
-        else:
-            self.milling_type_label.hide()
-            self.milling_type_radio.hide()
-
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
             tt = cw.currentText()
-            if tt == 'V':
-                typ = 'Iso'
-            else:
-                typ = "Rough"
+            typ = 'Iso' if tt == 'V' else "Rough"
 
             self.ncc_tools[current_uid].update({
                 'type': typ,
                 'tool_type': tt,
             })
 
+        if cw_col == 4:
+            op = cw.currentText()
+
+            if op == 'iso_op':
+                self.milling_type_label.show()
+                self.milling_type_radio.show()
+            else:
+                self.milling_type_label.hide()
+                self.milling_type_radio.hide()
+
+            self.ncc_tools[current_uid].update({
+                'operation': op
+            })
+
     def on_tool_type(self, val):
         if val == 'V':
             self.addtool_entry_lbl.setDisabled(True)
@@ -1254,7 +1240,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
             return float(self.addtool_entry.get_value())
 
     def on_tool_add(self, dia=None, muted=None):
-        self.ui_disconnect()
+        self.blockSignals(True)
+
         self.units = self.app.defaults['units'].upper()
 
         if dia:
@@ -1296,7 +1283,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
             if muted is None:
                 self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding tool cancelled. Tool already in Tool Table."))
             # self.tools_table.itemChanged.connect(self.on_tool_edit)
-            self.ui_connect()
+            self.blockSignals(False)
+
             return
         else:
             if muted is None:
@@ -1314,10 +1302,12 @@ class NonCopperClear(FlatCAMTool, Gerber):
                 }
             })
 
+        self.blockSignals(False)
+
         self.build_ui()
 
     def on_tool_edit(self):
-        self.ui_disconnect()
+        self.blockSignals(True)
 
         old_tool_dia = ''
         tool_dias = []
@@ -1335,8 +1325,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
                 try:
                     new_tool_dia = float(self.tools_table.item(row, 1).text().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit('[ERROR_NOTCL]  %s' % _("Wrong value format entered, "
-                                         "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL]  %s' % _("Wrong value format entered, use a number."))
+                    self.blockSignals(False)
                     return
 
             tooluid = int(self.tools_table.item(row, 3).text())
@@ -1345,6 +1335,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
             if new_tool_dia not in tool_dias:
                 self.ncc_tools[tooluid]['tooldia'] = new_tool_dia
                 self.app.inform.emit('[success] %s' % _("Tool from Tool Table was edited."))
+                self.blockSignals(False)
                 self.build_ui()
                 return
             else:
@@ -1357,6 +1348,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                 restore_dia_item.setText(str(old_tool_dia))
                 self.app.inform.emit('[WARNING_NOTCL] %s' % _("Edit cancelled. "
                                                               "New diameter value is already in the Tool Table."))
+        self.blockSignals(False)
         self.build_ui()
 
     def on_tool_delete(self, rows_to_delete=None, all_tools=None):
@@ -1367,12 +1359,13 @@ class NonCopperClear(FlatCAMTool, Gerber):
         :param all_tools: delete all tools in the tool table
         :return:
         """
-        self.ui_disconnect()
+        self.blockSignals(True)
 
         deleted_tools_list = []
 
         if all_tools:
             self.paint_tools.clear()
+            self.blockSignals(False)
             self.build_ui()
             return
 
@@ -1382,10 +1375,13 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     tooluid_del = int(self.tools_table.item(row, 3).text())
                     deleted_tools_list.append(tooluid_del)
             except TypeError:
-                deleted_tools_list.append(rows_to_delete)
+                tooluid_del = int(self.tools_table.item(rows_to_delete, 3).text())
+                deleted_tools_list.append(tooluid_del)
 
             for t in deleted_tools_list:
                 self.ncc_tools.pop(t, None)
+
+            self.blockSignals(False)
             self.build_ui()
             return
 
@@ -1403,11 +1399,13 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         except AttributeError:
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Delete failed. Select a tool to delete."))
+            self.blockSignals(False)
             return
         except Exception as e:
             log.debug(str(e))
 
         self.app.inform.emit('[success] %s' % _("Tool(s) deleted from Tool Table."))
+        self.blockSignals(False)
         self.build_ui()
 
     def on_ncc_click(self):
@@ -1720,7 +1718,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
             ncc_select = self.reference_radio.get_value()
 
         overlap = overlap if overlap is not None else float(self.app.defaults["tools_nccoverlap"]) / 100.0
-
         connect = connect if connect else self.app.defaults["tools_nccconnect"]
         contour = contour if contour else self.app.defaults["tools_ncccontour"]
         order = order if order else self.ncc_order_radio.get_value()
@@ -1818,7 +1815,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         elif ncc_select == 'box':
             geo_n = ncc_sel_obj.solid_geometry
-            if isinstance(ncc_sel_obj, FlatCAMGeometry):
+            if ncc_sel_obj.kind == 'geometry':
                 try:
                     __ = iter(geo_n)
                 except Exception as e:
@@ -1833,7 +1830,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
 
                 bounding_box = cascaded_union(geo_buff_list)
-            elif isinstance(ncc_sel_obj, FlatCAMGerber):
+            elif ncc_sel_obj.kind == 'gerber':
                 geo_n = cascaded_union(geo_n).convex_hull
                 bounding_box = cascaded_union(self.ncc_obj.solid_geometry).convex_hull.intersection(geo_n)
                 bounding_box = bounding_box.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
@@ -1856,7 +1853,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         # Initializes the new geometry object ######################################################
         # ##########################################################################################
         def gen_clear_area(geo_obj, app_obj):
-            assert isinstance(geo_obj, FlatCAMGeometry), \
+            assert geo_obj.kind == 'geometry', \
                 "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
 
             # provide the app with a way to process the GUI events when in a blocking loop
@@ -1898,7 +1895,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
             log.debug("NCC Tool. Calculate 'empty' area.")
             self.app.inform.emit(_("NCC Tool. Calculate 'empty' area."))
 
-            if isinstance(ncc_obj, FlatCAMGerber) and not isotooldia:
+            if ncc_obj.kind == 'gerber' and not isotooldia:
                 # unfortunately for this function to work time efficient,
                 # if the Gerber was loaded without buffering then it require the buffering now.
                 if self.app.defaults['gerber_buffering'] == 'no':
@@ -1919,7 +1916,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     app_obj.inform.emit('[ERROR_NOTCL] %s' %
                                         _("Could not get the extent of the area to be non copper cleared."))
                     return 'fail'
-            elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia:
+            elif ncc_obj.kind == 'gerber' and isotooldia:
                 isolated_geo = []
 
                 # unfortunately for this function to work time efficient,
@@ -2024,7 +2021,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                                         _("Isolation geometry is broken. Margin is less than isolation tool diameter."))
                     return 'fail'
 
-            elif isinstance(ncc_obj, FlatCAMGeometry):
+            elif ncc_obj.kind == 'geometry':
                 sol_geo = cascaded_union(ncc_obj.solid_geometry)
                 if has_offset is True:
                     app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
@@ -2295,7 +2292,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         # Initializes the new geometry object for the case of the rest-machining ####################
         # ###########################################################################################
         def gen_clear_area_rest(geo_obj, app_obj):
-            assert isinstance(geo_obj, FlatCAMGeometry), \
+            assert geo_obj.kind == 'geometry', \
                 "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
 
             log.debug("NCC Tool. Rest machining copper clearing task started.")
@@ -2328,7 +2325,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
             # ###################################################################################################
             # Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
             # ###################################################################################################
-            if isinstance(ncc_obj, FlatCAMGerber) and not isotooldia:
+            if ncc_obj.kind == 'gerber' and not isotooldia:
                 sol_geo = ncc_obj.solid_geometry
                 if has_offset is True:
                     app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
@@ -2342,7 +2339,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     app_obj.inform.emit('[ERROR_NOTCL] %s' %
                                         _("Could not get the extent of the area to be non copper cleared."))
                     return 'fail'
-            elif isinstance(ncc_obj, FlatCAMGerber) and isotooldia:
+            elif ncc_obj.kind == 'gerber' and isotooldia:
                 isolated_geo = []
                 self.solid_geometry = ncc_obj.solid_geometry
 
@@ -2444,7 +2441,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                                         _("Isolation geometry is broken. Margin is less than isolation tool diameter."))
                     return 'fail'
 
-            elif isinstance(ncc_obj, FlatCAMGeometry):
+            elif ncc_obj.kind == 'geometry':
                 sol_geo = cascaded_union(ncc_obj.solid_geometry)
                 if has_offset is True:
                     app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
@@ -2715,359 +2712,1003 @@ class NonCopperClear(FlatCAMTool, Gerber):
         else:
             job_thread(app_obj=self.app)
 
-    # def on_ncc(self):
-    #
-    #     # Prepare non-copper polygons
-    #     if self.reference_radio.get_value() == 'area':
-    #         geo_n = self.sel_rect
-    #
-    #         geo_buff_list = []
-    #         for poly in geo_n:
-    #             geo_buff_list.append(poly.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre))
-    #         bounding_box = cascaded_union(geo_buff_list)
-    #     else:
-    #         geo_n = self.bound_obj.solid_geometry
-    #
-    #         try:
-    #             if isinstance(geo_n, MultiPolygon):
-    #                 env_obj = geo_n.convex_hull
-    #             elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
-    #                     (isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
-    #                 env_obj = cascaded_union(geo_n)
-    #             else:
-    #                 env_obj = cascaded_union(geo_n)
-    #                 env_obj = env_obj.convex_hull
-    #             bounding_box = env_obj.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
-    #         except Exception as e:
-    #             log.debug("NonCopperClear.on_ncc() --> %s" % str(e))
-    #             self.app.inform.emit(_("[ERROR_NOTCL] No object available."))
-    #             return
-    #
-    #     # calculate the empty area by subtracting the solid_geometry from the object bounding box geometry
-    #     if isinstance(self.ncc_obj, FlatCAMGerber):
-    #         if self.ncc_choice_offset_cb.isChecked():
-    #             self.app.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
-    #             offseted_geo = self.ncc_obj.solid_geometry.buffer(distance=ncc_offset_value)
-    #             self.app.inform.emit(_("[success] Buffering finished ..."))
-    #             empty = self.get_ncc_empty_area(target=offseted_geo, boundary=bounding_box)
-    #         else:
-    #             empty = self.get_ncc_empty_area(target=self.ncc_obj.solid_geometry, boundary=bounding_box)
-    #     elif isinstance(self.ncc_obj, FlatCAMGeometry):
-    #         sol_geo = cascaded_union(self.ncc_obj.solid_geometry)
-    #         if self.ncc_choice_offset_cb.isChecked():
-    #             self.app.inform.emit(_("[WARNING_NOTCL] Buffering ..."))
-    #             offseted_geo = sol_geo.buffer(distance=ncc_offset_value)
-    #             self.app.inform.emit(_("[success] Buffering finished ..."))
-    #             empty = self.get_ncc_empty_area(target=offseted_geo, boundary=bounding_box)
-    #         else:
-    #             empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
-    #     else:
-    #         self.inform.emit(_('[ERROR_NOTCL] The selected object is not suitable for copper clearing.'))
-    #         return
-    #
-    #     if type(empty) is Polygon:
-    #         empty = MultiPolygon([empty])
-    #
-    #     if empty.is_empty:
-    #         self.app.inform.emit(_("[ERROR_NOTCL] Could not get the extent of the area to be non copper cleared."))
-    #         return
-    #
-    #     # clear non copper using standard algorithm
-    #     if clearing_method is False:
-    #         self.clear_non_copper(
-    #             empty=empty,
-    #             over=over,
-    #             pol_method=pol_method,
-    #             connect=connect,
-    #             contour=contour
-    #         )
-    #     # clear non copper using rest machining algorithm
-    #     else:
-    #         self.clear_non_copper_rest(
-    #             empty=empty,
-    #             over=over,
-    #             pol_method=pol_method,
-    #             connect=connect,
-    #             contour=contour
-    #         )
-    #
-    # def clear_non_copper(self, empty, over, pol_method, outname=None, connect=True, contour=True):
-    #
-    #     name = outname if outname else self.obj_name + "_ncc"
-    #
-    #     # Sort tools in descending order
-    #     sorted_tools = []
-    #     for k, v in self.ncc_tools.items():
-    #         sorted_tools.append(float('%.4f' % float(v['tooldia'])))
-    #
-    #     order = self.ncc_order_radio.get_value()
-    #     if order == 'fwd':
-    #         sorted_tools.sort(reverse=False)
-    #     elif order == 'rev':
-    #         sorted_tools.sort(reverse=True)
-    #     else:
-    #         pass
-    #
-    #     # Do job in background
-    #     proc = self.app.proc_container.new(_("Clearing Non-Copper areas."))
-    #
-    #     def initialize(geo_obj, app_obj):
-    #         assert isinstance(geo_obj, FlatCAMGeometry), \
-    #             "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
-    #
-    #         cleared_geo = []
-    #         # Already cleared area
-    #         cleared = MultiPolygon()
-    #
-    #         # flag for polygons not cleared
-    #         app_obj.poly_not_cleared = False
-    #
-    #         # Generate area for each tool
-    #         offset = sum(sorted_tools)
-    #         current_uid = int(1)
-    #         tool = eval(self.app.defaults["tools_ncctools"])[0]
-    #
-    #         for tool in sorted_tools:
-    #             self.app.inform.emit(_('[success] Non-Copper Clearing with ToolDia = %s started.') % str(tool))
-    #             cleared_geo[:] = []
-    #
-    #             # Get remaining tools offset
-    #             offset -= (tool - 1e-12)
-    #
-    #             # Area to clear
-    #             area = empty.buffer(-offset)
-    #             try:
-    #                 area = area.difference(cleared)
-    #             except Exception as e:
-    #                 continue
-    #
-    #             # Transform area to MultiPolygon
-    #             if type(area) is Polygon:
-    #                 area = MultiPolygon([area])
-    #
-    #             if area.geoms:
-    #                 if len(area.geoms) > 0:
-    #                     for p in area.geoms:
-    #                         try:
-    #                             if pol_method == 'standard':
-    #                                 cp = self.clear_polygon(p, tool, self.app.defaults["gerber_circle_steps"],
-    #                                                         overlap=over, contour=contour, connect=connect)
-    #                             elif pol_method == 'seed':
-    #                                 cp = self.clear_polygon2(p, tool, self.app.defaults["gerber_circle_steps"],
-    #                                                          overlap=over, contour=contour, connect=connect)
-    #                             else:
-    #                                 cp = self.clear_polygon3(p, tool, self.app.defaults["gerber_circle_steps"],
-    #                                                          overlap=over, contour=contour, connect=connect)
-    #                             if cp:
-    #                                 cleared_geo += list(cp.get_objects())
-    #                         except Exception as e:
-    #                             log.warning("Polygon can not be cleared. %s" % str(e))
-    #                             app_obj.poly_not_cleared = True
-    #                             continue
-    #
-    #                     # check if there is a geometry at all in the cleared geometry
-    #                     if cleared_geo:
-    #                         # Overall cleared area
-    #                         cleared = empty.buffer(-offset * (1 + over)).buffer(-tool / 1.999999).buffer(
-    #                             tool / 1.999999)
-    #
-    #                         # clean-up cleared geo
-    #                         cleared = cleared.buffer(0)
-    #
-    #                         # find the tooluid associated with the current tool_dia so we know where to add the tool
-    #                         # solid_geometry
-    #                         for k, v in self.ncc_tools.items():
-    #                             if float('%.4f' % v['tooldia']) == float('%.4f' % tool):
-    #                                 current_uid = int(k)
-    #
-    #                                 # add the solid_geometry to the current too in self.paint_tools dictionary
-    #                                 # and then reset the temporary list that stored that solid_geometry
-    #                                 v['solid_geometry'] = deepcopy(cleared_geo)
-    #                                 v['data']['name'] = name
-    #                                 break
-    #                         geo_obj.tools[current_uid] = dict(self.ncc_tools[current_uid])
-    #                     else:
-    #                         log.debug("There are no geometries in the cleared polygon.")
-    #
-    #         geo_obj.options["cnctooldia"] = str(tool)
-    #         geo_obj.multigeo = True
-    #
-    #     def job_thread(app_obj):
-    #         try:
-    #             app_obj.new_object("geometry", name, initialize)
-    #         except Exception as e:
-    #             proc.done()
-    #             self.app.inform.emit(_('[ERROR_NOTCL] NCCTool.clear_non_copper() --> %s') % str(e))
-    #             return
-    #         proc.done()
-    #
-    #         if app_obj.poly_not_cleared is False:
-    #             self.app.inform.emit(_('[success] NCC Tool finished.'))
-    #         else:
-    #             self.app.inform.emit(_('[WARNING_NOTCL] NCC Tool finished but some PCB features could not be cleared. '
-    #                                  'Check the result.'))
-    #         # reset the variable for next use
-    #         app_obj.poly_not_cleared = False
-    #
-    #         # focus on Selected Tab
-    #         self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
-    #
-    #     # Promise object with the new name
-    #     self.app.collection.promise(name)
-    #
-    #     # Background
-    #     self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
-    #
-    # # clear copper with 'rest-machining' algorithm
-    # def clear_non_copper_rest(self, empty, over, pol_method, outname=None, connect=True, contour=True):
-    #
-    #     name = outname if outname is not None else self.obj_name + "_ncc_rm"
-    #
-    #     # Sort tools in descending order
-    #     sorted_tools = []
-    #     for k, v in self.ncc_tools.items():
-    #         sorted_tools.append(float('%.4f' % float(v['tooldia'])))
-    #     sorted_tools.sort(reverse=True)
-    #
-    #     # Do job in background
-    #     proc = self.app.proc_container.new(_("Clearing Non-Copper areas."))
-    #
-    #     def initialize_rm(geo_obj, app_obj):
-    #         assert isinstance(geo_obj, FlatCAMGeometry), \
-    #             "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
-    #
-    #         cleared_geo = []
-    #         cleared_by_last_tool = []
-    #         rest_geo = []
-    #         current_uid = 1
-    #         tool = eval(self.app.defaults["tools_ncctools"])[0]
-    #
-    #         # repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
-    #         app_obj.poly_not_cleared = True
-    #
-    #         area = empty.buffer(0)
-    #         # Generate area for each tool
-    #         while sorted_tools:
-    #             tool = sorted_tools.pop(0)
-    #             self.app.inform.emit(_('[success] Non-Copper Rest Clearing with ToolDia = %s started.') % str(tool))
-    #
-    #             tool_used = tool - 1e-12
-    #             cleared_geo[:] = []
-    #
-    #             # Area to clear
-    #             for poly in cleared_by_last_tool:
-    #                 try:
-    #                     area = area.difference(poly)
-    #                 except Exception as e:
-    #                     pass
-    #             cleared_by_last_tool[:] = []
-    #
-    #             # Transform area to MultiPolygon
-    #             if type(area) is Polygon:
-    #                 area = MultiPolygon([area])
-    #
-    #             # add the rest that was not able to be cleared previously; area is a MultyPolygon
-    #             # and rest_geo it's a list
-    #             allparts = [p.buffer(0) for p in area.geoms]
-    #             allparts += deepcopy(rest_geo)
-    #             rest_geo[:] = []
-    #             area = MultiPolygon(deepcopy(allparts))
-    #             allparts[:] = []
-    #
-    #             if area.geoms:
-    #                 if len(area.geoms) > 0:
-    #                     for p in area.geoms:
-    #                         try:
-    #                             if pol_method == 'standard':
-    #                                 cp = self.clear_polygon(p, tool_used, self.app.defaults["gerber_circle_steps"],
-    #                                                         overlap=over, contour=contour, connect=connect)
-    #                             elif pol_method == 'seed':
-    #                                 cp = self.clear_polygon2(p, tool_used,
-    #                                                          self.app.defaults["gerber_circle_steps"],
-    #                                                          overlap=over, contour=contour, connect=connect)
-    #                             else:
-    #                                 cp = self.clear_polygon3(p, tool_used,
-    #                                                          self.app.defaults["gerber_circle_steps"],
-    #                                                          overlap=over, contour=contour, connect=connect)
-    #                             cleared_geo.append(list(cp.get_objects()))
-    #                         except:
-    #                             log.warning("Polygon can't be cleared.")
-    #                             # this polygon should be added to a list and then try clear it with a smaller tool
-    #                             rest_geo.append(p)
-    #
-    #                     # check if there is a geometry at all in the cleared geometry
-    #                     if cleared_geo:
-    #                         # Overall cleared area
-    #                         cleared_area = list(self.flatten_list(cleared_geo))
-    #
-    #                         # cleared = MultiPolygon([p.buffer(tool_used / 2).buffer(-tool_used / 2)
-    #                         #                         for p in cleared_area])
-    #
-    #                         # here we store the poly's already processed in the original geometry by the current tool
-    #                         # into cleared_by_last_tool list
-    #                         # this will be sustracted from the original geometry_to_be_cleared and make data for
-    #                         # the next tool
-    #                         buffer_value = tool_used / 2
-    #                         for p in cleared_area:
-    #                             poly = p.buffer(buffer_value)
-    #                             cleared_by_last_tool.append(poly)
-    #
-    #                         # find the tooluid associated with the current tool_dia so we know
-    #                         # where to add the tool solid_geometry
-    #                         for k, v in self.ncc_tools.items():
-    #                             if float('%.4f' % v['tooldia']) == float('%.4f' % tool):
-    #                                 current_uid = int(k)
-    #
-    #                                 # add the solid_geometry to the current too in self.paint_tools dictionary
-    #                                 # and then reset the temporary list that stored that solid_geometry
-    #                                 v['solid_geometry'] = deepcopy(cleared_area)
-    #                                 v['data']['name'] = name
-    #                                 cleared_area[:] = []
-    #                                 break
-    #
-    #                         geo_obj.tools[current_uid] = dict(self.ncc_tools[current_uid])
-    #                     else:
-    #                         log.debug("There are no geometries in the cleared polygon.")
-    #
-    #         geo_obj.multigeo = True
-    #         geo_obj.options["cnctooldia"] = str(tool)
-    #
-    #         # check to see if geo_obj.tools is empty
-    #         # it will be updated only if there is a solid_geometry for tools
-    #         if geo_obj.tools:
-    #             return
-    #         else:
-    #             # I will use this variable for this purpose although it was meant for something else
-    #             # signal that we have no geo in the object therefore don't create it
-    #             app_obj.poly_not_cleared = False
-    #             return "fail"
-    #
-    #     def job_thread(app_obj):
-    #         try:
-    #             app_obj.new_object("geometry", name, initialize_rm)
-    #         except Exception as e:
-    #             proc.done()
-    #             app_obj.inform.emit(_('[ERROR_NOTCL] NCCTool.clear_non_copper_rest() --> %s') % str(e))
-    #             return
-    #
-    #         if app_obj.poly_not_cleared is True:
-    #             app_obj.inform.emit('[success] NCC Tool finished.')
-    #             # focus on Selected Tab
-    #             app_obj.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
-    #         else:
-    #             app_obj.inform.emit(_('[ERROR_NOTCL] NCC Tool finished but could not clear the object '
-    #                                  'with current settings.'))
-    #             # focus on Project Tab
-    #             app_obj.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
-    #         proc.done()
-    #         # reset the variable for next use
-    #         app_obj.poly_not_cleared = False
-    #
-    #     # Promise object with the new name
-    #     self.app.collection.promise(name)
-    #
-    #     # Background
-    #     self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
+    def clear_copper_tcl(self, ncc_obj,
+                         sel_obj=None,
+                         ncctooldia=None,
+                         isotooldia=None,
+                         margin=None,
+                         has_offset=None,
+                         offset=None,
+                         select_method=None,
+                         outname=None,
+                         overlap=None,
+                         connect=None,
+                         contour=None,
+                         order=None,
+                         method=None,
+                         rest=None,
+                         tools_storage=None,
+                         plot=True,
+                         run_threaded=False):
+        """
+        Clear the excess copper from the entire object. To be used only in a TCL command.
+
+        :param ncc_obj: ncc cleared object
+        :param ncctooldia: a tuple or single element made out of diameters of the tools to be used to ncc clear
+        :param isotooldia: a tuple or single element made out of diameters of the tools to be used for isolation
+        :param overlap: value by which the paths will overlap
+        :param order: if the tools are ordered and how
+        :param select_method: if to do ncc on the whole object, on an defined area or on an area defined by
+        another object
+        :param has_offset: True if an offset is needed
+        :param offset: distance from the copper features where the copper clearing is stopping
+        :param margin: a border around cleared area
+        :param outname: name of the resulting object
+        :param connect: Connect lines to avoid tool lifts.
+        :param contour: Paint around the edges.
+        :param method: choice out of 'seed', 'normal', 'lines'
+        :param rest: True if to use rest-machining
+        :param tools_storage: whether to use the current tools_storage self.ncc_tools or a different one.
+        Usage of the different one is related to when this function is called from a TcL command.
+        :param plot: if True after the job is finished the result will be plotted, else it will not.
+        :param run_threaded: If True the method will be run in a threaded way suitable for GUI usage; if False it will
+        run non-threaded for TclShell usage
+        :return:
+        """
+        if run_threaded:
+            proc = self.app.proc_container.new(_("Non-Copper clearing ..."))
+        else:
+            self.app.proc_container.view.set_busy(_("Non-Copper clearing ..."))
+            QtWidgets.QApplication.processEvents()
+
+        # #####################################################################
+        # ####### Read the parameters #########################################
+        # #####################################################################
+
+        units = self.app.defaults['units']
+
+        log.debug("NCC Tool started. Reading parameters.")
+        self.app.inform.emit(_("NCC Tool started. Reading parameters."))
+
+        ncc_method = method
+        ncc_margin = margin
+        ncc_select = select_method
+        overlap = overlap
+
+        connect = connect
+        contour = contour
+        order = order
+
+        if tools_storage is not None:
+            tools_storage = tools_storage
+        else:
+            tools_storage = self.ncc_tools
+
+        ncc_offset = 0.0
+        if has_offset is True:
+            ncc_offset = offset
+
+        # ######################################################################################################
+        # # Read the tooldia parameter and create a sorted list out them - they may be more than one diameter ##
+        # ######################################################################################################
+        sorted_tools = []
+        try:
+            sorted_tools = [float(eval(dia)) for dia in ncctooldia.split(",") if dia != '']
+        except AttributeError:
+            if not isinstance(ncctooldia, list):
+                sorted_tools = [float(ncctooldia)]
+            else:
+                sorted_tools = ncctooldia
+
+        # ##############################################################################################################
+        # Prepare non-copper polygons. Create the bounding box area from which the copper features will be subtracted ##
+        # ##############################################################################################################
+        log.debug("NCC Tool. Preparing non-copper polygons.")
+        self.app.inform.emit(_("NCC Tool. Preparing non-copper polygons."))
+
+        try:
+            if sel_obj is None or sel_obj == 'itself':
+                ncc_sel_obj = ncc_obj
+            else:
+                ncc_sel_obj = sel_obj
+        except Exception as e:
+            log.debug("NonCopperClear.clear_copper() --> %s" % str(e))
+            return 'fail'
+
+        bounding_box = None
+        if ncc_select == 'itself':
+            geo_n = ncc_sel_obj.solid_geometry
+
+            try:
+                if isinstance(geo_n, MultiPolygon):
+                    env_obj = geo_n.convex_hull
+                elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
+                        (isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
+                    env_obj = cascaded_union(geo_n)
+                else:
+                    env_obj = cascaded_union(geo_n)
+                    env_obj = env_obj.convex_hull
+
+                bounding_box = env_obj.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
+            except Exception as e:
+                log.debug("NonCopperClear.clear_copper() 'itself'  --> %s" % str(e))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
+                return 'fail'
+
+        elif ncc_select == 'area':
+            geo_n = cascaded_union(self.sel_rect)
+            try:
+                __ = iter(geo_n)
+            except Exception as e:
+                log.debug("NonCopperClear.clear_copper() 'area' --> %s" % str(e))
+                geo_n = [geo_n]
+
+            geo_buff_list = []
+            for poly in geo_n:
+                if self.app.abort_flag:
+                    # graceful abort requested by the user
+                    raise FlatCAMApp.GracefulException
+                geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
+
+            bounding_box = cascaded_union(geo_buff_list)
+
+        elif ncc_select == 'box':
+            geo_n = ncc_sel_obj.solid_geometry
+            if ncc_sel_obj.kind == 'geometry':
+                try:
+                    __ = iter(geo_n)
+                except Exception as e:
+                    log.debug("NonCopperClear.clear_copper() 'box' --> %s" % str(e))
+                    geo_n = [geo_n]
+
+                geo_buff_list = []
+                for poly in geo_n:
+                    if self.app.abort_flag:
+                        # graceful abort requested by the user
+                        raise FlatCAMApp.GracefulException
+                    geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
+
+                bounding_box = cascaded_union(geo_buff_list)
+            elif ncc_sel_obj.kind == 'gerber':
+                geo_n = cascaded_union(geo_n).convex_hull
+                bounding_box = cascaded_union(self.ncc_obj.solid_geometry).convex_hull.intersection(geo_n)
+                bounding_box = bounding_box.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
+            else:
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("The reference object type is not supported."))
+                return 'fail'
+
+        log.debug("NCC Tool. Finished non-copper polygons.")
+        # ########################################################################################################
+        # set the name for the future Geometry object
+        # I do it here because it is also stored inside the gen_clear_area() and gen_clear_area_rest() methods
+        # ########################################################################################################
+        rest_machining_choice = rest
+        if rest_machining_choice is True:
+            name = outname if outname is not None else self.obj_name + "_ncc_rm"
+        else:
+            name = outname if outname is not None else self.obj_name + "_ncc"
+
+        # ##########################################################################################
+        # Initializes the new geometry object ######################################################
+        # ##########################################################################################
+        def gen_clear_area(geo_obj, app_obj):
+            assert geo_obj.kind == 'geometry', \
+                "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
+
+            # provide the app with a way to process the GUI events when in a blocking loop
+            if not run_threaded:
+                QtWidgets.QApplication.processEvents()
+
+            log.debug("NCC Tool. Normal copper clearing task started.")
+            self.app.inform.emit(_("NCC Tool. Finished non-copper polygons. Normal copper clearing task started."))
+
+            # a flag to signal that the isolation is broken by the bounding box in 'area' and 'box' cases
+            # will store the number of tools for which the isolation is broken
+            warning_flag = 0
+
+            if order == 'fwd':
+                sorted_tools.sort(reverse=False)
+            elif order == 'rev':
+                sorted_tools.sort(reverse=True)
+            else:
+                pass
+
+            cleared_geo = []
+            # Already cleared area
+            cleared = MultiPolygon()
+
+            # flag for polygons not cleared
+            app_obj.poly_not_cleared = False
+
+            # Generate area for each tool
+            offset = sum(sorted_tools)
+            current_uid = int(1)
+            try:
+                tool = eval(self.app.defaults["tools_ncctools"])[0]
+            except TypeError:
+                tool = eval(self.app.defaults["tools_ncctools"])
+
+            # ###################################################################################################
+            # Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
+            # ###################################################################################################
+            log.debug("NCC Tool. Calculate 'empty' area.")
+            self.app.inform.emit(_("NCC Tool. Calculate 'empty' area."))
+
+            if ncc_obj.kind == 'gerber' and not isotooldia:
+                # unfortunately for this function to work time efficient,
+                # if the Gerber was loaded without buffering then it require the buffering now.
+                if self.app.defaults['gerber_buffering'] == 'no':
+                    sol_geo = ncc_obj.solid_geometry.buffer(0)
+                else:
+                    sol_geo = ncc_obj.solid_geometry
+
+                if has_offset is True:
+                    app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
+                    sol_geo = sol_geo.buffer(distance=ncc_offset)
+                    app_obj.inform.emit('[success] %s ...' % _("Buffering finished"))
+
+                empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
+                if empty == 'fail':
+                    return 'fail'
+
+                if empty.is_empty:
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("Could not get the extent of the area to be non copper cleared."))
+                    return 'fail'
+            elif ncc_obj.kind == 'gerber' and isotooldia:
+                isolated_geo = []
+
+                # unfortunately for this function to work time efficient,
+                # if the Gerber was loaded without buffering then it require the buffering now.
+                if self.app.defaults['gerber_buffering'] == 'no':
+                    self.solid_geometry = ncc_obj.solid_geometry.buffer(0)
+                else:
+                    self.solid_geometry = ncc_obj.solid_geometry
+
+                # if milling type is climb then the move is counter-clockwise around features
+                milling_type = self.app.defaults["tools_nccmilling_type"]
+
+                for tool_iso in isotooldia:
+                    new_geometry = []
+
+                    if milling_type == 'cl':
+                        isolated_geo = self.generate_envelope(tool_iso / 2, 1)
+                    else:
+                        isolated_geo = self.generate_envelope(tool_iso / 2, 0)
+
+                    if isolated_geo == 'fail':
+                        app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
+                    else:
+                        if ncc_margin < tool_iso:
+                            app_obj.inform.emit('[WARNING_NOTCL] %s' % _("Isolation geometry is broken. Margin is less "
+                                                                         "than isolation tool diameter."))
+                        try:
+                            for geo_elem in isolated_geo:
+                                # provide the app with a way to process the GUI events when in a blocking loop
+                                QtWidgets.QApplication.processEvents()
+
+                                if self.app.abort_flag:
+                                    # graceful abort requested by the user
+                                    raise FlatCAMApp.GracefulException
+
+                                if isinstance(geo_elem, Polygon):
+                                    for ring in self.poly2rings(geo_elem):
+                                        new_geo = ring.intersection(bounding_box)
+                                        if new_geo and not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                                elif isinstance(geo_elem, MultiPolygon):
+                                    for poly in geo_elem:
+                                        for ring in self.poly2rings(poly):
+                                            new_geo = ring.intersection(bounding_box)
+                                            if new_geo and not new_geo.is_empty:
+                                                new_geometry.append(new_geo)
+                                elif isinstance(geo_elem, LineString):
+                                    new_geo = geo_elem.intersection(bounding_box)
+                                    if new_geo:
+                                        if not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                                elif isinstance(geo_elem, MultiLineString):
+                                    for line_elem in geo_elem:
+                                        new_geo = line_elem.intersection(bounding_box)
+                                        if new_geo and not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                        except TypeError:
+                            if isinstance(isolated_geo, Polygon):
+                                for ring in self.poly2rings(isolated_geo):
+                                    new_geo = ring.intersection(bounding_box)
+                                    if new_geo:
+                                        if not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                            elif isinstance(isolated_geo, LineString):
+                                new_geo = isolated_geo.intersection(bounding_box)
+                                if new_geo and not new_geo.is_empty:
+                                    new_geometry.append(new_geo)
+                            elif isinstance(isolated_geo, MultiLineString):
+                                for line_elem in isolated_geo:
+                                    new_geo = line_elem.intersection(bounding_box)
+                                    if new_geo and not new_geo.is_empty:
+                                        new_geometry.append(new_geo)
+
+                        # a MultiLineString geometry element will show that the isolation is broken for this tool
+                        for geo_e in new_geometry:
+                            if type(geo_e) == MultiLineString:
+                                warning_flag += 1
+                                break
+
+                        for k, v in tools_storage.items():
+                            if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
+                                                                                                tool_iso)):
+                                current_uid = int(k)
+                                # add the solid_geometry to the current too in self.paint_tools dictionary
+                                # and then reset the temporary list that stored that solid_geometry
+                                v['solid_geometry'] = deepcopy(new_geometry)
+                                v['data']['name'] = name
+                                break
+                        geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
+
+                sol_geo = cascaded_union(isolated_geo)
+                if has_offset is True:
+                    app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
+                    sol_geo = sol_geo.buffer(distance=ncc_offset)
+                    app_obj.inform.emit('[success] %s ...' % _("Buffering finished"))
+                empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
+                if empty == 'fail':
+                    return 'fail'
+
+                if empty.is_empty:
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("Isolation geometry is broken. Margin is less than isolation tool diameter."))
+                    return 'fail'
+
+            elif ncc_obj.kind == 'geometry':
+                sol_geo = cascaded_union(ncc_obj.solid_geometry)
+                if has_offset is True:
+                    app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
+                    sol_geo = sol_geo.buffer(distance=ncc_offset)
+                    app_obj.inform.emit('[success] %s ...' %  _("Buffering finished"))
+                empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
+                if empty == 'fail':
+                    return 'fail'
+
+                if empty.is_empty:
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("Could not get the extent of the area to be non copper cleared."))
+                    return 'fail'
+
+            else:
+                app_obj.inform.emit('[ERROR_NOTCL] %s' %  _('The selected object is not suitable for copper clearing.'))
+                return 'fail'
+
+            if type(empty) is Polygon:
+                empty = MultiPolygon([empty])
+
+            log.debug("NCC Tool. Finished calculation of 'empty' area.")
+            self.app.inform.emit(_("NCC Tool. Finished calculation of 'empty' area."))
+
+            # COPPER CLEARING #
+            cp = None
+            for tool in sorted_tools:
+                log.debug("Starting geometry processing for tool: %s" % str(tool))
+                if self.app.abort_flag:
+                    # graceful abort requested by the user
+                    raise FlatCAMApp.GracefulException
+
+                # provide the app with a way to process the GUI events when in a blocking loop
+                QtWidgets.QApplication.processEvents()
+
+                app_obj.inform.emit('[success] %s = %s%s %s' % (
+                    _('NCC Tool clearing with tool diameter'), str(tool), units.lower(), _('started.'))
+                )
+                app_obj.proc_container.update_view_text(' %d%%' % 0)
+
+                cleared_geo[:] = []
+
+                # Get remaining tools offset
+                offset -= (tool - 1e-12)
+
+                # Area to clear
+                area = empty.buffer(-offset)
+                try:
+                    area = area.difference(cleared)
+                except Exception as e:
+                    continue
+
+                # Transform area to MultiPolygon
+                if type(area) is Polygon:
+                    area = MultiPolygon([area])
+
+                # variables to display the percentage of work done
+                geo_len = len(area.geoms)
+
+                old_disp_number = 0
+                log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
+
+                if area.geoms:
+                    if len(area.geoms) > 0:
+                        pol_nr = 0
+                        for p in area.geoms:
+                            # provide the app with a way to process the GUI events when in a blocking loop
+                            QtWidgets.QApplication.processEvents()
+
+                            if self.app.abort_flag:
+                                # graceful abort requested by the user
+                                raise FlatCAMApp.GracefulException
+
+                            # clean the polygon
+                            p = p.buffer(0)
+
+                            if p is not None and p.is_valid:
+                                poly_processed = list()
+                                try:
+                                    for pol in p:
+                                        if pol is not None and isinstance(pol, Polygon):
+                                            if ncc_method == 'standard':
+                                                cp = self.clear_polygon(pol, tool,
+                                                                        self.grb_circle_steps,
+                                                                        overlap=overlap, contour=contour,
+                                                                        connect=connect,
+                                                                        prog_plot=False)
+                                            elif ncc_method == 'seed':
+                                                cp = self.clear_polygon2(pol, tool,
+                                                                         self.grb_circle_steps,
+                                                                         overlap=overlap, contour=contour,
+                                                                         connect=connect,
+                                                                         prog_plot=False)
+                                            else:
+                                                cp = self.clear_polygon3(pol, tool,
+                                                                         self.grb_circle_steps,
+                                                                         overlap=overlap, contour=contour,
+                                                                         connect=connect,
+                                                                         prog_plot=False)
+                                            if cp:
+                                                cleared_geo += list(cp.get_objects())
+                                                poly_processed.append(True)
+                                            else:
+                                                poly_processed.append(False)
+                                                log.warning("Polygon in MultiPolygon can not be cleared.")
+                                        else:
+                                            log.warning("Geo in Iterable can not be cleared because it is not Polygon. "
+                                                        "It is: %s" % str(type(pol)))
+                                except TypeError:
+                                    if isinstance(p, Polygon):
+                                        if ncc_method == 'standard':
+                                            cp = self.clear_polygon(p, tool, self.grb_circle_steps,
+                                                                    overlap=overlap, contour=contour, connect=connect,
+                                                                    prog_plot=False)
+                                        elif ncc_method == 'seed':
+                                            cp = self.clear_polygon2(p, tool, self.grb_circle_steps,
+                                                                     overlap=overlap, contour=contour, connect=connect,
+                                                                     prog_plot=False)
+                                        else:
+                                            cp = self.clear_polygon3(p, tool, self.grb_circle_steps,
+                                                                     overlap=overlap, contour=contour, connect=connect,
+                                                                     prog_plot=False)
+                                        if cp:
+                                            cleared_geo += list(cp.get_objects())
+                                            poly_processed.append(True)
+                                        else:
+                                            poly_processed.append(False)
+                                            log.warning("Polygon can not be cleared.")
+                                    else:
+                                        log.warning("Geo can not be cleared because it is: %s" % str(type(p)))
+
+                                p_cleared = poly_processed.count(True)
+                                p_not_cleared = poly_processed.count(False)
+
+                                if p_not_cleared:
+                                    app_obj.poly_not_cleared = True
+
+                                if p_cleared == 0:
+                                    continue
+
+                                pol_nr += 1
+                                disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+                                # log.debug("Polygons cleared: %d" % pol_nr)
+
+                                if old_disp_number < disp_number <= 100:
+                                    self.app.proc_container.update_view_text(' %d%%' % disp_number)
+                                    old_disp_number = disp_number
+                                    # log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
+
+                            # check if there is a geometry at all in the cleared geometry
+                        if cleared_geo:
+                            # Overall cleared area
+                            cleared = empty.buffer(-offset * (1 + overlap)).buffer(-tool / 1.999999).buffer(
+                                tool / 1.999999)
+
+                            # clean-up cleared geo
+                            cleared = cleared.buffer(0)
+
+                            # find the tooluid associated with the current tool_dia so we know where to add the tool
+                            # solid_geometry
+                            for k, v in tools_storage.items():
+                                if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
+                                                                                                    tool)):
+                                    current_uid = int(k)
+
+                                    # add the solid_geometry to the current too in self.paint_tools dictionary
+                                    # and then reset the temporary list that stored that solid_geometry
+                                    v['solid_geometry'] = deepcopy(cleared_geo)
+                                    v['data']['name'] = name
+                                    break
+                            geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
+                        else:
+                            log.debug("There are no geometries in the cleared polygon.")
+
+            # delete tools with empty geometry
+            # look for keys in the tools_storage dict that have 'solid_geometry' values empty
+            for uid, uid_val in list(tools_storage.items()):
+                try:
+                    # if the solid_geometry (type=list) is empty
+                    if not uid_val['solid_geometry']:
+                        tools_storage.pop(uid, None)
+                except KeyError:
+                    tools_storage.pop(uid, None)
+
+            geo_obj.options["cnctooldia"] = str(tool)
+
+            geo_obj.multigeo = True
+            geo_obj.tools.clear()
+            geo_obj.tools = dict(tools_storage)
+
+            # test if at least one tool has solid_geometry. If no tool has solid_geometry we raise an Exception
+            has_solid_geo = 0
+            for tooluid in geo_obj.tools:
+                if geo_obj.tools[tooluid]['solid_geometry']:
+                    has_solid_geo += 1
+            if has_solid_geo == 0:
+                app_obj.inform.emit('[ERROR] %s' %
+                                    _("There is no NCC Geometry in the file.\n"
+                                      "Usually it means that the tool diameter is too big for the painted geometry.\n"
+                                      "Change the painting parameters and try again."))
+                return 'fail'
+
+            # check to see if geo_obj.tools is empty
+            # it will be updated only if there is a solid_geometry for tools
+            if geo_obj.tools:
+                if warning_flag == 0:
+                    self.app.inform.emit('[success] %s' % _("NCC Tool clear all done."))
+                else:
+                    self.app.inform.emit('[WARNING] %s: %s %s.' % (
+                        _("NCC Tool clear all done but the copper features isolation is broken for"),
+                        str(warning_flag),
+                        _("tools")))
+                    return
+
+                # create the solid_geometry
+                geo_obj.solid_geometry = list()
+                for tooluid in geo_obj.tools:
+                    if geo_obj.tools[tooluid]['solid_geometry']:
+                        try:
+                            for geo in geo_obj.tools[tooluid]['solid_geometry']:
+                                geo_obj.solid_geometry.append(geo)
+                        except TypeError:
+                            geo_obj.solid_geometry.append(geo_obj.tools[tooluid]['solid_geometry'])
+            else:
+                # I will use this variable for this purpose although it was meant for something else
+                # signal that we have no geo in the object therefore don't create it
+                app_obj.poly_not_cleared = False
+                return "fail"
+
+        # ###########################################################################################
+        # Initializes the new geometry object for the case of the rest-machining ####################
+        # ###########################################################################################
+        def gen_clear_area_rest(geo_obj, app_obj):
+            assert geo_obj.kind == 'geometry', \
+                "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
+
+            log.debug("NCC Tool. Rest machining copper clearing task started.")
+            app_obj.inform.emit('_(NCC Tool. Rest machining copper clearing task started.')
+
+            # provide the app with a way to process the GUI events when in a blocking loop
+            if not run_threaded:
+                QtWidgets.QApplication.processEvents()
+
+            # a flag to signal that the isolation is broken by the bounding box in 'area' and 'box' cases
+            # will store the number of tools for which the isolation is broken
+            warning_flag = 0
+
+            sorted_tools.sort(reverse=True)
+
+            cleared_geo = []
+            cleared_by_last_tool = []
+            rest_geo = []
+            current_uid = 1
+            try:
+                tool = eval(self.app.defaults["tools_ncctools"])[0]
+            except TypeError:
+                tool = eval(self.app.defaults["tools_ncctools"])
+
+            # repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
+            app_obj.poly_not_cleared = True
+            log.debug("NCC Tool. Calculate 'empty' area.")
+            app_obj.inform.emit("NCC Tool. Calculate 'empty' area.")
+
+            # ###################################################################################################
+            # Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
+            # ###################################################################################################
+            if ncc_obj.kind == 'gerber' and not isotooldia:
+                sol_geo = ncc_obj.solid_geometry
+                if has_offset is True:
+                    app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
+                    sol_geo = sol_geo.buffer(distance=ncc_offset)
+                    app_obj.inform.emit('[success] %s ...' %  _("Buffering finished"))
+                empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
+                if empty == 'fail':
+                    return 'fail'
+
+                if empty.is_empty:
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("Could not get the extent of the area to be non copper cleared."))
+                    return 'fail'
+            elif ncc_obj.kind == 'gerber' and isotooldia:
+                isolated_geo = []
+                self.solid_geometry = ncc_obj.solid_geometry
+
+                # if milling type is climb then the move is counter-clockwise around features
+                milling_type = self.app.defaults["tools_nccmilling_type"]
+
+                for tool_iso in isotooldia:
+                    new_geometry = []
+
+                    if milling_type == 'cl':
+                        isolated_geo = self.generate_envelope(tool_iso, 1)
+                    else:
+                        isolated_geo = self.generate_envelope(tool_iso, 0)
+
+                    if isolated_geo == 'fail':
+                        app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
+                    else:
+                        app_obj.inform.emit('[WARNING_NOTCL] %s' % _("Isolation geometry is broken. Margin is less "
+                                                                     "than isolation tool diameter."))
+
+                        try:
+                            for geo_elem in isolated_geo:
+                                # provide the app with a way to process the GUI events when in a blocking loop
+                                QtWidgets.QApplication.processEvents()
+
+                                if self.app.abort_flag:
+                                    # graceful abort requested by the user
+                                    raise FlatCAMApp.GracefulException
+
+                                if isinstance(geo_elem, Polygon):
+                                    for ring in self.poly2rings(geo_elem):
+                                        new_geo = ring.intersection(bounding_box)
+                                        if new_geo and not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                                elif isinstance(geo_elem, MultiPolygon):
+                                    for poly in geo_elem:
+                                        for ring in self.poly2rings(poly):
+                                            new_geo = ring.intersection(bounding_box)
+                                            if new_geo and not new_geo.is_empty:
+                                                new_geometry.append(new_geo)
+                                elif isinstance(geo_elem, LineString):
+                                    new_geo = geo_elem.intersection(bounding_box)
+                                    if new_geo:
+                                        if not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                                elif isinstance(geo_elem, MultiLineString):
+                                    for line_elem in geo_elem:
+                                        new_geo = line_elem.intersection(bounding_box)
+                                        if new_geo and not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                        except TypeError:
+                            try:
+                                if isinstance(isolated_geo, Polygon):
+                                    for ring in self.poly2rings(isolated_geo):
+                                        new_geo = ring.intersection(bounding_box)
+                                        if new_geo:
+                                            if not new_geo.is_empty:
+                                                new_geometry.append(new_geo)
+                                elif isinstance(isolated_geo, LineString):
+                                    new_geo = isolated_geo.intersection(bounding_box)
+                                    if new_geo and not new_geo.is_empty:
+                                        new_geometry.append(new_geo)
+                                elif isinstance(isolated_geo, MultiLineString):
+                                    for line_elem in isolated_geo:
+                                        new_geo = line_elem.intersection(bounding_box)
+                                        if new_geo and not new_geo.is_empty:
+                                            new_geometry.append(new_geo)
+                            except Exception as e:
+                                pass
+
+                        # a MultiLineString geometry element will show that the isolation is broken for this tool
+                        for geo_e in new_geometry:
+                            if type(geo_e) == MultiLineString:
+                                warning_flag += 1
+                                break
+
+                        for k, v in tools_storage.items():
+                            if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
+                                                                                                tool_iso)):
+                                current_uid = int(k)
+                                # add the solid_geometry to the current too in self.paint_tools dictionary
+                                # and then reset the temporary list that stored that solid_geometry
+                                v['solid_geometry'] = deepcopy(new_geometry)
+                                v['data']['name'] = name
+                                break
+                        geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
+
+                sol_geo = cascaded_union(isolated_geo)
+                if has_offset is True:
+                    app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
+                    sol_geo = sol_geo.buffer(distance=ncc_offset)
+                    app_obj.inform.emit('[success] %s ...' % _("Buffering finished"))
+                empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
+                if empty == 'fail':
+                    return 'fail'
+
+                if empty.is_empty:
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("Isolation geometry is broken. Margin is less than isolation tool diameter."))
+                    return 'fail'
+
+            elif ncc_obj.kind == 'geometry':
+                sol_geo = cascaded_union(ncc_obj.solid_geometry)
+                if has_offset is True:
+                    app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
+                    sol_geo = sol_geo.buffer(distance=ncc_offset)
+                    app_obj.inform.emit('[success] %s ...' % _("Buffering finished"))
+                empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box)
+                if empty == 'fail':
+                    return 'fail'
+
+                if empty.is_empty:
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("Could not get the extent of the area to be non copper cleared."))
+                    return 'fail'
+            else:
+                app_obj.inform.emit('[ERROR_NOTCL] %s' % _('The selected object is not suitable for copper clearing.'))
+                return
+
+            if self.app.abort_flag:
+                # graceful abort requested by the user
+                raise FlatCAMApp.GracefulException
+
+            if type(empty) is Polygon:
+                empty = MultiPolygon([empty])
+
+            area = empty.buffer(0)
+
+            log.debug("NCC Tool. Finished calculation of 'empty' area.")
+            app_obj.inform.emit("NCC Tool. Finished calculation of 'empty' area.")
+
+            # Generate area for each tool
+            while sorted_tools:
+                if self.app.abort_flag:
+                    # graceful abort requested by the user
+                    raise FlatCAMApp.GracefulException
+
+                tool = sorted_tools.pop(0)
+                log.debug("Starting geometry processing for tool: %s" % str(tool))
+
+                app_obj.inform.emit('[success] %s = %s%s %s' % (
+                    _('NCC Tool clearing with tool diameter'), str(tool), units.lower(), _('started.'))
+                )
+                app_obj.proc_container.update_view_text(' %d%%' % 0)
+
+                tool_used = tool - 1e-12
+                cleared_geo[:] = []
+
+                # Area to clear
+                for poly in cleared_by_last_tool:
+                    # provide the app with a way to process the GUI events when in a blocking loop
+                    QtWidgets.QApplication.processEvents()
+
+                    if self.app.abort_flag:
+                        # graceful abort requested by the user
+                        raise FlatCAMApp.GracefulException
+                    try:
+                        area = area.difference(poly)
+                    except Exception as e:
+                        pass
+                cleared_by_last_tool[:] = []
+
+                # Transform area to MultiPolygon
+                if type(area) is Polygon:
+                    area = MultiPolygon([area])
+
+                # add the rest that was not able to be cleared previously; area is a MultyPolygon
+                # and rest_geo it's a list
+                allparts = [p.buffer(0) for p in area.geoms]
+                allparts += deepcopy(rest_geo)
+                rest_geo[:] = []
+                area = MultiPolygon(deepcopy(allparts))
+                allparts[:] = []
+
+                # variables to display the percentage of work done
+                geo_len = len(area.geoms)
+                disp_number = 0
+                old_disp_number = 0
+                log.warning("Total number of polygons to be cleared. %s" % str(geo_len))
+
+                if area.geoms:
+                    if len(area.geoms) > 0:
+                        pol_nr = 0
+                        for p in area.geoms:
+                            if self.app.abort_flag:
+                                # graceful abort requested by the user
+                                raise FlatCAMApp.GracefulException
+
+                            # clean the polygon
+                            p = p.buffer(0)
+
+                            if p is not None and p.is_valid:
+                                # provide the app with a way to process the GUI events when in a blocking loop
+                                QtWidgets.QApplication.processEvents()
+
+                                if isinstance(p, Polygon):
+                                    try:
+                                        if ncc_method == 'standard':
+                                            cp = self.clear_polygon(p, tool_used,
+                                                                    self.grb_circle_steps,
+                                                                    overlap=overlap, contour=contour, connect=connect,
+                                                                    prog_plot=False)
+                                        elif ncc_method == 'seed':
+                                            cp = self.clear_polygon2(p, tool_used,
+                                                                     self.grb_circle_steps,
+                                                                     overlap=overlap, contour=contour, connect=connect,
+                                                                     prog_plot=False)
+                                        else:
+                                            cp = self.clear_polygon3(p, tool_used,
+                                                                     self.grb_circle_steps,
+                                                                     overlap=overlap, contour=contour, connect=connect,
+                                                                     prog_plot=False)
+                                        cleared_geo.append(list(cp.get_objects()))
+                                    except Exception as e:
+                                        log.warning("Polygon can't be cleared. %s" % str(e))
+                                        # this polygon should be added to a list and then try clear it with
+                                        # a smaller tool
+                                        rest_geo.append(p)
+                                elif isinstance(p, MultiPolygon):
+                                    for poly in p:
+                                        if poly is not None:
+                                            # provide the app with a way to process the GUI events when
+                                            # in a blocking loop
+                                            QtWidgets.QApplication.processEvents()
+
+                                            try:
+                                                if ncc_method == 'standard':
+                                                    cp = self.clear_polygon(poly, tool_used,
+                                                                            self.grb_circle_steps,
+                                                                            overlap=overlap, contour=contour,
+                                                                            connect=connect,
+                                                                            prog_plot=False)
+                                                elif ncc_method == 'seed':
+                                                    cp = self.clear_polygon2(poly, tool_used,
+                                                                             self.grb_circle_steps,
+                                                                             overlap=overlap, contour=contour,
+                                                                             connect=connect,
+                                                                             prog_plot=False)
+                                                else:
+                                                    cp = self.clear_polygon3(poly, tool_used,
+                                                                             self.grb_circle_steps,
+                                                                             overlap=overlap, contour=contour,
+                                                                             connect=connect,
+                                                                             prog_plot=False)
+                                                cleared_geo.append(list(cp.get_objects()))
+                                            except Exception as e:
+                                                log.warning("Polygon can't be cleared. %s" % str(e))
+                                                # this polygon should be added to a list and then try clear it with
+                                                # a smaller tool
+                                                rest_geo.append(poly)
+
+                                pol_nr += 1
+                                disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+                                # log.debug("Polygons cleared: %d" % pol_nr)
+
+                                if old_disp_number < disp_number <= 100:
+                                    self.app.proc_container.update_view_text(' %d%%' % disp_number)
+                                    old_disp_number = disp_number
+                                    # log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number))
+
+                        if self.app.abort_flag:
+                            # graceful abort requested by the user
+                            raise FlatCAMApp.GracefulException
+
+                        # check if there is a geometry at all in the cleared geometry
+                        if cleared_geo:
+                            # Overall cleared area
+                            cleared_area = list(self.flatten_list(cleared_geo))
+
+                            # cleared = MultiPolygon([p.buffer(tool_used / 2).buffer(-tool_used / 2)
+                            #                         for p in cleared_area])
+
+                            # here we store the poly's already processed in the original geometry by the current tool
+                            # into cleared_by_last_tool list
+                            # this will be sutracted from the original geometry_to_be_cleared and make data for
+                            # the next tool
+                            buffer_value = tool_used / 2
+                            for p in cleared_area:
+                                if self.app.abort_flag:
+                                    # graceful abort requested by the user
+                                    raise FlatCAMApp.GracefulException
+
+                                poly = p.buffer(buffer_value)
+                                cleared_by_last_tool.append(poly)
+
+                            # find the tooluid associated with the current tool_dia so we know
+                            # where to add the tool solid_geometry
+                            for k, v in tools_storage.items():
+                                if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
+                                                                                                    tool)):
+                                    current_uid = int(k)
+
+                                    # add the solid_geometry to the current too in self.paint_tools dictionary
+                                    # and then reset the temporary list that stored that solid_geometry
+                                    v['solid_geometry'] = deepcopy(cleared_area)
+                                    v['data']['name'] = name
+                                    cleared_area[:] = []
+                                    break
+
+                            geo_obj.tools[current_uid] = dict(tools_storage[current_uid])
+                        else:
+                            log.debug("There are no geometries in the cleared polygon.")
+
+            geo_obj.multigeo = True
+            geo_obj.options["cnctooldia"] = str(tool)
+
+            # check to see if geo_obj.tools is empty
+            # it will be updated only if there is a solid_geometry for tools
+            if geo_obj.tools:
+                if warning_flag == 0:
+                    self.app.inform.emit('[success] %s' % _("NCC Tool Rest Machining clear all done."))
+                else:
+                    self.app.inform.emit(
+                        '[WARNING] %s: %s %s.' % (_("NCC Tool Rest Machining clear all done but the copper features "
+                                                    "isolation is broken for"), str(warning_flag), _("tools")))
+                    return
+
+                # create the solid_geometry
+                geo_obj.solid_geometry = list()
+                for tooluid in geo_obj.tools:
+                    if geo_obj.tools[tooluid]['solid_geometry']:
+                        try:
+                            for geo in geo_obj.tools[tooluid]['solid_geometry']:
+                                geo_obj.solid_geometry.append(geo)
+                        except TypeError:
+                            geo_obj.solid_geometry.append(geo_obj.tools[tooluid]['solid_geometry'])
+            else:
+                # I will use this variable for this purpose although it was meant for something else
+                # signal that we have no geo in the object therefore don't create it
+                app_obj.poly_not_cleared = False
+                return "fail"
+
+        # ###########################################################################################
+        # Create the Job function and send it to the worker to be processed in another thread #######
+        # ###########################################################################################
+        def job_thread(app_obj):
+            try:
+                if rest_machining_choice is True:
+                    app_obj.new_object("geometry", name, gen_clear_area_rest, plot=plot)
+                else:
+                    app_obj.new_object("geometry", name, gen_clear_area, plot=plot)
+            except FlatCAMApp.GracefulException:
+                if run_threaded:
+                    proc.done()
+                return
+            except Exception:
+                if run_threaded:
+                    proc.done()
+                traceback.print_stack()
+                return
+
+            if run_threaded:
+                proc.done()
+            else:
+                app_obj.proc_container.view.set_idle()
+
+            # focus on Selected Tab
+            self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
+
+        if run_threaded:
+            # Promise object with the new name
+            self.app.collection.promise(name)
+
+            # Background
+            self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
+        else:
+            job_thread(app_obj=self.app)
 
     def get_ncc_empty_area(self, target, boundary=None):
         """

+ 0 - 3
flatcamTools/ToolPanelize.py

@@ -484,11 +484,8 @@ class Panelize(FlatCAMTool):
             if panel_obj is not None:
                 self.app.inform.emit(_("Generating panel ... "))
 
-                self.app.progress.emit(0)
-
                 def job_init_excellon(obj_fin, app_obj):
                     currenty = 0.0
-                    self.app.progress.emit(10)
                     obj_fin.tools = copied_tools
                     obj_fin.drills = []
                     obj_fin.slots = []

+ 2 - 6
flatcamTools/ToolPcbWizard.py

@@ -417,8 +417,6 @@ class PcbWizard(FlatCAMTool):
 
         # How the object should be initialized
         def obj_init(excellon_obj, app_obj):
-            # self.progress.emit(20)
-
             try:
                 ret = excellon_obj.parse_file(file_obj=excellon_fileobj)
                 if ret == "fail":
@@ -427,10 +425,8 @@ class PcbWizard(FlatCAMTool):
                                         _("This is not Excellon file."))
                     return "fail"
             except IOError:
-                app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (
-                        _("Cannot parse file"), self.outname))
+                app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Cannot parse file"), self.outname))
                 app_obj.log.debug("Could not import Excellon object.")
-                app_obj.progress.emit(0)
                 return "fail"
             except Exception as e:
                 app_obj.log.debug("PcbWizard.on_import_excellon().obj_init() %s" % str(e))
@@ -443,7 +439,7 @@ class PcbWizard(FlatCAMTool):
             if ret == 'fail':
                 app_obj.log.debug("Could not create geometry for Excellon object.")
                 return "fail"
-            app_obj.progress.emit(100)
+
             for tool in excellon_obj.tools:
                 if excellon_obj.tools[tool]['solid_geometry']:
                     return

+ 0 - 6
flatcamTools/ToolSolderPaste.py

@@ -1348,8 +1348,6 @@ class SolderPaste(FlatCAMTool):
             job_obj.options['ymax'] = ymax
 
             for tooluid_key, tooluid_value in obj.tools.items():
-                app_obj.progress.emit(20)
-
                 # find the tool_dia associated with the tooluid_key
                 tool_dia = tooluid_value['tooldia']
                 tool_cnc_dict = deepcopy(tooluid_value)
@@ -1380,8 +1378,6 @@ class SolderPaste(FlatCAMTool):
                 # tell gcode_parse from which point to start drawing the lines depending on what kind of
                 # object is the source of gcode
                 job_obj.toolchange_xy_type = "geometry"
-                app_obj.progress.emit(80)
-
                 job_obj.cnc_tools.update({
                     tooluid_key: deepcopy(tool_cnc_dict)
                 })
@@ -1394,8 +1390,6 @@ class SolderPaste(FlatCAMTool):
                     if app_obj.new_object("cncjob", name, job_init) != 'fail':
                         app_obj.inform.emit('[success] [success] %s: %s' %
                                             (_("ToolSolderPaste CNCjob created"), name))
-                        app_obj.progress.emit(100)
-
             # Create a promise with the name
             self.app.collection.promise(name)
             # Send to worker

+ 2 - 23
flatcamTools/ToolTransform.py

@@ -682,8 +682,6 @@ class ToolTransform(FlatCAMTool):
                     xmaximal = max(xmaxlist)
                     ymaximal = max(ymaxlist)
 
-                    self.app.progress.emit(20)
-
                     px = 0.5 * (xminimal + xmaximal)
                     py = 0.5 * (yminimal + ymaximal)
                     for sel_obj in obj_list:
@@ -697,8 +695,6 @@ class ToolTransform(FlatCAMTool):
                         sel_obj.options['rotate'] = num
                         sel_obj.plot()
                     self.app.inform.emit('[success] %s...' % _('Rotate done'))
-                    self.app.progress.emit(100)
-
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
                                          (_("Due of"), str(e), _("action was not executed.")))
@@ -743,8 +739,6 @@ class ToolTransform(FlatCAMTool):
                         px = 0.5 * (xminimal + xmaximal)
                         py = 0.5 * (yminimal + ymaximal)
 
-                    self.app.progress.emit(20)
-
                     # execute mirroring
                     for sel_obj in obj_list:
                         if isinstance(sel_obj, FlatCAMCNCjob):
@@ -768,12 +762,9 @@ class ToolTransform(FlatCAMTool):
                                     sel_obj.options['mirror_x'] = not sel_obj.options['mirror_x']
                                 else:
                                     sel_obj.options['mirror_x'] = True
-                                self.app.inform.emit('[success] %s...' %
-                                                     _('Flip on the X axis done'))
+                                self.app.inform.emit('[success] %s...' % _('Flip on the X axis done'))
                             self.app.object_changed.emit(sel_obj)
                         sel_obj.plot()
-                    self.app.progress.emit(100)
-
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
                                          (_("Due of"), str(e), _("action was not executed.")))
@@ -809,8 +800,6 @@ class ToolTransform(FlatCAMTool):
                     xminimal = min(xminlist)
                     yminimal = min(yminlist)
 
-                    self.app.progress.emit(20)
-
                     for sel_obj in obj_list:
                         if isinstance(sel_obj, FlatCAMCNCjob):
                             self.app.inform.emit(_("CNCJob objects can't be skewed."))
@@ -825,10 +814,7 @@ class ToolTransform(FlatCAMTool):
                                 sel_obj.options['skew_y'] = num
                             self.app.object_changed.emit(sel_obj)
                         sel_obj.plot()
-                    self.app.inform.emit('[success] %s %s %s...' %
-                                         (_('Skew on the'),  str(axis), _("axis done")))
-                    self.app.progress.emit(100)
-
+                    self.app.inform.emit('[success] %s %s %s...' % (_('Skew on the'),  str(axis), _("axis done")))
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
                                          (_("Due of"), str(e), _("action was not executed.")))
@@ -865,8 +851,6 @@ class ToolTransform(FlatCAMTool):
                     xmaximal = max(xmaxlist)
                     ymaximal = max(ymaxlist)
 
-                    self.app.progress.emit(20)
-
                     if point is None:
                         px = 0.5 * (xminimal + xmaximal)
                         py = 0.5 * (yminimal + ymaximal)
@@ -887,7 +871,6 @@ class ToolTransform(FlatCAMTool):
 
                     self.app.inform.emit('[success] %s %s %s...' %
                                          (_('Scale on the'), str(axis), _('axis done')))
-                    self.app.progress.emit(100)
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
                                          (_("Due of"), str(e), _("action was not executed.")))
@@ -903,8 +886,6 @@ class ToolTransform(FlatCAMTool):
         else:
             with self.app.proc_container.new(_("Applying Offset")):
                 try:
-                    self.app.progress.emit(20)
-
                     for sel_obj in obj_list:
                         if isinstance(sel_obj, FlatCAMCNCjob):
                             self.app.inform.emit(_("CNCJob objects can't be offset."))
@@ -922,8 +903,6 @@ class ToolTransform(FlatCAMTool):
 
                     self.app.inform.emit('[success] %s %s %s...' %
                                          (_('Offset on the'), str(axis), _('axis done')))
-                    self.app.progress.emit(100)
-
                 except Exception as e:
                     self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
                                          (_("Due of"), str(e),  _("action was not executed.")))

+ 33 - 33
tclCommands/TclCommandCopperClear.py

@@ -219,22 +219,22 @@ class TclCommandCopperClear(TclCommand):
 
         # Non-Copper clear all polygons in the non-copper clear object
         if 'all' in args and bool(args['all']):
-            self.app.ncclear_tool.clear_copper(ncc_obj=obj,
-                                               select_method='itself',
-                                               ncctooldia=tooldia,
-                                               overlap=overlap,
-                                               order=order,
-                                               margin=margin,
-                                               has_offset=has_offset,
-                                               offset=offset,
-                                               method=method,
-                                               outname=outname,
-                                               connect=connect,
-                                               contour=contour,
-                                               rest=rest,
-                                               tools_storage=ncc_tools,
-                                               plot=False,
-                                               run_threaded=False)
+            self.app.ncclear_tool.clear_copper_tcl(ncc_obj=obj,
+                                                   select_method='itself',
+                                                   ncctooldia=tooldia,
+                                                   overlap=overlap,
+                                                   order=order,
+                                                   margin=margin,
+                                                   has_offset=has_offset,
+                                                   offset=offset,
+                                                   method=method,
+                                                   outname=outname,
+                                                   connect=connect,
+                                                   contour=contour,
+                                                   rest=rest,
+                                                   tools_storage=ncc_tools,
+                                                   plot=False,
+                                                   run_threaded=False)
             return
 
         # Non-Copper clear all polygons found within the box object from the the non_copper cleared object
@@ -252,23 +252,23 @@ class TclCommandCopperClear(TclCommand):
                     self.raise_tcl_error("%s: %s" % (_("Could not retrieve box object"), name))
                     return "Could not retrieve object: %s" % name
 
-                self.app.ncclear_tool.clear_copper(ncc_obj=obj,
-                                                   sel_obj=box_obj,
-                                                   select_method='box',
-                                                   ncctooldia=tooldia,
-                                                   overlap=overlap,
-                                                   order=order,
-                                                   margin=margin,
-                                                   has_offset=has_offset,
-                                                   offset=offset,
-                                                   method=method,
-                                                   outname=outname,
-                                                   connect=connect,
-                                                   contour=contour,
-                                                   rest=rest,
-                                                   tools_storage=ncc_tools,
-                                                   plot=False,
-                                                   run_threaded=False)
+                self.app.ncclear_tool.clear_copper_tcl(ncc_obj=obj,
+                                                       sel_obj=box_obj,
+                                                       select_method='box',
+                                                       ncctooldia=tooldia,
+                                                       overlap=overlap,
+                                                       order=order,
+                                                       margin=margin,
+                                                       has_offset=has_offset,
+                                                       offset=offset,
+                                                       method=method,
+                                                       outname=outname,
+                                                       connect=connect,
+                                                       contour=contour,
+                                                       rest=rest,
+                                                       tools_storage=ncc_tools,
+                                                       plot=False,
+                                                       run_threaded=False)
             return
         else:
             self.raise_tcl_error("%s:" % _("None of the following args: 'ref', 'all' were found or none was set to 1.\n"

+ 0 - 6
tclCommands/TclCommandPanelize.py

@@ -177,11 +177,8 @@ class TclCommandPanelize(TclCommand):
             if obj is not None:
                 self.app.inform.emit("Generating panel ... Please wait.")
 
-                self.app.progress.emit(0)
-
                 def job_init_excellon(obj_fin, app_obj):
                     currenty = 0.0
-                    self.app.progress.emit(10)
                     obj_fin.tools = obj.tools.copy()
                     obj_fin.drills = []
                     obj_fin.slots = []
@@ -247,7 +244,6 @@ class TclCommandPanelize(TclCommand):
                             for tool in obj.tools:
                                 obj_fin.tools[tool]['solid_geometry'][:] = []
 
-                    self.app.progress.emit(0)
                     for row in range(rows):
                         currentx = 0.0
 
@@ -271,10 +267,8 @@ class TclCommandPanelize(TclCommand):
                         currenty += lenghty
 
                 if isinstance(obj, FlatCAMExcellon):
-                    self.app.progress.emit(50)
                     self.app.new_object("excellon", outname, job_init_excellon, plot=False, autoselected=True)
                 else:
-                    self.app.progress.emit(50)
                     self.app.new_object("geometry", outname, job_init_geometry, plot=False, autoselected=True)
 
         if threaded is True: