Sfoglia il codice sorgente

Merged in marius_stanciu/flatcam_beta/Beta_8.918 (pull request #149)

Beta 8.918
Marius Stanciu 6 anni fa
parent
commit
cd64b633a0
68 ha cambiato i file con 2246 aggiunte e 1885 eliminazioni
  1. 246 175
      FlatCAMApp.py
  2. 3 2
      FlatCAMCommon.py
  3. 72 50
      FlatCAMObj.py
  4. 2 2
      FlatCAMPostProc.py
  5. 2 2
      FlatCAMProcess.py
  6. 2 2
      FlatCAMTool.py
  7. 2 2
      FlatCAMTranslation.py
  8. 2 2
      FlatCAMWorker.py
  9. 26 26
      ObjectCollection.py
  10. 65 0
      README.md
  11. 142 157
      camlib.py
  12. 221 170
      flatcamEditors/FlatCAMExcEditor.py
  13. 18 21
      flatcamEditors/FlatCAMGeoEditor.py
  14. 40 18
      flatcamEditors/FlatCAMGrbEditor.py
  15. 167 171
      flatcamGUI/FlatCAMGUI.py
  16. 44 4
      flatcamGUI/GUIElements.py
  17. 50 37
      flatcamGUI/ObjectUI.py
  18. 4 4
      flatcamGUI/PlotCanvas.py
  19. 2 2
      flatcamGUI/VisPyCanvas.py
  20. 2 2
      flatcamGUI/VisPyPatches.py
  21. 2 2
      flatcamGUI/VisPyTesselators.py
  22. 17 5
      flatcamGUI/VisPyVisuals.py
  23. 2 2
      flatcamParsers/ParseDXF.py
  24. 2 2
      flatcamParsers/ParseDXF_Spline.py
  25. 6 6
      flatcamParsers/ParseFont.py
  26. 2 2
      flatcamParsers/ParseSVG.py
  27. 21 21
      flatcamTools/ToolCalculators.py
  28. 9 3
      flatcamTools/ToolCutOut.py
  29. 14 14
      flatcamTools/ToolDblSided.py
  30. 3 3
      flatcamTools/ToolFilm.py
  31. 4 4
      flatcamTools/ToolImage.py
  32. 4 4
      flatcamTools/ToolMeasurement.py
  33. 2 2
      flatcamTools/ToolMove.py
  34. 8 8
      flatcamTools/ToolNonCopperClear.py
  35. 4 4
      flatcamTools/ToolPDF.py
  36. 12 12
      flatcamTools/ToolPaint.py
  37. 3 3
      flatcamTools/ToolPanelize.py
  38. 4 4
      flatcamTools/ToolPcbWizard.py
  39. 3 3
      flatcamTools/ToolProperties.py
  40. 2 2
      flatcamTools/ToolShell.py
  41. 21 21
      flatcamTools/ToolSolderPaste.py
  42. 2 2
      flatcamTools/ToolSub.py
  43. 15 15
      flatcamTools/ToolTransform.py
  44. BIN
      locale/de/LC_MESSAGES/strings.mo
  45. 197 193
      locale/de/LC_MESSAGES/strings.po
  46. BIN
      locale/en/LC_MESSAGES/strings.mo
  47. 219 215
      locale/en/LC_MESSAGES/strings.po
  48. BIN
      locale/ro/LC_MESSAGES/strings.mo
  49. 197 193
      locale/ro/LC_MESSAGES/strings.po
  50. 223 219
      locale_template/strings.pot
  51. 4 4
      make_win.py
  52. 2 2
      postprocessors/Paste_1.py
  53. 2 2
      postprocessors/Repetier.py
  54. 2 2
      postprocessors/Roland_MDX_20.py
  55. 2 2
      postprocessors/Toolchange_Custom.py
  56. 2 2
      postprocessors/Toolchange_Probe_MACH3.py
  57. 2 2
      postprocessors/Toolchange_manual.py
  58. 2 2
      postprocessors/default.py
  59. 2 2
      postprocessors/grbl_11.py
  60. 2 2
      postprocessors/grbl_laser.py
  61. 2 2
      postprocessors/hpgl.py
  62. 2 2
      postprocessors/line_xyz.py
  63. 2 2
      postprocessors/marlin.py
  64. BIN
      share/disable16.png
  65. BIN
      share/disable32.png
  66. BIN
      share/notebook16.png
  67. 1 1
      tclCommands/TclCommandCutout.py
  68. 107 43
      tclCommands/TclCommandGeoCutout.py

File diff suppressed because it is too large
+ 246 - 175
FlatCAMApp.py


+ 3 - 2
FlatCAMCommon.py

@@ -1,10 +1,11 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
+
 
 class LoudDict(dict):
     """

+ 72 - 50
FlatCAMObj.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 import copy
 import inspect  # TODO: For debugging only.
@@ -35,9 +35,9 @@ class ValidationError(Exception):
 
         self.errors = errors
 
-# #######################################
-# #            FlatCAMObj              ##
-# #######################################
+# ##################################### ##
+# #            FlatCAMObj              # ##
+# ##################################### ##
 
 
 class FlatCAMObj(QtCore.QObject):
@@ -90,6 +90,8 @@ class FlatCAMObj(QtCore.QObject):
         self.isHovering = False
         self.notHovering = True
 
+        self.units = 'IN'
+
         # assert isinstance(self.ui, ObjectUI)
         # self.ui.name_entry.returnPressed.connect(self.on_name_activate)
         # self.ui.offset_button.clicked.connect(self.on_offset_button_click)
@@ -135,8 +137,7 @@ class FlatCAMObj(QtCore.QObject):
         if key == 'plot':
             self.visible = self.options['plot']
 
-        # self.emit(QtCore.SIGNAL("optionChanged"), key)
-        self.optionChanged.emit(key)
+        # self.optionChanged.emit(key)
 
     def set_ui(self, ui):
         self.ui = ui
@@ -296,8 +297,6 @@ class FlatCAMObj(QtCore.QObject):
             return False
 
         self.clear()
-
-
         return True
 
     def serialize(self):
@@ -340,13 +339,18 @@ class FlatCAMObj(QtCore.QObject):
 
     @visible.setter
     def visible(self, value):
-        self.shapes.visible = value
+        log.debug("FlatCAMObj.visible()")
 
-        # Not all object types has annotations
-        try:
-            self.annotation.visible = value
-        except AttributeError:
-            pass
+        def worker_task(app_obj):
+            app_obj.shapes.visible = value
+
+            # Not all object types has annotations
+            try:
+                app_obj.annotation.visible = value
+            except Exception as e:
+                pass
+
+        self.app.worker_task.emit({'fcn': worker_task, 'params': [self]})
 
     @property
     def drawing_tolerance(self):
@@ -365,12 +369,6 @@ class FlatCAMObj(QtCore.QObject):
         except AttributeError:
             pass
 
-        # Not all object types have mark_shapes
-        # try:
-        #     self.mark_shapes.clear(update)
-        # except AttributeError:
-        #     pass
-
     def delete(self):
         # Free resources
         del self.ui
@@ -1053,7 +1051,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         :param kwargs: color and face_color
         :return:
         """
-
         FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> FlatCAMGerber.plot()")
 
         # Does all the required setup and returns False
@@ -1065,6 +1062,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             color = kwargs['color']
         else:
             color = self.app.defaults['global_plot_line']
+
         if 'face_color' in kwargs:
             face_color = kwargs['face_color']
         else:
@@ -1078,7 +1076,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
 
         # Make sure geometry is iterable.
         try:
-            _ = iter(geometry)
+            __ = iter(geometry)
         except TypeError:
             geometry = [geometry]
 
@@ -2342,7 +2340,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                 "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
             app_obj.progress.emit(20)
 
-            ### Add properties to the object
+            # ## Add properties to the object
 
             # get the tool_table items in a list of row items
             tool_table_items = self.get_selected_tools_table_items()
@@ -2436,7 +2434,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                 "Initializer expected a FlatCAMGeometry, got %s" % type(geo_obj)
             app_obj.progress.emit(20)
 
-            ### Add properties to the object
+            # ## Add properties to the object
 
             # get the tool_table items in a list of row items
             tool_table_items = self.get_selected_tools_table_items()
@@ -2561,7 +2559,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             # insert an information only element in the front
             tool_table_items.insert(0, [_("Tool_nr"), _("Diameter"), _("Drills_Nr"), _("Slots_Nr")])
 
-            ### Add properties to the object
+            # ## Add properties to the object
 
             job_obj.origin_kind = 'excellon'
 
@@ -2795,7 +2793,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
         # this stays for compatibility reasons, in case we try to open old projects
         try:
-            _ = iter(self.solid_geometry)
+            __ = iter(self.solid_geometry)
         except TypeError:
             self.solid_geometry = [self.solid_geometry]
 
@@ -2932,18 +2930,18 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         """
         pts = []
 
-        ## Iterable: descend into each item.
+        # Iterable: descend into each item.
         try:
             for subo in o:
                 pts += FlatCAMGeometry.get_pts(subo)
 
-        ## Non-iterable
+        # Non-iterable
         except TypeError:
             if o is not None:
                 if type(o) == MultiPolygon:
                     for poly in o:
                         pts += FlatCAMGeometry.get_pts(poly)
-                ## Descend into .exerior and .interiors
+                # ## Descend into .exerior and .interiors
                 elif type(o) == Polygon:
                     pts += FlatCAMGeometry.get_pts(o.exterior)
                     for i in o.interiors:
@@ -2951,7 +2949,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 elif type(o) == MultiLineString:
                     for line in o:
                         pts += FlatCAMGeometry.get_pts(line)
-                ## Has .coords: list them.
+                # ## Has .coords: list them.
                 else:
                     pts += list(o.coords)
             else:
@@ -3033,6 +3031,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         # engine of FlatCAM. Most likely are generated by some of tools and are special cases of geometries.
         self. special_group = None
 
+        self.old_pp_state = ''
+        self.old_toolchangeg_state = ''
+
+        # store here the default data for Geometry Data
+        self.default_data = {}
+
         # Attributes to be included in serialization
         # Always append to it because it carries contents
         # from predecessors.
@@ -3101,7 +3105,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             self.ui.geo_tools_table.setCellWidget(row_no, 3, type_item)
             self.ui.geo_tools_table.setCellWidget(row_no, 4, tool_type_item)
 
-            ### REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY ###
+            # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
             self.ui.geo_tools_table.setItem(row_no, 5, tool_uid_item)  # Tool unique ID
             self.ui.geo_tools_table.setCellWidget(row_no, 6, plot_item)
 
@@ -3391,11 +3395,11 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             try:
                 # works for CheckBoxes
                 self.ui.grid3.itemAt(i).widget().stateChanged.connect(self.gui_form_to_storage)
-            except:
+            except Exception as e:
                 # works for ComboBoxes
                 try:
                     self.ui.grid3.itemAt(i).widget().currentIndexChanged.connect(self.gui_form_to_storage)
-                except:
+                except Exception as e2:
                     # works for Entry
                     try:
                         self.ui.grid3.itemAt(i).widget().editingFinished.connect(self.gui_form_to_storage)
@@ -3492,13 +3496,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
     def on_tool_add(self, dia=None):
         self.ui_disconnect()
 
-        last_offset = None
-        last_offset_value = None
-        last_type = None
-        last_tool_type = None
-        last_data = None
-        last_solid_geometry = []
-
         # if a Tool diameter entered is a char instead a number the final message of Tool adding is changed
         # because the Default value for Tool is used.
         change_message = False
@@ -3547,7 +3544,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             self.tools.update({
                 self.tooluid: {
                     'tooldia': tooldia,
-                    'offset': ('Path'),
+                    'offset': 'Path',
                     'offset_value': 0.0,
                     'type': _('Rough'),
                     'tool_type': 'C1',
@@ -3556,7 +3553,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 }
             })
         else:
-            # print("LAST", self.tools[maxuid])
             last_data = self.tools[max_uid]['data']
             last_offset = self.tools[max_uid]['offset']
             last_offset_value = self.tools[max_uid]['offset_value']
@@ -3580,7 +3576,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                     'solid_geometry': deepcopy(last_solid_geometry)
                 }
             })
-            # print("CURRENT", self.tools[-1])
 
         self.ui.tool_offset_entry.hide()
         self.ui.tool_offset_lbl.hide()
@@ -4277,6 +4272,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         # Object initialization function for app.new_object()
         # RUNNING ON SEPARATE THREAD!
         def job_init_single_geometry(job_obj, app_obj):
+            log.debug("Creating a CNCJob out of a single-geometry")
+
             assert isinstance(job_obj, FlatCAMCNCjob), \
                 "Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj)
 
@@ -4451,8 +4448,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
 
                 app_obj.progress.emit(40)
 
+                tol = float(self.app.defaults['global_tolerance'])
                 res = job_obj.generate_from_geometry_2(
-                    self, tooldia=tooldia_val, offset=tool_offset, tolerance=0.0005,
+                    self, tooldia=tooldia_val, offset=tool_offset, tolerance=tol,
                     z_cut=z_cut, z_move=z_move,
                     feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
                     spindlespeed=spindlespeed, spindledir=spindledir, dwell=dwell, dwelltime=dwelltime,
@@ -4488,6 +4486,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         # Object initialization function for app.new_object()
         # RUNNING ON SEPARATE THREAD!
         def job_init_multi_geometry(job_obj, app_obj):
+            log.debug("Creating a CNCJob out of a multi-geometry")
+
             assert isinstance(job_obj, FlatCAMCNCjob), \
                 "Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj)
 
@@ -4682,9 +4682,10 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 spindledir = self.app.defaults['geometry_spindledir']
 
                 tool_solid_geometry = self.tools[current_uid]['solid_geometry']
+                tol = float(self.app.defaults['global_tolerance'])
                 res = job_obj.generate_from_multitool_geometry(
                     tool_solid_geometry, tooldia=tooldia_val, offset=tool_offset,
-                    tolerance=0.0005, z_cut=z_cut, z_move=z_move,
+                    tolerance=tol, z_cut=z_cut, z_move=z_move,
                     feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
                     spindlespeed=spindlespeed, spindledir=spindledir, dwell=dwell, dwelltime=dwelltime,
                     multidepth=multidepth, depthpercut=depthpercut,
@@ -4741,7 +4742,6 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             else:
                 self.app.new_object("cncjob", outname, job_init_multi_geometry)
 
-
     def generatecncjob(self, outname=None,
                        tooldia=None, offset=None,
                        z_cut=None, z_move=None,
@@ -4850,8 +4850,13 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                             'or self.options["feedrate_probe"]'
                         ))
 
-            # TODO: The tolerance should not be hard coded. Just for testing.
-            job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=0.0005,
+            job_obj.options['xmin'] = self.options['xmin']
+            job_obj.options['ymin'] = self.options['ymin']
+            job_obj.options['xmax'] = self.options['xmax']
+            job_obj.options['ymax'] = self.options['ymax']
+
+            tol = float(self.app.defaults['global_tolerance'])
+            job_obj.generate_from_geometry_2(self, tooldia=tooldia, offset=offset, tolerance=tol,
                                              z_cut=z_cut, z_move=z_move,
                                              feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
                                              spindlespeed=spindlespeed, dwell=dwell, dwelltime=dwelltime,
@@ -5379,7 +5384,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
             self.ui.cnc_tools_table.setItem(row_no, 3, type_item)  # Toolpath Type
             self.ui.cnc_tools_table.setItem(row_no, 4, tool_type_item)  # Tool Type
 
-            ### REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY ###
+            # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
             self.ui.cnc_tools_table.setItem(row_no, 5, tool_uid_item)  # Tool unique ID)
             self.ui.cnc_tools_table.setCellWidget(row_no, 6, plot_item)
 
@@ -5464,6 +5469,15 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         # set the kind of geometries are plotted by default with plot2() from camlib.CNCJob
         self.ui.cncplot_method_combo.set_value(self.app.defaults["cncjob_plot_kind"])
 
+        try:
+            self.ui.annotation_cb.stateChanged.disconnect(self.on_annotation_change)
+        except:
+            pass
+        self.ui.annotation_cb.stateChanged.connect(self.on_annotation_change)
+
+        # set if to display text annotations
+        self.ui.annotation_cb.set_value(self.app.defaults["cncjob_annotation"])
+
         # Show/Hide Advanced Options
         if self.app.defaults["global_app_level"] == 'b':
             self.ui.level.setText(_(
@@ -5913,6 +5927,14 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
             self.shapes.clear(update=True)
             self.annotation.clear(update=True)
 
+    def on_annotation_change(self):
+        if self.ui.annotation_cb.get_value():
+            self.app.plotcanvas.text_collection.enabled = True
+        else:
+            self.app.plotcanvas.text_collection.enabled = False
+        kind = self.ui.cncplot_method_combo.get_value()
+        self.plot(kind=kind)
+
     def convert_units(self, units):
         factor = CNCjob.convert_units(self, units)
         FlatCAMApp.App.log.debug("FlatCAMCNCjob.convert_units()")

+ 2 - 2
FlatCAMPostProc.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Matthieu Berthomé                           #
 # Date: 5/26/2017                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from importlib.machinery import SourceFileLoader
 import os

+ 2 - 2
FlatCAMProcess.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from flatcamGUI.FlatCAMGUI import FlatCAMActivityView
 from PyQt5 import QtCore

+ 2 - 2
FlatCAMTool.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets
 from PyQt5.QtCore import Qt

+ 2 - 2
FlatCAMTranslation.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 import os
 import sys

+ 2 - 2
FlatCAMWorker.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from PyQt5 import QtCore
 

+ 26 - 26
ObjectCollection.py

@@ -1,14 +1,14 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
-############################################################
+# ########################################################## ##
 # File modified by: Dennis Hayrullin                       #
-############################################################
+# ########################################################## ##
 
 # from PyQt5.QtCore import QModelIndex
 from FlatCAMObj import *
@@ -20,9 +20,9 @@ from PyQt5.QtCore import Qt
 
 import gettext
 import FlatCAMTranslation as fcTranslate
+import builtins
 
 fcTranslate.apply_language('strings')
-import builtins
 if '_' not in builtins.__dict__:
     _ = gettext.gettext
 
@@ -210,7 +210,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
 
         QtCore.QAbstractItemModel.__init__(self)
 
-        ### Icons for the list view
+        # ## Icons for the list view
         self.icons = {}
         for kind in ObjectCollection.icon_files:
             self.icons[kind] = QtGui.QPixmap(ObjectCollection.icon_files[kind])
@@ -230,7 +230,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
         #     print i.data(0)
         #     i.append_child(TreeItem(["empty"]))
 
-        ### Data ###
+        # ## Data # ##
         self.checked_indexes = []
 
         # Names of objects that are expected to become available.
@@ -245,7 +245,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
 
         self.app = app
 
-        ### View
+        # ## View
         self.view = KeySensitiveListView(app)
         self.view.setModel(self)
 
@@ -261,7 +261,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
         font.setFamily("Seagoe UI")
         self.view.setFont(font)
 
-        ## GUI Events
+        # ## GUI Events
         self.view.selectionModel().selectionChanged.connect(self.on_list_selection_change)
         self.view.activated.connect(self.on_item_activated)
         # self.view.keyPressed.connect(self.on_key)
@@ -399,11 +399,11 @@ class ObjectCollection(QtCore.QAbstractItemModel):
         if index.isValid():
             obj = index.internalPointer().obj
             if obj:
-                old_name = str(obj.options['name'])
+                old_name = obj.options['name']
                 new_name = str(data)
                 if old_name != new_name and new_name != '':
                     # rename the object
-                    obj.options["name"] = str(data)
+                    obj.options["name"] = deepcopy(data)
 
                     # update the SHELL auto-completer model data
                     try:
@@ -411,11 +411,12 @@ class ObjectCollection(QtCore.QAbstractItemModel):
                         self.app.myKeywords.append(new_name)
                         self.app.shell._edit.set_model_data(self.app.myKeywords)
                         self.app.ui.code_editor.set_model_data(self.app.myKeywords)
-                    except:
+                    except Exception as e:
                         log.debug(
-                            "setData() --> Could not remove the old object name from auto-completer model list")
+                            "setData() --> Could not remove the old object name from auto-completer model list. %s" %
+                            str(e))
 
-                    obj.build_ui()
+                    # obj.build_ui()
                     self.app.inform.emit(_("Object renamed from <b>{old}</b> to <b>{new}</b>").format(old=old_name,
                                                                                                       new=new_name))
 
@@ -452,7 +453,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
 
         # Prevent same name
         while name in self.get_names():
-            ## Create a new name
+            # ## Create a new name
             # Ends with number?
             FlatCAMApp.App.log.debug("new_object(): Object name (%s) exists, changing." % name)
             match = re.search(r'(.*[^\d])?(\d+)$', name)
@@ -525,8 +526,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
                 ymin = min([ymin, gymin])
                 xmax = max([xmax, gxmax])
                 ymax = max([ymax, gymax])
-            except:
-                FlatCAMApp.App.log.warning("DEV WARNING: Tried to get bounds of empty geometry.")
+            except Exception as e:
+                FlatCAMApp.App.log.warning("DEV WARNING: Tried to get bounds of empty geometry. %s" % str(e))
 
         return [xmin, ymin, xmax, ymax]
 
@@ -536,12 +537,12 @@ class ObjectCollection(QtCore.QAbstractItemModel):
 
         :param name: The name of the object.
         :type name: str
+        :param isCaseSensitive: whether searching of the object is done by name where the name is case sensitive
         :return: The requested object or None if no such object.
         :rtype: FlatCAMObj or None
         """
         FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.get_by_name()")
 
-
         if isCaseSensitive is None or isCaseSensitive is True:
             for obj in self.get_list():
                 if obj.options['name'] == name:
@@ -569,10 +570,9 @@ class ObjectCollection(QtCore.QAbstractItemModel):
             self.app.myKeywords.remove(name)
             self.app.shell._edit.set_model_data(self.app.myKeywords)
             self.app.ui.code_editor.set_model_data(self.app.myKeywords)
-        except:
+        except Exception as e:
             log.debug(
-                "delete_active() --> Could not remove the old object name from auto-completer model list")
-
+                "delete_active() --> Could not remove the old object name from auto-completer model list. %s" % str(e))
 
         self.beginRemoveRows(self.index(group.row(), 0, QtCore.QModelIndex()), active.row(), active.row())
 
@@ -649,12 +649,12 @@ class ObjectCollection(QtCore.QAbstractItemModel):
         :return: List of objects
         """
 
-        l = self.get_list()
+        obj_list = self.get_list()
 
         for sel in self.get_selected():
-            l.remove(sel)
+            obj_list.remove(sel)
 
-        return l
+        return obj_list
 
     def set_active(self, name):
         """
@@ -731,8 +731,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
             self.app.inform.emit('')
             try:
                 self.app.ui.selected_scroll_area.takeWidget()
-            except:
-                FlatCAMApp.App.log.debug("Nothing to remove")
+            except Exception as e:
+                FlatCAMApp.App.log.debug("Nothing to remove. %s" % str(e))
 
             self.app.setup_component_editor()
             return

+ 65 - 0
README.md

@@ -9,12 +9,77 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+7.06.2019
+
+- fixed bug in ToolCutout where creating a cutout object geometry from another external isolation geometry failed
+- fixed bug in cncjob TclCommand where the gcode could not be correctly generated due of missing bounds params in obj.options dict
+- fixed a hardcoded tolerance in FlatCAMGeometry.generatecncjob() and in FlatCAMGeometry.mtool_gen_cncjob() to use the parameter from Preferences
+- updated translations
+- RELEASE 8.918
+
+5.06.2019
+
+- updated translations
+- some layout changes in Edit -> Preferences such that the German translation (longer words than English) to fit correctly
+- after editing an parameter the focus is lost so the user knows that something happened
+
+4.06.2019
+
+- PEP8 updates in FlatCAMExcEditor.py
+- added the Excellon Editor parameters to the Edit -> Preferences -> Excellon GUI
+- fixed a small bug in Excellon Editor
+- PEP8 cleanup in FlatCAMGui
+- finished adding the Excellon Editor parameters into the app logic and added a selection limit within Excellon Editor just like in the other editors
+
+3.06.2019
+
+- TclCommand Geocutout is now creating a new geometry object when working on a geometry, preserving also the origin object
+- added a new parameter in Edit -> Preferences -> CNCJob named Annotation Color; it controls the color of the font used for annotations
+- added a new parameter in Edit -> Preferences -> CNCJob named Annotation Size; it controls the size of the font used for annotations
+- made visibility change threaded in FlatCAMObj()
+
+2.06.2019
+
+- fixed issue with geometry name not being updated immediately after change while doing geocutout TclCommand
+- some changes to enable/disable project context menu entry handlers
+
+1.06.2019
+
+- fixed text annotation for CNC job so there are no overlapping numbers when 2 lines meet on the same point
+- fixed issue in CNC job plotting where some of the isolation polygons are painted incorrectly
+- fixed issue in CNCJob where the set circle steps is not used 
+
+31.05.2019
+
+- added the possibility to display text annotation for the CNC travel lines. The setting is both in Preferences and in the CNC object properties
+
+30.05.2019
+
+- editing a multi geometry will no longer pop-up a Tcl window
+- solved issue #292 where a new geometry renamed with many underscores failed to store the name in a saved project
+- the name for the saved projects are updated to the current time and not to the time of the app startup
+- some PEP8 changes related to comments starting with only one '#' symbol
+- more PEP8 cleanup
+- solved issue where after the opening of an object the file path is not saved for further open operations
+
+24.05.2019
+
+- added a toggle Grid button to the canvas context menu in the Grids submenu
+- added a toggle left panel button to the canvas context menu
+
+23.05.2019
+
+- fixed bug in Gerber editor FCDisk and FCSemiDisc that the resulting geometry was not stored into the '0' aperture where all the solids are stored
+- fixed minor issue in Gerber Editor where apertures were included in the saved object even if there was no geometric data for that aperture
+- some PEP8 cleanup in FlatCAMApp.py
+
 22.05.2019
 
 - Geo Editor - added a new editor tool, Eraser
 - some PEP8 cleanup of the Geo Editor
 - fixed some selection issues in the new tool Eraser in Geometry Editor
 - updated the translation files
+- RELEASE 8.917
 
 21.05.2019
 

File diff suppressed because it is too large
+ 142 - 157
camlib.py


+ 221 - 170
flatcamEditors/FlatCAMExcEditor.py

@@ -16,9 +16,9 @@ from copy import copy, deepcopy
 
 import gettext
 import FlatCAMTranslation as fcTranslate
+import builtins
 
 fcTranslate.apply_language('strings')
-import builtins
 if '_' not in builtins.__dict__:
     _ = gettext.gettext
 
@@ -34,9 +34,9 @@ class FCDrillAdd(FCShapeTool):
 
         self.selected_dia = None
         try:
-            self.draw_app.app.inform.emit(self.start_msg)
-            # self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.tools_table_exc.currentRow() + 1]
+            self.draw_app.app.inform.emit(_("Click to place ..."))
             self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.last_tool_selected]
+
             # as a visual marker, select again in tooltable the actual tool that we are using
             # remember that it was deselected when clicking on canvas
             item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
@@ -140,7 +140,7 @@ class FCDrillArray(FCShapeTool):
         self.pt = []
 
         try:
-            self.draw_app.app.inform.emit(self.start_msg)
+            self.draw_app.app.inform.emit(_("Click to place ..."))
             self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.last_tool_selected]
             # as a visual marker, select again in tooltable the actual tool that we are using
             # remember that it was deselected when clicking on canvas
@@ -204,7 +204,7 @@ class FCDrillArray(FCShapeTool):
                     _("[ERROR_NOTCL] The value is not Float. Check for comma instead of dot separator."))
                 return
         except Exception as e:
-            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] The value is mistyped. Check the value."))
+            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] The value is mistyped. Check the value. %s") % str(e))
             return
 
         if self.drill_array == 'Linear':
@@ -350,7 +350,9 @@ class FCDrillResize(FCShapeTool):
         try:
             new_dia = self.draw_app.resdrill_entry.get_value()
         except:
-            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] Resize drill(s) failed. Please enter a diameter for resize."))
+            self.draw_app.app.inform.emit(
+                _("[ERROR_NOTCL] Resize drill(s) failed. Please enter a diameter for resize.")
+            )
             return
 
         if new_dia not in self.draw_app.olddia_newdia:
@@ -406,7 +408,7 @@ class FCDrillResize(FCShapeTool):
                         if new_dia not in self.draw_app.points_edit:
                             self.draw_app.points_edit[new_dia] = [(0, 0)]
                         else:
-                            self.draw_app.points_edit[new_dia].append((0,0))
+                            self.draw_app.points_edit[new_dia].append((0, 0))
                         self.geometry = []
 
                         # if following the resize of the drills there will be no more drills for the selected tool then
@@ -416,7 +418,6 @@ class FCDrillResize(FCShapeTool):
 
             for shp in sel_shapes_to_be_deleted:
                 self.draw_app.selected.remove(shp)
-            sel_shapes_to_be_deleted = []
 
             self.draw_app.build_ui()
             self.draw_app.replot()
@@ -440,6 +441,8 @@ class FCDrillMove(FCShapeTool):
         # self.shape_buffer = self.draw_app.shape_buffer
         self.origin = None
         self.destination = None
+        self.sel_limit = self.draw_app.app.defaults["excellon_editor_sel_limit"]
+        self.selection_shape = self.selection_bbox()
         self.selected_dia_list = []
 
         if self.draw_app.launched_from_shortcuts is True:
@@ -503,6 +506,25 @@ class FCDrillMove(FCShapeTool):
         self.draw_app.build_ui()
         self.draw_app.app.inform.emit(_("[success] Done. Drill(s) Move completed."))
 
+    def selection_bbox(self):
+        geo_list = []
+        for select_shape in self.draw_app.get_selected():
+            geometric_data = select_shape.geo
+            try:
+                for g in geometric_data:
+                    geo_list.append(g)
+            except TypeError:
+                geo_list.append(geometric_data)
+
+        xmin, ymin, xmax, ymax = get_shapely_list_bounds(geo_list)
+
+        pt1 = (xmin, ymin)
+        pt2 = (xmax, ymin)
+        pt3 = (xmax, ymax)
+        pt4 = (xmin, ymax)
+
+        return Polygon([pt1, pt2, pt3, pt4])
+
     def utility_geometry(self, data=None):
         """
         Temporary geometry on screen while using this tool.
@@ -520,9 +542,22 @@ class FCDrillMove(FCShapeTool):
 
         dx = data[0] - self.origin[0]
         dy = data[1] - self.origin[1]
-        for geom in self.draw_app.get_selected():
-            geo_list.append(affinity.translate(geom.geo, xoff=dx, yoff=dy))
-        return DrawToolUtilityShape(geo_list)
+
+        if len(self.draw_app.get_selected()) <= self.sel_limit:
+            try:
+                for geom in self.draw_app.get_selected():
+                    geo_list.append(affinity.translate(geom.geo, xoff=dx, yoff=dy))
+            except AttributeError:
+                self.draw_app.select_tool('drill_select')
+                self.draw_app.selected = []
+                return
+            return DrawToolUtilityShape(geo_list)
+        else:
+            try:
+                ss_el = affinity.translate(self.selection_shape, xoff=dx, yoff=dy)
+            except ValueError:
+                ss_el = None
+            return DrawToolUtilityShape(ss_el)
 
 
 class FCDrillCopy(FCDrillMove):
@@ -595,8 +630,8 @@ class FCDrillSelect(DrawTool):
 
         try:
             for storage in self.exc_editor_app.storage_dict:
-                for shape in self.exc_editor_app.storage_dict[storage].get_objects():
-                    self.sel_storage.insert(shape)
+                for sh in self.exc_editor_app.storage_dict[storage].get_objects():
+                    self.sel_storage.insert(sh)
 
             _, closest_shape = self.sel_storage.nearest(pos)
 
@@ -718,19 +753,18 @@ class FlatCAMExcEditor(QtCore.QObject):
     draw_shape_idx = -1
 
     def __init__(self, app):
-        assert isinstance(app, FlatCAMApp.App), \
-            "Expected the app to be a FlatCAMApp.App, got %s" % type(app)
+        assert isinstance(app, FlatCAMApp.App), "Expected the app to be a FlatCAMApp.App, got %s" % type(app)
 
         super(FlatCAMExcEditor, self).__init__()
 
         self.app = app
         self.canvas = self.app.plotcanvas
 
-        ## Current application units in Upper Case
+        # ## Current application units in Upper Case
         self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
 
         self.exc_edit_widget = QtWidgets.QWidget()
-        ## Box for custom widgets
+        # ## Box for custom widgets
         # This gets populated in offspring implementations.
         layout = QtWidgets.QVBoxLayout()
         self.exc_edit_widget.setLayout(layout)
@@ -744,22 +778,22 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.tools_box.setContentsMargins(0, 0, 0, 0)
         self.drills_frame.setLayout(self.tools_box)
 
-        ## Page Title box (spacing between children)
+        # ## Page Title box (spacing between children)
         self.title_box = QtWidgets.QHBoxLayout()
         self.tools_box.addLayout(self.title_box)
 
-        ## Page Title icon
+        # ## Page Title icon
         pixmap = QtGui.QPixmap('share/flatcam_icon32.png')
         self.icon = QtWidgets.QLabel()
         self.icon.setPixmap(pixmap)
         self.title_box.addWidget(self.icon, stretch=0)
 
-        ## Title label
+        # ## Title label
         self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Excellon Editor'))
         self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
         self.title_box.addWidget(self.title_label, stretch=1)
 
-        ## Object name
+        # ## Object name
         self.name_box = QtWidgets.QHBoxLayout()
         self.tools_box.addLayout(self.name_box)
         name_label = QtWidgets.QLabel(_("Name:"))
@@ -767,11 +801,11 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.name_entry = FCEntry()
         self.name_box.addWidget(self.name_entry)
 
-        #### Tools Drills ####
+        # ### Tools Drills ## ##
         self.tools_table_label = QtWidgets.QLabel("<b>%s</b>" % _('Tools Table'))
         self.tools_table_label.setToolTip(
-           _( "Tools in this Excellon object\n"
-            "when are used for drilling.")
+           _("Tools in this Excellon object\n"
+             "when are used for drilling.")
         )
         self.tools_box.addWidget(self.tools_table_label)
 
@@ -789,11 +823,11 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.empty_label = QtWidgets.QLabel('')
         self.tools_box.addWidget(self.empty_label)
 
-        #### Add a new Tool ####
+        # ### Add a new Tool ## ##
         self.addtool_label = QtWidgets.QLabel('<b>%s</b>' % _('Add/Delete Tool'))
         self.addtool_label.setToolTip(
             _("Add/Delete a tool to the tool list\n"
-            "for this Excellon object.")
+              "for this Excellon object.")
         )
         self.tools_box.addWidget(self.addtool_label)
 
@@ -802,9 +836,8 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         addtool_entry_lbl = QtWidgets.QLabel(_('Tool Dia:'))
         addtool_entry_lbl.setToolTip(
-        _("Diameter for the new tool")
+            _("Diameter for the new tool")
         )
-        grid1.addWidget(addtool_entry_lbl, 0, 0)
 
         hlay = QtWidgets.QHBoxLayout()
         self.addtool_entry = FCEntry()
@@ -813,11 +846,13 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         self.addtool_btn = QtWidgets.QPushButton(_('Add Tool'))
         self.addtool_btn.setToolTip(
-           _( "Add a new tool to the tool list\n"
-            "with the diameter specified above.")
+           _("Add a new tool to the tool list\n"
+             "with the diameter specified above.")
         )
         self.addtool_btn.setFixedWidth(80)
         hlay.addWidget(self.addtool_btn)
+
+        grid1.addWidget(addtool_entry_lbl, 0, 0)
         grid1.addLayout(hlay, 0, 1)
 
         grid2 = QtWidgets.QGridLayout()
@@ -825,8 +860,8 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         self.deltool_btn = QtWidgets.QPushButton(_('Delete Tool'))
         self.deltool_btn.setToolTip(
-           _( "Delete a tool in the tool list\n"
-            "by selecting a row in the tool table.")
+           _("Delete a tool in the tool list\n"
+             "by selecting a row in the tool table.")
         )
         grid2.addWidget(self.deltool_btn, 0, 1)
 
@@ -839,7 +874,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.resize_box.setContentsMargins(0, 0, 0, 0)
         self.resize_frame.setLayout(self.resize_box)
 
-        #### Resize a  drill ####
+        # ### Resize a  drill ## ##
         self.emptyresize_label = QtWidgets.QLabel('')
         self.resize_box.addWidget(self.emptyresize_label)
 
@@ -882,7 +917,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.array_box.setContentsMargins(0, 0, 0, 0)
         self.array_frame.setLayout(self.array_box)
 
-        #### Add DRILL Array ####
+        # ### Add DRILL Array ## ##
         self.emptyarray_label = QtWidgets.QLabel('')
         self.array_box.addWidget(self.emptyarray_label)
 
@@ -895,7 +930,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.array_type_combo = FCComboBox()
         self.array_type_combo.setToolTip(
            _( "Select the type of drills array to create.\n"
-            "It can be Linear X(Y) or Circular")
+              "It can be Linear X(Y) or Circular")
         )
         self.array_type_combo.addItem(_("Linear"))
         self.array_type_combo.addItem(_("Circular"))
@@ -905,6 +940,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.array_form = QtWidgets.QFormLayout()
         self.array_box.addLayout(self.array_form)
 
+        # Set the number of drill holes in the drill array
         self.drill_array_size_label = QtWidgets.QLabel(_('Nr of drills:'))
         self.drill_array_size_label.setToolTip(
             _("Specify how many drills to be in the array.")
@@ -924,21 +960,22 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.linear_form = QtWidgets.QFormLayout()
         self.linear_box.addLayout(self.linear_form)
 
+        # Linear Drill Array direction
         self.drill_axis_label = QtWidgets.QLabel(_('Direction:'))
         self.drill_axis_label.setToolTip(
             _("Direction on which the linear array is oriented:\n"
-            "- 'X' - horizontal axis \n"
-            "- 'Y' - vertical axis or \n"
-            "- 'Angle' - a custom angle for the array inclination")
+              "- 'X' - horizontal axis \n"
+              "- 'Y' - vertical axis or \n"
+              "- 'Angle' - a custom angle for the array inclination")
         )
         self.drill_axis_label.setFixedWidth(100)
 
         self.drill_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
                                           {'label': 'Y', 'value': 'Y'},
                                           {'label': 'Angle', 'value': 'A'}])
-        self.drill_axis_radio.set_value('X')
         self.linear_form.addRow(self.drill_axis_label, self.drill_axis_radio)
 
+        # Linear Drill Array pitch distance
         self.drill_pitch_label = QtWidgets.QLabel(_('Pitch:'))
         self.drill_pitch_label.setToolTip(
             _("Pitch = Distance between elements of the array.")
@@ -948,12 +985,13 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.drill_pitch_entry = LengthEntry()
         self.linear_form.addRow(self.drill_pitch_label, self.drill_pitch_entry)
 
+        # Linear Drill Array angle
         self.linear_angle_label = QtWidgets.QLabel(_('Angle:'))
         self.linear_angle_label.setToolTip(
            _( "Angle at which the linear array is placed.\n"
-            "The precision is of max 2 decimals.\n"
-            "Min value is: -359.99 degrees.\n"
-            "Max value is:  360.00 degrees.")
+              "The precision is of max 2 decimals.\n"
+              "Min value is: -359.99 degrees.\n"
+              "Max value is:  360.00 degrees.")
         )
         self.linear_angle_label.setFixedWidth(100)
 
@@ -972,7 +1010,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.drill_direction_label = QtWidgets.QLabel(_('Direction:'))
         self.drill_direction_label.setToolTip(
            _( "Direction for circular array."
-            "Can be CW = clockwise or CCW = counter clockwise.")
+              "Can be CW = clockwise or CCW = counter clockwise.")
         )
         self.drill_direction_label.setFixedWidth(100)
 
@@ -981,7 +1019,6 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         self.drill_direction_radio = RadioSet([{'label': 'CW', 'value': 'CW'},
                                                {'label': 'CCW.', 'value': 'CCW'}])
-        self.drill_direction_radio.set_value('CW')
         self.circular_form.addRow(self.drill_direction_label, self.drill_direction_radio)
 
         self.drill_angle_label = QtWidgets.QLabel(_('Angle:'))
@@ -1001,23 +1038,23 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.array_frame.hide()
         self.tools_box.addStretch()
 
-        ## Toolbar events and properties
+        # ## Toolbar events and properties
         self.tools_exc = {
             "drill_select": {"button": self.app.ui.select_drill_btn,
-                       "constructor": FCDrillSelect},
+                             "constructor": FCDrillSelect},
             "drill_add": {"button": self.app.ui.add_drill_btn,
-                    "constructor": FCDrillAdd},
+                          "constructor": FCDrillAdd},
             "drill_array": {"button": self.app.ui.add_drill_array_btn,
-                          "constructor": FCDrillArray},
+                            "constructor": FCDrillArray},
             "drill_resize": {"button": self.app.ui.resize_drill_btn,
-                       "constructor": FCDrillResize},
+                             "constructor": FCDrillResize},
             "drill_copy": {"button": self.app.ui.copy_drill_btn,
-                     "constructor": FCDrillCopy},
+                           "constructor": FCDrillCopy},
             "drill_move": {"button": self.app.ui.move_drill_btn,
-                     "constructor": FCDrillMove},
+                           "constructor": FCDrillMove},
         }
 
-        ### Data
+        # ## Data
         self.active_tool = None
 
         self.in_action = False
@@ -1073,12 +1110,6 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         self.app.ui.exc_move_drill_menuitem.triggered.connect(self.exc_move_drills)
 
-        # Init GUI
-        self.drill_array_size_entry.set_value(5)
-        self.drill_pitch_entry.set_value(2.54)
-        self.drill_angle_entry.set_value(12)
-        self.drill_direction_radio.set_value('CW')
-        self.drill_axis_radio.set_value('X')
         self.exc_obj = None
 
         # VisPy Visuals
@@ -1090,7 +1121,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.shapes.enabled = False
         self.tool_shape.enabled = False
 
-        ## List of selected shapes.
+        # ## List of selected shapes.
         self.selected = []
 
         self.move_timer = QtCore.QTimer()
@@ -1105,6 +1136,8 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.snap_y = None
         self.pos = None
 
+        self.complete = False
+
         def make_callback(thetool):
             def f():
                 self.on_tool_select(thetool)
@@ -1159,15 +1192,13 @@ class FlatCAMExcEditor(QtCore.QObject):
 
     @staticmethod
     def make_storage():
-
-        ## Shape storage.
+        # ## Shape storage.
         storage = FlatCAMRTreeStorage()
         storage.get_points = DrawToolShape.get_pts
 
         return storage
 
     def set_ui(self):
-
         # updated units
         self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
 
@@ -1186,6 +1217,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                     self.points_edit[tool_dia].append(drill['point'])
                 except KeyError:
                     self.points_edit[tool_dia] = [drill['point']]
+
         # update the olddia_newdia dict to make sure we have an updated state of the tool_table
         for key in self.points_edit:
             self.olddia_newdia[key] = key
@@ -1211,6 +1243,15 @@ class FlatCAMExcEditor(QtCore.QObject):
                     tool_dia = float('%.2f' % v['C'])
                 self.tool2tooldia[int(k)] = tool_dia
 
+        # Init GUI
+        self.addtool_entry.set_value(float(self.app.defaults['excellon_editor_newdia']))
+        self.drill_array_size_entry.set_value(int(self.app.defaults['excellon_editor_array_size']))
+        self.drill_axis_radio.set_value(self.app.defaults['excellon_editor_lin_dir'])
+        self.drill_pitch_entry.set_value(float(self.app.defaults['excellon_editor_lin_pitch']))
+        self.linear_angle_spinner.set_value(float(self.app.defaults['excellon_editor_lin_angle']))
+        self.drill_direction_radio.set_value(self.app.defaults['excellon_editor_circ_dir'])
+        self.drill_angle_entry.set_value(float(self.app.defaults['excellon_editor_circ_angle']))
+
     def build_ui(self, first_run=None):
 
         try:
@@ -1231,11 +1272,6 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.edited_obj_name = self.exc_obj.options['name']
         self.name_entry.set_value(self.edited_obj_name)
 
-        if self.units == "IN":
-            self.addtool_entry.set_value(0.039)
-        else:
-            self.addtool_entry.set_value(1.00)
-
         sort_temp = []
 
         for diam in self.olddia_newdia:
@@ -1278,9 +1314,9 @@ class FlatCAMExcEditor(QtCore.QObject):
                 # slot editing not implemented
                 pass
 
-            id = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
-            id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
-            self.tools_table_exc.setItem(self.tool_row, 0, id)  # Tool name/id
+            idd = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
+            idd.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            self.tools_table_exc.setItem(self.tool_row, 0, idd)  # Tool name/id
 
             # Make sure that the drill diameter when in MM is with no more than 2 decimals
             # There are no drill bits in MM with more than 3 decimals diameter
@@ -1375,7 +1411,6 @@ class FlatCAMExcEditor(QtCore.QObject):
             self.tools_table_exc.item(self.tool_row, kl).setFont(font)
             self.tools_table_exc.item(self.tool_row, kl).setForeground(QtGui.QColor(0, 70, 255))
 
-
         # all the tools are selected by default
         self.tools_table_exc.selectColumn(0)
         #
@@ -1458,7 +1493,8 @@ class FlatCAMExcEditor(QtCore.QObject):
         # we add a new entry in the tool2tooldia dict
         self.tool2tooldia[len(self.olddia_newdia)] = tool_dia
 
-        self.app.inform.emit(_("[success] Added new tool with dia: {dia} {units}").format(dia=str(tool_dia), units=str(self.units)))
+        self.app.inform.emit(_("[success] Added new tool with dia: {dia} {units}").format(dia=str(tool_dia),
+                                                                                          units=str(self.units)))
 
         self.build_ui()
 
@@ -1475,7 +1511,6 @@ class FlatCAMExcEditor(QtCore.QObject):
     def on_tool_delete(self, dia=None):
         self.is_modified = True
         deleted_tool_dia_list = []
-        deleted_tool_offset_list = []
 
         try:
             if dia is None or dia is False:
@@ -1516,14 +1551,15 @@ class FlatCAMExcEditor(QtCore.QObject):
                     # delete the tool
                     self.tool2tooldia.pop(tool_to_be_deleted, None)
 
-                    # delete also the drills from points_edit dict just in case we add the tool again, we don't want to show the
-                    # number of drills from before was deleter
+                    # delete also the drills from points_edit dict just in case we add the tool again,
+                    # we don't want to show the number of drills from before was deleter
                     self.points_edit[deleted_tool_dia] = []
-                flag_del = []
 
             self.olddia_newdia.pop(deleted_tool_dia, None)
 
-            self.app.inform.emit(_("[success] Deleted tool with dia: {del_dia} {units}").format(del_dia=str(deleted_tool_dia), units=str(self.units)))
+            self.app.inform.emit(_("[success] Deleted tool with dia: {del_dia} {units}").format(
+                del_dia=str(deleted_tool_dia),
+                units=str(self.units)))
 
         self.replot()
         # self.app.inform.emit("Could not delete selected tool")
@@ -1539,7 +1575,6 @@ class FlatCAMExcEditor(QtCore.QObject):
         # self.tools_table_exc.selectionModel().currentChanged.disconnect()
 
         self.is_modified = True
-        geometry = []
         current_table_dia_edited = None
 
         if self.tools_table_exc.currentItem() is not None:
@@ -1565,7 +1600,7 @@ class FlatCAMExcEditor(QtCore.QObject):
             self.tool2tooldia[key_in_tool2tooldia] = current_table_dia_edited
 
             # update the tool offset
-            modified_offset = self.exc_obj.tool_offset.pop(dia_changed ,None)
+            modified_offset = self.exc_obj.tool_offset.pop(dia_changed, None)
             if modified_offset is not None:
                 self.exc_obj.tool_offset[current_table_dia_edited] = modified_offset
 
@@ -1575,10 +1610,11 @@ class FlatCAMExcEditor(QtCore.QObject):
             factor = current_table_dia_edited / dia_changed
             geometry = []
 
-            for shape in self.storage_dict[dia_changed].get_objects():
-                geometry.append(DrawToolShape(
-                    MultiLineString([affinity.scale(subgeo, xfact=factor, yfact=factor, origin='center')
-                                     for subgeo in shape.geo])))
+            for shape_exc in self.storage_dict[dia_changed].get_objects():
+                scaled_geo = MultiLineString(
+                    [affinity.scale(subgeo, xfact=factor, yfact=factor, origin='center') for subgeo in shape_exc.geo]
+                )
+                geometry.append(DrawToolShape(scaled_geo))
 
                 # add bogus drill points (for total count of drills)
                 for k, v in self.olddia_newdia.items():
@@ -1586,8 +1622,8 @@ class FlatCAMExcEditor(QtCore.QObject):
                         self.points_edit[k].append((0, 0))
                         break
 
-            # search for the oldia that correspond to the newdia and add the drills in it's storage
-            # everything will be sort out later, when the edited excellon is updated
+            # search for the old dia that correspond to the new dia and add the drills in it's storage
+            # everything will be sort out later, when the edited Excellon is updated
             for k, v in self.olddia_newdia.items():
                 if v == current_table_dia_edited:
                     self.add_exc_shape(geometry, self.storage_dict[k])
@@ -1739,7 +1775,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.drills_frame.hide()
 
     def connect_canvas_event_handlers(self):
-        ## Canvas events
+        # ## Canvas events
 
         # first connect to new, then disconnect the old handlers
         # don't ask why but if there is nothing connected I've seen issues
@@ -1833,7 +1869,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         Imports the geometry from the given FlatCAM Excellon object
         into the editor.
 
-        :param fcgeometry: FlatCAMExcellon
+        :param exc_obj: FlatCAMExcellon object
         :return: None
         """
 
@@ -1870,16 +1906,16 @@ class FlatCAMExcEditor(QtCore.QObject):
                 stop_hor_line = ((point.x + (tool_dia / 2)), point.y)
                 start_vert_line = (point.x, (point.y - (tool_dia / 2)))
                 stop_vert_line = (point.x, (point.y + (tool_dia / 2)))
-                shape = MultiLineString([(start_hor_line, stop_hor_line),(start_vert_line, stop_vert_line)])
-                if shape is not None:
-                    self.add_exc_shape(DrawToolShape(shape), storage_elem)
+                shape_geo = MultiLineString([(start_hor_line, stop_hor_line), (start_vert_line, stop_vert_line)])
+                if shape_geo is not None:
+                    self.add_exc_shape(DrawToolShape(shape_geo), storage_elem)
             self.storage_dict[tool_dia] = storage_elem
 
         self.replot()
 
         # add a first tool in the Tool Table but only if the Excellon Object is empty
         if not self.tool2tooldia:
-            self.on_tool_add(tooldia=1.00)
+            self.on_tool_add(tooldia=float(self.app.defaults['excellon_editor_newdia']))
 
     def update_fcexcellon(self, exc_obj):
         """
@@ -1907,7 +1943,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                 # create a tuple with the coordinates (x, y) and add it to the list that is the value of the
                 # edited_points dictionary
                 point = (x_coord, y_coord)
-                if not storage_tooldia in edited_points:
+                if storage_tooldia not in edited_points:
                     edited_points[storage_tooldia] = [point]
                 else:
                     edited_points[storage_tooldia].append(point)
@@ -1964,8 +2000,8 @@ class FlatCAMExcEditor(QtCore.QObject):
         if self.is_modified is True:
             if "_edit" in self.edited_obj_name:
                 try:
-                    id = int(self.edited_obj_name[-1]) + 1
-                    self.edited_obj_name = self.edited_obj_name[:-1] + str(id)
+                    idd = int(self.edited_obj_name[-1]) + 1
+                    self.edited_obj_name = self.edited_obj_name[:-1] + str(idd)
                 except ValueError:
                     self.edited_obj_name += "_1"
             else:
@@ -2035,7 +2071,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                 excellon_obj.create_geometry()
             except KeyError:
                 self.app.inform.emit(
-                   _( "[ERROR_NOTCL] There are no Tools definitions in the file. Aborting Excellon creation.")
+                   _("[ERROR_NOTCL] There are no Tools definitions in the file. Aborting Excellon creation.")
                 )
             except:
                 msg = _("[ERROR] An internal error has ocurred. See shell.\n")
@@ -2116,7 +2152,7 @@ class FlatCAMExcEditor(QtCore.QObject):
 
     def toolbar_tool_toggle(self, key):
         self.options[key] = self.sender().isChecked()
-        if self.options[key] == True:
+        if self.options[key] is True:
             return 1
         else:
             return 0
@@ -2151,7 +2187,7 @@ class FlatCAMExcEditor(QtCore.QObject):
             if self.active_tool is not None and event.button is 1:
                 # Dispatch event to active_tool
                 # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
-                msg = self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
+                self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
 
                 # If it is a shape generating tool
                 if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
@@ -2207,6 +2243,7 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         :param shape: Shape to be added.
         :type shape: DrawToolShape
+        :param storage: object where to store the shapes
         :return: None
         """
         # List of DrawToolShape?
@@ -2221,8 +2258,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         assert shape.geo is not None, \
             "Shape object has empty geometry (None)"
 
-        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
-               not isinstance(shape.geo, list), \
+        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or not isinstance(shape.geo, list), \
             "Shape objects has empty geometry ([])"
 
         if isinstance(shape, DrawToolUtilityShape):
@@ -2251,8 +2287,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         assert shape.geo is not None, \
             "Shape object has empty geometry (None)"
 
-        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
-               not isinstance(shape.geo, list), \
+        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or not isinstance(shape.geo, list), \
             "Shape objects has empty geometry ([])"
 
         if isinstance(shape, DrawToolUtilityShape):
@@ -2318,14 +2353,15 @@ class FlatCAMExcEditor(QtCore.QObject):
             log.warning("Error: %s" % str(e))
             raise
 
-    def draw_selection_area_handler(self, start_pos, end_pos, sel_type):
+    def draw_selection_area_handler(self, start, end, sel_type):
         """
         :param start_pos: mouse position when the selection LMB click was done
         :param end_pos: mouse position when the left mouse button is released
         :param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
-        :type Bool
         :return:
         """
+        start_pos = (start[0], start[1])
+        end_pos = (end[0], end[1])
         poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
 
         self.app.delete_selection_shape()
@@ -2394,7 +2430,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         if self.active_tool is None:
             return
 
-        ### Snap coordinates
+        # ## Snap coordinates
         if self.app.grid_status():
             x, y = self.app.geo_editor.snap(x, y)
             self.app.app_cursor.enabled = True
@@ -2408,7 +2444,7 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         # update the position label in the infobar since the APP mouse event handlers are disconnected
         self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                       "<b>Y</b>: %.4f" % (x, y))
+                                           "<b>Y</b>: %.4f" % (x, y))
 
         if self.pos is None:
             self.pos = (0, 0)
@@ -2417,9 +2453,9 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         # update the reference position label in the infobar since the APP mouse event handlers are disconnected
         self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-                                           "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
 
-        ### Utility geometry (animated)
+        # ## Utility geometry (animated)
         geo = self.active_tool.utility_geometry(data=(x, y))
 
         if isinstance(geo, DrawToolShape) and geo.geo is not None:
@@ -2427,7 +2463,7 @@ class FlatCAMExcEditor(QtCore.QObject):
             self.tool_shape.clear(update=True)
             self.draw_utility_geometry(geo=geo)
 
-        ### Selection area on canvas section ###
+        # ## Selection area on canvas section # ##
         if event.is_dragging == 1 and event.button == 1:
             # I make an exception for FCDrillAdd and FCDrillArray because clicking and dragging while making regions
             # can create strange issues
@@ -2437,12 +2473,12 @@ class FlatCAMExcEditor(QtCore.QObject):
                 dx = pos[0] - self.pos[0]
                 self.app.delete_selection_shape()
                 if dx < 0:
-                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y),
-                         color=self.app.defaults["global_alt_sel_line"],
-                         face_color=self.app.defaults['global_alt_sel_fill'])
+                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
+                                                         color=self.app.defaults["global_alt_sel_line"],
+                                                         face_color=self.app.defaults['global_alt_sel_fill'])
                     self.app.selection_type = False
                 else:
-                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y))
+                    self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y))
                     self.app.selection_type = True
         else:
             self.app.selection_type = None
@@ -2454,43 +2490,41 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.key = None
 
     def draw_utility_geometry(self, geo):
-            # Add the new utility shape
-            try:
-                # this case is for the Font Parse
-                for el in list(geo.geo):
-                    if type(el) == MultiPolygon:
-                        for poly in el:
-                            self.tool_shape.add(
-                                shape=poly,
-                                color=(self.app.defaults["global_draw_color"] + '80'),
-                                update=False,
-                                layer=0,
-                                tolerance=None
-                            )
-                    elif type(el) == MultiLineString:
-                        for linestring in el:
-                            self.tool_shape.add(
-                                shape=linestring,
-                                color=(self.app.defaults["global_draw_color"] + '80'),
-                                update=False,
-                                layer=0,
-                                tolerance=None
-                            )
-                    else:
+        # Add the new utility shape
+        try:
+            # this case is for the Font Parse
+            for el in list(geo.geo):
+                if type(el) == MultiPolygon:
+                    for poly in el:
                         self.tool_shape.add(
-                            shape=el,
+                            shape=poly,
                             color=(self.app.defaults["global_draw_color"] + '80'),
                             update=False,
                             layer=0,
                             tolerance=None
                         )
-            except TypeError:
-                self.tool_shape.add(
-                    shape=geo.geo, color=(self.app.defaults["global_draw_color"] + '80'),
-                    update=False, layer=0, tolerance=None)
-
-            self.tool_shape.redraw()
-
+                elif type(el) == MultiLineString:
+                    for linestring in el:
+                        self.tool_shape.add(
+                            shape=linestring,
+                            color=(self.app.defaults["global_draw_color"] + '80'),
+                            update=False,
+                            layer=0,
+                            tolerance=None
+                        )
+                else:
+                    self.tool_shape.add(
+                        shape=el,
+                        color=(self.app.defaults["global_draw_color"] + '80'),
+                        update=False,
+                        layer=0,
+                        tolerance=None
+                    )
+        except TypeError:
+            self.tool_shape.add(
+                shape=geo.geo, color=(self.app.defaults["global_draw_color"] + '80'),
+                update=False, layer=0, tolerance=None)
+        self.tool_shape.redraw()
 
     def replot(self):
         self.plot_all()
@@ -2526,10 +2560,8 @@ class FlatCAMExcEditor(QtCore.QObject):
         #
         #     self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_draw_color'])
 
-
-
-        for shape in self.utility:
-            self.plot_shape(geometry=shape.geo, linewidth=1)
+        for shape_form in self.utility:
+            self.plot_shape(geometry=shape_form.geo, linewidth=1)
             continue
 
         self.shapes.redraw()
@@ -2553,13 +2585,13 @@ class FlatCAMExcEditor(QtCore.QObject):
             for geo in geometry:
                 plot_elements += self.plot_shape(geometry=geo, color=color, linewidth=linewidth)
 
-        ## Non-iterable
+        # ## Non-iterable
         except TypeError:
-            ## DrawToolShape
+            # ## DrawToolShape
             if isinstance(geometry, DrawToolShape):
                 plot_elements += self.plot_shape(geometry=geometry.geo, color=color, linewidth=linewidth)
 
-            ## Polygon: Descend into exterior and each interior.
+            # ## Polygon: Descend into exterior and each interior.
             if type(geometry) == Polygon:
                 plot_elements += self.plot_shape(geometry=geometry.exterior, color=color, linewidth=linewidth)
                 plot_elements += self.plot_shape(geometry=geometry.interiors, color=color, linewidth=linewidth)
@@ -2604,11 +2636,11 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.build_ui()
         self.app.inform.emit(_("[success] Done. Drill(s) deleted."))
 
-    def delete_shape(self, shape):
+    def delete_shape(self, del_shape):
         self.is_modified = True
 
-        if shape in self.utility:
-            self.utility.remove(shape)
+        if del_shape in self.utility:
+            self.utility.remove(del_shape)
             return
 
         for storage in self.storage_dict:
@@ -2616,8 +2648,8 @@ class FlatCAMExcEditor(QtCore.QObject):
             #     self.storage_dict[storage].remove(shape)
             # except:
             #     pass
-            if shape in self.storage_dict[storage].get_objects():
-                self.storage_dict[storage].remove(shape)
+            if del_shape in self.storage_dict[storage].get_objects():
+                self.storage_dict[storage].remove(del_shape)
                 # a hack to make the tool_table display less drills per diameter
                 # self.points_edit it's only useful first time when we load the data into the storage
                 # but is still used as referecen when building tool_table in self.build_ui()
@@ -2625,15 +2657,13 @@ class FlatCAMExcEditor(QtCore.QObject):
                 # deleting self.points_edit elements (doesn't matter who but just the number) solved the display issue.
                 del self.points_edit[storage][0]
 
-        if shape in self.selected:
-            self.selected.remove(shape)  # TODO: Check performance
+        if del_shape in self.selected:
+            self.selected.remove(del_shape)  # TODO: Check performance
 
     def delete_utility_geometry(self):
-        # for_deletion = [shape for shape in self.shape_buffer if shape.utility]
-        # for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
-        for_deletion = [shape for shape in self.utility]
-        for shape in for_deletion:
-            self.delete_shape(shape)
+        for_deletion = [util_shape for util_shape in self.utility]
+        for util_shape in for_deletion:
+            self.delete_shape(util_shape)
 
         self.tool_shape.clear(update=True)
         self.tool_shape.redraw()
@@ -2652,17 +2682,17 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.tools_exc[toolname]["button"].setChecked(True)
         self.on_tool_select(toolname)
 
-    def set_selected(self, shape):
+    def set_selected(self, sel_shape):
 
         # Remove and add to the end.
-        if shape in self.selected:
-            self.selected.remove(shape)
+        if sel_shape in self.selected:
+            self.selected.remove(sel_shape)
 
-        self.selected.append(shape)
+        self.selected.append(sel_shape)
 
-    def set_unselected(self, shape):
-        if shape in self.selected:
-            self.selected.remove(shape)
+    def set_unselected(self, unsel_shape):
+        if unsel_shape in self.selected:
+            self.selected.remove(unsel_shape)
 
     def on_array_type_combo(self):
         if self.array_type_combo.currentIndex() == 0:
@@ -2701,4 +2731,25 @@ class FlatCAMExcEditor(QtCore.QObject):
 
     def exc_move_drills(self):
         self.select_tool('drill_move')
-        return
+        return
+
+
+def get_shapely_list_bounds(geometry_list):
+    xmin = Inf
+    ymin = Inf
+    xmax = -Inf
+    ymax = -Inf
+
+    for gs in geometry_list:
+        try:
+            gxmin, gymin, gxmax, gymax = gs.bounds
+            xmin = min([xmin, gxmin])
+            ymin = min([ymin, gymin])
+            xmax = max([xmax, gxmax])
+            ymax = max([ymax, gymax])
+        except Exception as e:
+            log.warning("DEVELOPMENT: Tried to get bounds of empty geometry. --> %s" % str(e))
+
+    return [xmin, ymin, xmax, ymax]
+
+# EOF

+ 18 - 21
flatcamEditors/FlatCAMGeoEditor.py

@@ -1,15 +1,15 @@
-# ###########################################################
+# ######################################################### ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-# ###########################################################
+# ######################################################### ##
 
 # ###########################################################                                      #
 # File Modified: Marius Adrian Stanciu (c)                 #
 # Date: 3/10/2019                                          #
-# ###########################################################
+# ######################################################### ##
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtCore import Qt, QSettings
@@ -629,7 +629,7 @@ class TransformEditorTool(FlatCAMTool):
 
         self.transform_lay = QtWidgets.QVBoxLayout()
         self.layout.addLayout(self.transform_lay)
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % (_('Editor %s') % self.toolName))
         title_label.setStyleSheet("""
                 QLabel
@@ -2917,9 +2917,9 @@ class FCTransform(FCShapeTool):
         self.draw_app.transform_tool.run()
 
 
-# #######################
-# ## Main Application ###
-# #######################
+# ##################### ##
+# # ## Main Application # ##
+# ##################### ##
 class FlatCAMGeoEditor(QtCore.QObject):
 
     transform_complete = QtCore.pyqtSignal()
@@ -2935,7 +2935,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         self.app = app
         self.canvas = app.plotcanvas
 
-        ## Toolbar events and properties
+        # ## Toolbar events and properties
         self.tools = {
             "select": {"button": self.app.ui.geo_select_btn,
                        "constructor": FCSelect},
@@ -2965,7 +2965,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
                      "constructor": FCCopy}
         }
 
-        # ## Data
+        # # ## Data
         self.active_tool = None
 
         self.storage = FlatCAMGeoEditor.make_storage()
@@ -3373,10 +3373,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
     def toolbar_tool_toggle(self, key):
         self.options[key] = self.sender().isChecked()
-        if self.options[key] == True:
-            return 1
-        else:
-            return 0
+        return 1 if self.options[key] == True else 0
 
     def clear(self):
         self.active_tool = None
@@ -3416,7 +3413,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         if multigeo_tool:
             self.multigeo_tool = multigeo_tool
             geo_to_edit = fcgeometry.flatten(geometry=fcgeometry.tools[self.multigeo_tool]['solid_geometry'])
-            self.app.inform.emit(_("[WARNING] Editing MultiGeo Geometry, tool: {tool} with diameter: {dia}").
+            self.app.inform.emit(_("[WARNING_NOTCL] Editing MultiGeo Geometry, tool: {tool} with diameter: {dia}").
                                  format(tool=self.multigeo_tool, dia=fcgeometry.tools[self.multigeo_tool]['tooldia']))
         else:
             geo_to_edit = fcgeometry.flatten()
@@ -3572,7 +3569,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         if self.active_tool is None:
             return
 
-        # ## Snap coordinates
+        # # ## Snap coordinates
         if self.app.grid_status():
             x, y = self.snap(x, y)
             self.app.app_cursor.enabled = True
@@ -3600,14 +3597,14 @@ class FlatCAMGeoEditor(QtCore.QObject):
         if event.button == 1 and event.is_dragging == 1 and isinstance(self.active_tool, FCEraser):
             pass
         else:
-            # ## Utility geometry (animated)
+            # # ## Utility geometry (animated)
             geo = self.active_tool.utility_geometry(data=(x, y))
             if isinstance(geo, DrawToolShape) and geo.geo is not None:
                 # Remove any previous utility shape
                 self.tool_shape.clear(update=True)
                 self.draw_utility_geometry(geo=geo)
 
-        # ## Selection area on canvas section ###
+        # # ## Selection area on canvas section # ##
         dx = pos[0] - self.pos[0]
         if event.is_dragging == 1 and event.button == 1:
             self.app.delete_selection_shape()
@@ -3941,9 +3938,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
         snap_x, snap_y = (x, y)
         snap_distance = Inf
 
-        # ## Object (corner?) snap
-        # ## No need for the objects, just the coordinates
-        # ## in the index.
+        # # ## Object (corner?) snap
+        # # ## No need for the objects, just the coordinates
+        # # ## in the index.
         if self.options["corner_snap"]:
             try:
                 nearest_pt, shape = self.storage.nearest((x, y))
@@ -3955,7 +3952,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
             except (StopIteration, AssertionError):
                 pass
 
-        # ## Grid snap
+        # # ## Grid snap
         if self.options["grid_snap"]:
             if self.options["global_gridx"] != 0:
                 snap_x_ = round(x / self.options["global_gridx"]) * self.options['global_gridx']

+ 40 - 18
flatcamEditors/FlatCAMGrbEditor.py

@@ -52,7 +52,7 @@ class DrawToolShape(object):
         """
         pts = []
 
-        ## Iterable: descend into each item.
+        # ## Iterable: descend into each item.
         try:
             for sub_o in o:
                 pts += DrawToolShape.get_pts(sub_o)
@@ -64,7 +64,7 @@ class DrawToolShape(object):
                 if isinstance(o, DrawToolShape):
                     pts += DrawToolShape.get_pts(o.geo)
 
-                ## Descend into .exerior and .interiors
+                # ## Descend into .exerior and .interiors
                 elif type(o) == Polygon:
                     pts += DrawToolShape.get_pts(o.exterior)
                     for i in o.interiors:
@@ -72,7 +72,7 @@ class DrawToolShape(object):
                 elif type(o) == MultiLineString:
                     for line in o:
                         pts += DrawToolShape.get_pts(line)
-                ## Has .coords: list them.
+                # ## Has .coords: list them.
                 else:
                     if DrawToolShape.tolerance is not None:
                         pts += list(o.simplify(DrawToolShape.tolerance).coords)
@@ -1349,7 +1349,14 @@ class FCDisc(FCShapeTool):
         size_ap = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size'])
         self.buf_val = (size_ap / 2) if size_ap > 0 else 0.0000001
 
-        self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['geometry']
+        if '0' in self.draw_app.storage_dict:
+            self.storage_obj = self.draw_app.storage_dict['0']['geometry']
+        else:
+            self.draw_app.storage_dict['0'] = dict()
+            self.draw_app.storage_dict['0']['type'] = 'C'
+            self.draw_app.storage_dict['0']['size'] = 0.0
+            self.draw_app.storage_dict['0']['geometry'] = list()
+            self.storage_obj = self.draw_app.storage_dict['0']['geometry']
 
         self.draw_app.app.inform.emit(_("Click on Center point ..."))
 
@@ -1436,7 +1443,14 @@ class FCSemiDisc(FCShapeTool):
         size_ap = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size'])
         self.buf_val = (size_ap / 2) if size_ap > 0 else 0.0000001
 
-        self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['geometry']
+        if '0' in self.draw_app.storage_dict:
+            self.storage_obj = self.draw_app.storage_dict['0']['geometry']
+        else:
+            self.draw_app.storage_dict['0'] = dict()
+            self.draw_app.storage_dict['0']['type'] = 'C'
+            self.draw_app.storage_dict['0']['size'] = 0.0
+            self.draw_app.storage_dict['0']['geometry'] = list()
+            self.storage_obj = self.draw_app.storage_dict['0']['geometry']
 
         self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"]
 
@@ -2050,7 +2064,9 @@ class FCEraser(FCShapeTool):
                     if 'solid' in geo_el.geo:
                         geometric_data = geo_el.geo['solid']
                         if eraser_sel_shapes.within(geometric_data) or eraser_sel_shapes.intersects(geometric_data):
-                            geo_el.geo['solid'] = geometric_data.difference(eraser_sel_shapes)
+                            geos = geometric_data.difference(eraser_sel_shapes)
+                            geos = geos.buffer(0)
+                            geo_el.geo['solid'] = deepcopy(geos)
             except KeyError:
                 pass
 
@@ -2258,7 +2274,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         layout.addLayout(self.custom_box)
 
 
-        # ### Gerber Apertures ####
+        # # ## Gerber Apertures ## ##
         self.apertures_table_label = QtWidgets.QLabel(_('<b>Apertures:</b>'))
         self.apertures_table_label.setToolTip(
             _("Apertures Table for the Gerber Object.")
@@ -2300,7 +2316,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.apertures_box.setContentsMargins(0, 0, 0, 0)
         self.apertures_frame.setLayout(self.apertures_box)
 
-        # ### Add/Delete an new Aperture ####
+        # # ## Add/Delete an new Aperture ## ##
 
         grid1 = QtWidgets.QGridLayout()
         self.apertures_box.addLayout(grid1)
@@ -2374,7 +2390,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         hlay_ad.addWidget(self.addaperture_btn)
         hlay_ad.addWidget(self.delaperture_btn)
 
-        # ## BUFFER TOOL ###
+        # # ## BUFFER TOOL # ##
 
         self.buffer_tool_frame = QtWidgets.QFrame()
         self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
@@ -2418,7 +2434,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.buffer_button = QtWidgets.QPushButton(_("Buffer"))
         hlay_buf.addWidget(self.buffer_button)
 
-        # ## SCALE TOOL ###
+        # # ## SCALE TOOL # ##
 
         self.scale_tool_frame = QtWidgets.QFrame()
         self.scale_tool_frame.setContentsMargins(0, 0, 0, 0)
@@ -2465,7 +2481,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.array_box.setContentsMargins(0, 0, 0, 0)
         self.array_frame.setLayout(self.array_box)
 
-        # ### Add Pad Array ####
+        # # ## Add Pad Array ## ##
         self.emptyarray_label = QtWidgets.QLabel('')
         self.array_box.addWidget(self.emptyarray_label)
 
@@ -2617,7 +2633,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
                      "constructor": FCApertureMove},
         }
 
-        # ## Data
+        # # ## Data
         self.active_tool = None
 
         self.storage_dict = {}
@@ -3463,9 +3479,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         self.gerber_obj.apertures = conv_apertures
 
-        # ###############################################################
+        # ############################################################# ##
         # APPLY CLEAR_GEOMETRY on the SOLID_GEOMETRY
-        # ###############################################################
+        # ############################################################# ##
 
         # log.warning("Applying clear geometry in the apertures dict.")
         # list of clear geos that are to be applied to the entire file
@@ -3621,7 +3637,13 @@ class FlatCAMGrbEditor(QtCore.QObject):
                            self.gerber_obj.options['name'].upper())
 
         out_name = outname
-        local_storage_dict = deepcopy(self.storage_dict)
+
+        local_storage_dict = dict()
+        for aperture in self.storage_dict:
+            if 'geometry' in self.storage_dict[aperture]:
+                # add aperture only if it has geometry
+                if len(self.storage_dict[aperture]['geometry']) > 0:
+                    local_storage_dict[aperture] = deepcopy(self.storage_dict[aperture])
 
         # How the object should be initialized
         def obj_init(grb_obj, app_obj):
@@ -4057,7 +4079,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         if self.active_tool is None:
             return
 
-        # ## Snap coordinates
+        # # ## Snap coordinates
         if self.app.grid_status():
             x, y = self.app.geo_editor.snap(x, y)
             self.app.app_cursor.enabled = True
@@ -4082,7 +4104,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: " 
                                                "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
 
-        # ## Utility geometry (animated)
+        # # ## Utility geometry (animated)
         geo = self.active_tool.utility_geometry(data=(x, y))
 
         if isinstance(geo, DrawToolShape) and geo.geo is not None:
@@ -4090,7 +4112,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
             self.tool_shape.clear(update=True)
             self.draw_utility_geometry(geo=geo)
 
-        # ## Selection area on canvas section ###
+        # # ## Selection area on canvas section # ##
         if event.is_dragging == 1 and event.button == 1:
             # I make an exception for FCRegion and FCTrack because clicking and dragging while making regions can
             # create strange issues like missing a point in a track/region

File diff suppressed because it is too large
+ 167 - 171
flatcamGUI/FlatCAMGUI.py


+ 44 - 4
flatcamGUI/GUIElements.py

@@ -1,15 +1,15 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
-############################################################
+# ########################################################## ##
 # File Modified (major mod): Marius Adrian Stanciu         #
 # Date: 3/10/2019                                          #
-############################################################
+# ########################################################## ##
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
@@ -159,6 +159,10 @@ class LengthEntry(QtWidgets.QLineEdit):
                    'MM': 1.0}
         }
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, Parent=None):
         super(LengthEntry, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -209,6 +213,10 @@ class FloatEntry(QtWidgets.QLineEdit):
     def __init__(self, parent=None):
         super(FloatEntry, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, Parent=None):
         super(FloatEntry, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -256,6 +264,10 @@ class FloatEntry2(QtWidgets.QLineEdit):
     def __init__(self, parent=None):
         super(FloatEntry2, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, Parent=None):
         super(FloatEntry2, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -295,6 +307,10 @@ class IntEntry(QtWidgets.QLineEdit):
         self.allow_empty = allow_empty
         self.empty_val = empty_val
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, Parent=None):
         super(IntEntry, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -335,6 +351,10 @@ class FCEntry(QtWidgets.QLineEdit):
     def __init__(self, parent=None):
         super(FCEntry, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, Parent=None):
         super(FCEntry, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -365,6 +385,10 @@ class FCEntry2(FCEntry):
     def __init__(self, parent=None):
         super(FCEntry2, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def set_value(self, val):
         try:
@@ -378,6 +402,10 @@ class EvalEntry(QtWidgets.QLineEdit):
     def __init__(self, parent=None):
         super(EvalEntry, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, Parent=None):
         super(EvalEntry, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -420,6 +448,10 @@ class EvalEntry2(QtWidgets.QLineEdit):
     def __init__(self, parent=None):
         super(EvalEntry2, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, Parent=None):
         super(EvalEntry2, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -1462,6 +1494,10 @@ class FCSpinner(QtWidgets.QSpinBox):
     def __init__(self, parent=None):
         super(FCSpinner, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, parent=None):
         super(FCSpinner, self).mousePressEvent(e)  # required to deselect on 2e click
@@ -1497,6 +1533,10 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
     def __init__(self, parent=None):
         super(FCDoubleSpinner, self).__init__(parent)
         self.readyToEdit = True
+        self.editingFinished.connect(self.on_edit_finished)
+
+    def on_edit_finished(self):
+        self.clearFocus()
 
     def mousePressEvent(self, e, parent=None):
         super(FCDoubleSpinner, self).mousePressEvent(e)  # required to deselect on 2e click

+ 50 - 37
flatcamGUI/ObjectUI.py

@@ -1,15 +1,15 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
-############################################################
+# ########################################################## ##
 # File Modified (major mod): Marius Adrian Stanciu         #
 # Date: 3/10/2019                                          #
-############################################################
+# ########################################################## ##
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtCore import Qt
@@ -36,22 +36,22 @@ class ObjectUI(QtWidgets.QWidget):
         layout = QtWidgets.QVBoxLayout()
         self.setLayout(layout)
 
-        ## Page Title box (spacing between children)
+        # ## Page Title box (spacing between children)
         self.title_box = QtWidgets.QHBoxLayout()
         layout.addLayout(self.title_box)
 
-        ## Page Title icon
+        # ## Page Title icon
         pixmap = QtGui.QPixmap(icon_file)
         self.icon = QtWidgets.QLabel()
         self.icon.setPixmap(pixmap)
         self.title_box.addWidget(self.icon, stretch=0)
 
-        ## Title label
+        # ## Title label
         self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % title)
         self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
         self.title_box.addWidget(self.title_label, stretch=1)
 
-        ## App Level label
+        # ## App Level label
         self.level = QtWidgets.QLabel("")
         self.level.setToolTip(
             _(
@@ -66,16 +66,16 @@ class ObjectUI(QtWidgets.QWidget):
         self.level.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         self.title_box.addWidget(self.level)
 
-        ## Box box for custom widgets
+        # ## Box box for custom widgets
         # This gets populated in offspring implementations.
         self.custom_box = QtWidgets.QVBoxLayout()
         layout.addLayout(self.custom_box)
 
-        ###########################
-        ## Common to all objects ##
-        ###########################
+        ######################### ##
+        # ## Common to all objects # ##
+        ######################### ##
 
-        #### Scale ####
+        #### Scale ## ##
         self.scale_label = QtWidgets.QLabel(_('<b>Scale:</b>'))
         self.scale_label.setToolTip(
             _("Change the size of the object.")
@@ -104,7 +104,7 @@ class ObjectUI(QtWidgets.QWidget):
         self.scale_button.setFixedWidth(70)
         self.scale_grid.addWidget(self.scale_button, 0, 2)
 
-        #### Offset ####
+        #### Offset ## ##
         self.offset_label = QtWidgets.QLabel(_('<b>Offset:</b>'))
         self.offset_label.setToolTip(
             _("Change the position of this object.")
@@ -176,7 +176,7 @@ class GerberObjectUI(ObjectUI):
         self.plot_cb.setFixedWidth(59)
         grid0.addWidget(self.plot_cb, 0, 3)
 
-        ## Object name
+        # ## Object name
         self.name_hlay = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(self.name_hlay)
         name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
@@ -188,7 +188,7 @@ class GerberObjectUI(ObjectUI):
         hlay_plot = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(hlay_plot)
 
-        #### Gerber Apertures ####
+        #### Gerber Apertures ## ##
         self.apertures_table_label = QtWidgets.QLabel(_('<b>Apertures:</b>'))
         self.apertures_table_label.setToolTip(
             _("Apertures Table for the Gerber Object.")
@@ -380,7 +380,7 @@ class GerberObjectUI(ObjectUI):
         grid2 = QtWidgets.QGridLayout()
         self.custom_box.addLayout(grid2)
 
-        ## Clear non-copper regions
+        # ## Clear non-copper regions
         self.clearcopper_label = QtWidgets.QLabel(_("<b>Clear N-copper:</b>"))
         self.clearcopper_label.setToolTip(
             _("Create a Geometry object with\n"
@@ -396,7 +396,7 @@ class GerberObjectUI(ObjectUI):
         )
         grid2.addWidget(self.generate_ncc_button, 0, 1)
 
-        ## Board cutout
+        # ## Board cutout
         self.board_cutout_label = QtWidgets.QLabel(_("<b>Board cutout:</b>"))
         self.board_cutout_label.setToolTip(
             _("Create toolpaths to cut around\n"
@@ -412,7 +412,7 @@ class GerberObjectUI(ObjectUI):
         )
         grid2.addWidget(self.generate_cutout_button, 1, 1)
 
-        ## Non-copper regions
+        # ## Non-copper regions
         self.noncopper_label = QtWidgets.QLabel(_("<b>Non-copper regions:</b>"))
         self.noncopper_label.setToolTip(
             _("Create polygons covering the\n"
@@ -450,7 +450,7 @@ class GerberObjectUI(ObjectUI):
         self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
         grid4.addWidget(self.generate_noncopper_button, 1, 1)
 
-        ## Bounding box
+        # ## Bounding box
         self.boundingbox_label = QtWidgets.QLabel(_('<b>Bounding Box:</b>'))
         self.boundingbox_label.setToolTip(
             _("Create a geometry surrounding the Gerber object.\n"
@@ -498,7 +498,7 @@ class ExcellonObjectUI(ObjectUI):
                           icon_file='share/drill32.png',
                           parent=parent)
 
-        #### Plot options ####
+        #### Plot options ## ##
         hlay_plot = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(hlay_plot)
 
@@ -511,7 +511,7 @@ class ExcellonObjectUI(ObjectUI):
         hlay_plot.addStretch()
         hlay_plot.addWidget(self.solid_cb)
 
-        ## Object name
+        # ## Object name
         self.name_hlay = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(self.name_hlay)
         name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
@@ -532,7 +532,7 @@ class ExcellonObjectUI(ObjectUI):
         hlay_plot = QtWidgets.QHBoxLayout()
         self.tools_box.addLayout(hlay_plot)
 
-        #### Tools Drills ####
+        #### Tools Drills ## ##
         self.tools_table_label = QtWidgets.QLabel(_('<b>Tools Table</b>'))
         self.tools_table_label.setToolTip(
             _("Tools in this Excellon object\n"
@@ -580,7 +580,7 @@ class ExcellonObjectUI(ObjectUI):
         self.empty_label = QtWidgets.QLabel('')
         self.tools_box.addWidget(self.empty_label)
 
-        #### Create CNC Job ####
+        #### Create CNC Job ## ##
         self.cncjob_label = QtWidgets.QLabel(_('<b>Create CNC Job</b>'))
         self.cncjob_label.setToolTip(
             _("Create a CNC Job object\n"
@@ -768,7 +768,7 @@ class ExcellonObjectUI(ObjectUI):
         )
         self.tools_box.addWidget(self.generate_cnc_button)
 
-        #### Milling Holes Drills####
+        #### Milling Holes Drills## ##
         self.mill_hole_label = QtWidgets.QLabel(_('<b>Mill Holes</b>'))
         self.mill_hole_label.setToolTip(
             _("Create Geometry for milling holes.")
@@ -833,7 +833,7 @@ class GeometryObjectUI(ObjectUI):
         self.plot_options_label = QtWidgets.QLabel(_("<b>Plot Options:</b>"))
         self.custom_box.addWidget(self.plot_options_label)
 
-        ## Object name
+        # ## Object name
         self.name_hlay = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(self.name_hlay)
         name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
@@ -854,7 +854,7 @@ class GeometryObjectUI(ObjectUI):
         hlay_plot = QtWidgets.QHBoxLayout()
         self.geo_tools_box.addLayout(hlay_plot)
 
-        #### Tools ####
+        #### Tools ## ##
         self.tools_table_label = QtWidgets.QLabel(_('<b>Tools Table</b>'))
         self.tools_table_label.setToolTip(
             _("Tools in this Geometry object used for cutting.\n"
@@ -964,7 +964,7 @@ class GeometryObjectUI(ObjectUI):
         self.grid1.addWidget(self.tool_offset_entry, 0, 1)
         self.grid1.addWidget(spacer_lbl, 0, 2)
 
-        #### Add a new Tool ####
+        #### Add a new Tool ## ##
         hlay = QtWidgets.QHBoxLayout()
         self.geo_tools_box.addLayout(hlay)
 
@@ -1022,7 +1022,7 @@ class GeometryObjectUI(ObjectUI):
         #-----------------------------------
         # Create CNC Job
         #-----------------------------------
-        #### Tools Data ####
+        #### Tools Data ## ##
         self.tool_data_label = QtWidgets.QLabel(_('<b>Tool Data</b>'))
         self.tool_data_label.setToolTip(
             _(
@@ -1346,7 +1346,7 @@ class CNCObjectUI(ObjectUI):
         self.offset_label.hide()
         self.offset_button.hide()
 
-        ## Plot options
+        # ## Plot options
         self.plot_options_label = QtWidgets.QLabel(_("<b>Plot Options:</b>"))
         self.custom_box.addWidget(self.plot_options_label)
 
@@ -1366,7 +1366,17 @@ class CNCObjectUI(ObjectUI):
             {"label": "Cut", "value": "cut"}
         ], stretch=False)
 
-        ## Object name
+        self.annotation_label = QtWidgets.QLabel(_("<b>Display Annotation:</b>"))
+        self.annotation_label.setToolTip(
+            _(
+                "This selects if to display text annotation on the plot.\n"
+                "When checked it will display numbers in order for each end\n"
+                "of a travel line."
+            )
+        )
+        self.annotation_cb = FCCheckBox()
+
+        # ## Object name
         self.name_hlay = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(self.name_hlay)
         name_label = QtWidgets.QLabel(_("<b>Name:</b>"))
@@ -1399,9 +1409,12 @@ class CNCObjectUI(ObjectUI):
         f_lay.addWidget(self.cncplot_method_label, 0, 0)
         f_lay.addWidget(self.cncplot_method_combo, 0, 1)
         f_lay.addWidget(QtWidgets.QLabel(''), 0, 2)
-        f_lay.addWidget(self.t_distance_label, 1, 0)
-        f_lay.addWidget(self.t_distance_entry, 1, 1)
-        f_lay.addWidget(self.units_label, 1, 2)
+        f_lay.addWidget(self.annotation_label, 1, 0)
+        f_lay.addWidget(self.annotation_cb, 1, 1)
+        f_lay.addWidget(QtWidgets.QLabel(''), 1, 2)
+        f_lay.addWidget(self.t_distance_label, 2, 0)
+        f_lay.addWidget(self.t_distance_entry, 2, 1)
+        f_lay.addWidget(self.units_label, 2, 2)
 
         self.t_distance_label.hide()
         self.t_distance_entry.setVisible(False)
@@ -1462,9 +1475,9 @@ class CNCObjectUI(ObjectUI):
         )
         self.custom_box.addWidget(self.updateplot_button)
 
-        ##################
-        ## Export G-Code
-        ##################
+        ################ ##
+        # ## Export G-Code
+        ################ ##
         self.export_gcode_label = QtWidgets.QLabel(_("<b>Export CNC Code:</b>"))
         self.export_gcode_label.setToolTip(
             _("Export and save G-Code to\n"

+ 4 - 4
flatcamGUI/PlotCanvas.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://caram.cl/software/flatcam                         #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from PyQt5 import QtCore
 
@@ -51,7 +51,7 @@ class PlotCanvas(QtCore.QObject):
         self.vispy_canvas.native.setParent(self.app.ui)
         self.container.addWidget(self.vispy_canvas.native)
 
-        ### AXIS ###
+        # ## AXIS # ##
         self.v_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 1.0), vertical=True,
                                    parent=self.vispy_canvas.view.scene)
 
@@ -74,7 +74,7 @@ class PlotCanvas(QtCore.QObject):
         self.text_collection = self.new_text_collection()
 
         # TODO: Should be setting to show/hide CNC job annotations (global or per object)
-        self.text_collection.enabled = False
+        self.text_collection.enabled = True
 
     # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
     # all CNC have a limited workspace

+ 2 - 2
flatcamGUI/VisPyCanvas.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Dennis Hayrullin                            #
 # Date: 2/5/2016                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 import numpy as np
 from PyQt5.QtGui import QPalette

+ 2 - 2
flatcamGUI/VisPyPatches.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Dennis Hayrullin                            #
 # Date: 2/5/2016                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from vispy.visuals import markers, LineVisual, InfiniteLineVisual
 from vispy.visuals.axis import Ticker, _get_ticks_talbot

+ 2 - 2
flatcamGUI/VisPyTesselators.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Dennis Hayrullin                            #
 # Date: 2/5/2016                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from OpenGL import GLU
 

+ 17 - 5
flatcamGUI/VisPyVisuals.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Dennis Hayrullin                            #
 # Date: 2/5/2016                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from vispy.visuals import CompoundVisual, LineVisual, MeshVisual, TextVisual, MarkersVisual
 from vispy.scene.visuals import VisualNode, generate_docstring, visuals
@@ -456,20 +456,26 @@ class TextCollectionVisual(TextVisual):
         self.data = {}
         self.last_key = -1
         self.lock = threading.Lock()
-
+        self.method = 'gpu'
         super(TextCollectionVisual, self).__init__(**kwargs)
 
         self.freeze()
 
-    def add(self, text, pos, visible=True, update=True):
+    def add(self, text, pos, visible=True, update=True, font_size=9, color='black'):
         """
         Adds array of text to collection
         :param text: list
             Array of strings ['str1', 'str2', ... ]
         :param pos: list
             Array of string positions   [(0, 0), (10, 10), ... ]
+        :param visible: bool
+        |   Set True to make it visible
         :param update: bool
             Set True to redraw collection
+        :param font_size: int
+            Set font size to redraw collection
+        :param color: string
+            Set font color to redraw collection
         :return: int
             Index of array
         """
@@ -480,7 +486,7 @@ class TextCollectionVisual(TextVisual):
         self.lock.release()
 
         # Prepare data for translation
-        self.data[key] = {'text': text, 'pos': pos, 'visible': visible}
+        self.data[key] = {'text': text, 'pos': pos, 'visible': visible,'font_size': font_size, 'color': color}
 
         if update:
             self.redraw()
@@ -516,6 +522,8 @@ class TextCollectionVisual(TextVisual):
         """
         labels = []
         pos = []
+        font_s = 9
+        color = 'black'
 
         # Merge buffers
         for data in list(self.data.values()):
@@ -523,6 +531,8 @@ class TextCollectionVisual(TextVisual):
                 try:
                     labels += data['text']
                     pos += data['pos']
+                    font_s = data['font_size']
+                    color = data['color']
                 except Exception as e:
                     print("Data error", e)
 
@@ -530,6 +540,8 @@ class TextCollectionVisual(TextVisual):
         if len(labels) > 0:
             self.text = labels
             self.pos = pos
+            self.font_size = font_s
+            self.color = color
         else:
             self.text = None
             self.pos = (0, 0)

+ 2 - 2
flatcamParsers/ParseDXF.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from shapely.geometry import LineString
 import logging

+ 2 - 2
flatcamParsers/ParseDXF_Spline.py

@@ -2,12 +2,12 @@
 # Vasilis Vlachoudis
 # Date: 20-Oct-2015
 
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File modified: Marius Adrian Stanciu                     #
 # Date: 3/10/2019                                          #
-############################################################
+# ########################################################## ##
 
 import math
 import sys

+ 6 - 6
flatcamParsers/ParseFont.py

@@ -1,15 +1,15 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
-#########################################################################
-### Borrowed code from 'https://github.com/gddc/ttfquery/blob/master/ ###
-### and made it work with Python 3                          #############
-#########################################################################
+# ####################################################################### ##
+# ## Borrowed code from 'https://github.com/gddc/ttfquery/blob/master/ # ##
+# ## and made it work with Python 3                          ########### ##
+# ####################################################################### ##
 
 import re, os, sys, glob
 import itertools

+ 2 - 2
flatcamParsers/ParseSVG.py

@@ -1,4 +1,4 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
@@ -17,7 +17,7 @@
 #  * All transformations                                   #
 #                                                          #
 #  Reference: www.w3.org/TR/SVG/Overview.html              #
-############################################################
+# ########################################################## ##
 
 # import xml.etree.ElementTree as ET
 from svg.path import Line, Arc, CubicBezier, QuadraticBezier, parse_path

+ 21 - 21
flatcamTools/ToolCalculators.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from FlatCAMObj import *
@@ -31,7 +31,7 @@ class ToolCalculator(FlatCAMTool):
 
         self.app = app
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel
@@ -42,14 +42,14 @@ class ToolCalculator(FlatCAMTool):
                         """)
         self.layout.addWidget(title_label)
 
-        ######################
-        ## Units Calculator ##
-        ######################
+        #################### ##
+        # ## Units Calculator # ##
+        #################### ##
 
         self.unists_spacer_label = QtWidgets.QLabel(" ")
         self.layout.addWidget(self.unists_spacer_label)
 
-        ## Title of the Units Calculator
+        # ## Title of the Units Calculator
         units_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.unitsName)
         self.layout.addWidget(units_label)
 
@@ -76,18 +76,18 @@ class ToolCalculator(FlatCAMTool):
         grid_units_layout.addWidget(self.inch_entry, 1, 1)
 
 
-        ############################
-        ## V-shape Tool Calculator ##
-        ############################
+        ########################## ##
+        # ## V-shape Tool Calculator # ##
+        ########################## ##
 
         self.v_shape_spacer_label = QtWidgets.QLabel(" ")
         self.layout.addWidget(self.v_shape_spacer_label)
 
-        ## Title of the V-shape Tools Calculator
+        # ## Title of the V-shape Tools Calculator
         v_shape_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.v_shapeName)
         self.layout.addWidget(v_shape_title_label)
 
-        ## Form Layout
+        # ## Form Layout
         form_layout = QtWidgets.QFormLayout()
         self.layout.addLayout(form_layout)
 
@@ -127,7 +127,7 @@ class ToolCalculator(FlatCAMTool):
         form_layout.addRow(self.cutDepth_label, self.cutDepth_entry)
         form_layout.addRow(self.effectiveToolDia_label, self.effectiveToolDia_entry)
 
-        ## Buttons
+        # ## Buttons
         self.calculate_vshape_button = QtWidgets.QPushButton(_("Calculate"))
         # self.calculate_button.setFixedWidth(70)
         self.calculate_vshape_button.setToolTip(
@@ -139,14 +139,14 @@ class ToolCalculator(FlatCAMTool):
         form_layout.addRow(self.empty_label, self.calculate_vshape_button)
 
 
-        ####################################
-        ## ElectroPlating Tool Calculator ##
-        ####################################
+        ################################## ##
+        # ## ElectroPlating Tool Calculator # ##
+        ################################## ##
 
         self.plate_spacer_label = QtWidgets.QLabel(" ")
         self.layout.addWidget(self.plate_spacer_label)
 
-        ## Title of the ElectroPlating Tools Calculator
+        # ## Title of the ElectroPlating Tools Calculator
         plate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.eplateName)
         plate_title_label.setToolTip(
             _("This calculator is useful for those who plate the via/pad/drill holes,\n"
@@ -154,7 +154,7 @@ class ToolCalculator(FlatCAMTool):
         )
         self.layout.addWidget(plate_title_label)
 
-        ## Plate Form Layout
+        # ## Plate Form Layout
         plate_form_layout = QtWidgets.QFormLayout()
         self.layout.addLayout(plate_form_layout)
 
@@ -210,7 +210,7 @@ class ToolCalculator(FlatCAMTool):
         plate_form_layout.addRow(self.cvaluelabel, self.cvalue_entry)
         plate_form_layout.addRow(self.timelabel, self.time_entry)
 
-        ## Buttons
+        # ## Buttons
         self.calculate_plate_button = QtWidgets.QPushButton(_("Calculate"))
         # self.calculate_button.setFixedWidth(70)
         self.calculate_plate_button.setToolTip(
@@ -223,7 +223,7 @@ class ToolCalculator(FlatCAMTool):
 
         self.layout.addStretch()
 
-        ## Signals
+        # ## Signals
         self.cutDepth_entry.textChanged.connect(self.on_calculate_tool_dia)
         self.cutDepth_entry.editingFinished.connect(self.on_calculate_tool_dia)
         self.tipDia_entry.editingFinished.connect(self.on_calculate_tool_dia)
@@ -264,7 +264,7 @@ class ToolCalculator(FlatCAMTool):
     def set_tool_ui(self):
         self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
 
-        ## Initialize form
+        # ## Initialize form
         self.mm_entry.set_value('0')
         self.inch_entry.set_value('0')
 

+ 9 - 3
flatcamTools/ToolCutOut.py

@@ -415,7 +415,7 @@ class CutOut(FlatCAMTool):
                 object_geo = cutout_obj.solid_geometry
 
             try:
-                _ = iter(object_geo)
+                __ = iter(object_geo)
             except TypeError:
                 object_geo = [object_geo]
 
@@ -424,7 +424,8 @@ class CutOut(FlatCAMTool):
                     geo = (geo.buffer(margin + abs(dia / 2))).exterior
 
                 # Get min and max data for each object as we just cut rectangles across X or Y
-                xmin, ymin, xmax, ymax = geo.bounds
+                xmin, ymin, xmax, ymax = recursive_bounds(geo)
+
                 px = 0.5 * (xmin + xmax) + margin
                 py = 0.5 * (ymin + ymax) + margin
                 lenx = (xmax - xmin) + (margin * 2)
@@ -475,6 +476,11 @@ class CutOut(FlatCAMTool):
                     solid_geo.append(geo)
 
             geo_obj.solid_geometry = deepcopy(solid_geo)
+            xmin, ymin, xmax, ymax = recursive_bounds(geo_obj.solid_geometry)
+            geo_obj.options['xmin'] = xmin
+            geo_obj.options['ymin'] = ymin
+            geo_obj.options['xmax'] = xmax
+            geo_obj.options['ymax'] = ymax
 
         outname = cutout_obj.options["name"] + "_cutout"
         self.app.new_object('geometry', outname, geo_init)
@@ -565,7 +571,7 @@ class CutOut(FlatCAMTool):
             object_geo = cutout_obj.solid_geometry
 
             try:
-                _ = iter(object_geo)
+                __ = iter(object_geo)
             except TypeError:
                 object_geo = [object_geo]
 

+ 14 - 14
flatcamTools/ToolDblSided.py

@@ -20,7 +20,7 @@ class DblSidedTool(FlatCAMTool):
     def __init__(self, app):
         FlatCAMTool.__init__(self, app)
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel
@@ -34,11 +34,11 @@ class DblSidedTool(FlatCAMTool):
         self.empty_lb = QtWidgets.QLabel("")
         self.layout.addWidget(self.empty_lb)
 
-        ## Grid Layout
+        # ## Grid Layout
         grid_lay = QtWidgets.QGridLayout()
         self.layout.addLayout(grid_lay)
 
-        ## Gerber Object to mirror
+        # ## Gerber Object to mirror
         self.gerber_object_combo = QtWidgets.QComboBox()
         self.gerber_object_combo.setModel(self.app.collection)
         self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@@ -62,7 +62,7 @@ class DblSidedTool(FlatCAMTool):
         grid_lay.addWidget(self.gerber_object_combo, 1, 0)
         grid_lay.addWidget(self.mirror_gerber_button, 1, 1)
 
-        ## Excellon Object to mirror
+        # ## Excellon Object to mirror
         self.exc_object_combo = QtWidgets.QComboBox()
         self.exc_object_combo.setModel(self.app.collection)
         self.exc_object_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
@@ -86,7 +86,7 @@ class DblSidedTool(FlatCAMTool):
         grid_lay.addWidget(self.exc_object_combo, 3, 0)
         grid_lay.addWidget(self.mirror_exc_button, 3, 1)
 
-        ## Geometry Object to mirror
+        # ## Geometry Object to mirror
         self.geo_object_combo = QtWidgets.QComboBox()
         self.geo_object_combo.setModel(self.app.collection)
         self.geo_object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
@@ -110,11 +110,11 @@ class DblSidedTool(FlatCAMTool):
         grid_lay.addWidget(self.geo_object_combo, 5, 0)
         grid_lay.addWidget(self.mirror_geo_button, 5, 1)
 
-        ## Grid Layout
+        # ## Grid Layout
         grid_lay1 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid_lay1)
 
-        ## Axis
+        # ## Axis
         self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'},
                                      {'label': 'Y', 'value': 'Y'}])
         self.mirax_label = QtWidgets.QLabel(_("Mirror Axis:"))
@@ -127,7 +127,7 @@ class DblSidedTool(FlatCAMTool):
         grid_lay1.addWidget(self.mirax_label, 7, 0)
         grid_lay1.addWidget(self.mirror_axis, 7, 1)
 
-        ## Axis Location
+        # ## Axis Location
         self.axis_location = RadioSet([{'label': 'Point', 'value': 'point'},
                                        {'label': 'Box', 'value': 'box'}])
         self.axloc_label = QtWidgets.QLabel(_("Axis Ref:"))
@@ -143,11 +143,11 @@ class DblSidedTool(FlatCAMTool):
         self.empty_lb2 = QtWidgets.QLabel("")
         grid_lay1.addWidget(self.empty_lb2, 9, 0)
 
-        ## Grid Layout
+        # ## Grid Layout
         grid_lay2 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid_lay2)
 
-        ## Point/Box
+        # ## Point/Box
         self.point_box_container = QtWidgets.QVBoxLayout()
         self.pb_label = QtWidgets.QLabel("<b>%s</b>" % _('Point/Box Reference:'))
         self.pb_label.setToolTip(
@@ -189,7 +189,7 @@ class DblSidedTool(FlatCAMTool):
         self.box_combo_type.hide()
 
 
-        ## Alignment holes
+        # ## Alignment holes
         self.ah_label = QtWidgets.QLabel("<b>%s</b>" % _('Alignment Drill Coordinates:'))
         self.ah_label.setToolTip(
            _( "Alignment holes (x1, y1), (x2, y2), ... "
@@ -220,7 +220,7 @@ class DblSidedTool(FlatCAMTool):
         grid_lay3.addWidget(self.alignment_holes, 0, 0)
         grid_lay3.addWidget(self.add_drill_point_button, 0, 1)
 
-        ## Drill diameter for alignment holes
+        # ## Drill diameter for alignment holes
         self.dt_label = QtWidgets.QLabel("<b>%s</b>:" % _('Alignment Drill Diameter'))
         self.dt_label.setToolTip(
             _("Diameter of the drill for the "
@@ -243,7 +243,7 @@ class DblSidedTool(FlatCAMTool):
         hlay2 = QtWidgets.QHBoxLayout()
         self.layout.addLayout(hlay2)
 
-        ## Buttons
+        # ## Buttons
         self.create_alignment_hole_button = QtWidgets.QPushButton(_("Create Excellon Object"))
         self.create_alignment_hole_button.setToolTip(
             _("Creates an Excellon Object containing the\n"
@@ -261,7 +261,7 @@ class DblSidedTool(FlatCAMTool):
 
         self.layout.addStretch()
 
-        ## Signals
+        # ## Signals
         self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
         self.mirror_gerber_button.clicked.connect(self.on_mirror_gerber)
         self.mirror_exc_button.clicked.connect(self.on_mirror_exc)

+ 3 - 3
flatcamTools/ToolFilm.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 
@@ -165,7 +165,7 @@ class Film(FlatCAMTool):
 
         self.layout.addStretch()
 
-        ## Signals
+        # ## Signals
         self.film_object_button.clicked.connect(self.on_film_creation)
         self.tf_type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
         self.tf_type_box_combo.currentIndexChanged.connect(self.on_type_box_index_changed)

+ 4 - 4
flatcamTools/ToolImage.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 
@@ -144,7 +144,7 @@ class ToolImage(FlatCAMTool):
 
         self.layout.addStretch()
 
-        ## Signals
+        # ## Signals
         self.import_button.clicked.connect(self.on_file_importimage)
 
     def run(self, toggle=True):
@@ -173,7 +173,7 @@ class ToolImage(FlatCAMTool):
         FlatCAMTool.install(self, icon, separator, **kwargs)
 
     def set_tool_ui(self):
-        ## Initialize form
+        # ## Initialize form
         self.dpi_entry.set_value(96)
         self.image_type.set_value('black')
         self.mask_bw_entry.set_value(250)

+ 4 - 4
flatcamTools/ToolMeasurement.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from FlatCAMObj import *
@@ -32,11 +32,11 @@ class Measurement(FlatCAMTool):
         self.canvas = self.app.plotcanvas
         self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("<font size=4><b>%s</b></font><br>" % self.toolName)
         self.layout.addWidget(title_label)
 
-        ## Form Layout
+        # ## Form Layout
         form_layout = QtWidgets.QFormLayout()
         self.layout.addLayout(form_layout)
 

+ 2 - 2
flatcamTools/ToolMove.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from FlatCAMObj import *

+ 8 - 8
flatcamTools/ToolNonCopperClear.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Modified by: Marius Adrian Stanciu (c)              #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from copy import copy,deepcopy
@@ -38,7 +38,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.tools_box.setContentsMargins(0, 0, 0, 0)
         self.tools_frame.setLayout(self.tools_box)
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel
@@ -49,11 +49,11 @@ class NonCopperClear(FlatCAMTool, Gerber):
                         """)
         self.tools_box.addWidget(title_label)
 
-        ## Form Layout
+        # ## Form Layout
         form_layout = QtWidgets.QFormLayout()
         self.tools_box.addLayout(form_layout)
 
-        ## Object
+        # ## Object
         self.object_combo = QtWidgets.QComboBox()
         self.object_combo.setModel(self.app.collection)
         self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@@ -68,7 +68,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         form_layout.addRow(self.object_label, self.object_combo)
         form_layout.addRow(e_lab_0)
 
-        #### Tools ####
+        #### Tools ## ##
         self.tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('Tools Table'))
         self.tools_table_label.setToolTip(
             _("Tools pool from which the algorithm\n"
@@ -113,7 +113,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         self.empty_label = QtWidgets.QLabel('')
         self.tools_box.addWidget(self.empty_label)
 
-        #### Add a new Tool ####
+        #### Add a new Tool ## ##
         hlay = QtWidgets.QHBoxLayout()
         self.tools_box.addLayout(hlay)
 
@@ -420,7 +420,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     self.tools_table.setItem(row_no, 1, dia)  # Diameter
                     self.tools_table.setCellWidget(row_no, 2, tool_type_item)
 
-                    ### REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY ###
+                    # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
                     self.tools_table.setItem(row_no, 3, tool_uid_item)  # Tool unique ID
 
         # make the diameter column editable

+ 4 - 4
flatcamTools/ToolPDF.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 4/23/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from shapely.geometry import Point, Polygon, LineString
@@ -1242,9 +1242,9 @@ class ToolPDF(FlatCAMTool):
                             new_el['follow'] = pdf_geo.exterior
                             apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
 
-                # ###############################################
+                # ############################################# ##
                 # store the found geometry for filling the path #
-                # ###############################################
+                # ############################################# ##
 
                 # in case that a color change to white (transparent) occurred
                 if flag_clear_geo is True:

+ 12 - 12
flatcamTools/ToolPaint.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Modified: Marius Adrian Stanciu (c)                 #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from copy import copy,deepcopy
@@ -29,7 +29,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         FlatCAMTool.__init__(self, app)
         Geometry.__init__(self, geo_steps_per_circle=self.app.defaults["geometry_circle_steps"])
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel
@@ -47,11 +47,11 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.tools_box.setContentsMargins(0, 0, 0, 0)
         self.tools_frame.setLayout(self.tools_box)
 
-        ## Form Layout
+        # ## Form Layout
         form_layout = QtWidgets.QFormLayout()
         self.tools_box.addLayout(form_layout)
 
-        ## Object
+        # ## Object
         self.object_combo = QtWidgets.QComboBox()
         self.object_combo.setModel(self.app.collection)
         self.object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
@@ -65,7 +65,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         form_layout.addRow(self.object_label, self.object_combo)
         form_layout.addRow(e_lab_0)
 
-        #### Tools ####
+        #### Tools ## ##
         self.tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('Tools Table'))
         self.tools_table_label.setToolTip(
             _("Tools pool from which the algorithm\n"
@@ -110,7 +110,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.empty_label = QtWidgets.QLabel('')
         self.tools_box.addWidget(self.empty_label)
 
-        #### Add a new Tool ####
+        #### Add a new Tool ## ##
         hlay = QtWidgets.QHBoxLayout()
         self.tools_box.addLayout(hlay)
 
@@ -305,7 +305,7 @@ class ToolPaint(FlatCAMTool, Gerber):
 
         self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
 
-        ## Signals
+        # ## Signals
         self.addtool_btn.clicked.connect(self.on_tool_add)
         self.addtool_entry.returnPressed.connect(self.on_tool_add)
         # self.copytool_btn.clicked.connect(lambda: self.on_tool_copy())
@@ -365,7 +365,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.tools_frame.show()
         self.reset_fields()
 
-        ## Init the GUI interface
+        # ## Init the GUI interface
         self.paintmargin_entry.set_value(self.default_data["paintmargin"])
         self.paintmethod_combo.set_value(self.default_data["paintmethod"])
         self.selectmethod_combo.set_value(self.default_data["selectmethod"])
@@ -484,7 +484,7 @@ class ToolPaint(FlatCAMTool, Gerber):
                     self.tools_table.setItem(row_no, 1, dia)  # Diameter
                     self.tools_table.setCellWidget(row_no, 2, tool_type_item)
 
-                    ### REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY ###
+                    # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
                     self.tools_table.setItem(row_no, 3, tool_uid_item)  # Tool unique ID
 
         # make the diameter column editable
@@ -1035,13 +1035,13 @@ class ToolPaint(FlatCAMTool, Gerber):
             if reset:
                 self.flat_geometry = []
 
-            ## If iterable, expand recursively.
+            # ## If iterable, expand recursively.
             try:
                 for geo in geometry:
                     if geo is not None:
                         recurse(geometry=geo, reset=False)
 
-            ## Not iterable, do the actual indexing and add.
+            # ## Not iterable, do the actual indexing and add.
             except TypeError:
                 self.flat_geometry.append(geometry)
 

+ 3 - 3
flatcamTools/ToolPanelize.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from copy import copy, deepcopy
@@ -28,7 +28,7 @@ class Panelize(FlatCAMTool):
         super(Panelize, self).__init__(self)
         self.app = app
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel

+ 4 - 4
flatcamTools/ToolPcbWizard.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 4/15/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 
@@ -152,7 +152,7 @@ class PcbWizard(FlatCAMTool):
 
         self.modified_excellon_file = ''
 
-        ## Signals
+        # ## Signals
         self.excellon_brn.clicked.connect(self.on_load_excellon_click)
         self.inf_btn.clicked.connect(self.on_load_inf_click)
         self.import_button.clicked.connect(lambda: self.on_import_excellon(
@@ -207,7 +207,7 @@ class PcbWizard(FlatCAMTool):
         self.exc_file_content = None
         self.tools_from_inf = {}
 
-        ## Initialize form
+        # ## Initialize form
         self.int_entry.set_value(self.integral)
         self.frac_entry.set_value(self.fractional)
         self.zeros_radio.set_value(self.zeros)

+ 3 - 3
flatcamTools/ToolProperties.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtCore import Qt
@@ -37,7 +37,7 @@ class Properties(FlatCAMTool):
         self.properties_box.setContentsMargins(0, 0, 0, 0)
         self.properties_frame.setLayout(self.properties_box)
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel

+ 2 - 2
flatcamTools/ToolShell.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
 # Date: 2/5/2014                                           #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 # from PyQt5.QtCore import pyqtSignal
 from PyQt5.QtCore import Qt

+ 21 - 21
flatcamTools/ToolSolderPaste.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from FlatCAMCommon import LoudDict
@@ -39,7 +39,7 @@ class SolderPaste(FlatCAMTool):
     def __init__(self, app):
         FlatCAMTool.__init__(self, app)
 
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel
@@ -50,11 +50,11 @@ class SolderPaste(FlatCAMTool):
                         """)
         self.layout.addWidget(title_label)
 
-        ## Form Layout
+        # ## Form Layout
         obj_form_layout = QtWidgets.QFormLayout()
         self.layout.addLayout(obj_form_layout)
 
-        ## Gerber Object to be used for solderpaste dispensing
+        # ## Gerber Object to be used for solderpaste dispensing
         self.obj_combo = FCComboBox(callback=self.on_rmb_combo)
         self.obj_combo.setModel(self.app.collection)
         self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@@ -66,7 +66,7 @@ class SolderPaste(FlatCAMTool):
         )
         obj_form_layout.addRow(self.object_label, self.obj_combo)
 
-        #### Tools ####
+        #### Tools ## ##
         self.tools_table_label = QtWidgets.QLabel('<b>%s</b>' % _('Tools Table'))
         self.tools_table_label.setToolTip(
             _("Tools pool from which the algorithm\n"
@@ -94,7 +94,7 @@ class SolderPaste(FlatCAMTool):
            _( "Nozzle tool Diameter. It's value (in current FlatCAM units)\n"
             "is the width of the solder paste dispensed."))
 
-        #### Add a new Tool ####
+        #### Add a new Tool ## ##
         hlay_tools = QtWidgets.QHBoxLayout()
         self.layout.addLayout(hlay_tools)
 
@@ -135,7 +135,7 @@ class SolderPaste(FlatCAMTool):
 
         self.layout.addSpacing(10)
 
-        ## Buttons
+        # ## Buttons
         grid0_1 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid0_1)
 
@@ -157,7 +157,7 @@ class SolderPaste(FlatCAMTool):
         self.gcode_box.setContentsMargins(0, 0, 0, 0)
         self.gcode_frame.setLayout(self.gcode_box)
 
-        ## Form Layout
+        # ## Form Layout
         self.gcode_form_layout = QtWidgets.QFormLayout()
         self.gcode_box.addLayout(self.gcode_form_layout)
 
@@ -283,7 +283,7 @@ class SolderPaste(FlatCAMTool):
         self.pp_combo.setStyleSheet('background-color: rgb(255,255,255)')
         self.gcode_form_layout.addRow(pp_label, self.pp_combo)
 
-        ## Buttons
+        # ## Buttons
         grid1 = QtWidgets.QGridLayout()
         self.gcode_box.addLayout(grid1)
 
@@ -301,7 +301,7 @@ class SolderPaste(FlatCAMTool):
         self.generation_frame.setLayout(self.generation_box)
 
 
-        ## Buttons
+        # ## Buttons
         grid2 = QtWidgets.QGridLayout()
         self.generation_box.addLayout(grid2)
 
@@ -313,11 +313,11 @@ class SolderPaste(FlatCAMTool):
         grid2.addWidget(step2_lbl, 0, 0)
         grid2.addWidget(self.soldergeo_btn, 0, 2)
 
-        ## Form Layout
+        # ## Form Layout
         geo_form_layout = QtWidgets.QFormLayout()
         self.generation_box.addLayout(geo_form_layout)
 
-        ## Geometry Object to be used for solderpaste dispensing
+        # ## Geometry Object to be used for solderpaste dispensing
         self.geo_obj_combo = FCComboBox(callback=self.on_rmb_combo)
         self.geo_obj_combo.setModel(self.app.collection)
         self.geo_obj_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
@@ -346,11 +346,11 @@ class SolderPaste(FlatCAMTool):
         grid3.addWidget(step3_lbl, 0, 0)
         grid3.addWidget(self.solder_gcode_btn, 0, 2)
 
-        ## Form Layout
+        # ## Form Layout
         cnc_form_layout = QtWidgets.QFormLayout()
         self.generation_box.addLayout(cnc_form_layout)
 
-        ## Gerber Object to be used for solderpaste dispensing
+        # ## Gerber Object to be used for solderpaste dispensing
         self.cnc_obj_combo = FCComboBox(callback=self.on_rmb_combo)
         self.cnc_obj_combo.setModel(self.app.collection)
         self.cnc_obj_combo.setRootModelIndex(self.app.collection.index(3, 0, QtCore.QModelIndex()))
@@ -412,7 +412,7 @@ class SolderPaste(FlatCAMTool):
         # action to be added in the combobox context menu
         self.combo_context_del_action = QtWidgets.QAction(QtGui.QIcon('share/trash16.png'), _("Delete Object"))
 
-        ## Signals
+        # ## Signals
         self.combo_context_del_action.triggered.connect(self.on_delete_object)
         self.addtool_btn.clicked.connect(self.on_tool_add)
         self.addtool_entry.returnPressed.connect(self.on_tool_add)
@@ -995,7 +995,7 @@ class SolderPaste(FlatCAMTool):
 
             if reset:
                 self.flat_geometry = []
-            ## If iterable, expand recursively.
+            # ## If iterable, expand recursively.
             try:
                 for geo in geometry:
                     if geo is not None:
@@ -1003,7 +1003,7 @@ class SolderPaste(FlatCAMTool):
                                 reset=False,
                                 pathonly=pathonly)
 
-            ## Not iterable, do the actual indexing and add.
+            # ## Not iterable, do the actual indexing and add.
             except TypeError:
                 if pathonly and type(geometry) == Polygon:
                     self.flat_geometry.append(geometry.exterior)
@@ -1226,7 +1226,7 @@ class SolderPaste(FlatCAMTool):
                 job_obj.options["tooldia"] = tool_dia
                 job_obj.options['tool_dia'] = tool_dia
 
-                ### CREATE GCODE ###
+                # ## CREATE GCODE # ##
                 res = job_obj.generate_gcode_from_solderpaste_geo(**tooluid_value)
 
                 if res == 'fail':
@@ -1235,7 +1235,7 @@ class SolderPaste(FlatCAMTool):
                 else:
                     tool_cnc_dict['gcode'] = res
 
-                ### PARSE GCODE ###
+                # ## PARSE GCODE # ##
                 tool_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
 
                 # TODO this serve for bounding box creation only; should be optimized
@@ -1380,7 +1380,7 @@ class SolderPaste(FlatCAMTool):
             gcode += obj.cnc_tools[tool]['gcode']
         lines = StringIO(gcode)
 
-        ## Write
+        # ## Write
         if filename is not None:
             try:
                 with open(filename, 'w') as f:

+ 2 - 2
flatcamTools/ToolSub.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 4/24/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 
 from FlatCAMTool import FlatCAMTool

+ 15 - 15
flatcamTools/ToolTransform.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMTool import FlatCAMTool
 from FlatCAMObj import *
@@ -32,7 +32,7 @@ class ToolTransform(FlatCAMTool):
 
         self.transform_lay = QtWidgets.QVBoxLayout()
         self.layout.addLayout(self.transform_lay)
-        ## Title
+        # ## Title
         title_label = QtWidgets.QLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
                         QLabel
@@ -56,11 +56,11 @@ class ToolTransform(FlatCAMTool):
         self.empty_label4.setFixedWidth(70)
         self.transform_lay.addWidget(self.empty_label)
 
-        ## Rotate Title
+        # ## Rotate Title
         rotate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.rotateName)
         self.transform_lay.addWidget(rotate_title_label)
 
-        ## Layout
+        # ## Layout
         form_layout = QtWidgets.QFormLayout()
         self.transform_lay.addLayout(form_layout)
         form_child = QtWidgets.QHBoxLayout()
@@ -94,11 +94,11 @@ class ToolTransform(FlatCAMTool):
 
         self.transform_lay.addWidget(self.empty_label1)
 
-        ## Skew Title
+        # ## Skew Title
         skew_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.skewName)
         self.transform_lay.addWidget(skew_title_label)
 
-        ## Form Layout
+        # ## Form Layout
         form1_layout = QtWidgets.QFormLayout()
         self.transform_lay.addLayout(form1_layout)
         form1_child_1 = QtWidgets.QHBoxLayout()
@@ -151,11 +151,11 @@ class ToolTransform(FlatCAMTool):
 
         self.transform_lay.addWidget(self.empty_label2)
 
-        ## Scale Title
+        # ## Scale Title
         scale_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.scaleName)
         self.transform_lay.addWidget(scale_title_label)
 
-        ## Form Layout
+        # ## Form Layout
         form2_layout = QtWidgets.QFormLayout()
         self.transform_lay.addLayout(form2_layout)
         form2_child_1 = QtWidgets.QHBoxLayout()
@@ -225,11 +225,11 @@ class ToolTransform(FlatCAMTool):
 
         self.transform_lay.addWidget(self.empty_label3)
 
-        ## Offset Title
+        # ## Offset Title
         offset_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.offsetName)
         self.transform_lay.addWidget(offset_title_label)
 
-        ## Form Layout
+        # ## Form Layout
         form3_layout = QtWidgets.QFormLayout()
         self.transform_lay.addLayout(form3_layout)
         form3_child_1 = QtWidgets.QHBoxLayout()
@@ -280,11 +280,11 @@ class ToolTransform(FlatCAMTool):
 
         self.transform_lay.addWidget(self.empty_label4)
 
-        ## Flip Title
+        # ## Flip Title
         flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
         self.transform_lay.addWidget(flip_title_label)
 
-        ## Form Layout
+        # ## Form Layout
         form4_layout = QtWidgets.QFormLayout()
         form4_child_hlay = QtWidgets.QHBoxLayout()
         self.transform_lay.addLayout(form4_child_hlay)
@@ -355,7 +355,7 @@ class ToolTransform(FlatCAMTool):
 
         self.transform_lay.addStretch()
 
-        ## Signals
+        # ## Signals
         self.rotate_button.clicked.connect(self.on_rotate)
         self.skewx_button.clicked.connect(self.on_skewx)
         self.skewy_button.clicked.connect(self.on_skewy)
@@ -401,7 +401,7 @@ class ToolTransform(FlatCAMTool):
         FlatCAMTool.install(self, icon, separator, shortcut='ALT+R', **kwargs)
 
     def set_tool_ui(self):
-        ## Initialize form
+        # ## Initialize form
         if self.app.defaults["tools_transform_rotate"]:
             self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])
         else:

BIN
locale/de/LC_MESSAGES/strings.mo


File diff suppressed because it is too large
+ 197 - 193
locale/de/LC_MESSAGES/strings.po


BIN
locale/en/LC_MESSAGES/strings.mo


File diff suppressed because it is too large
+ 219 - 215
locale/en/LC_MESSAGES/strings.po


BIN
locale/ro/LC_MESSAGES/strings.mo


File diff suppressed because it is too large
+ 197 - 193
locale/ro/LC_MESSAGES/strings.po


File diff suppressed because it is too large
+ 223 - 219
locale_template/strings.pot


+ 4 - 4
make_win.py

@@ -1,4 +1,4 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # Author: Juan Pablo Caram (c)                             #
@@ -11,12 +11,12 @@
 # This is not an aid to install FlatCAM from source on     #
 # Windows platforms. It is only useful when FlatCAM is up  #
 # and running and ready to be packaged.                    #
-############################################################
+# ########################################################## ##
 
-############################################################
+# ########################################################## ##
 # File Modified (major mod): Marius Adrian Stanciu         #
 # Date: 3/10/2019                                          #
-############################################################
+# ########################################################## ##
 
 
 # Files not needed: Qt, tk.dll, tcl.dll, tk/, tcl/, vtk/,

+ 2 - 2
postprocessors/Paste_1.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/Repetier.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/Roland_MDX_20.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/Toolchange_Custom.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/Toolchange_Probe_MACH3.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/Toolchange_manual.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/default.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Matthieu Berthomé                           #
 # Date: 5/26/2017                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/grbl_11.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Matthieu Berthomé                           #
 # Date: 5/26/2017                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/grbl_laser.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Matthieu Berthomé                           #
 # Date: 5/26/2017                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/hpgl.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/line_xyz.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

+ 2 - 2
postprocessors/marlin.py

@@ -1,10 +1,10 @@
-############################################################
+# ########################################################## ##
 # FlatCAM: 2D Post-processing for Manufacturing            #
 # http://flatcam.org                                       #
 # File Author: Marius Adrian Stanciu (c)                   #
 # Date: 3/10/2019                                          #
 # MIT Licence                                              #
-############################################################
+# ########################################################## ##
 
 from FlatCAMPostProc import *
 

BIN
share/disable16.png


BIN
share/disable32.png


BIN
share/notebook16.png


+ 1 - 1
tclCommands/TclCommandCutout.py

@@ -120,7 +120,7 @@ class TclCommandCutout(TclCommand):
             geo_obj.solid_geometry = cascaded_union([LineString(segment) for segment in cuts])
 
         try:
-            obj.app.new_object("geometry", name + "_cutout", geo_init_me)
+            self.app.new_object("geometry", name + "_cutout", geo_init_me)
             self.app.inform.emit("[success] Rectangular-form Cutout operation finished.")
         except Exception as e:
             return "Operation failed: %s" % str(e)

+ 107 - 43
tclCommands/TclCommandGeoCutout.py

@@ -165,6 +165,11 @@ class TclCommandGeoCutout(TclCommandSignaled):
 
         # Get min and max data for each object as we just cut rectangles across X or Y
         xmin, ymin, xmax, ymax = cutout_obj.bounds()
+        cutout_obj.options['xmin'] = xmin
+        cutout_obj.options['ymin'] = ymin
+        cutout_obj.options['xmax'] = xmax
+        cutout_obj.options['ymax'] = ymax
+
         px = 0.5 * (xmin + xmax) + margin
         py = 0.5 * (ymin + ymax) + margin
         lenghtx = (xmax - xmin) + (margin * 2)
@@ -179,48 +184,102 @@ class TclCommandGeoCutout(TclCommandSignaled):
 
         if isinstance(cutout_obj, FlatCAMGeometry):
             # rename the obj name so it can be identified as cutout
-            cutout_obj.options["name"] += "_cutout"
-
-            if gaps_u == 8 or gaps_u == '2lr':
-                subtract_rectangle(cutout_obj,
-                                   xmin - gapsize,  # botleft_x
-                                   py - gapsize + lenghty / 4,  # botleft_y
-                                   xmax + gapsize,  # topright_x
-                                   py + gapsize + lenghty / 4)  # topright_y
-                subtract_rectangle(cutout_obj,
-                                   xmin - gapsize,
-                                   py - gapsize - lenghty / 4,
-                                   xmax + gapsize,
-                                   py + gapsize - lenghty / 4)
-
-            if gaps_u == 8 or gaps_u == '2tb':
-                subtract_rectangle(cutout_obj,
-                                   px - gapsize + lenghtx / 4,
-                                   ymin - gapsize,
-                                   px + gapsize + lenghtx / 4,
-                                   ymax + gapsize)
-                subtract_rectangle(cutout_obj,
-                                   px - gapsize - lenghtx / 4,
-                                   ymin - gapsize,
-                                   px + gapsize - lenghtx / 4,
-                                   ymax + gapsize)
-
-            if gaps_u == 4 or gaps_u == 'lr':
-                subtract_rectangle(cutout_obj,
-                                   xmin - gapsize,
-                                   py - gapsize,
-                                   xmax + gapsize,
-                                   py + gapsize)
-
-            if gaps_u == 4 or gaps_u == 'tb':
-                subtract_rectangle(cutout_obj,
-                                   px - gapsize,
-                                   ymin - gapsize,
-                                   px + gapsize,
-                                   ymax + gapsize)
-
-            cutout_obj.plot()
-            self.app.inform.emit("[success] Any-form Cutout operation finished.")
+            # cutout_obj.options["name"] += "_cutout"
+
+            # if gaps_u == 8 or gaps_u == '2lr':
+            #     subtract_rectangle(cutout_obj,
+            #                        xmin - gapsize,  # botleft_x
+            #                        py - gapsize + lenghty / 4,  # botleft_y
+            #                        xmax + gapsize,  # topright_x
+            #                        py + gapsize + lenghty / 4)  # topright_y
+            #     subtract_rectangle(cutout_obj,
+            #                        xmin - gapsize,
+            #                        py - gapsize - lenghty / 4,
+            #                        xmax + gapsize,
+            #                        py + gapsize - lenghty / 4)
+            #
+            # if gaps_u == 8 or gaps_u == '2tb':
+            #     subtract_rectangle(cutout_obj,
+            #                        px - gapsize + lenghtx / 4,
+            #                        ymin - gapsize,
+            #                        px + gapsize + lenghtx / 4,
+            #                        ymax + gapsize)
+            #     subtract_rectangle(cutout_obj,
+            #                        px - gapsize - lenghtx / 4,
+            #                        ymin - gapsize,
+            #                        px + gapsize - lenghtx / 4,
+            #                        ymax + gapsize)
+            #
+            # if gaps_u == 4 or gaps_u == 'lr':
+            #     subtract_rectangle(cutout_obj,
+            #                        xmin - gapsize,
+            #                        py - gapsize,
+            #                        xmax + gapsize,
+            #                        py + gapsize)
+            #
+            # if gaps_u == 4 or gaps_u == 'tb':
+            #     subtract_rectangle(cutout_obj,
+            #                        px - gapsize,
+            #                        ymin - gapsize,
+            #                        px + gapsize,
+            #                        ymax + gapsize)
+
+            def geo_init(geo_obj, app_obj):
+                geo = deepcopy(cutout_obj.solid_geometry)
+
+                if gaps_u == 8 or gaps_u == '2lr':
+                    geo = substract_rectangle_geo(geo,
+                                                  xmin - gapsize,  # botleft_x
+                                                  py - gapsize + lenghty / 4,  # botleft_y
+                                                  xmax + gapsize,  # topright_x
+                                                  py + gapsize + lenghty / 4)  # topright_y
+                    geo = substract_rectangle_geo(geo,
+                                                  xmin - gapsize,
+                                                  py - gapsize - lenghty / 4,
+                                                  xmax + gapsize,
+                                                  py + gapsize - lenghty / 4)
+
+                if gaps_u == 8 or gaps_u == '2tb':
+                    geo = substract_rectangle_geo(geo,
+                                                  px - gapsize + lenghtx / 4,
+                                                  ymin - gapsize,
+                                                  px + gapsize + lenghtx / 4,
+                                                  ymax + gapsize)
+                    geo = substract_rectangle_geo(geo,
+                                                  px - gapsize - lenghtx / 4,
+                                                  ymin - gapsize,
+                                                  px + gapsize - lenghtx / 4,
+                                                  ymax + gapsize)
+
+                if gaps_u == 4 or gaps_u == 'lr':
+                    geo = substract_rectangle_geo(geo,
+                                                  xmin - gapsize,
+                                                  py - gapsize,
+                                                  xmax + gapsize,
+                                                  py + gapsize)
+
+                if gaps_u == 4 or gaps_u == 'tb':
+                    geo = substract_rectangle_geo(geo,
+                                                  px - gapsize,
+                                                  ymin - gapsize,
+                                                  px + gapsize,
+                                                  ymax + gapsize)
+                geo_obj.solid_geometry = deepcopy(geo)
+                geo_obj.options['xmin'] = cutout_obj.options['xmin']
+                geo_obj.options['ymin'] = cutout_obj.options['ymin']
+                geo_obj.options['xmax'] = cutout_obj.options['xmax']
+                geo_obj.options['ymax'] = cutout_obj.options['ymax']
+
+                app_obj.disable_plots(objects=[cutout_obj])
+
+                app_obj.inform.emit("[success] Any-form Cutout operation finished.")
+
+            outname = cutout_obj.options["name"] + "_cutout"
+            self.app.new_object('geometry', outname, geo_init)
+
+            # cutout_obj.plot()
+            # self.app.inform.emit("[success] Any-form Cutout operation finished.")
+            # self.app.plots_updated.emit()
         elif isinstance(cutout_obj, FlatCAMGerber):
 
             def geo_init(geo_obj, app_obj):
@@ -267,7 +326,12 @@ class TclCommandGeoCutout(TclCommandSignaled):
                                                   ymin - gapsize,
                                                   px + gapsize,
                                                   ymax + gapsize)
-                geo_obj.solid_geometry = geo
+                geo_obj.solid_geometry = deepcopy(geo)
+                geo_obj.options['xmin'] = cutout_obj.options['xmin']
+                geo_obj.options['ymin'] = cutout_obj.options['ymin']
+                geo_obj.options['xmax'] = cutout_obj.options['xmax']
+                geo_obj.options['ymax'] = cutout_obj.options['ymax']
+                app_obj.inform.emit("[success] Any-form Cutout operation finished.")
 
             outname = cutout_obj.options["name"] + "_cutout"
             self.app.new_object('geometry', outname, geo_init)

Some files were not shown because too many files changed in this diff