Bladeren bron

Merged in Beta_8.994 (pull request #17)

Beta 8.994
Marius Stanciu 5 jaren geleden
bovenliggende
commit
f04d8be50f
100 gewijzigde bestanden met toevoegingen van 6595 en 3578 verwijderingen
  1. 10 10
      Bookmark.py
  2. 232 3
      CHANGELOG.md
  3. 17 16
      appCommon/Common.py
  4. 4 4
      appCommon/bilinear.py
  5. 178 109
      appDatabase.py
  6. 506 120
      appEditors/AppExcEditor.py
  7. 145 137
      appEditors/AppGeoEditor.py
  8. 413 212
      appEditors/AppGerberEditor.py
  9. 4 2
      appEditors/AppTextEditor.py
  10. 7 12
      appEditors/appGCodeEditor.py
  11. 777 22
      appGUI/GUIElements.py
  12. 278 163
      appGUI/MainGUI.py
  13. 162 139
      appGUI/ObjectUI.py
  14. 25 19
      appGUI/PlotCanvas.py
  15. 29 15
      appGUI/PlotCanvasLegacy.py
  16. 3 2
      appGUI/VisPyTesselators.py
  17. 3 2
      appGUI/VisPyVisuals.py
  18. 22 4
      appGUI/preferences/PreferencesUIManager.py
  19. 1 0
      appGUI/preferences/__init__.py
  20. 6 6
      appGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py
  21. 2 2
      appGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py
  22. 1 1
      appGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py
  23. 4 4
      appGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py
  24. 2 2
      appGUI/preferences/excellon/ExcellonExpPrefGroupUI.py
  25. 2 2
      appGUI/preferences/excellon/ExcellonGenPrefGroupUI.py
  26. 2 2
      appGUI/preferences/excellon/ExcellonOptPrefGroupUI.py
  27. 5 5
      appGUI/preferences/general/GeneralAppPrefGroupUI.py
  28. 1 141
      appGUI/preferences/general/GeneralGUIPrefGroupUI.py
  29. 113 16
      appGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py
  30. 12 11
      appGUI/preferences/geometry/GeometryOptPrefGroupUI.py
  31. 1 1
      appGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py
  32. 2 2
      appGUI/preferences/gerber/GerberExpPrefGroupUI.py
  33. 2 2
      appGUI/preferences/gerber/GerberGenPrefGroupUI.py
  34. 95 68
      appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py
  35. 5 5
      appGUI/preferences/tools/Tools2CalPrefGroupUI.py
  36. 6 6
      appGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py
  37. 3 3
      appGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py
  38. 1 1
      appGUI/preferences/tools/Tools2InvertPrefGroupUI.py
  39. 6 6
      appGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py
  40. 4 4
      appGUI/preferences/tools/Tools2sidedPrefGroupUI.py
  41. 40 29
      appGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py
  42. 47 20
      appGUI/preferences/tools/ToolsCornersPrefGroupUI.py
  43. 12 12
      appGUI/preferences/tools/ToolsCutoutPrefGroupUI.py
  44. 18 17
      appGUI/preferences/tools/ToolsDrillPrefGroupUI.py
  45. 4 4
      appGUI/preferences/tools/ToolsFilmPrefGroupUI.py
  46. 13 13
      appGUI/preferences/tools/ToolsISOPrefGroupUI.py
  47. 19 21
      appGUI/preferences/tools/ToolsNCCPrefGroupUI.py
  48. 16 16
      appGUI/preferences/tools/ToolsPaintPrefGroupUI.py
  49. 4 4
      appGUI/preferences/tools/ToolsPanelizePrefGroupUI.py
  50. 8 8
      appGUI/preferences/tools/ToolsPreferencesUI.py
  51. 12 12
      appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py
  52. 7 1
      appGUI/preferences/tools/ToolsSubPrefGroupUI.py
  53. 8 8
      appGUI/preferences/tools/ToolsTransformPrefGroupUI.py
  54. 4 5
      appObjects/AppObject.py
  55. 86 59
      appObjects/FlatCAMCNCJob.py
  56. 2 6
      appObjects/FlatCAMDocument.py
  57. 11 14
      appObjects/FlatCAMExcellon.py
  58. 362 108
      appObjects/FlatCAMGeometry.py
  59. 57 54
      appObjects/FlatCAMGerber.py
  60. 4 4
      appObjects/FlatCAMObj.py
  61. 4 9
      appObjects/FlatCAMScript.py
  62. 12 12
      appObjects/ObjectCollection.py
  63. 10 10
      appParsers/ParseDXF.py
  64. 1 2
      appParsers/ParseDXF_Spline.py
  65. 16 14
      appParsers/ParseFont.py
  66. 61 64
      appParsers/ParseGerber.py
  67. 1 1
      appParsers/ParseHPGL2.py
  68. 71 112
      appParsers/ParsePDF.py
  69. 5 1
      appParsers/ParseSVG.py
  70. 4 2
      appPreProcessor.py
  71. 5 4
      appTools/ToolAlignObjects.py
  72. 197 68
      appTools/ToolCalculators.py
  73. 21 21
      appTools/ToolCalibration.py
  74. 339 280
      appTools/ToolCopperThieving.py
  75. 310 98
      appTools/ToolCorners.py
  76. 56 43
      appTools/ToolCutOut.py
  77. 40 34
      appTools/ToolDblSided.py
  78. 3 2
      appTools/ToolDistance.py
  79. 22 22
      appTools/ToolDrilling.py
  80. 10 13
      appTools/ToolEtchCompensation.py
  81. 40 44
      appTools/ToolExtractDrills.py
  82. 129 101
      appTools/ToolFiducials.py
  83. 20 15
      appTools/ToolFilm.py
  84. 3 4
      appTools/ToolImage.py
  85. 13 15
      appTools/ToolInvertGerber.py
  86. 213 120
      appTools/ToolIsolation.py
  87. 26 26
      appTools/ToolMilling.py
  88. 8 8
      appTools/ToolMove.py
  89. 282 185
      appTools/ToolNCC.py
  90. 2 1
      appTools/ToolOptimal.py
  91. 14 9
      appTools/ToolPDF.py
  92. 93 110
      appTools/ToolPaint.py
  93. 28 29
      appTools/ToolPanelize.py
  94. 5 5
      appTools/ToolPcbWizard.py
  95. 2 2
      appTools/ToolProperties.py
  96. 545 256
      appTools/ToolPunchGerber.py
  97. 63 56
      appTools/ToolQRCode.py
  98. 63 56
      appTools/ToolRulesCheck.py
  99. 3 0
      appTools/ToolShell.py
  100. 30 28
      appTools/ToolSolderPaste.py

+ 10 - 10
Bookmark.py

@@ -240,7 +240,7 @@ class BookmarkManager(QtWidgets.QWidget):
             index_list.append(index)
             index_list.append(index)
             title_to_remove = self.table_widget.item(model_index.row(), 1).text()
             title_to_remove = self.table_widget.item(model_index.row(), 1).text()
 
 
-            if title_to_remove == 'FlatCAM' or title_to_remove == 'Backup Site':
+            if title_to_remove == 'FlatCAM' or title_to_remove == _('Backup Site'):
                 self.app.inform.emit('[WARNING_NOTCL] %s.' % _("This bookmark can not be removed"))
                 self.app.inform.emit('[WARNING_NOTCL] %s.' % _("This bookmark can not be removed"))
                 self.build_bm_ui()
                 self.build_bm_ui()
                 return
                 return
@@ -287,12 +287,12 @@ class BookmarkManager(QtWidgets.QWidget):
         date = date.replace(' ', '_')
         date = date.replace(' ', '_')
 
 
         filter__ = "Text File (*.TXT);;All Files (*.*)"
         filter__ = "Text File (*.TXT);;All Files (*.*)"
-        filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Bookmarks"),
-                                                           directory='{l_save}/{n}_{date}'.format(
-                                                                l_save=str(self.app.get_last_save_folder()),
-                                                                n=_("Bookmarks"),
-                                                                date=date),
-                                                           ext_filter=filter__)
+        filename, _f = FCFileSaveDialog.get_saved_filename(
+            caption=_("Export Bookmarks"),
+            directory='{l_save}/{n}_{date}'.format(l_save=str(self.app.get_last_save_folder()),
+                                                   n=_("Bookmarks"),
+                                                   date=date),
+            ext_filter=filter__)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -314,9 +314,9 @@ class BookmarkManager(QtWidgets.QWidget):
                 f.close()
                 f.close()
             except Exception:
             except Exception:
                 e = sys.exc_info()[0]
                 e = sys.exc_info()[0]
-                self.app.log.error("Could not load defaults file.")
+                self.app.log.error("Could not load the file.")
                 self.app.log.error(str(e))
                 self.app.log.error(str(e))
-                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load bookmarks file."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load the file."))
                 return
                 return
 
 
             # Save Bookmarks to a file
             # Save Bookmarks to a file
@@ -346,7 +346,7 @@ class BookmarkManager(QtWidgets.QWidget):
                     bookmarks = f.readlines()
                     bookmarks = f.readlines()
             except IOError:
             except IOError:
                 self.app.log.error("Could not load bookmarks file.")
                 self.app.log.error("Could not load bookmarks file.")
-                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load bookmarks file."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load the file."))
                 return
                 return
 
 
             for line in bookmarks:
             for line in bookmarks:

+ 232 - 3
CHANGELOG.md

@@ -7,6 +7,235 @@ CHANGELOG for FlatCAM beta
 
 
 =================================================
 =================================================
 
 
+7.11.2020
+
+- fixed a small issue in Excellon Editor that reset the delta coordinates on right mouse button click too, which was incorrect. Only left mouse button click should reset the delta coordinates.
+- In Gerber Editor upgraded the UI
+- in Gerber Editor made sure that trying to add a Circular Pad array with null radius will fail
+- in Gerber Editor when the radius is zero the utility geometry is deleted
+- in Excellon Editor made sure that trying to add a Circular Drill/Slot array with null radius will fail
+- in Excellon Editor when the radius is zero the utility geometry is deleted
+- in Gerber Editor fixed an error in the Eraser tool trying to disconnect the Jump signal
+- small UI change in the Isolation Tool for the Reference Object selection
+- small UI changes in NCC Tool and in Paint Tool for the Reference Object selection
+- language strings recompiled to make sure that the .MO files are well optimized
+RELEASE 8.994
+
+6.11.2020
+
+- in Gerber Editor made the selection multithreaded in a bid to get more performance but until Shapely will start working on vectorized geometry this don't yield too much improvement
+- in Gerber Editor, for selection now the intersection of the click point and the geometry is determined for chunks of the original geometry, each chunk gets done in a separate process
+- updated the French translation (by Olivier Cornet)
+- fixed the new InputDialog widget to set its passed values in the constructor
+- in Gerber Editor fixed the Add circular array capability
+- in Gerber Editor remade the utility geometry generation for Circular Pad Array to show the array updated in real time and also fixed the adding of array in negative quadrants
+- in Excellon Editor remade the utility geometry generation for Circular Drill/Slot Array to show the array updated in real time and also fixed the adding of array in negative quadrants
+- Turkish language strings updated (by Mehmet Kaya)
+- both for Excellon and Gerber editor fixed the direction of slots/pads when adding a circular array
+- in Gerber editor added the G key shortcut to toggle the grid snapping
+- made some changes in the Region Tool from the Gerber Editor
+
+5.11.2020
+
+- fixed the annotation plotting in the CNCJob object
+- created a new InputDialog widget that has the buttons and the context menu translated and replaced the old widget throughout the app
+- updated the translation strings
+- Turkish language strings updated
+- set some policy rules back the way they were for the combo boxes in Geometry Object properties
+- updated the Italian translation (by Massimiliano Golfetto)
+- finished the Google-translation of the German language strings
+
+4.11.2020
+
+- updated all the translation files
+- fixed issue with arrays of items could not be added in the Gerber/Excellon Editor when a translation is used
+- fixed issue in the Excellon Editor where the Space key did not toggle the direction of the array of drills
+- combed the application strings all over the app and trimmed them up until those starting with letter 'O'
+- updated the translation strings
+- fixed the UI layout in Excellon Editor and made sure that after changing a value in the Notebook side after the mouse is inside the canvas, the canvas takes the focus allowing the key shortcuts to work
+- Turkish language strings updated (by Mehmet Kaya)
+- in Gerber Editor added the shortcut key 'Space' to change the direction of the array of pads
+- updated all the translation languages. Translated by Google the Spanish, Russian. Romanian translation updated.
+- refactored the name of the classes from the Gerber Editor
+- added more icons in the Gerber and Excellon Editors for the buttons
+
+3.11.2020
+
+- fixed an issue in Tool Isolation used with tools from the Tools Database: the set offset value was not used
+- updated the Tools Database to include all the Geometry keys in the every tool from database
+- made sure that the Operation Type values ('Iso', 'Rough' and 'Finish') are not translated as this may create issues all over the application
+- fix an older issue that made that only the Custom choice created an effect when changing the Offset in the Geometry Object Tool Table
+- trying to optimize Gerber Editor selection with the mouse
+- optimized some of the strings
+- fixed the project context save functionality to work in the new program configuration
+- updated Turkish translation (by Mehmet Kaya)
+- in NCC and Isolation Tools, the Validity Checking of the tools is now multithreaded when the Check Validity UI control is checked
+- translation strings updated
+- fixed an error in Gerber parser, when it encounter a pen-up followed by pen-down move while in a region
+- trimmed the application strings
+- updated the Italian translation (by Massimiliano Golfetto)
+- fixed a series of issues in Gerber Editor tools when the user is trying to use the tools by preselecting a aperture without size (aperture macro)
+- moved all the UI stuff out of the Gerber Editor class in its own class
+- in the Excellon Editor, added shortcut keys Space and Ctrl+Space for toggling the direction of the Slots, respectively for the Array of Slots
+- updated the translation strings to the latest changes in the app strings
+
+2.11.2020
+
+- fixed the Tcl Command AlignDrill
+- fixed the Tcl Command AlignDrillGrid
+- fixed the Tcl COmmand Panelize, Excellon panelization section
+- Fixed an issue in Tool Calibration export_excellon method call
+- PEP8 corrections all over the app
+- made sure that the object selection will not work while in Editors or in the App Tools
+- some minor changes to strings and icons
+- in Corner Markers Tool - the new Gerber object will have also follow_geometry
+- upgraded the Fiducials Tool to create new objects instead of updating in place the source objects
+- upgraded the Copper Thieving Tool to create new objects instead of updating in place the source objects
+- in Copper Thieving Tool added a new parameter to filter areas too small to be desired in the copper thieving; added it to Preferences too
+- Copper Thieving Tool added a new parameter to select what extra geometry to include in the Pattern Plating Mask; added it to the Preferences
+- made a wide change on the spinners GUI ranges: from 9999.9999 all values to 10000.0000
+- fixed some late issues in Corner Markers Tool new feature (messages)
+- upgraded Calculator Tool and added the new parameter is the Preferences
+- updated translation strings
+- fixed borderline bug in Gerber editor when the edited Gerber object last aperture is a aperture without size (Aperture Macro)
+- improved the loading of a Gerber object in the Gerber Editor
+- updated translation strings
+
+1.11.2020
+
+- updated the French Translation (by Olivier Cornet)
+- fixed issue in Corner Markers Tool that crashed the app if only one corner was checked
+- fixed issue in Isolation Tool where Area Isolation selection was not working 
+- added to the translatable strings the category labels in the Project Tab and also updated the translations
+- fixed a small issue (messages) in Corner Markers Tool
+- in Corners Markers Tool added a new feature: possibility to use cross shape markers
+- in Corner Marker Tool add new feature: ability to create an Excellon object with drill holes in the corner markes
+- in Corner Marker Tool, will no longer update the current object with the marker geometry but create a new Gerber object
+- in Join Excellon functionality made sure that the new Combo Exellon object will have copied the data from source objects and not just references, therefore will survive the delete of its parents
+- updated Turkish translation (by Mehmet Kaya)
+- updated all the languages except Turkish
+- in the Tool PDF fixed the creation of Excellon objects to the current Excellon object data structure
+
+31.10.2020
+
+- adapted HPGL importer to work within the new app
+- in Gerber Editor fixed an error when using the Distance Tool with "Snap to center" option active: if clicking not on a pad Distance Tool was not working
+- updated the Turkish translation strings (by Mehmet Kaya)
+- typo fixed in Copper Thieving Tool (due of recent changes)
+- fixed issue #457; wrong reference when saving a project
+- fixed issue in Excellon Editor that crashed the app if using the Resize Drill feature by clicking in menu/toolbar
+- fixed issue in Excellon Editor when using the menu links to Move or Copy Drills/Slots
+- updated the strings 
+- updated the Turkish translation strings (by Mehmet Kaya)
+- added a parent to some of the FCInputDialog widgets used in the app such that those pop-up windows will b displayed in the center of the app main window as opposed to the center of the screen
+- finished the Google-translation of not translated strings in Russian language
+
+30.10.2020
+
+- fixed the Punch Gerber Tool bug that did not allowed the projects to be loaded or to create a new project. Fixed issue #456
+- in Tool Subtract added an option to delete the source objects after a successful operation. Fixed issue #455
+- when entering into an Editor now the Project tab is disabled and the Properties tab where the Editor is installed change the text to 'Editor' and the color is set in Red. After exiting the Tab text is reverted to previous state.
+- fixed and issue where the Tab color that was changed in various states of the app was reverted back to a default color 'black'. Now it reverts to whatever color had before therefore being compatible with an usage of black theme
+- fixed bug that did not allow joining of any object to a Geometry object
+- working on solving the lost triggered signals for the Editor Toolbars buttons after changing the layout
+- fixed issue #454; trigger signals for Editor Toolbars lost after changing the layout
+- updated the translation strings
+- more bugs that were introduced by recent changes done to solve other bugs and so on: fixed issues with the Editors and Delete shortcut
+- fixed an error in the Gerber Editor
+
+29.10.2020
+
+- added icons in most application Tools
+- updated Punch Gerber Tool such that the aperture table is updated upon clicking of the checboxes in Processed Pads Type
+- updated Punch Gerber Tool: the Excellon method now takes into consideration the pads choice 
+- minor change for the FCComboBox UI element by setting its size policy as ignored so it will not expand the notebook when the name of one of its items is very long
+- added a protection on opening the tools database UI if the tools DB file is not loaded
+- fixed NCC Tool not working with the new changes; the check for not having complete isolation is just a Warning
+- fixed the sizePolicy for the FCComboBox widgets in the Preferences that holds the preprocessors
+- fixed issue with how the preamble / postamble GCode were inserted into the final GCode
+- fixed a small issue in GCode Editor where the signals for the buttons were attached again at each launch of the GCode Editor
+- fixed issues in the Tools Database due of recent changes in how the data structure is created
+- made sure that the right tools go only to the intended use, in Tools Database otherwise an error status message is created and Tools DB is closed on adding a wrong tool
+- fixed the usage for Tools Database in Unix-like OS's; fixed issue #453
+- done some modest refactoring
+- fixed the Search and Add feature in Geometry Object UI
+- fixed issue with preamble not being inserted when used alone
+- modified the way that the start GCode is stored such that now the bug in GCode Editor that did not allowed selection of the first tool is now solved
+- in Punch Gerber Tool added a column in the apertures table that allow marking of the selected aperture so the user can see what apertures are selected
+- improvements in the Punch Gerber Tool aperture markings
+- improved the Geometry Object functionality in regards of Tools DB, deleting a tool and adding a tool
+- when using the 'T' shortcut key with Properties Tab in focus and populated with the properties of a Geometry Object made the popped up spinner to have the value autoselected
+- optimized the UI in Extract Drills Tool
+- added some more icons for buttons
+
+28.10.2020
+
+- a series of PEP8 corrections in the FlatCAMGeometry.py
+- in Geometry UI finished a very basic way for the Polish feature (this will be upgraded in the future, for now is very rough)
+- added some new GUI elements by subclassing some widgets for the dialog pop-ups
+- in NCC Tool and Isolation Tool, pressing the shortcut key 'T' will bring the add new tool pop up in which now it is included the button to get the optimal diameter
+- in Geometry UI and for Solderpaste Tool replaced the pop up window that is launched when using shortcut key with one that has the context menu translated
+- some UI cleanup in the Geometry UI
+- updated the translation strings except Russian which could be in the works
+- fixed an error that did not allowed for the older preferences to be deleted when installing a different version of the software
+- in Legacy Mode fixed a small issue: the status bar icon for the Grid axis was not colored on app start
+- added a new string to the translatable strings
+- fixed an error that sometime showed in Legacy Mode when moving the mouse outside canvas
+- reactivated the shortcut key 'S' in TCL Shell, to close the shell dock when it was open (of course the focus has to be not on the command line)
+- brought up-to-date and fixed the Tcl Command Drillcncjob and Cncjob
+- fixed Tcl command Isolate to not print messages on message bar in case it is run headless
+- fixed Tcl command Copper Clear (NCC)
+- fixed Tcl command Paint
+- temporary fix for comboboxes not finding the the value in the items when setting themselves with a value by defaulting to the first item in the list
+- fix in Tool Subtract where there was a typo
+- upgraded the punch Gerber Tool
+- updated the Turkish translation strings (by Mehmet Kaya)
+- fixed an issue in Isolation Tool when running the app in Basic mode;
+- fixed Paint, Isolation and NCC Tools such the translated comboboxes values are now stored as indexes instead of translated words as before
+- in Geometry Object made sure that the widgets in the Tool Table gets populated regardless of encountering non-recognizable translated values
+- in Paint Tool found a small bug and fixed it
+- fixed the Tool Subtractor algorithms
+
+27.10.2020
+
+- created custom classes derived from TextEdit and from LineEdit where I overloaded the context menu and I made all the other classes that were inheriting from them to inherit from those new classes
+- minor fix in ToolsDB2UI
+- updated the Turkish translation strings (by Mehmet Kaya)
+- fixed a bug in conversion of any to Gerber in the section of Excellon conversion
+- some PEP8 fixes
+- fixed a bug due of recent chagnes in FileMenuHandlers class
+- fixed an issue in Tools Database (ToolsDB2 class) that did not made the Tab name in Red color when adding/deleting a tool by using the context menu
+- optimized the Tools Database
+- small string change
+
+26.10.2020
+
+- added a new menu entry and functionality in the View category: enable all non-selected (shortcut key ALT+3)
+- fixed shortcut keys for a number of functionality and in some cases added some new
+- fixed the enable/disable all plots functionality
+- fixed issue with the app window restored in a shifted position after doing Fullscreen
+- fixed issue with coords, delta_coords and status toolbars being disabled when entering fullscreen mode and remaining disabled after restore to normal mode
+- changed some of the strings (added a few in the How To section)
+- more strings updated
+- modified the shortcut strings and the way the shortcuts were listed in the Shortcut keys list such that it will allow a future Shortcuts Manager
+- updated all the language strings according to the modifications done above
+- fixed a small issue with using the shortcut key for toggling the display of Properties tab
+- fixed issue with not updating the model view on the model used in the Project tab when using the shortcut keys 1, 2, 3 which made the color of the tree element not reflect the change in status
+- minor string fix; removed the 'Close' menu entry on the context menu for the TCL Shell
+- overloaded the context menu in the Tcl Shell and added key shortcuts; the menu entries are now translatable
+- overloaded the context menu in several classes from GUI Elements such that the menus are now translated
+- fixed a formatting issue in the MainGUI.py file
+- updated the translations for the new strings that were added
+- another GUI element for which I've overloaded the context menu to make it translatable: _ExpandableTextEdit
+- overloaded the context menu for FCSpinner and for FCDoubleSpinner
+- added new strings and therefore updated the translation strings
+- fixed some minor issues when doing a project save
+
+25.10.2020
+
+- updated the Italian translation (by Massimiliano Golfetto)
+- finished the update of the Spanish translation (Google translate)
+
 24.10.2020
 24.10.2020
 
 
 - added a new GUI element, an InputDialog made out of FCSliderWithSpinner named FCInputDialogSlider
 - added a new GUI element, an InputDialog made out of FCSliderWithSpinner named FCInputDialogSlider
@@ -2895,7 +3124,7 @@ RELEASE 8.993
 - added all the tools from Gerber Editor to the the contextual menu
 - added all the tools from Gerber Editor to the the contextual menu
 - added the menu entry "Edit" in the Project contextual menu for Gerber objects
 - added the menu entry "Edit" in the Project contextual menu for Gerber objects
 - started to work in adding slots and slots array in Excellon Editor
 - started to work in adding slots and slots array in Excellon Editor
-- in FCSlot finished the utility geometry and the GUI for it
+- in SlotAdd finished the utility geometry and the GUI for it
 
 
 12.08.2019
 12.08.2019
 
 
@@ -3183,7 +3412,7 @@ RELEASE 8.993
 
 
 23.05.2019
 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 bug in Gerber editor FCDisk and DiscSemiEditorGrb 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
 - 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
 - some PEP8 cleanup in FlatCAMApp.py
 
 
@@ -3258,7 +3487,7 @@ RELEASE 8.993
 - made the Feedrate Rapids parameter to depend on the type of preprocessor choosed. It will be showed only for a preprocessor which the name contain 'marlin' and for any preprocessor's that have 'custom' in the name
 - made the Feedrate Rapids parameter to depend on the type of preprocessor choosed. It will be showed only for a preprocessor which the name contain 'marlin' and for any preprocessor's that have 'custom' in the name
 - fixed the camlib.Gerber functions of mirror, scale, offset, skew and rotate to work with the new data structure for apertures geometry
 - fixed the camlib.Gerber functions of mirror, scale, offset, skew and rotate to work with the new data structure for apertures geometry
 - fixed Gerber Editor selection to work with the new Gerber data structure in self.apertures
 - fixed Gerber Editor selection to work with the new Gerber data structure in self.apertures
-- fixed Gerber Editor FCPad class to work with the new Gerber data structure in self.apertures
+- fixed Gerber Editor PadEditorGrb class to work with the new Gerber data structure in self.apertures
 - fixed camlib.Gerber issues related to what happen after parsing rectangular apertures 
 - fixed camlib.Gerber issues related to what happen after parsing rectangular apertures 
 - wip in camblib.Gerber
 - wip in camblib.Gerber
 - completely converted the Gerber editor to the new data structure
 - completely converted the Gerber editor to the new data structure

+ 17 - 16
appCommon/Common.py

@@ -12,7 +12,7 @@
 # ##########################################################
 # ##########################################################
 from PyQt5 import QtCore
 from PyQt5 import QtCore
 
 
-from shapely.geometry import Polygon, Point, LineString, MultiPoint
+from shapely.geometry import Polygon, Point, LineString
 from shapely.ops import unary_union
 from shapely.ops import unary_union
 
 
 from appGUI.VisPyVisuals import ShapeCollection
 from appGUI.VisPyVisuals import ShapeCollection
@@ -20,7 +20,6 @@ from appTool import AppTool
 
 
 from copy import deepcopy
 from copy import deepcopy
 import collections
 import collections
-import traceback
 
 
 import numpy as np
 import numpy as np
 # from voronoi import Voronoi
 # from voronoi import Voronoi
@@ -39,6 +38,7 @@ class GracefulException(Exception):
     """
     """
     Graceful Exception raised when the user is requesting to cancel the current threaded task
     Graceful Exception raised when the user is requesting to cancel the current threaded task
     """
     """
+
     def __init__(self):
     def __init__(self):
         super().__init__()
         super().__init__()
 
 
@@ -95,7 +95,7 @@ class LoudUniqueList(list, collections.MutableSequence):
         super().__init__()
         super().__init__()
         self.callback = lambda x: None
         self.callback = lambda x: None
 
 
-        if not arg is None:
+        if arg is not None:
             if isinstance(arg, list):
             if isinstance(arg, list):
                 self.extend(arg)
                 self.extend(arg)
             else:
             else:
@@ -110,32 +110,32 @@ class LoudUniqueList(list, collections.MutableSequence):
     def append(self, v):
     def append(self, v):
         if v in self:
         if v in self:
             raise ValueError("One of the added items is already in the list.")
             raise ValueError("One of the added items is already in the list.")
-        l = len(self)
-        self.callback(l)
+        le = len(self)
+        self.callback(le)
         return super().append(v)
         return super().append(v)
 
 
     def extend(self, t):
     def extend(self, t):
         for v in t:
         for v in t:
             if v in self:
             if v in self:
                 raise ValueError("One of the added items is already in the list.")
                 raise ValueError("One of the added items is already in the list.")
-        l = len(self)
-        self.callback(l)
+        le = len(self)
+        self.callback(le)
         return super().extend(t)
         return super().extend(t)
 
 
     def __add__(self, t):  # This is for something like `LoudUniqueList([1, 2, 3]) + list([4, 5, 6])`...
     def __add__(self, t):  # This is for something like `LoudUniqueList([1, 2, 3]) + list([4, 5, 6])`...
         for v in t:
         for v in t:
             if v in self:
             if v in self:
                 raise ValueError("One of the added items is already in the list.")
                 raise ValueError("One of the added items is already in the list.")
-        l = len(self)
-        self.callback(l)
+        le = len(self)
+        self.callback(le)
         return super().__add__(t)
         return super().__add__(t)
 
 
     def __iadd__(self, t):  # This is for something like `l = LoudUniqueList(); l += [1, 2, 3]`
     def __iadd__(self, t):  # This is for something like `l = LoudUniqueList(); l += [1, 2, 3]`
         for v in t:
         for v in t:
             if v in self:
             if v in self:
                 raise ValueError("One of the added items is already in the list.")
                 raise ValueError("One of the added items is already in the list.")
-        l = len(self)
-        self.callback(l)
+        le = len(self)
+        self.callback(le)
         return super().__iadd__(t)
         return super().__iadd__(t)
 
 
     def __setitem__(self, i, v):
     def __setitem__(self, i, v):
@@ -146,7 +146,7 @@ class LoudUniqueList(list, collections.MutableSequence):
         except TypeError:
         except TypeError:
             if v in self:
             if v in self:
                 raise ValueError("One of the modified items is already in the list.")
                 raise ValueError("One of the modified items is already in the list.")
-        if not v is None:
+        if v is not None:
             self.callback(i)
             self.callback(i)
         return super().__setitem__(i, v)
         return super().__setitem__(i, v)
 
 
@@ -230,6 +230,7 @@ class ExclusionAreas(QtCore.QObject):
 
 
         self.app = app
         self.app = app
 
 
+        self.app.log.debug("+ Adding Exclusion Areas")
         # Storage for shapes, storage that can be used by FlatCAm tools for utility geometry
         # Storage for shapes, storage that can be used by FlatCAm tools for utility geometry
         # VisPy visuals
         # VisPy visuals
         if self.app.is_legacy is False:
         if self.app.is_legacy is False:
@@ -373,10 +374,10 @@ class ExclusionAreas(QtCore.QObject):
                     #     "overz":      float < - self.over_z_button
                     #     "overz":      float < - self.over_z_button
                     # }
                     # }
                     new_el = {
                     new_el = {
-                        "obj_type":     self.obj_type,
-                        "shape":        new_rectangle,
-                        "strategy":     self.strategy_button.get_value(),
-                        "overz":        self.over_z_button.get_value()
+                        "obj_type": self.obj_type,
+                        "shape": new_rectangle,
+                        "strategy": self.strategy_button.get_value(),
+                        "overz": self.over_z_button.get_value()
                     }
                     }
                     self.exclusion_areas_storage.append(new_el)
                     self.exclusion_areas_storage.append(new_el)
 
 

+ 4 - 4
appCommon/bilinear.py

@@ -71,10 +71,10 @@ class BilinearInterpolation(object):
         self.y_length = y_length
         self.y_length = y_length
         self.extrapolate = True
         self.extrapolate = True
 
 
-        #slopes = self.slopes = []
-        #for j in range(y_length):
-            #intervals = zip(x_index, x_index[1:], values[j], values[j][1:])
-            #slopes.append([(y2 - y1) / (x2 - x1) for x1, x2, y1, y2 in intervals])
+        # slopes = self.slopes = []
+        # for j in range(y_length):
+        #     intervals = zip(x_index, x_index[1:], values[j], values[j][1:])
+        #     slopes.append([(y2 - y1) / (x2 - x1) for x1, x2, y1, y2 in intervals])
 
 
     def __call__(self, x, y):
     def __call__(self, x, y):
         # local lookups
         # local lookups

+ 178 - 109
appDatabase.py

@@ -1,6 +1,6 @@
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5 import QtGui, QtCore, QtWidgets
 from appGUI.GUIElements import FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
 from appGUI.GUIElements import FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
-    FCTree, RadioSet, FCFileSaveDialog, FCLabel
+    FCTree, RadioSet, FCFileSaveDialog, FCLabel, FCComboBox2
 from camlib import to_dict
 from camlib import to_dict
 
 
 import sys
 import sys
@@ -25,6 +25,10 @@ class ToolsDB2UI:
         self.app = app
         self.app = app
         self.decimals = self.app.decimals
         self.decimals = self.app.decimals
 
 
+        self.offset_item_options = ["Path", "In", "Out", "Custom"]
+        self.type_item_options = ['Iso', 'Rough', 'Finish']
+        self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
+
         settings = QtCore.QSettings("Open Source", "FlatCAM")
         settings = QtCore.QSettings("Open Source", "FlatCAM")
         if settings.contains("machinist"):
         if settings.contains("machinist"):
             self.machinist_setting = settings.value('machinist', type=int)
             self.machinist_setting = settings.value('machinist', type=int)
@@ -177,20 +181,19 @@ class ToolsDB2UI:
         descript_vlay.addLayout(tools_vlay)
         descript_vlay.addLayout(tools_vlay)
         descript_vlay.addStretch()
         descript_vlay.addStretch()
 
 
-        milling_vlay = QtWidgets.QVBoxLayout()
-        milling_vlay.addWidget(self.milling_box)
-        milling_vlay.addStretch()
+        mill_vlay = QtWidgets.QVBoxLayout()
+        mill_vlay.addWidget(self.milling_box)
+        mill_vlay.addStretch()
 
 
         drilling_vlay = QtWidgets.QVBoxLayout()
         drilling_vlay = QtWidgets.QVBoxLayout()
         drilling_vlay.addWidget(self.drill_box)
         drilling_vlay.addWidget(self.drill_box)
 
 
         param_hlay.addLayout(descript_vlay)
         param_hlay.addLayout(descript_vlay)
-        param_hlay.addLayout(milling_vlay)
         param_hlay.addLayout(drilling_vlay)
         param_hlay.addLayout(drilling_vlay)
         param_hlay.addLayout(tools_vlay)
         param_hlay.addLayout(tools_vlay)
 
 
         # always visible, always to be included last
         # always visible, always to be included last
-        param_hlay.addLayout(milling_vlay)
+        param_hlay.addLayout(mill_vlay)
 
 
         param_hlay.addStretch()
         param_hlay.addStretch()
 
 
@@ -219,10 +222,10 @@ class ToolsDB2UI:
         # Tool Dia
         # Tool Dia
         self.dia_label = FCLabel('%s:' % _('Diameter'))
         self.dia_label = FCLabel('%s:' % _('Diameter'))
         self.dia_label.setToolTip(
         self.dia_label.setToolTip(
-            _("Tool Diameter."))
+            '%s.' % _("Tool Diameter"))
 
 
         self.dia_entry = FCDoubleSpinner()
         self.dia_entry = FCDoubleSpinner()
-        self.dia_entry.set_range(-9999.9999, 9999.9999)
+        self.dia_entry.set_range(-10000.0000, 10000.0000)
         self.dia_entry.set_precision(self.decimals)
         self.dia_entry.set_precision(self.decimals)
         self.dia_entry.setObjectName('gdb_dia')
         self.dia_entry.setObjectName('gdb_dia')
 
 
@@ -244,7 +247,7 @@ class ToolsDB2UI:
         )
         )
         self.tol_min_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.tol_min_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.tol_min_entry.set_precision(self.decimals)
         self.tol_min_entry.set_precision(self.decimals)
-        self.tol_min_entry.set_range(0, 9999.9999)
+        self.tol_min_entry.set_range(0, 10000.0000)
         self.tol_min_entry.setSingleStep(0.1)
         self.tol_min_entry.setSingleStep(0.1)
         self.tol_min_entry.setObjectName("gdb_tol_min")
         self.tol_min_entry.setObjectName("gdb_tol_min")
 
 
@@ -258,7 +261,7 @@ class ToolsDB2UI:
         )
         )
         self.tol_max_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.tol_max_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.tol_max_entry.set_precision(self.decimals)
         self.tol_max_entry.set_precision(self.decimals)
-        self.tol_max_entry.set_range(0, 9999.9999)
+        self.tol_max_entry.set_range(0, 10000.0000)
         self.tol_max_entry.setSingleStep(0.1)
         self.tol_max_entry.setSingleStep(0.1)
         self.tol_max_entry.setObjectName("gdb_tol_max")
         self.tol_max_entry.setObjectName("gdb_tol_max")
 
 
@@ -270,9 +273,9 @@ class ToolsDB2UI:
         self.tool_op_label.setToolTip(
         self.tool_op_label.setToolTip(
             _("The kind of Application Tool where this tool is to be used."))
             _("The kind of Application Tool where this tool is to be used."))
 
 
-        self.tool_op_combo = FCComboBox()
+        self.tool_op_combo = FCComboBox2()
         self.tool_op_combo.addItems(
         self.tool_op_combo.addItems(
-            [_("General"), _("Milling"), _("Drilling"), _('Isolation'), _('Paint'), _('NCC'), _("Cutout")])
+            [_("General"), _("Milling"), _("Drilling"), _('Isolation'), _('Paint'), _('NCC'), _('Cutout')])
         self.tool_op_combo.setObjectName('gdb_tool_target')
         self.tool_op_combo.setObjectName('gdb_tool_target')
 
 
         self.grid_tool.addWidget(self.tool_op_label, 8, 0)
         self.grid_tool.addWidget(self.tool_op_label, 8, 0)
@@ -297,7 +300,7 @@ class ToolsDB2UI:
               "V = v-shape milling tool"))
               "V = v-shape milling tool"))
 
 
         self.mill_shape_combo = FCComboBox()
         self.mill_shape_combo = FCComboBox()
-        self.mill_shape_combo.addItems(["C1", "C2", "C3", "C4", "B", "V"])
+        self.mill_shape_combo.addItems(self.tool_type_item_options)
         self.mill_shape_combo.setObjectName('gdb_shape')
         self.mill_shape_combo.setObjectName('gdb_shape')
 
 
         self.grid0.addWidget(self.shape_label, 2, 0)
         self.grid0.addWidget(self.shape_label, 2, 0)
@@ -310,7 +313,7 @@ class ToolsDB2UI:
               "Diameter of the tip for V-Shape Tools."))
               "Diameter of the tip for V-Shape Tools."))
 
 
         self.mill_vdia_entry = FCDoubleSpinner()
         self.mill_vdia_entry = FCDoubleSpinner()
-        self.mill_vdia_entry.set_range(0.0000, 9999.9999)
+        self.mill_vdia_entry.set_range(0.0000, 10000.0000)
         self.mill_vdia_entry.set_precision(self.decimals)
         self.mill_vdia_entry.set_precision(self.decimals)
         self.mill_vdia_entry.setObjectName('gdb_vdia')
         self.mill_vdia_entry.setObjectName('gdb_vdia')
 
 
@@ -346,7 +349,7 @@ class ToolsDB2UI:
               "Finish = finishing cut, high feedrate"))
               "Finish = finishing cut, high feedrate"))
 
 
         self.mill_type_combo = FCComboBox()
         self.mill_type_combo = FCComboBox()
-        self.mill_type_combo.addItems(["Iso", "Rough", "Finish"])
+        self.mill_type_combo.addItems(self.type_item_options)
         self.mill_type_combo.setObjectName('gdb_type')
         self.mill_type_combo.setObjectName('gdb_type')
 
 
         self.grid0.addWidget(self.type_label, 10, 0)
         self.grid0.addWidget(self.type_label, 10, 0)
@@ -363,7 +366,7 @@ class ToolsDB2UI:
               "Custom = custom offset using the Custom Offset value"))
               "Custom = custom offset using the Custom Offset value"))
 
 
         self.mill_tooloffset_combo = FCComboBox()
         self.mill_tooloffset_combo = FCComboBox()
-        self.mill_tooloffset_combo.addItems(["Path", "In", "Out", "Custom"])
+        self.mill_tooloffset_combo.addItems(self.offset_item_options)
         self.mill_tooloffset_combo.setObjectName('gdb_tool_offset')
         self.mill_tooloffset_combo.setObjectName('gdb_tool_offset')
 
 
         self.grid0.addWidget(self.tooloffset_label, 12, 0)
         self.grid0.addWidget(self.tooloffset_label, 12, 0)
@@ -376,7 +379,7 @@ class ToolsDB2UI:
               "A value to be used as offset from the current path."))
               "A value to be used as offset from the current path."))
 
 
         self.mill_custom_offset_entry = FCDoubleSpinner()
         self.mill_custom_offset_entry = FCDoubleSpinner()
-        self.mill_custom_offset_entry.set_range(-9999.9999, 9999.9999)
+        self.mill_custom_offset_entry.set_range(-10000.0000, 10000.0000)
         self.mill_custom_offset_entry.set_precision(self.decimals)
         self.mill_custom_offset_entry.set_precision(self.decimals)
         self.mill_custom_offset_entry.setObjectName('gdb_custom_offset')
         self.mill_custom_offset_entry.setObjectName('gdb_custom_offset')
 
 
@@ -395,7 +398,7 @@ class ToolsDB2UI:
               "The depth at which to cut into material."))
               "The depth at which to cut into material."))
 
 
         self.mill_cutz_entry = FCDoubleSpinner()
         self.mill_cutz_entry = FCDoubleSpinner()
-        self.mill_cutz_entry.set_range(-9999.9999, 9999.9999)
+        self.mill_cutz_entry.set_range(-10000.0000, 10000.0000)
         self.mill_cutz_entry.set_precision(self.decimals)
         self.mill_cutz_entry.set_precision(self.decimals)
         self.mill_cutz_entry.setObjectName('gdb_cutz')
         self.mill_cutz_entry.setObjectName('gdb_cutz')
 
 
@@ -422,7 +425,7 @@ class ToolsDB2UI:
               "The value used to cut into material on each pass."))
               "The value used to cut into material on each pass."))
 
 
         self.mill_multidepth_entry = FCDoubleSpinner()
         self.mill_multidepth_entry = FCDoubleSpinner()
-        self.mill_multidepth_entry.set_range(-9999.9999, 9999.9999)
+        self.mill_multidepth_entry.set_range(-10000.0000, 10000.0000)
         self.mill_multidepth_entry.set_precision(self.decimals)
         self.mill_multidepth_entry.set_precision(self.decimals)
         self.mill_multidepth_entry.setObjectName('gdb_multidepth_entry')
         self.mill_multidepth_entry.setObjectName('gdb_multidepth_entry')
 
 
@@ -437,7 +440,7 @@ class ToolsDB2UI:
               "above the surface of the material, avoiding all fixtures."))
               "above the surface of the material, avoiding all fixtures."))
 
 
         self.mill_travelz_entry = FCDoubleSpinner()
         self.mill_travelz_entry = FCDoubleSpinner()
-        self.mill_travelz_entry.set_range(-9999.9999, 9999.9999)
+        self.mill_travelz_entry.set_range(-10000.0000, 10000.0000)
         self.mill_travelz_entry.set_precision(self.decimals)
         self.mill_travelz_entry.set_precision(self.decimals)
         self.mill_travelz_entry.setObjectName('gdb_travelz')
         self.mill_travelz_entry.setObjectName('gdb_travelz')
 
 
@@ -470,7 +473,7 @@ class ToolsDB2UI:
               "the extra cut."))
               "the extra cut."))
 
 
         self.mill_ecut_length_entry = FCDoubleSpinner()
         self.mill_ecut_length_entry = FCDoubleSpinner()
-        self.mill_ecut_length_entry.set_range(0.0000, 9999.9999)
+        self.mill_ecut_length_entry.set_range(0.0000, 10000.0000)
         self.mill_ecut_length_entry.set_precision(self.decimals)
         self.mill_ecut_length_entry.set_precision(self.decimals)
         self.mill_ecut_length_entry.setObjectName('gdb_ecut_length')
         self.mill_ecut_length_entry.setObjectName('gdb_ecut_length')
 
 
@@ -489,7 +492,7 @@ class ToolsDB2UI:
               "The speed on XY plane used while cutting into material."))
               "The speed on XY plane used while cutting into material."))
 
 
         self.mill_frxy_entry = FCDoubleSpinner()
         self.mill_frxy_entry = FCDoubleSpinner()
-        self.mill_frxy_entry.set_range(-999999.9999, 999999.9999)
+        self.mill_frxy_entry.set_range(-9910000.0000, 9910000.0000)
         self.mill_frxy_entry.set_precision(self.decimals)
         self.mill_frxy_entry.set_precision(self.decimals)
         self.mill_frxy_entry.setObjectName('gdb_frxy')
         self.mill_frxy_entry.setObjectName('gdb_frxy')
 
 
@@ -503,7 +506,7 @@ class ToolsDB2UI:
               "The speed on Z plane."))
               "The speed on Z plane."))
 
 
         self.mill_frz_entry = FCDoubleSpinner()
         self.mill_frz_entry = FCDoubleSpinner()
-        self.mill_frz_entry.set_range(-999999.9999, 999999.9999)
+        self.mill_frz_entry.set_range(-9910000.0000, 9910000.0000)
         self.mill_frz_entry.set_precision(self.decimals)
         self.mill_frz_entry.set_precision(self.decimals)
         self.mill_frz_entry.setObjectName('gdb_frz')
         self.mill_frz_entry.setObjectName('gdb_frz')
 
 
@@ -519,7 +522,7 @@ class ToolsDB2UI:
               "the G0 g-code command. Mostly 3D printers."))
               "the G0 g-code command. Mostly 3D printers."))
 
 
         self.mill_frapids_entry = FCDoubleSpinner()
         self.mill_frapids_entry = FCDoubleSpinner()
-        self.mill_frapids_entry.set_range(0.0000, 9999.9999)
+        self.mill_frapids_entry.set_range(0.0000, 10000.0000)
         self.mill_frapids_entry.set_precision(self.decimals)
         self.mill_frapids_entry.set_precision(self.decimals)
         self.mill_frapids_entry.setObjectName('gdb_frapids')
         self.mill_frapids_entry.setObjectName('gdb_frapids')
 
 
@@ -539,7 +542,7 @@ class ToolsDB2UI:
               "The speed of the spindle in RPM."))
               "The speed of the spindle in RPM."))
 
 
         self.mill_spindle_entry = FCDoubleSpinner()
         self.mill_spindle_entry = FCDoubleSpinner()
-        self.mill_spindle_entry.set_range(-999999.9999, 999999.9999)
+        self.mill_spindle_entry.set_range(-9910000.0000, 9910000.0000)
         self.mill_spindle_entry.set_precision(self.decimals)
         self.mill_spindle_entry.set_precision(self.decimals)
         self.mill_spindle_entry.setObjectName('gdb_spindle')
         self.mill_spindle_entry.setObjectName('gdb_spindle')
 
 
@@ -566,7 +569,7 @@ class ToolsDB2UI:
               "A delay used to allow the motor spindle reach its set speed."))
               "A delay used to allow the motor spindle reach its set speed."))
 
 
         self.mill_dwelltime_entry = FCDoubleSpinner()
         self.mill_dwelltime_entry = FCDoubleSpinner()
-        self.mill_dwelltime_entry.set_range(0.0000, 9999.9999)
+        self.mill_dwelltime_entry.set_range(0.0000, 10000.0000)
         self.mill_dwelltime_entry.set_precision(self.decimals)
         self.mill_dwelltime_entry.set_precision(self.decimals)
         self.mill_dwelltime_entry.setObjectName('gdb_dwelltime')
         self.mill_dwelltime_entry.setObjectName('gdb_dwelltime')
 
 
@@ -604,7 +607,7 @@ class ToolsDB2UI:
         # Milling Type Radio Button
         # Milling Type Radio Button
         self.milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.milling_type_label.setToolTip(
         self.milling_type_label.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -612,7 +615,7 @@ class ToolsDB2UI:
         self.ncc_milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
         self.ncc_milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
                                                 {'label': _('Conventional'), 'value': 'cv'}])
                                                 {'label': _('Conventional'), 'value': 'cv'}])
         self.ncc_milling_type_radio.setToolTip(
         self.ncc_milling_type_radio.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -626,8 +629,8 @@ class ToolsDB2UI:
         nccoverlabel.setToolTip(
         nccoverlabel.setToolTip(
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
               "Adjust the value starting with lower values\n"
               "Adjust the value starting with lower values\n"
-              "and increasing it if areas that should be cleared are still \n"
-              "not cleared.\n"
+              "and increasing it if areas that should be processed are still \n"
+              "not processed.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "due of too many paths.")
               "due of too many paths.")
@@ -649,7 +652,7 @@ class ToolsDB2UI:
         )
         )
         self.ncc_margin_entry = FCDoubleSpinner()
         self.ncc_margin_entry = FCDoubleSpinner()
         self.ncc_margin_entry.set_precision(self.decimals)
         self.ncc_margin_entry.set_precision(self.decimals)
-        self.ncc_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.ncc_margin_entry.set_range(-10000.0000, 10000.0000)
         self.ncc_margin_entry.setObjectName("gdb_n_margin")
         self.ncc_margin_entry.setObjectName("gdb_n_margin")
 
 
         self.grid2.addWidget(nccmarginlabel, 16, 0)
         self.grid2.addWidget(nccmarginlabel, 16, 0)
@@ -664,7 +667,7 @@ class ToolsDB2UI:
               "- Line-based: Parallel lines.")
               "- Line-based: Parallel lines.")
         )
         )
 
 
-        self.ncc_method_combo = FCComboBox()
+        self.ncc_method_combo = FCComboBox2()
         self.ncc_method_combo.addItems(
         self.ncc_method_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
             [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
         )
         )
@@ -700,8 +703,7 @@ class ToolsDB2UI:
         self.ncc_choice_offset_cb.setToolTip(
         self.ncc_choice_offset_cb.setToolTip(
             _("If used, it will add an offset to the copper features.\n"
             _("If used, it will add an offset to the copper features.\n"
               "The copper clearing will finish to a distance\n"
               "The copper clearing will finish to a distance\n"
-              "from the copper features.\n"
-              "The value can be between 0 and 10 FlatCAM units.")
+              "from the copper features.")
         )
         )
         self.grid2.addWidget(self.ncc_choice_offset_cb, 19, 0)
         self.grid2.addWidget(self.ncc_choice_offset_cb, 19, 0)
 
 
@@ -735,8 +737,8 @@ class ToolsDB2UI:
         ovlabel.setToolTip(
         ovlabel.setToolTip(
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
               "Adjust the value starting with lower values\n"
               "Adjust the value starting with lower values\n"
-              "and increasing it if areas that should be painted are still \n"
-              "not painted.\n"
+              "and increasing it if areas that should be processed are still \n"
+              "not processed.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "due of too many paths.")
               "due of too many paths.")
@@ -760,7 +762,7 @@ class ToolsDB2UI:
         )
         )
         self.paint_offset_entry = FCDoubleSpinner()
         self.paint_offset_entry = FCDoubleSpinner()
         self.paint_offset_entry.set_precision(self.decimals)
         self.paint_offset_entry.set_precision(self.decimals)
-        self.paint_offset_entry.set_range(-9999.9999, 9999.9999)
+        self.paint_offset_entry.set_range(-10000.0000, 10000.0000)
         self.paint_offset_entry.setObjectName('gdb_p_offset')
         self.paint_offset_entry.setObjectName('gdb_p_offset')
 
 
         self.grid3.addWidget(marginlabel, 2, 0)
         self.grid3.addWidget(marginlabel, 2, 0)
@@ -779,7 +781,7 @@ class ToolsDB2UI:
               "in the order specified.")
               "in the order specified.")
         )
         )
 
 
-        self.paint_method_combo = FCComboBox()
+        self.paint_method_combo = FCComboBox2()
         self.paint_method_combo.addItems(
         self.paint_method_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
             [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
         )
         )
@@ -850,7 +852,7 @@ class ToolsDB2UI:
         # Milling Type Radio Button
         # Milling Type Radio Button
         self.iso_milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.iso_milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.iso_milling_type_label.setToolTip(
         self.iso_milling_type_label.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -858,7 +860,7 @@ class ToolsDB2UI:
         self.iso_milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
         self.iso_milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
                                                 {'label': _('Conventional'), 'value': 'cv'}])
                                                 {'label': _('Conventional'), 'value': 'cv'}])
         self.iso_milling_type_radio.setToolTip(
         self.iso_milling_type_radio.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -877,8 +879,8 @@ class ToolsDB2UI:
 
 
         self.iso_follow_cb = FCCheckBox()
         self.iso_follow_cb = FCCheckBox()
         self.iso_follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
         self.iso_follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
-                                    "This means that it will cut through\n"
-                                    "the middle of the trace."))
+                                        "This means that it will cut through\n"
+                                        "the middle of the trace."))
         self.iso_follow_cb.setObjectName("gdb_i_follow")
         self.iso_follow_cb.setObjectName("gdb_i_follow")
 
 
         self.grid4.addWidget(self.follow_label, 6, 0)
         self.grid4.addWidget(self.follow_label, 6, 0)
@@ -924,9 +926,9 @@ class ToolsDB2UI:
         self.drill_cutz_entry.set_precision(self.decimals)
         self.drill_cutz_entry.set_precision(self.decimals)
 
 
         if self.machinist_setting == 0:
         if self.machinist_setting == 0:
-            self.drill_cutz_entry.set_range(-9999.9999, 0.0000)
+            self.drill_cutz_entry.set_range(-10000.0000, 0.0000)
         else:
         else:
-            self.drill_cutz_entry.set_range(-9999.9999, 9999.9999)
+            self.drill_cutz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.drill_cutz_entry.setSingleStep(0.1)
         self.drill_cutz_entry.setSingleStep(0.1)
         self.drill_cutz_entry.setObjectName("gdb_e_cutz")
         self.drill_cutz_entry.setObjectName("gdb_e_cutz")
@@ -944,7 +946,7 @@ class ToolsDB2UI:
 
 
         self.drill_offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_offset_entry.set_precision(self.decimals)
         self.drill_offset_entry.set_precision(self.decimals)
-        self.drill_offset_entry.set_range(-9999.9999, 9999.9999)
+        self.drill_offset_entry.set_range(-10000.0000, 10000.0000)
         self.drill_offset_entry.setObjectName("gdb_e_offset")
         self.drill_offset_entry.setObjectName("gdb_e_offset")
 
 
         self.grid5.addWidget(self.tool_offset_label, 6, 0)
         self.grid5.addWidget(self.tool_offset_label, 6, 0)
@@ -973,7 +975,7 @@ class ToolsDB2UI:
               "The value used to cut into material on each pass."))
               "The value used to cut into material on each pass."))
         self.drill_maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_maxdepth_entry.set_precision(self.decimals)
         self.drill_maxdepth_entry.set_precision(self.decimals)
-        self.drill_maxdepth_entry.set_range(0, 9999.9999)
+        self.drill_maxdepth_entry.set_range(0, 10000.0000)
         self.drill_maxdepth_entry.setSingleStep(0.1)
         self.drill_maxdepth_entry.setSingleStep(0.1)
 
 
         self.drill_maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
         self.drill_maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
@@ -993,9 +995,9 @@ class ToolsDB2UI:
         self.drill_travelz_entry.set_precision(self.decimals)
         self.drill_travelz_entry.set_precision(self.decimals)
 
 
         if self.machinist_setting == 0:
         if self.machinist_setting == 0:
-            self.drill_travelz_entry.set_range(0.00001, 9999.9999)
+            self.drill_travelz_entry.set_range(0.00001, 10000.0000)
         else:
         else:
-            self.drill_travelz_entry.set_range(-9999.9999, 9999.9999)
+            self.drill_travelz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.drill_travelz_entry.setSingleStep(0.1)
         self.drill_travelz_entry.setSingleStep(0.1)
         self.drill_travelz_entry.setObjectName("gdb_e_travelz")
         self.drill_travelz_entry.setObjectName("gdb_e_travelz")
@@ -1018,7 +1020,7 @@ class ToolsDB2UI:
         )
         )
         self.drill_feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_feedrate_z_entry.set_precision(self.decimals)
         self.drill_feedrate_z_entry.set_precision(self.decimals)
-        self.drill_feedrate_z_entry.set_range(0.0, 99999.9999)
+        self.drill_feedrate_z_entry.set_range(0.0, 910000.0000)
         self.drill_feedrate_z_entry.setSingleStep(0.1)
         self.drill_feedrate_z_entry.setSingleStep(0.1)
         self.drill_feedrate_z_entry.setObjectName("gdb_e_feedratez")
         self.drill_feedrate_z_entry.setObjectName("gdb_e_feedratez")
 
 
@@ -1036,7 +1038,7 @@ class ToolsDB2UI:
         )
         )
         self.drill_feedrate_rapid_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_feedrate_rapid_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_feedrate_rapid_entry.set_precision(self.decimals)
         self.drill_feedrate_rapid_entry.set_precision(self.decimals)
-        self.drill_feedrate_rapid_entry.set_range(0.0, 99999.9999)
+        self.drill_feedrate_rapid_entry.set_range(0.0, 910000.0000)
         self.drill_feedrate_rapid_entry.setSingleStep(0.1)
         self.drill_feedrate_rapid_entry.setSingleStep(0.1)
         self.drill_feedrate_rapid_entry.setObjectName("gdb_e_fr_rapid")
         self.drill_feedrate_rapid_entry.setObjectName("gdb_e_fr_rapid")
 
 
@@ -1083,7 +1085,7 @@ class ToolsDB2UI:
               "A delay used to allow the motor spindle reach its set speed."))
               "A delay used to allow the motor spindle reach its set speed."))
         self.drill_dwelltime_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_dwelltime_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.drill_dwelltime_entry.set_precision(self.decimals)
         self.drill_dwelltime_entry.set_precision(self.decimals)
-        self.drill_dwelltime_entry.set_range(0.0, 9999.9999)
+        self.drill_dwelltime_entry.set_range(0.0, 10000.0000)
         self.drill_dwelltime_entry.setSingleStep(0.1)
         self.drill_dwelltime_entry.setSingleStep(0.1)
         self.drill_dwelltime_entry.setObjectName("gdb_e_dwelltime")
         self.drill_dwelltime_entry.setObjectName("gdb_e_dwelltime")
 
 
@@ -1146,7 +1148,7 @@ class ToolsDB2UI:
 
 
         # Margin
         # Margin
         self.cutout_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.cutout_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.cutout_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.cutout_margin_entry.set_range(-10000.0000, 10000.0000)
         self.cutout_margin_entry.setSingleStep(0.1)
         self.cutout_margin_entry.setSingleStep(0.1)
         self.cutout_margin_entry.set_precision(self.decimals)
         self.cutout_margin_entry.set_precision(self.decimals)
         self.cutout_margin_entry.setObjectName('gdb_ct_margin')
         self.cutout_margin_entry.setObjectName('gdb_ct_margin')
@@ -1208,9 +1210,9 @@ class ToolsDB2UI:
         self.cutout_thin_depth_entry.setObjectName('gdb_ct_gap_depth')
         self.cutout_thin_depth_entry.setObjectName('gdb_ct_gap_depth')
 
 
         if self.machinist_setting == 0:
         if self.machinist_setting == 0:
-            self.cutout_thin_depth_entry.setRange(-9999.9999, -0.00001)
+            self.cutout_thin_depth_entry.setRange(-10000.0000, -0.00001)
         else:
         else:
-            self.cutout_thin_depth_entry.setRange(-9999.9999, 9999.9999)
+            self.cutout_thin_depth_entry.setRange(-10000.0000, 10000.0000)
         self.cutout_thin_depth_entry.setSingleStep(0.1)
         self.cutout_thin_depth_entry.setSingleStep(0.1)
 
 
         self.grid6.addWidget(self.thin_depth_label, 17, 0)
         self.grid6.addWidget(self.thin_depth_label, 17, 0)
@@ -1394,19 +1396,16 @@ class ToolsDB2(QtWidgets.QWidget):
 
 
     mark_tools_rows = QtCore.pyqtSignal()
     mark_tools_rows = QtCore.pyqtSignal()
 
 
-    def __init__(self, app, callback_on_edited, callback_on_tool_request, parent=None):
+    def __init__(self, app, callback_on_tool_request, parent=None):
         super(ToolsDB2, self).__init__(parent)
         super(ToolsDB2, self).__init__(parent)
 
 
         self.app = app
         self.app = app
         self.app_ui = self.app.ui
         self.app_ui = self.app.ui
         self.decimals = self.app.decimals
         self.decimals = self.app.decimals
-        self.callback_app = callback_on_edited
 
 
         self.on_tool_request = callback_on_tool_request
         self.on_tool_request = callback_on_tool_request
 
 
-        self.offset_item_options = ["Path", "In", "Out", "Custom"]
-        self.type_item_options = ["Iso", "Rough", "Finish"]
-        self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
+        self.tools_db_changed_flag = False
 
 
         '''
         '''
         dict to hold all the tools in the Tools DB
         dict to hold all the tools in the Tools DB
@@ -1417,7 +1416,7 @@ class ToolsDB2(QtWidgets.QWidget):
                 'tooldia': self.app.defaults["geometry_cnctooldia"]
                 'tooldia': self.app.defaults["geometry_cnctooldia"]
                 'offset': 'Path'
                 'offset': 'Path'
                 'offset_value': 0.0
                 'offset_value': 0.0
-                'type':  _('Rough'),
+                'type':  'Rough',
                 'tool_type': 'C1'
                 'tool_type': 'C1'
                 'data': dict()
                 'data': dict()
             }
             }
@@ -1425,6 +1424,8 @@ class ToolsDB2(QtWidgets.QWidget):
         '''
         '''
         self.db_tool_dict = {}
         self.db_tool_dict = {}
 
 
+        self.old_color = QtGui.QColor('black')
+
         # ##############################################################################
         # ##############################################################################
         # ##############################################################################
         # ##############################################################################
         # TOOLS DATABASE UI
         # TOOLS DATABASE UI
@@ -1719,7 +1720,13 @@ class ToolsDB2(QtWidgets.QWidget):
         self.ui_connect()
         self.ui_connect()
 
 
     def setup_db_ui(self):
     def setup_db_ui(self):
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+
+        # set the old color for the Tools Database Tab
+        for idx in range(self.app.ui.plot_tab_area.count()):
+            if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                self.old_color = self.app.ui.plot_tab_area.tabBar.tabTextColor(idx)
+
+        filename = self.app.tools_database_path()
 
 
         # load the database tools from the file
         # load the database tools from the file
         try:
         try:
@@ -1727,7 +1734,7 @@ class ToolsDB2(QtWidgets.QWidget):
                 tools = f.read()
                 tools = f.read()
         except IOError:
         except IOError:
             self.app.log.error("Could not load tools DB file.")
             self.app.log.error("Could not load tools DB file.")
-            self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
+            self.app.inform.emit('[ERROR] %s' % _("Could not load the file."))
             return
             return
 
 
         try:
         try:
@@ -1811,7 +1818,7 @@ class ToolsDB2(QtWidgets.QWidget):
 
 
         self.ui.tool_description_box.setEnabled(True)
         self.ui.tool_description_box.setEnabled(True)
         if self.db_tool_dict:
         if self.db_tool_dict:
-            if tool_target == _("General"):
+            if tool_target == 0:    # _("General")
                 self.ui.milling_box.setEnabled(True)
                 self.ui.milling_box.setEnabled(True)
                 self.ui.ncc_box.setEnabled(True)
                 self.ui.ncc_box.setEnabled(True)
                 self.ui.paint_box.setEnabled(True)
                 self.ui.paint_box.setEnabled(True)
@@ -1833,33 +1840,33 @@ class ToolsDB2(QtWidgets.QWidget):
                 self.ui.drill_box.hide()
                 self.ui.drill_box.hide()
                 self.ui.cutout_box.hide()
                 self.ui.cutout_box.hide()
 
 
-                if tool_target == _("Milling"):
+                if tool_target == 1:    # _("Milling")
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
                     self.ui.milling_box.show()
 
 
-                if tool_target == _("Drilling"):
+                if tool_target == 2:    # _("Drilling")
                     self.ui.drill_box.setEnabled(True)
                     self.ui.drill_box.setEnabled(True)
                     self.ui.drill_box.show()
                     self.ui.drill_box.show()
 
 
-                if tool_target == _("Isolation"):
+                if tool_target == 3:    # _("Isolation")
                     self.ui.iso_box.setEnabled(True)
                     self.ui.iso_box.setEnabled(True)
                     self.ui.iso_box.show()
                     self.ui.iso_box.show()
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
                     self.ui.milling_box.show()
 
 
-                if tool_target == _("Paint"):
+                if tool_target == 4:    # _("Paint")
                     self.ui.paint_box.setEnabled(True)
                     self.ui.paint_box.setEnabled(True)
                     self.ui.paint_box.show()
                     self.ui.paint_box.show()
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
                     self.ui.milling_box.show()
 
 
-                if tool_target == _("NCC"):
+                if tool_target == 5:    # _("NCC")
                     self.ui.ncc_box.setEnabled(True)
                     self.ui.ncc_box.setEnabled(True)
                     self.ui.ncc_box.show()
                     self.ui.ncc_box.show()
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.show()
                     self.ui.milling_box.show()
 
 
-                if tool_target == _("Cutout"):
+                if tool_target == 6:    # _("Cutout")
                     self.ui.cutout_box.setEnabled(True)
                     self.ui.cutout_box.setEnabled(True)
                     self.ui.cutout_box.show()
                     self.ui.cutout_box.show()
                     self.ui.milling_box.setEnabled(True)
                     self.ui.milling_box.setEnabled(True)
@@ -1874,7 +1881,7 @@ class ToolsDB2(QtWidgets.QWidget):
         default_data = {}
         default_data = {}
         default_data.update({
         default_data.update({
             "plot":             True,
             "plot":             True,
-            "tool_target": _("General"),
+            "tool_target": 0,   # _("General")
             "tol_min": 0.0,
             "tol_min": 0.0,
             "tol_max": 0.0,
             "tol_max": 0.0,
 
 
@@ -1899,6 +1906,28 @@ class ToolsDB2(QtWidgets.QWidget):
             "toolchangez":      float(self.app.defaults["geometry_toolchangez"]),
             "toolchangez":      float(self.app.defaults["geometry_toolchangez"]),
             "startz":           self.app.defaults["geometry_startz"],
             "startz":           self.app.defaults["geometry_startz"],
             "endz":             float(self.app.defaults["geometry_endz"]),
             "endz":             float(self.app.defaults["geometry_endz"]),
+            "endxy":            self.app.defaults["geometry_endxy"],
+            "search_time":      int(self.app.defaults["geometry_search_time"]),
+            "z_pdepth":         float(self.app.defaults["geometry_z_pdepth"]),
+            "f_plunge":         float(self.app.defaults["geometry_f_plunge"]),
+
+            "spindledir":               self.app.defaults["geometry_spindledir"],
+            "optimization_type":        self.app.defaults["geometry_optimization_type"],
+            "feedrate_probe":           self.app.defaults["geometry_feedrate_probe"],
+
+            "segx":             self.app.defaults["geometry_segx"],
+            "segy":             self.app.defaults["geometry_segy"],
+            "area_exclusion":   self.app.defaults["geometry_area_exclusion"],
+            "area_shape":       self.app.defaults["geometry_area_shape"],
+            "area_strategy":    self.app.defaults["geometry_area_strategy"],
+            "area_overz":       self.app.defaults["geometry_area_overz"],
+            "polish":           self.app.defaults["geometry_polish"],
+            "polish_dia":       self.app.defaults["geometry_polish_dia"],
+            "polish_pressure":  self.app.defaults["geometry_polish_pressure"],
+            "polish_travelz":   self.app.defaults["geometry_polish_travelz"],
+            "polish_margin":    self.app.defaults["geometry_polish_margin"],
+            "polish_overlap":   self.app.defaults["geometry_polish_overlap"],
+            "polish_method":    self.app.defaults["geometry_polish_method"],
 
 
             # NCC
             # NCC
             "tools_ncc_operation":       self.app.defaults["tools_ncc_operation"],
             "tools_ncc_operation":       self.app.defaults["tools_ncc_operation"],
@@ -1963,8 +1992,7 @@ class ToolsDB2(QtWidgets.QWidget):
         else:
         else:
             new_name = "new_tool_1"
             new_name = "new_tool_1"
 
 
-        dict_elem = {}
-        dict_elem['name'] = new_name
+        dict_elem = {'name': new_name}
         if type(self.app.defaults["geometry_cnctooldia"]) == float:
         if type(self.app.defaults["geometry_cnctooldia"]) == float:
             dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
             dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
         else:
         else:
@@ -2029,7 +2057,7 @@ class ToolsDB2(QtWidgets.QWidget):
             self.ui.tree_widget.setCurrentItem(last_item)
             self.ui.tree_widget.setCurrentItem(last_item)
             last_item.setSelected(True)
             last_item.setSelected(True)
 
 
-        self.callback_app()
+        self.on_tools_db_edited()
         self.app.inform.emit('[success] %s' % _("Tool copied from Tools DB."))
         self.app.inform.emit('[success] %s' % _("Tool copied from Tools DB."))
 
 
     def on_tool_delete(self):
     def on_tool_delete(self):
@@ -2068,12 +2096,13 @@ class ToolsDB2(QtWidgets.QWidget):
         date = date.replace(' ', '_')
         date = date.replace(' ', '_')
 
 
         filter__ = "Text File (*.TXT);;All Files (*.*)"
         filter__ = "Text File (*.TXT);;All Files (*.*)"
-        filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Tools Database"),
-                                                           directory='{l_save}/FlatCAM_{n}_{date}'.format(
-                                                                l_save=str(self.app.get_last_save_folder()),
-                                                                n=_("Tools_Database"),
-                                                                date=date),
-                                                           ext_filter=filter__)
+
+        filename, _f = FCFileSaveDialog.get_saved_filename(
+            caption=_("Export Tools Database"),
+            directory='{l_save}/FlatCAM_{n}_{date}'.format(l_save=str(self.app.get_last_save_folder()),
+                                                           n=_("Tools_Database"),
+                                                           date=date),
+            ext_filter=filter__)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -2097,7 +2126,7 @@ class ToolsDB2(QtWidgets.QWidget):
                 e = sys.exc_info()[0]
                 e = sys.exc_info()[0]
                 self.app.log.error("Could not load Tools DB file.")
                 self.app.log.error("Could not load Tools DB file.")
                 self.app.log.error(str(e))
                 self.app.log.error(str(e))
-                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load the file."))
                 return
                 return
 
 
             # Save update options
             # Save update options
@@ -2131,7 +2160,7 @@ class ToolsDB2(QtWidgets.QWidget):
                     tools_in_db = f.read()
                     tools_in_db = f.read()
             except IOError:
             except IOError:
                 self.app.log.error("Could not load Tools DB file.")
                 self.app.log.error("Could not load Tools DB file.")
-                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load the file."))
                 return
                 return
 
 
             try:
             try:
@@ -2149,12 +2178,12 @@ class ToolsDB2(QtWidgets.QWidget):
     def on_save_tools_db(self, silent=False):
     def on_save_tools_db(self, silent=False):
         self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
         self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
 
 
-        filename = self.app.data_path + "/tools_db.FlatDB"
+        filename = self.app.tools_database_path()
 
 
         # Preferences save, update the color of the Tools DB Tab text
         # Preferences save, update the color of the Tools DB Tab text
         for idx in range(self.app_ui.plot_tab_area.count()):
         for idx in range(self.app_ui.plot_tab_area.count()):
             if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
             if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
-                self.app_ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+                self.app_ui.plot_tab_area.tabBar.setTabTextColor(idx, self.old_color)
                 self.ui.save_db_btn.setStyleSheet("")
                 self.ui.save_db_btn.setStyleSheet("")
 
 
                 # clean the dictionary and leave only keys of interest
                 # clean the dictionary and leave only keys of interest
@@ -2170,7 +2199,7 @@ class ToolsDB2(QtWidgets.QWidget):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Drilling'):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Drilling'):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                             if str(k).startswith('tools_'):
                             if str(k).startswith('tools_'):
-                                if str(k).startswith('tools_drill'):
+                                if str(k).startswith('tools_drill') or str(k).startswith('tools_mill'):
                                     pass
                                     pass
                                 else:
                                 else:
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
@@ -2178,7 +2207,7 @@ class ToolsDB2(QtWidgets.QWidget):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Isolation'):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Isolation'):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                             if str(k).startswith('tools_'):
                             if str(k).startswith('tools_'):
-                                if str(k).startswith('tools_iso'):
+                                if str(k).startswith('tools_iso') or str(k).startswith('tools_mill'):
                                     pass
                                     pass
                                 else:
                                 else:
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
@@ -2186,7 +2215,7 @@ class ToolsDB2(QtWidgets.QWidget):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Paint'):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Paint'):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                             if str(k).startswith('tools_'):
                             if str(k).startswith('tools_'):
-                                if str(k).startswith('tools_paint'):
+                                if str(k).startswith('tools_paint') or str(k).startswith('tools_mill'):
                                     pass
                                     pass
                                 else:
                                 else:
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
@@ -2194,7 +2223,15 @@ class ToolsDB2(QtWidgets.QWidget):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('NCC'):
                     if self.db_tool_dict[tool_id]['data']['tool_target'] == _('NCC'):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                         for k in list(self.db_tool_dict[tool_id]['data'].keys()):
                             if str(k).startswith('tools_'):
                             if str(k).startswith('tools_'):
-                                if str(k).startswith('tools_ncc'):
+                                if str(k).startswith('tools_ncc') or str(k).startswith('tools_mill'):
+                                    pass
+                                else:
+                                    self.db_tool_dict[tool_id]['data'].pop(k, None)
+
+                    if self.db_tool_dict[tool_id]['data']['tool_target'] == _('Cutout'):
+                        for k in list(self.db_tool_dict[tool_id]['data'].keys()):
+                            if str(k).startswith('tools_'):
+                                if str(k).startswith('tools_cutout') or str(k).startswith('tools_mill'):
                                     pass
                                     pass
                                 else:
                                 else:
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
                                     self.db_tool_dict[tool_id]['data'].pop(k, None)
@@ -2355,11 +2392,24 @@ class ToolsDB2(QtWidgets.QWidget):
             if wdg is None:
             if wdg is None:
                 return
                 return
 
 
+            if isinstance(wdg, FCButton) or isinstance(wdg, QtWidgets.QAction):
+                # this is called when adding a new tool; no need to run the update below since that section is for
+                # when editing a tool
+                self.on_tools_db_edited()
+                return
+
             wdg_name = wdg.objectName()
             wdg_name = wdg.objectName()
             val = wdg.get_value()
             val = wdg.get_value()
-        except AttributeError:
+        except AttributeError as err:
+            self.app.log.debug("ToolsDB2.update_storage() -> %s" % str(err))
             return
             return
 
 
+        # #############################################################################################################
+        # #############################################################################################################
+        # ################ EDITING PARAMETERS IN A TOOL SECTION
+        # #############################################################################################################
+        # #############################################################################################################
+
         # #############################################################################################################
         # #############################################################################################################
         # this might change in the future; it makes sense to change values at once for all tools
         # this might change in the future; it makes sense to change values at once for all tools
         # for now change values only for one tool at once
         # for now change values only for one tool at once
@@ -2523,7 +2573,7 @@ class ToolsDB2(QtWidgets.QWidget):
             elif wdg_name == "gdb_ct_mb_spacing":
             elif wdg_name == "gdb_ct_mb_spacing":
                 self.db_tool_dict[tool_id]['data']['tools_cutout_mb_spacing'] = val
                 self.db_tool_dict[tool_id]['data']['tools_cutout_mb_spacing'] = val
 
 
-        self.callback_app()
+        self.on_tools_db_edited()
 
 
     def on_tool_requested_from_app(self):
     def on_tool_requested_from_app(self):
         if not self.ui.tree_widget.selectedItems():
         if not self.ui.tree_widget.selectedItems():
@@ -2542,6 +2592,25 @@ class ToolsDB2(QtWidgets.QWidget):
                     selected_tool = self.db_tool_dict[key]
                     selected_tool = self.db_tool_dict[key]
                     self.on_tool_request(tool=selected_tool)
                     self.on_tool_request(tool=selected_tool)
 
 
+    def on_tools_db_edited(self, silent=None):
+        """
+        Executed whenever a tool is edited in Tools Database.
+        Will color the text of the Tools Database tab to Red color.
+
+        :return:
+        """
+
+        for idx in range(self.app.ui.plot_tab_area.count()):
+            if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                self.app.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('red'))
+
+        self.ui.save_db_btn.setStyleSheet("QPushButton {color: red;}")
+
+        self.tools_db_changed_flag = True
+        if silent is None:
+            msg = '[WARNING_NOTCL] %s' % _("Tools in Tools Database edited but not saved.")
+            self.app.inform[str, bool].emit(msg, False)
+
     def on_cancel_tool(self):
     def on_cancel_tool(self):
         for idx in range(self.app_ui.plot_tab_area.count()):
         for idx in range(self.app_ui.plot_tab_area.count()):
             if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
             if self.app_ui.plot_tab_area.tabText(idx) == _("Tools Database"):
@@ -2594,7 +2663,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #                 'tooldia': self.app.defaults["geometry_cnctooldia"]
 #                 'tooldia': self.app.defaults["geometry_cnctooldia"]
 #                 'offset': 'Path'
 #                 'offset': 'Path'
 #                 'offset_value': 0.0
 #                 'offset_value': 0.0
-#                 'type':  _('Rough'),
+#                 'type':  'Rough',
 #                 'tool_type': 'C1'
 #                 'tool_type': 'C1'
 #                 'data': dict()
 #                 'data': dict()
 #             }
 #             }
@@ -2845,7 +2914,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #               "A position on Z plane to move immediately after job stop."))
 #               "A position on Z plane to move immediately after job stop."))
 #
 #
 #     def setup_db_ui(self):
 #     def setup_db_ui(self):
-#         filename = self.app.data_path + '/tools_db.FlatDB'
+#         filename = self.app.tools_database_path()
 #
 #
 #         # load the database tools from the file
 #         # load the database tools from the file
 #         try:
 #         try:
@@ -2928,7 +2997,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #         dia_item = FCDoubleSpinner()
 #         dia_item = FCDoubleSpinner()
 #         dia_item.set_precision(self.decimals)
 #         dia_item.set_precision(self.decimals)
 #         dia_item.setSingleStep(0.1)
 #         dia_item.setSingleStep(0.1)
-#         dia_item.set_range(0.0, 9999.9999)
+#         dia_item.set_range(0.0, 10000.0000)
 #         dia_item.set_value(float(tooldict['tooldia']))
 #         dia_item.set_value(float(tooldict['tooldia']))
 #         widget.setCellWidget(row, 2, dia_item)
 #         widget.setCellWidget(row, 2, dia_item)
 #
 #
@@ -2941,7 +3010,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #         c_offset_item = FCDoubleSpinner()
 #         c_offset_item = FCDoubleSpinner()
 #         c_offset_item.set_precision(self.decimals)
 #         c_offset_item.set_precision(self.decimals)
 #         c_offset_item.setSingleStep(0.1)
 #         c_offset_item.setSingleStep(0.1)
-#         c_offset_item.set_range(-9999.9999, 9999.9999)
+#         c_offset_item.set_range(-10000.0000, 10000.0000)
 #         c_offset_item.set_value(float(tooldict['offset_value']))
 #         c_offset_item.set_value(float(tooldict['offset_value']))
 #         widget.setCellWidget(row, 4, c_offset_item)
 #         widget.setCellWidget(row, 4, c_offset_item)
 #
 #
@@ -2961,9 +3030,9 @@ class ToolsDB2(QtWidgets.QWidget):
 #         cutz_item.set_precision(self.decimals)
 #         cutz_item.set_precision(self.decimals)
 #         cutz_item.setSingleStep(0.1)
 #         cutz_item.setSingleStep(0.1)
 #         if self.app.defaults['global_machinist_setting']:
 #         if self.app.defaults['global_machinist_setting']:
-#             cutz_item.set_range(-9999.9999, 9999.9999)
+#             cutz_item.set_range(-10000.0000, 10000.0000)
 #         else:
 #         else:
-#             cutz_item.set_range(-9999.9999, -0.0000)
+#             cutz_item.set_range(-10000.0000, -0.0000)
 #
 #
 #         cutz_item.set_value(float(data['cutz']))
 #         cutz_item.set_value(float(data['cutz']))
 #         widget.setCellWidget(row, 7, cutz_item)
 #         widget.setCellWidget(row, 7, cutz_item)
@@ -2985,14 +3054,14 @@ class ToolsDB2(QtWidgets.QWidget):
 #         depth_per_pass_item = FCDoubleSpinner()
 #         depth_per_pass_item = FCDoubleSpinner()
 #         depth_per_pass_item.set_precision(self.decimals)
 #         depth_per_pass_item.set_precision(self.decimals)
 #         depth_per_pass_item.setSingleStep(0.1)
 #         depth_per_pass_item.setSingleStep(0.1)
-#         depth_per_pass_item.set_range(0.0, 9999.9999)
+#         depth_per_pass_item.set_range(0.0, 10000.0000)
 #         depth_per_pass_item.set_value(float(data['depthperpass']))
 #         depth_per_pass_item.set_value(float(data['depthperpass']))
 #         widget.setCellWidget(row, 9, depth_per_pass_item)
 #         widget.setCellWidget(row, 9, depth_per_pass_item)
 #
 #
 #         vtip_dia_item = FCDoubleSpinner()
 #         vtip_dia_item = FCDoubleSpinner()
 #         vtip_dia_item.set_precision(self.decimals)
 #         vtip_dia_item.set_precision(self.decimals)
 #         vtip_dia_item.setSingleStep(0.1)
 #         vtip_dia_item.setSingleStep(0.1)
-#         vtip_dia_item.set_range(0.0, 9999.9999)
+#         vtip_dia_item.set_range(0.0, 10000.0000)
 #         vtip_dia_item.set_value(float(data['vtipdia']))
 #         vtip_dia_item.set_value(float(data['vtipdia']))
 #         widget.setCellWidget(row, 10, vtip_dia_item)
 #         widget.setCellWidget(row, 10, vtip_dia_item)
 #
 #
@@ -3007,28 +3076,28 @@ class ToolsDB2(QtWidgets.QWidget):
 #         travelz_item.set_precision(self.decimals)
 #         travelz_item.set_precision(self.decimals)
 #         travelz_item.setSingleStep(0.1)
 #         travelz_item.setSingleStep(0.1)
 #         if self.app.defaults['global_machinist_setting']:
 #         if self.app.defaults['global_machinist_setting']:
-#             travelz_item.set_range(-9999.9999, 9999.9999)
+#             travelz_item.set_range(-10000.0000, 10000.0000)
 #         else:
 #         else:
-#             travelz_item.set_range(0.0000, 9999.9999)
+#             travelz_item.set_range(0.0000, 10000.0000)
 #
 #
 #         travelz_item.set_value(float(data['travelz']))
 #         travelz_item.set_value(float(data['travelz']))
 #         widget.setCellWidget(row, 12, travelz_item)
 #         widget.setCellWidget(row, 12, travelz_item)
 #
 #
 #         fr_item = FCDoubleSpinner()
 #         fr_item = FCDoubleSpinner()
 #         fr_item.set_precision(self.decimals)
 #         fr_item.set_precision(self.decimals)
-#         fr_item.set_range(0.0, 9999.9999)
+#         fr_item.set_range(0.0, 10000.0000)
 #         fr_item.set_value(float(data['feedrate']))
 #         fr_item.set_value(float(data['feedrate']))
 #         widget.setCellWidget(row, 13, fr_item)
 #         widget.setCellWidget(row, 13, fr_item)
 #
 #
 #         frz_item = FCDoubleSpinner()
 #         frz_item = FCDoubleSpinner()
 #         frz_item.set_precision(self.decimals)
 #         frz_item.set_precision(self.decimals)
-#         frz_item.set_range(0.0, 9999.9999)
+#         frz_item.set_range(0.0, 10000.0000)
 #         frz_item.set_value(float(data['feedrate_z']))
 #         frz_item.set_value(float(data['feedrate_z']))
 #         widget.setCellWidget(row, 14, frz_item)
 #         widget.setCellWidget(row, 14, frz_item)
 #
 #
 #         frrapids_item = FCDoubleSpinner()
 #         frrapids_item = FCDoubleSpinner()
 #         frrapids_item.set_precision(self.decimals)
 #         frrapids_item.set_precision(self.decimals)
-#         frrapids_item.set_range(0.0, 9999.9999)
+#         frrapids_item.set_range(0.0, 10000.0000)
 #         frrapids_item.set_value(float(data['feedrate_rapid']))
 #         frrapids_item.set_value(float(data['feedrate_rapid']))
 #         widget.setCellWidget(row, 15, frrapids_item)
 #         widget.setCellWidget(row, 15, frrapids_item)
 #
 #
@@ -3044,7 +3113,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #
 #
 #         dwelltime_item = FCDoubleSpinner()
 #         dwelltime_item = FCDoubleSpinner()
 #         dwelltime_item.set_precision(self.decimals)
 #         dwelltime_item.set_precision(self.decimals)
-#         dwelltime_item.set_range(0.0000, 9999.9999)
+#         dwelltime_item.set_range(0.0000, 10000.0000)
 #         dwelltime_item.set_value(float(data['dwelltime']))
 #         dwelltime_item.set_value(float(data['dwelltime']))
 #         widget.setCellWidget(row, 18, dwelltime_item)
 #         widget.setCellWidget(row, 18, dwelltime_item)
 #
 #
@@ -3060,7 +3129,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #
 #
 #         ecut_length_item = FCDoubleSpinner()
 #         ecut_length_item = FCDoubleSpinner()
 #         ecut_length_item.set_precision(self.decimals)
 #         ecut_length_item.set_precision(self.decimals)
-#         ecut_length_item.set_range(0.0000, 9999.9999)
+#         ecut_length_item.set_range(0.0000, 10000.0000)
 #         ecut_length_item.set_value(data['extracut_length'])
 #         ecut_length_item.set_value(data['extracut_length'])
 #         widget.setCellWidget(row, 21, ecut_length_item)
 #         widget.setCellWidget(row, 21, ecut_length_item)
 #
 #
@@ -3075,9 +3144,9 @@ class ToolsDB2(QtWidgets.QWidget):
 #         toolchangez_item.set_precision(self.decimals)
 #         toolchangez_item.set_precision(self.decimals)
 #         toolchangez_item.setSingleStep(0.1)
 #         toolchangez_item.setSingleStep(0.1)
 #         if self.app.defaults['global_machinist_setting']:
 #         if self.app.defaults['global_machinist_setting']:
-#             toolchangez_item.set_range(-9999.9999, 9999.9999)
+#             toolchangez_item.set_range(-10000.0000, 10000.0000)
 #         else:
 #         else:
-#             toolchangez_item.set_range(0.0000, 9999.9999)
+#             toolchangez_item.set_range(0.0000, 10000.0000)
 #
 #
 #         toolchangez_item.set_value(float(data['toolchangez']))
 #         toolchangez_item.set_value(float(data['toolchangez']))
 #         widget.setCellWidget(row, 24, toolchangez_item)
 #         widget.setCellWidget(row, 24, toolchangez_item)
@@ -3089,9 +3158,9 @@ class ToolsDB2(QtWidgets.QWidget):
 #         endz_item.set_precision(self.decimals)
 #         endz_item.set_precision(self.decimals)
 #         endz_item.setSingleStep(0.1)
 #         endz_item.setSingleStep(0.1)
 #         if self.app.defaults['global_machinist_setting']:
 #         if self.app.defaults['global_machinist_setting']:
-#             endz_item.set_range(-9999.9999, 9999.9999)
+#             endz_item.set_range(-10000.0000, 10000.0000)
 #         else:
 #         else:
-#             endz_item.set_range(0.0000, 9999.9999)
+#             endz_item.set_range(0.0000, 10000.0000)
 #
 #
 #         endz_item.set_value(float(data['endz']))
 #         endz_item.set_value(float(data['endz']))
 #         widget.setCellWidget(row, 26, endz_item)
 #         widget.setCellWidget(row, 26, endz_item)
@@ -3282,7 +3351,7 @@ class ToolsDB2(QtWidgets.QWidget):
 #     def on_save_tools_db(self, silent=False):
 #     def on_save_tools_db(self, silent=False):
 #         self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
 #         self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
 #
 #
-#         filename = self.app.data_path + "/tools_db.FlatDB"
+#         filename = self.app.tools_database_path()
 #
 #
 #         # Preferences save, update the color of the Tools DB Tab text
 #         # Preferences save, update the color of the Tools DB Tab text
 #         for idx in range(self.app_ui.plot_tab_area.count()):
 #         for idx in range(self.app_ui.plot_tab_area.count()):

File diff suppressed because it is too large
+ 506 - 120
appEditors/AppExcEditor.py


+ 145 - 137
appEditors/AppGeoEditor.py

@@ -12,21 +12,22 @@
 # ######################################################### ##
 # ######################################################### ##
 
 
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5 import QtGui, QtCore, QtWidgets
-from PyQt5.QtCore import Qt, QSettings
+from PyQt5.QtCore import Qt
 
 
 from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
 from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
 from appTool import AppTool
 from appTool import AppTool
 from appGUI.GUIElements import OptionalInputSection, FCCheckBox, FCLabel, FCComboBox, FCTextAreaRich, \
 from appGUI.GUIElements import OptionalInputSection, FCCheckBox, FCLabel, FCComboBox, FCTextAreaRich, \
-    FCDoubleSpinner, FCButton, FCInputDialog, FCTree, NumericalEvalTupleEntry
+    FCDoubleSpinner, FCButton, FCInputDoubleSpinner, FCTree, NumericalEvalTupleEntry
 from appParsers.ParseFont import *
 from appParsers.ParseFont import *
 
 
-from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
+from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point
 from shapely.ops import unary_union, linemerge
 from shapely.ops import unary_union, linemerge
 import shapely.affinity as affinity
 import shapely.affinity as affinity
 from shapely.geometry.polygon import orient
 from shapely.geometry.polygon import orient
 
 
 import numpy as np
 import numpy as np
 from numpy.linalg import norm as numpy_norm
 from numpy.linalg import norm as numpy_norm
+import logging
 
 
 from rtree import index as rtindex
 from rtree import index as rtindex
 
 
@@ -40,6 +41,8 @@ fcTranslate.apply_language('strings')
 if '_' not in builtins.__dict__:
 if '_' not in builtins.__dict__:
     _ = gettext.gettext
     _ = gettext.gettext
 
 
+log = logging.getLogger('base')
+
 
 
 class BufferSelectionTool(AppTool):
 class BufferSelectionTool(AppTool):
     """
     """
@@ -80,9 +83,9 @@ class BufferSelectionTool(AppTool):
         # Buffer distance
         # Buffer distance
         self.buffer_distance_entry = FCDoubleSpinner()
         self.buffer_distance_entry = FCDoubleSpinner()
         self.buffer_distance_entry.set_precision(self.decimals)
         self.buffer_distance_entry.set_precision(self.decimals)
-        self.buffer_distance_entry.set_range(0.0000, 999999.9999)
-        form_layout.addRow(_("Buffer distance:"), self.buffer_distance_entry)
-        self.buffer_corner_lbl = FCLabel(_("Buffer corner:"))
+        self.buffer_distance_entry.set_range(0.0000, 9910000.0000)
+        form_layout.addRow('%S:' % _("Buffer distance"), self.buffer_distance_entry)
+        self.buffer_corner_lbl = FCLabel('%s:' % _("Buffer corner"))
         self.buffer_corner_lbl.setToolTip(
         self.buffer_corner_lbl.setToolTip(
             _("There are 3 types of corners:\n"
             _("There are 3 types of corners:\n"
               " - 'Round': the corner is rounded for exterior buffer.\n"
               " - 'Round': the corner is rounded for exterior buffer.\n"
@@ -435,14 +438,14 @@ class PaintOptionsTool(AppTool):
         grid.setColumnStretch(1, 1)
         grid.setColumnStretch(1, 1)
 
 
         # Tool dia
         # Tool dia
-        ptdlabel = FCLabel('%s:' % _('Tool dia'))
+        ptdlabel = FCLabel('%s:' % _('Tool Dia'))
         ptdlabel.setToolTip(
         ptdlabel.setToolTip(
            _("Diameter of the tool to be used in the operation.")
            _("Diameter of the tool to be used in the operation.")
         )
         )
         grid.addWidget(ptdlabel, 0, 0)
         grid.addWidget(ptdlabel, 0, 0)
 
 
         self.painttooldia_entry = FCDoubleSpinner()
         self.painttooldia_entry = FCDoubleSpinner()
-        self.painttooldia_entry.set_range(-9999.9999, 9999.9999)
+        self.painttooldia_entry.set_range(-10000.0000, 10000.0000)
         self.painttooldia_entry.set_precision(self.decimals)
         self.painttooldia_entry.set_precision(self.decimals)
         grid.addWidget(self.painttooldia_entry, 0, 1)
         grid.addWidget(self.painttooldia_entry, 0, 1)
 
 
@@ -451,8 +454,8 @@ class PaintOptionsTool(AppTool):
         ovlabel.setToolTip(
         ovlabel.setToolTip(
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
               "Adjust the value starting with lower values\n"
               "Adjust the value starting with lower values\n"
-              "and increasing it if areas that should be painted are still \n"
-              "not painted.\n"
+              "and increasing it if areas that should be processed are still \n"
+              "not processed.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "due of too many paths.")
               "due of too many paths.")
@@ -474,7 +477,7 @@ class PaintOptionsTool(AppTool):
              "be painted.")
              "be painted.")
         )
         )
         self.paintmargin_entry = FCDoubleSpinner()
         self.paintmargin_entry = FCDoubleSpinner()
-        self.paintmargin_entry.set_range(-9999.9999, 9999.9999)
+        self.paintmargin_entry.set_range(-10000.0000, 10000.0000)
         self.paintmargin_entry.set_precision(self.decimals)
         self.paintmargin_entry.set_precision(self.decimals)
 
 
         grid.addWidget(marginlabel, 2, 0)
         grid.addWidget(marginlabel, 2, 0)
@@ -579,7 +582,7 @@ class PaintOptionsTool(AppTool):
 
 
     def on_paint(self):
     def on_paint(self):
         if not self.fcdraw.selected:
         if not self.fcdraw.selected:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
             return
             return
 
 
         tooldia = self.painttooldia_entry.get_value()
         tooldia = self.painttooldia_entry.get_value()
@@ -682,7 +685,7 @@ class TransformEditorTool(AppTool):
 
 
         self.rotate_label = FCLabel('%s:' % _("Angle"))
         self.rotate_label = FCLabel('%s:' % _("Angle"))
         self.rotate_label.setToolTip(
         self.rotate_label.setToolTip(
-            _("Angle for Rotation action, in degrees.\n"
+            _("Angle, in degrees.\n"
               "Float number between -360 and 359.\n"
               "Float number between -360 and 359.\n"
               "Positive numbers for CW motion.\n"
               "Positive numbers for CW motion.\n"
               "Negative numbers for CCW motion.")
               "Negative numbers for CCW motion.")
@@ -939,7 +942,7 @@ class TransformEditorTool(AppTool):
         self.buffer_entry.set_precision(self.decimals)
         self.buffer_entry.set_precision(self.decimals)
         self.buffer_entry.setSingleStep(0.1)
         self.buffer_entry.setSingleStep(0.1)
         self.buffer_entry.setWrapping(True)
         self.buffer_entry.setWrapping(True)
-        self.buffer_entry.set_range(-9999.9999, 9999.9999)
+        self.buffer_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.buffer_button = FCButton(_("Buffer D"))
         self.buffer_button = FCButton(_("Buffer D"))
         self.buffer_button.setToolTip(
         self.buffer_button.setToolTip(
@@ -1068,7 +1071,7 @@ class TransformEditorTool(AppTool):
 
 
     def template(self):
     def template(self):
         if not self.draw_app.selected:
         if not self.draw_app.selected:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
             return
             return
 
 
         self.draw_app.select_tool("select")
         self.draw_app.select_tool("select")
@@ -1263,7 +1266,7 @@ class TransformEditorTool(AppTool):
         """
         """
         Rotate geometry
         Rotate geometry
 
 
-        :param num:     Rotate with a known angle value, val
+        :param val:     Rotate with a known angle value, val
         :param point:   Reference point for rotation: tuple
         :param point:   Reference point for rotation: tuple
         :return:
         :return:
         """
         """
@@ -1281,9 +1284,9 @@ class TransformEditorTool(AppTool):
                     sel_sha.rotate(-val, point=(px, py))
                     sel_sha.rotate(-val, point=(px, py))
                     self.draw_app.replot()
                     self.draw_app.replot()
 
 
-                self.app.inform.emit('[success] %s' % _("Done. Rotate completed."))
+                self.app.inform.emit('[success] %s' % _("Done."))
             except Exception as e:
             except Exception as e:
-                self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Rotation action was not executed"), str(e)))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
                 return
                 return
 
 
     def on_flip(self, axis, point):
     def on_flip(self, axis, point):
@@ -1309,20 +1312,21 @@ class TransformEditorTool(AppTool):
                 for sha in shape_list:
                 for sha in shape_list:
                     if axis == 'X':
                     if axis == 'X':
                         sha.mirror('X', (px, py))
                         sha.mirror('X', (px, py))
-                        self.app.inform.emit('[success] %s...' % _('Flip on the Y axis done'))
+                        self.app.inform.emit('[success] %s...' % _('Flip on Y axis done'))
                     elif axis == 'Y':
                     elif axis == 'Y':
                         sha.mirror('Y', (px, py))
                         sha.mirror('Y', (px, py))
-                        self.app.inform.emit('[success] %s' % _('Flip on the X axis done'))
+                        self.app.inform.emit('[success] %s' % _('Flip on X axis done'))
                     self.draw_app.replot()
                     self.draw_app.replot()
 
 
             except Exception as e:
             except Exception as e:
-                self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Flip action was not executed"), str(e)))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
                 return
                 return
 
 
     def on_skew(self, axis, xval, yval, point):
     def on_skew(self, axis, xval, yval, point):
         """
         """
         Skew geometry
         Skew geometry
 
 
+        :param point:
         :param axis:    Axis on which to deform, skew
         :param axis:    Axis on which to deform, skew
         :param xval:    Skew value on X axis
         :param xval:    Skew value on X axis
         :param yval:    Skew value on Y axis
         :param yval:    Skew value on Y axis
@@ -1349,7 +1353,7 @@ class TransformEditorTool(AppTool):
                     self.app.inform.emit('[success] %s...' % _('Skew on the Y axis done'))
                     self.app.inform.emit('[success] %s...' % _('Skew on the Y axis done'))
 
 
             except Exception as e:
             except Exception as e:
-                self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Skew action was not executed"), str(e)))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
                 return
                 return
 
 
     def on_scale(self, axis, xfactor, yfactor, point=None):
     def on_scale(self, axis, xfactor, yfactor, point=None):
@@ -1383,7 +1387,7 @@ class TransformEditorTool(AppTool):
                 else:
                 else:
                     self.app.inform.emit('[success] %s...' % _('Scale on the Y axis done'))
                     self.app.inform.emit('[success] %s...' % _('Scale on the Y axis done'))
             except Exception as e:
             except Exception as e:
-                self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Scale action was not executed"), str(e)))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
                 return
                 return
 
 
     def on_offset(self, axis, num):
     def on_offset(self, axis, num):
@@ -1416,14 +1420,14 @@ class TransformEditorTool(AppTool):
                     self.app.inform.emit('[success] %s...' % _('Offset on the Y axis done'))
                     self.app.inform.emit('[success] %s...' % _('Offset on the Y axis done'))
 
 
             except Exception as e:
             except Exception as e:
-                self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Offset action was not executed"), str(e)))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
                 return
                 return
 
 
     def on_buffer_action(self, value, join, factor=None):
     def on_buffer_action(self, value, join, factor=None):
         shape_list = self.draw_app.selected
         shape_list = self.draw_app.selected
 
 
         if not shape_list:
         if not shape_list:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No shape selected"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No shape selected."))
             return
             return
         else:
         else:
             with self.app.proc_container.new(_("Applying Buffer")):
             with self.app.proc_container.new(_("Applying Buffer")):
@@ -1437,87 +1441,92 @@ class TransformEditorTool(AppTool):
 
 
                 except Exception as e:
                 except Exception as e:
                     self.app.log.debug("TransformEditorTool.on_buffer_action() --> %s" % str(e))
                     self.app.log.debug("TransformEditorTool.on_buffer_action() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed, due of"), str(e)))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s.' % (_("Action was not executed"), str(e)))
                     return
                     return
 
 
     def on_rotate_key(self):
     def on_rotate_key(self):
-        val_box = FCInputDialog(title=_("Rotate ..."),
-                                text='%s:' % _('Enter an Angle Value (degrees)'),
-                                min=-359.9999, max=360.0000, decimals=self.decimals,
-                                init_val=float(self.app.defaults['tools_transform_rotate']))
-        val_box.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/rotate.png'))
+        val_box = FCInputDoubleSpinner(title=_("Rotate ..."),
+                                       text='%s:' % _('Enter an Angle Value (degrees)'),
+                                       min=-359.9999, max=360.0000, decimals=self.decimals,
+                                       init_val=float(self.app.defaults['tools_transform_rotate']),
+                                       parent=self.app.ui)
+        val_box.set_icon(QtGui.QIcon(self.app.resource_location + '/rotate.png'))
 
 
         val, ok = val_box.get_value()
         val, ok = val_box.get_value()
         if ok:
         if ok:
             self.on_rotate(val=val, ref=1)
             self.on_rotate(val=val, ref=1)
-            self.app.inform.emit('[success] %s...' % _("Geometry shape rotate done"))
+            self.app.inform.emit('[success] %s...' % _("Rotate done"))
             return
             return
         else:
         else:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Geometry shape rotate cancelled"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Rotate cancelled"))
 
 
     def on_offx_key(self):
     def on_offx_key(self):
         units = self.app.defaults['units'].lower()
         units = self.app.defaults['units'].lower()
 
 
-        val_box = FCInputDialog(title=_("Offset on X axis ..."),
-                                text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
-                                min=-9999.9999, max=10000.0000, decimals=self.decimals,
-                                init_val=float(self.app.defaults['tools_transform_offset_x']))
-        val_box.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/offsetx32.png'))
+        val_box = FCInputDoubleSpinner(title=_("Offset on X axis ..."),
+                                       text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
+                                       min=-10000.0000, max=10000.0000, decimals=self.decimals,
+                                       init_val=float(self.app.defaults['tools_transform_offset_x']),
+                                       parent=self.app.ui)
+        val_box.set_icon(QtGui.QIcon(self.app.resource_location + '/offsetx32.png'))
 
 
         val, ok = val_box.get_value()
         val, ok = val_box.get_value()
         if ok:
         if ok:
             self.on_offx(val=val)
             self.on_offx(val=val)
-            self.app.inform.emit('[success] %s' % _("Geometry shape offset on X axis done"))
+            self.app.inform.emit('[success] %s' % _("Offset on the X axis done"))
             return
             return
         else:
         else:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Geometry shape offset X cancelled"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Offset X cancelled"))
 
 
     def on_offy_key(self):
     def on_offy_key(self):
         units = self.app.defaults['units'].lower()
         units = self.app.defaults['units'].lower()
 
 
-        val_box = FCInputDialog(title=_("Offset on Y axis ..."),
-                                text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
-                                min=-9999.9999, max=10000.0000, decimals=self.decimals,
-                                init_val=float(self.app.defaults['tools_transform_offset_y']))
-        val_box.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/offsety32.png'))
+        val_box = FCInputDoubleSpinner(title=_("Offset on Y axis ..."),
+                                       text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
+                                       min=-10000.0000, max=10000.0000, decimals=self.decimals,
+                                       init_val=float(self.app.defaults['tools_transform_offset_y']),
+                                       parent=self.app.ui)
+        val_box.set_icon(QtGui.QIcon(self.app.resource_location + '/offsety32.png'))
 
 
         val, ok = val_box.get_value()
         val, ok = val_box.get_value()
         if ok:
         if ok:
             self.on_offx(val=val)
             self.on_offx(val=val)
-            self.app.inform.emit('[success] %s...' % _("Geometry shape offset on Y axis done"))
+            self.app.inform.emit('[success] %s...' % _("Offset on Y axis done"))
             return
             return
         else:
         else:
-            self.app.inform.emit('[success] %s...' % _("Geometry shape offset on Y axis canceled"))
+            self.app.inform.emit('[success] %s...' % _("Offset on the Y axis canceled"))
 
 
     def on_skewx_key(self):
     def on_skewx_key(self):
-        val_box = FCInputDialog(title=_("Skew on X axis ..."),
-                                text='%s:' % _('Enter an Angle Value (degrees)'),
-                                min=-359.9999, max=360.0000, decimals=self.decimals,
-                                init_val=float(self.app.defaults['tools_transform_skew_x']))
-        val_box.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/skewX.png'))
+        val_box = FCInputDoubleSpinner(title=_("Skew on X axis ..."),
+                                       text='%s:' % _('Enter an Angle Value (degrees)'),
+                                       min=-359.9999, max=360.0000, decimals=self.decimals,
+                                       init_val=float(self.app.defaults['tools_transform_skew_x']),
+                                       parent=self.app.ui)
+        val_box.set_icon(QtGui.QIcon(self.app.resource_location + '/skewX.png'))
 
 
         val, ok = val_box.get_value()
         val, ok = val_box.get_value()
         if ok:
         if ok:
             self.on_skewx(val=val, ref=3)
             self.on_skewx(val=val, ref=3)
-            self.app.inform.emit('[success] %s...' % _("Geometry shape skew on X axis done"))
+            self.app.inform.emit('[success] %s...' % _("Skew on X axis done"))
             return
             return
         else:
         else:
-            self.app.inform.emit('[success] %s...' % _("Geometry shape skew on X axis canceled"))
+            self.app.inform.emit('[success] %s...' % _("Skew on X axis canceled"))
 
 
     def on_skewy_key(self):
     def on_skewy_key(self):
-        val_box = FCInputDialog(title=_("Skew on Y axis ..."),
-                                text='%s:' % _('Enter an Angle Value (degrees)'),
-                                min=-359.9999, max=360.0000, decimals=self.decimals,
-                                init_val=float(self.app.defaults['tools_transform_skew_y']))
-        val_box.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/skewY.png'))
+        val_box = FCInputDoubleSpinner(title=_("Skew on Y axis ..."),
+                                       text='%s:' % _('Enter an Angle Value (degrees)'),
+                                       min=-359.9999, max=360.0000, decimals=self.decimals,
+                                       init_val=float(self.app.defaults['tools_transform_skew_y']),
+                                       parent=self.app.ui)
+        val_box.set_icon(QtGui.QIcon(self.app.resource_location + '/skewY.png'))
 
 
         val, ok = val_box.get_value()
         val, ok = val_box.get_value()
         if ok:
         if ok:
             self.on_skewx(val=val, ref=3)
             self.on_skewx(val=val, ref=3)
-            self.app.inform.emit('[success] %s...' % _("Geometry shape skew on Y axis done"))
+            self.app.inform.emit('[success] %s...' % _("Skew on Y axis done"))
             return
             return
         else:
         else:
-            self.app.inform.emit('[success] %s...' % _("Geometry shape skew on Y axis canceled"))
+            self.app.inform.emit('[success] %s...' % _("Skew on Y axis canceled"))
 
 
     @staticmethod
     @staticmethod
     def alt_bounds(shapelist):
     def alt_bounds(shapelist):
@@ -1885,7 +1894,8 @@ class DrawTool(object):
     def utility_geometry(self, data=None):
     def utility_geometry(self, data=None):
         return None
         return None
 
 
-    def bounds(self, obj):
+    @staticmethod
+    def bounds(obj):
         def bounds_rec(o):
         def bounds_rec(o):
             if type(o) is list:
             if type(o) is list:
                 minx = np.Inf
                 minx = np.Inf
@@ -1992,7 +2002,7 @@ class FCCircle(FCShapeTool):
 
 
         self.draw_app.app.jump_signal.disconnect()
         self.draw_app.app.jump_signal.disconnect()
 
 
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Adding Circle completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def clean_up(self):
     def clean_up(self):
         self.draw_app.selected = []
         self.draw_app.selected = []
@@ -2071,7 +2081,7 @@ class FCArc(FCShapeTool):
     def on_key(self, key):
     def on_key(self, key):
         if key == 'D' or key == QtCore.Qt.Key_D:
         if key == 'D' or key == QtCore.Qt.Key_D:
             self.direction = 'cw' if self.direction == 'ccw' else 'ccw'
             self.direction = 'cw' if self.direction == 'ccw' else 'ccw'
-            return _('Direction: %s') % self.direction.upper()
+            return '%s: %s' % (_('Direction'), self.direction.upper())
 
 
         # Jump to coords
         # Jump to coords
         if key == QtCore.Qt.Key_J or key == 'J':
         if key == QtCore.Qt.Key_J or key == 'J':
@@ -2232,7 +2242,7 @@ class FCArc(FCShapeTool):
 
 
         self.draw_app.app.jump_signal.disconnect()
         self.draw_app.app.jump_signal.disconnect()
 
 
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Arc completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def clean_up(self):
     def clean_up(self):
         self.draw_app.selected = []
         self.draw_app.selected = []
@@ -2305,7 +2315,7 @@ class FCRectangle(FCShapeTool):
         self.complete = True
         self.complete = True
 
 
         self.draw_app.app.jump_signal.disconnect()
         self.draw_app.app.jump_signal.disconnect()
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Rectangle completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def clean_up(self):
     def clean_up(self):
         self.draw_app.selected = []
         self.draw_app.selected = []
@@ -2380,7 +2390,7 @@ class FCPolygon(FCShapeTool):
 
 
         self.draw_app.app.jump_signal.disconnect()
         self.draw_app.app.jump_signal.disconnect()
 
 
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Polygon completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def on_key(self, key):
     def on_key(self, key):
         # Jump to coords
         # Jump to coords
@@ -2437,7 +2447,7 @@ class FCPath(FCPolygon):
 
 
         self.draw_app.app.jump_signal.disconnect()
         self.draw_app.app.jump_signal.disconnect()
 
 
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Path completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def utility_geometry(self, data=None):
     def utility_geometry(self, data=None):
         if len(self.points) > 0:
         if len(self.points) > 0:
@@ -2596,7 +2606,7 @@ class FCExplode(FCShapeTool):
 
 
         self.draw_app.active_tool = self
         self.draw_app.active_tool = self
         if len(self.draw_app.get_selected()) == 0:
         if len(self.draw_app.get_selected()) == 0:
-            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s...' % _("No shape selected. Select a shape to explode"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % _("No shape selected."))
         else:
         else:
             self.make()
             self.make()
 
 
@@ -2629,7 +2639,7 @@ class FCExplode(FCShapeTool):
             geo_list.append(DrawToolShape(line))
             geo_list.append(DrawToolShape(line))
         self.geometry = geo_list
         self.geometry = geo_list
         self.draw_app.on_shape_complete()
         self.draw_app.on_shape_complete()
-        self.draw_app.app.inform.emit('[success] %s...' % _("Done. Polygons exploded into lines."))
+        self.draw_app.app.inform.emit('[success] %s...' % _("Done."))
 
 
     def clean_up(self):
     def clean_up(self):
         self.draw_app.selected = []
         self.draw_app.selected = []
@@ -2660,16 +2670,15 @@ class FCMove(FCShapeTool):
         self.selection_shape = self.selection_bbox()
         self.selection_shape = self.selection_bbox()
 
 
         if len(self.draw_app.get_selected()) == 0:
         if len(self.draw_app.get_selected()) == 0:
-            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s...' %
-                                          _("MOVE: No shape selected. Select a shape to move"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % _("No shape selected."))
             return
             return
         else:
         else:
-            self.draw_app.app.inform.emit(_(" MOVE: Click on reference point ..."))
+            self.draw_app.app.inform.emit(_("Click on reference location ..."))
 
 
         self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
         self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
 
 
     def set_origin(self, origin):
     def set_origin(self, origin):
-        self.draw_app.app.inform.emit(_(" Click on destination point ..."))
+        self.draw_app.app.inform.emit(_("Click on destination point ..."))
         self.origin = origin
         self.origin = origin
 
 
     def click(self, point):
     def click(self, point):
@@ -2684,7 +2693,7 @@ class FCMove(FCShapeTool):
             # self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Move cancelled. No shape selected."))
             # self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Move cancelled. No shape selected."))
             self.select_shapes(point)
             self.select_shapes(point)
             self.draw_app.replot()
             self.draw_app.replot()
-            self.draw_app.app.inform.emit(_(" MOVE: Click on reference point ..."))
+            self.draw_app.app.inform.emit(_("Click on reference location ..."))
             return
             return
 
 
         if self.origin is None:
         if self.origin is None:
@@ -2699,7 +2708,7 @@ class FCMove(FCShapeTool):
             return "Done."
             return "Done."
 
 
     def make(self):
     def make(self):
-        with self.draw_app.app.proc_container.new("Moving Geometry ..."):
+        with self.draw_app.app.proc_container.new(_("Moving ...")):
             # Create new geometry
             # Create new geometry
             dx = self.destination[0] - self.origin[0]
             dx = self.destination[0] - self.origin[0]
             dy = self.destination[1] - self.origin[1]
             dy = self.destination[1] - self.origin[1]
@@ -2709,7 +2718,7 @@ class FCMove(FCShapeTool):
             # Delete old
             # Delete old
             self.draw_app.delete_selected()
             self.draw_app.delete_selected()
             self.complete = True
             self.complete = True
-            self.draw_app.app.inform.emit('[success] %s' % _("Done. Geometry(s) Move completed."))
+            self.draw_app.app.inform.emit('[success] %s' % _("Done."))
             try:
             try:
                 self.draw_app.app.jump_signal.disconnect()
                 self.draw_app.app.jump_signal.disconnect()
             except TypeError:
             except TypeError:
@@ -2842,7 +2851,7 @@ class FCCopy(FCMove):
         self.geometry = [DrawToolShape(affinity.translate(geom.geo, xoff=dx, yoff=dy))
         self.geometry = [DrawToolShape(affinity.translate(geom.geo, xoff=dx, yoff=dy))
                          for geom in self.draw_app.get_selected()]
                          for geom in self.draw_app.get_selected()]
         self.complete = True
         self.complete = True
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Geometry(s) Copy completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
         try:
         try:
             self.draw_app.app.jump_signal.disconnect()
             self.draw_app.app.jump_signal.disconnect()
         except (TypeError, AttributeError):
         except (TypeError, AttributeError):
@@ -2915,7 +2924,7 @@ class FCText(FCShapeTool):
         self.text_gui.text_path = []
         self.text_gui.text_path = []
         self.text_gui.hide_tool()
         self.text_gui.hide_tool()
         self.complete = True
         self.complete = True
-        self.draw_app.app.inform.emit('[success]%s' % _(" Done. Adding Text completed."))
+        self.draw_app.app.inform.emit('[success]%s' % _("Done."))
 
 
     def utility_geometry(self, data=None):
     def utility_geometry(self, data=None):
         """
         """
@@ -2963,7 +2972,7 @@ class FCBuffer(FCShapeTool):
 
 
     def on_buffer(self):
     def on_buffer(self):
         if not self.draw_app.selected:
         if not self.draw_app.selected:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No shape selected."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
             return
             return
 
 
         try:
         try:
@@ -2987,11 +2996,11 @@ class FCBuffer(FCShapeTool):
         self.disactivate()
         self.disactivate()
         if ret_val == 'fail':
         if ret_val == 'fail':
             return
             return
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Buffer Tool completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def on_buffer_int(self):
     def on_buffer_int(self):
         if not self.draw_app.selected:
         if not self.draw_app.selected:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No shape selected."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
             return
             return
 
 
         try:
         try:
@@ -3015,11 +3024,11 @@ class FCBuffer(FCShapeTool):
         self.disactivate()
         self.disactivate()
         if ret_val == 'fail':
         if ret_val == 'fail':
             return
             return
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Buffer Int Tool completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def on_buffer_ext(self):
     def on_buffer_ext(self):
         if not self.draw_app.selected:
         if not self.draw_app.selected:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No shape selected."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
             return
             return
 
 
         try:
         try:
@@ -3030,7 +3039,7 @@ class FCBuffer(FCShapeTool):
                 buffer_distance = float(self.buff_tool.buffer_distance_entry.get_value().replace(',', '.'))
                 buffer_distance = float(self.buff_tool.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buff_tool.buffer_distance_entry.set_value(buffer_distance)
                 self.buff_tool.buffer_distance_entry.set_value(buffer_distance)
             except ValueError:
             except ValueError:
-                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
                                      _("Buffer distance value is missing or wrong format. Add it and retry."))
                                      _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
@@ -3043,7 +3052,7 @@ class FCBuffer(FCShapeTool):
         self.disactivate()
         self.disactivate()
         if ret_val == 'fail':
         if ret_val == 'fail':
             return
             return
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Buffer Ext Tool completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
 
 
     def activate(self):
     def activate(self):
         self.buff_tool.buffer_button.clicked.disconnect()
         self.buff_tool.buffer_button.clicked.disconnect()
@@ -3159,7 +3168,7 @@ class FCEraser(FCShapeTool):
 
 
         self.draw_app.delete_utility_geometry()
         self.draw_app.delete_utility_geometry()
         self.draw_app.plot_all()
         self.draw_app.plot_all()
-        self.draw_app.app.inform.emit('[success] %s' % _("Done. Eraser tool action completed."))
+        self.draw_app.app.inform.emit('[success] %s' % _("Done."))
         try:
         try:
             self.draw_app.app.jump_signal.disconnect()
             self.draw_app.app.jump_signal.disconnect()
         except (TypeError, AttributeError):
         except (TypeError, AttributeError):
@@ -3314,22 +3323,7 @@ class AppGeoEditor(QtCore.QObject):
         self.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
         self.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
 
 
         # ## Toolbar events and properties
         # ## Toolbar events and properties
-        self.tools = {
-            "select": {"button": self.app.ui.geo_select_btn, "constructor": FCSelect},
-            "arc": {"button": self.app.ui.geo_add_arc_btn, "constructor": FCArc},
-            "circle": {"button": self.app.ui.geo_add_circle_btn, "constructor": FCCircle},
-            "path": {"button": self.app.ui.geo_add_path_btn, "constructor": FCPath},
-            "rectangle": {"button": self.app.ui.geo_add_rectangle_btn, "constructor": FCRectangle},
-            "polygon": {"button": self.app.ui.geo_add_polygon_btn, "constructor": FCPolygon},
-            "text": {"button": self.app.ui.geo_add_text_btn, "constructor": FCText},
-            "buffer": {"button": self.app.ui.geo_add_buffer_btn, "constructor": FCBuffer},
-            "paint": {"button": self.app.ui.geo_add_paint_btn, "constructor": FCPaint},
-            "eraser": {"button": self.app.ui.geo_eraser_btn, "constructor": FCEraser},
-            "move": {"button": self.app.ui.geo_move_btn, "constructor": FCMove},
-            "transform": {"button": self.app.ui.geo_transform_btn, "constructor": FCTransform},
-            "copy": {"button": self.app.ui.geo_copy_btn, "constructor": FCCopy},
-            "explode": {"button": self.app.ui.geo_explode_btn, "constructor": FCExplode}
-        }
+        self.tools = {}
 
 
         # # ## Data
         # # ## Data
         self.active_tool = None
         self.active_tool = None
@@ -3385,15 +3379,6 @@ class AppGeoEditor(QtCore.QObject):
         # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
         # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
         self.launched_from_shortcuts = False
         self.launched_from_shortcuts = False
 
 
-        def make_callback(thetool):
-            def f():
-                self.on_tool_select(thetool)
-            return f
-
-        for tool in self.tools:
-            self.tools[tool]["button"].triggered.connect(make_callback(tool))  # Events
-            self.tools[tool]["button"].setCheckable(True)  # Checkable
-
         self.app.ui.grid_snap_btn.triggered.connect(self.on_grid_toggled)
         self.app.ui.grid_snap_btn.triggered.connect(self.on_grid_toggled)
         self.app.ui.corner_snap_btn.setCheckable(True)
         self.app.ui.corner_snap_btn.setCheckable(True)
         self.app.ui.corner_snap_btn.triggered.connect(lambda: self.toolbar_tool_toggle("corner_snap"))
         self.app.ui.corner_snap_btn.triggered.connect(lambda: self.toolbar_tool_toggle("corner_snap"))
@@ -3435,6 +3420,14 @@ class AppGeoEditor(QtCore.QObject):
         self.paint_tool = PaintOptionsTool(self.app, self)
         self.paint_tool = PaintOptionsTool(self.app, self)
         self.transform_tool = TransformEditorTool(self.app, self)
         self.transform_tool = TransformEditorTool(self.app, self)
 
 
+        # #############################################################################################################
+        # ####################### GEOMETRY Editor Signals #############################################################
+        # #############################################################################################################
+
+        # connect the toolbar signals
+        self.connect_geo_toolbar_signals()
+
+        # connect Geometry Editor Menu signals
         self.app.ui.geo_add_circle_menuitem.triggered.connect(lambda: self.select_tool('circle'))
         self.app.ui.geo_add_circle_menuitem.triggered.connect(lambda: self.select_tool('circle'))
         self.app.ui.geo_add_arc_menuitem.triggered.connect(lambda: self.select_tool('arc'))
         self.app.ui.geo_add_arc_menuitem.triggered.connect(lambda: self.select_tool('arc'))
         self.app.ui.geo_add_rectangle_menuitem.triggered.connect(lambda: self.select_tool('rectangle'))
         self.app.ui.geo_add_rectangle_menuitem.triggered.connect(lambda: self.select_tool('rectangle'))
@@ -3468,10 +3461,36 @@ class AppGeoEditor(QtCore.QObject):
         self.mm = None
         self.mm = None
         self.mr = None
         self.mr = None
 
 
-        # store the status of the editor so the Delete at object level will not work until the edit is finished
-        self.editor_active = False
         log.debug("Initialization of the Geometry Editor is finished ...")
         log.debug("Initialization of the Geometry Editor is finished ...")
 
 
+    def make_callback(self, thetool):
+        def f():
+            self.on_tool_select(thetool)
+
+        return f
+
+    def connect_geo_toolbar_signals(self):
+        self.tools.update({
+            "select": {"button": self.app.ui.geo_select_btn, "constructor": FCSelect},
+            "arc": {"button": self.app.ui.geo_add_arc_btn, "constructor": FCArc},
+            "circle": {"button": self.app.ui.geo_add_circle_btn, "constructor": FCCircle},
+            "path": {"button": self.app.ui.geo_add_path_btn, "constructor": FCPath},
+            "rectangle": {"button": self.app.ui.geo_add_rectangle_btn, "constructor": FCRectangle},
+            "polygon": {"button": self.app.ui.geo_add_polygon_btn, "constructor": FCPolygon},
+            "text": {"button": self.app.ui.geo_add_text_btn, "constructor": FCText},
+            "buffer": {"button": self.app.ui.geo_add_buffer_btn, "constructor": FCBuffer},
+            "paint": {"button": self.app.ui.geo_add_paint_btn, "constructor": FCPaint},
+            "eraser": {"button": self.app.ui.geo_eraser_btn, "constructor": FCEraser},
+            "move": {"button": self.app.ui.geo_move_btn, "constructor": FCMove},
+            "transform": {"button": self.app.ui.geo_transform_btn, "constructor": FCTransform},
+            "copy": {"button": self.app.ui.geo_copy_btn, "constructor": FCCopy},
+            "explode": {"button": self.app.ui.geo_explode_btn, "constructor": FCExplode}
+        })
+
+        for tool in self.tools:
+            self.tools[tool]["button"].triggered.connect(self.make_callback(tool))  # Events
+            self.tools[tool]["button"].setCheckable(True)  # Checkable
+
     def pool_recreated(self, pool):
     def pool_recreated(self, pool):
         self.shapes.pool = pool
         self.shapes.pool = pool
         self.tool_shape.pool = pool
         self.tool_shape.pool = pool
@@ -3646,9 +3665,6 @@ class AppGeoEditor(QtCore.QObject):
         # for w in sel_tab_widget_list:
         # for w in sel_tab_widget_list:
         #     w.setEnabled(False)
         #     w.setEnabled(False)
 
 
-        # Tell the App that the editor is active
-        self.editor_active = True
-
         self.item_selected.connect(self.on_geo_elem_selected)
         self.item_selected.connect(self.on_geo_elem_selected)
 
 
         # ## appGUI Events
         # ## appGUI Events
@@ -3678,7 +3694,6 @@ class AppGeoEditor(QtCore.QObject):
         self.clear()
         self.clear()
         self.app.ui.geo_edit_toolbar.setDisabled(True)
         self.app.ui.geo_edit_toolbar.setDisabled(True)
 
 
-        settings = QSettings("Open Source", "FlatCAM")
         self.app.ui.corner_snap_btn.setVisible(False)
         self.app.ui.corner_snap_btn.setVisible(False)
         self.app.ui.snap_magnet.setVisible(False)
         self.app.ui.snap_magnet.setVisible(False)
 
 
@@ -3698,9 +3713,6 @@ class AppGeoEditor(QtCore.QObject):
         self.app.ui.g_editor_cmenu.setEnabled(False)
         self.app.ui.g_editor_cmenu.setEnabled(False)
         self.app.ui.e_editor_cmenu.setEnabled(False)
         self.app.ui.e_editor_cmenu.setEnabled(False)
 
 
-        # Tell the app that the editor is no longer active
-        self.editor_active = False
-
         self.app.ui.popmenu_disable.setVisible(True)
         self.app.ui.popmenu_disable.setVisible(True)
         self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
         self.app.ui.cmenu_newmenu.menuAction().setVisible(True)
         self.app.ui.popmenu_properties.setVisible(True)
         self.app.ui.popmenu_properties.setVisible(True)
@@ -4397,7 +4409,7 @@ class AppGeoEditor(QtCore.QObject):
 
 
     def on_copy_click(self):
     def on_copy_click(self):
         if not self.selected:
         if not self.selected:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("No shape selected.")))
             return
             return
 
 
         self.app.ui.geo_copy_btn.setChecked(True)
         self.app.ui.geo_copy_btn.setChecked(True)
@@ -4791,7 +4803,7 @@ class AppGeoEditor(QtCore.QObject):
         except Exception as e:
         except Exception as e:
             log.debug("AppGeoEditor.intersection() --> %s" % str(e))
             log.debug("AppGeoEditor.intersection() --> %s" % str(e))
             self.app.inform.emit('[WARNING_NOTCL] %s' %
             self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("A selection of at least 2 geo items is required to do Intersection."))
+                                 _("A selection of minimum two items is required to do Intersection."))
             self.select_tool('select')
             self.select_tool('select')
             return
             return
 
 
@@ -4826,7 +4838,7 @@ class AppGeoEditor(QtCore.QObject):
         except Exception as e:
         except Exception as e:
             log.debug("AppGeoEditor.intersection() --> %s" % str(e))
             log.debug("AppGeoEditor.intersection() --> %s" % str(e))
             self.app.inform.emit('[WARNING_NOTCL] %s' %
             self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("A selection of at least 2 geo items is required to do Intersection."))
+                                 _("A selection of minimum two items is required to do Intersection."))
             self.select_tool('select')
             self.select_tool('select')
             return
             return
 
 
@@ -4921,13 +4933,11 @@ class AppGeoEditor(QtCore.QObject):
             return 'fail'
             return 'fail'
 
 
         if len(selected) == 0:
         if len(selected) == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Nothing selected for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected."))
             return 'fail'
             return 'fail'
 
 
         if not isinstance(buf_distance, float):
         if not isinstance(buf_distance, float):
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Invalid distance for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Invalid distance."))
 
 
             # deselect everything
             # deselect everything
             self.selected = []
             self.selected = []
@@ -4975,11 +4985,11 @@ class AppGeoEditor(QtCore.QObject):
             return 'fail'
             return 'fail'
 
 
         if len(selected) == 0:
         if len(selected) == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected."))
             return 'fail'
             return 'fail'
 
 
         if not isinstance(buf_distance, float):
         if not isinstance(buf_distance, float):
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Invalid distance for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Invalid distance."))
             # deselect everything
             # deselect everything
             self.selected = []
             self.selected = []
             self.replot()
             self.replot()
@@ -4999,7 +5009,7 @@ class AppGeoEditor(QtCore.QObject):
 
 
         if not results:
         if not results:
             self.app.inform.emit('[ERROR_NOTCL] %s' %
             self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("Failed, the result is empty. Choose a smaller buffer value."))
+                                 _("Failed, the result is empty. Choose a different buffer value."))
             # deselect everything
             # deselect everything
             self.selected = []
             self.selected = []
             self.replot()
             self.replot()
@@ -5024,13 +5034,11 @@ class AppGeoEditor(QtCore.QObject):
             return
             return
 
 
         if len(selected) == 0:
         if len(selected) == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Nothing selected for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected."))
             return
             return
 
 
         if not isinstance(buf_distance, float):
         if not isinstance(buf_distance, float):
-            self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Invalid distance for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Invalid distance."))
             # deselect everything
             # deselect everything
             self.selected = []
             self.selected = []
             self.replot()
             self.replot()
@@ -5073,7 +5081,7 @@ class AppGeoEditor(QtCore.QObject):
         selected = self.get_selected()
         selected = self.get_selected()
 
 
         if len(selected) == 0:
         if len(selected) == 0:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected for painting."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Nothing selected."))
             return
             return
 
 
         for param in [tooldia, overlap, margin]:
         for param in [tooldia, overlap, margin]:
@@ -5149,7 +5157,7 @@ class AppGeoEditor(QtCore.QObject):
         # This is a dirty patch:
         # This is a dirty patch:
         for r in results:
         for r in results:
             self.add_shape(DrawToolShape(r))
             self.add_shape(DrawToolShape(r))
-        self.app.inform.emit('[success] %s' % _("Paint done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
         self.replot()
         self.replot()
 
 
     def flatten(self, geometry, orient_val=1, reset=True, pathonly=False):
     def flatten(self, geometry, orient_val=1, reset=True, pathonly=False):

File diff suppressed because it is too large
+ 413 - 212
appEditors/AppGerberEditor.py


+ 4 - 2
appEditors/AppTextEditor.py

@@ -251,7 +251,9 @@ class AppTextEditor(QtWidgets.QWidget):
                 ext_filter=_filter_
                 ext_filter=_filter_
             )[0])
             )[0])
         except TypeError:
         except TypeError:
-            filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)[0])
+            filename = str(FCFileSaveDialog.get_saved_filename(
+                caption=_("Export Code ..."),
+                ext_filter=_filter_)[0])
 
 
         if filename == "":
         if filename == "":
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
@@ -330,7 +332,7 @@ class AppTextEditor(QtWidgets.QWidget):
         r = self.code_editor.find(str(text_to_be_found), flags)
         r = self.code_editor.find(str(text_to_be_found), flags)
         if r is False:
         if r is False:
             self.code_editor.moveCursor(QtGui.QTextCursor.Start)
             self.code_editor.moveCursor(QtGui.QTextCursor.Start)
-            r = self.code_editor.find(str(text_to_be_found), flags)
+            self.code_editor.find(str(text_to_be_found), flags)
 
 
     def handleReplaceGCode(self):
     def handleReplaceGCode(self):
 
 

+ 7 - 12
appEditors/appGCodeEditor.py

@@ -43,8 +43,13 @@ class AppGCodeEditor(QtCore.QObject):
         self.gcode_obj = None
         self.gcode_obj = None
         self.code_edited = ''
         self.code_edited = ''
 
 
-        # store the status of the editor so the Delete at object level will not work until the edit is finished
-        self.editor_active = False
+        # #################################################################################
+        # ################### SIGNALS #####################################################
+        # #################################################################################
+        self.ui.name_entry.returnPressed.connect(self.on_name_activate)
+        self.ui.update_gcode_button.clicked.connect(self.insert_gcode)
+        self.ui.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
+
         log.debug("Initialization of the GCode Editor is finished ...")
         log.debug("Initialization of the GCode Editor is finished ...")
 
 
     def set_ui(self):
     def set_ui(self):
@@ -103,13 +108,6 @@ class AppGCodeEditor(QtCore.QObject):
 
 
         self.activate()
         self.activate()
 
 
-        # #################################################################################
-        # ################### SIGNALS #####################################################
-        # #################################################################################
-        self.ui.name_entry.returnPressed.connect(self.on_name_activate)
-        self.ui.update_gcode_button.clicked.connect(self.insert_gcode)
-        self.ui.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
-
     def build_ui(self):
     def build_ui(self):
         """
         """
 
 
@@ -464,7 +462,6 @@ class AppGCodeEditor(QtCore.QObject):
                     end_sel = my_text_cursor.selectionEnd()
                     end_sel = my_text_cursor.selectionEnd()
                 else:
                 else:
                     pos_list = []
                     pos_list = []
-                    end_sel = 0
 
 
                     my_text_cursor = self.edit_area.textCursor()
                     my_text_cursor = self.edit_area.textCursor()
                     m6_pos = my_text_cursor.selectionEnd()
                     m6_pos = my_text_cursor.selectionEnd()
@@ -610,11 +607,9 @@ class AppGCodeEditor(QtCore.QObject):
                 file.close()
                 file.close()
 
 
     def activate(self):
     def activate(self):
-        self.editor_active = True
         self.app.call_source = 'gcode_editor'
         self.app.call_source = 'gcode_editor'
 
 
     def deactivate(self):
     def deactivate(self):
-        self.editor_active = False
         self.app.call_source = 'app'
         self.app.call_source = 'app'
 
 
     def on_name_activate(self):
     def on_name_activate(self):

File diff suppressed because it is too large
+ 777 - 22
appGUI/GUIElements.py


File diff suppressed because it is too large
+ 278 - 163
appGUI/MainGUI.py


File diff suppressed because it is too large
+ 162 - 139
appGUI/ObjectUI.py


+ 25 - 19
appGUI/PlotCanvas.py

@@ -191,13 +191,13 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
 
         # enable the HUD if it is activated in FlatCAM Preferences
         # enable the HUD if it is activated in FlatCAM Preferences
         if self.fcapp.defaults['global_hud'] is True:
         if self.fcapp.defaults['global_hud'] is True:
-            self.on_toggle_hud(state=True)
+            self.on_toggle_hud(state=True, silent=True)
 
 
         # Axis Display
         # Axis Display
         self.axis_enabled = True
         self.axis_enabled = True
 
 
         # enable Axis
         # enable Axis
-        self.on_toggle_axis(state=True)
+        self.on_toggle_axis(state=True, silent=True)
 
 
         # enable Grid lines
         # enable Grid lines
         self.grid_lines_enabled = True
         self.grid_lines_enabled = True
@@ -218,8 +218,8 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
 
         self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
         self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
 
 
-    def on_toggle_axis(self, signal=None, state=None):
-        if state is None:
+    def on_toggle_axis(self, signal=None, state=None, silent=None):
+        if not state:
             state = not self.axis_enabled
             state = not self.axis_enabled
 
 
         if state:
         if state:
@@ -234,16 +234,18 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
                                                               background-color: orange;
                                                               background-color: orange;
                                                           }
                                                           }
                                                           """)
                                                           """)
-            self.fcapp.inform[str, bool].emit(_("Axis enabled."), False)
+            if silent is None:
+                self.fcapp.inform[str, bool].emit(_("Axis enabled."), False)
         else:
         else:
             self.axis_enabled = False
             self.axis_enabled = False
             self.fcapp.defaults['global_axis'] = False
             self.fcapp.defaults['global_axis'] = False
             self.v_line.parent = None
             self.v_line.parent = None
             self.h_line.parent = None
             self.h_line.parent = None
             self.fcapp.ui.axis_status_label.setStyleSheet("")
             self.fcapp.ui.axis_status_label.setStyleSheet("")
-            self.fcapp.inform[str, bool].emit(_("Axis disabled."), False)
+            if silent is None:
+                self.fcapp.inform[str, bool].emit(_("Axis disabled."), False)
 
 
-    def on_toggle_hud(self, signal=None, state=None):
+    def on_toggle_hud(self, signal=None, state=None, silent=None):
         if state is None:
         if state is None:
             state = not self.hud_enabled
             state = not self.hud_enabled
 
 
@@ -259,7 +261,8 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
                                                       background-color: mediumpurple;
                                                       background-color: mediumpurple;
                                                   }
                                                   }
                                                   """)
                                                   """)
-            self.fcapp.inform[str, bool].emit(_("HUD enabled."), False)
+            if silent is None:
+                self.fcapp.inform[str, bool].emit(_("HUD enabled."), False)
 
 
         else:
         else:
             self.hud_enabled = False
             self.hud_enabled = False
@@ -267,21 +270,24 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
             self.text_hud.parent = None
             self.text_hud.parent = None
             self.fcapp.defaults['global_hud'] = False
             self.fcapp.defaults['global_hud'] = False
             self.fcapp.ui.hud_label.setStyleSheet("")
             self.fcapp.ui.hud_label.setStyleSheet("")
-            self.fcapp.inform[str, bool].emit(_("HUD disabled."), False)
+            if silent is None:
+                self.fcapp.inform[str, bool].emit(_("HUD disabled."), False)
 
 
-    def on_toggle_grid_lines(self):
+    def on_toggle_grid_lines(self, signal=None, silent=None):
         state = not self.grid_lines_enabled
         state = not self.grid_lines_enabled
 
 
         if state:
         if state:
             self.fcapp.defaults['global_grid_lines'] = True
             self.fcapp.defaults['global_grid_lines'] = True
             self.grid_lines_enabled = True
             self.grid_lines_enabled = True
             self.grid.parent = self.view.scene
             self.grid.parent = self.view.scene
-            self.fcapp.inform[str, bool].emit(_("Grid enabled."), False)
+            if silent is None:
+                self.fcapp.inform[str, bool].emit(_("Grid enabled."), False)
         else:
         else:
             self.fcapp.defaults['global_grid_lines'] = False
             self.fcapp.defaults['global_grid_lines'] = False
             self.grid_lines_enabled = False
             self.grid_lines_enabled = False
             self.grid.parent = None
             self.grid.parent = None
-            self.fcapp.inform[str, bool].emit(_("Grid disabled."), False)
+            if silent is None:
+                self.fcapp.inform[str, bool].emit(_("Grid disabled."), False)
 
 
         # HACK: enabling/disabling the cursor seams to somehow update the shapes on screen
         # HACK: enabling/disabling the cursor seams to somehow update the shapes on screen
         # - perhaps is a bug in VisPy implementation
         # - perhaps is a bug in VisPy implementation
@@ -569,13 +575,13 @@ class CursorBig(QtCore.QObject):
 
 
     def set_data(self, pos, **kwargs):
     def set_data(self, pos, **kwargs):
         """Internal event handler to draw the cursor when the mouse moves."""
         """Internal event handler to draw the cursor when the mouse moves."""
-        if 'edge_color' in kwargs:
-            color = kwargs['edge_color']
-        else:
-            if self.app.defaults['global_theme'] == 'white':
-                color = '#000000FF'
-            else:
-                color = '#FFFFFFFF'
+        # if 'edge_color' in kwargs:
+        #     color = kwargs['edge_color']
+        # else:
+        #     if self.app.defaults['global_theme'] == 'white':
+        #         color = '#000000FF'
+        #     else:
+        #         color = '#FFFFFFFF'
 
 
         position = [pos[0][0], pos[0][1]]
         position = [pos[0][0], pos[0][1]]
         self.mouse_position_updated.emit(position)
         self.mouse_position_updated.emit(position)

+ 29 - 15
appGUI/PlotCanvasLegacy.py

@@ -306,9 +306,13 @@ class PlotCanvasLegacy(QtCore.QObject):
         # signal if there is a doubleclick
         # signal if there is a doubleclick
         self.is_dblclk = False
         self.is_dblclk = False
 
 
+        # HUD Display
         self.hud_enabled = False
         self.hud_enabled = False
         self.text_hud = self.Thud(plotcanvas=self)
         self.text_hud = self.Thud(plotcanvas=self)
 
 
+        if self.app.defaults['global_hud'] is True:
+            self.on_toggle_hud(state=True, silent=None)
+
         # enable Grid lines
         # enable Grid lines
         self.grid_lines_enabled = True
         self.grid_lines_enabled = True
 
 
@@ -317,17 +321,21 @@ class PlotCanvasLegacy(QtCore.QObject):
         if self.app.defaults['global_workspace'] is True:
         if self.app.defaults['global_workspace'] is True:
             self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
             self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
 
 
-        if self.app.defaults['global_hud'] is True:
-            self.on_toggle_hud(state=True)
-
         # Axis Display
         # Axis Display
         self.axis_enabled = True
         self.axis_enabled = True
 
 
         # enable Axis
         # enable Axis
-        self.on_toggle_axis(state=True)
-
-    def on_toggle_axis(self, signal=None, state=None):
-        if state is None:
+        self.on_toggle_axis(state=True, silent=True)
+        self.app.ui.axis_status_label.setStyleSheet("""
+                                                    QLabel
+                                                    {
+                                                        color: black;
+                                                        background-color: orange;
+                                                    }
+                                                    """)
+
+    def on_toggle_axis(self, signal=None, state=None, silent=None):
+        if not state:
             state = not self.axis_enabled
             state = not self.axis_enabled
 
 
         if state:
         if state:
@@ -343,7 +351,8 @@ class PlotCanvasLegacy(QtCore.QObject):
                                                                 background-color: orange;
                                                                 background-color: orange;
                                                             }
                                                             }
                                                             """)
                                                             """)
-                self.app.inform[str, bool].emit(_("Axis enabled."), False)
+                if silent is None:
+                    self.app.inform[str, bool].emit(_("Axis enabled."), False)
         else:
         else:
             self.axis_enabled = False
             self.axis_enabled = False
             self.app.defaults['global_axis'] = False
             self.app.defaults['global_axis'] = False
@@ -351,11 +360,12 @@ class PlotCanvasLegacy(QtCore.QObject):
                 self.axes.lines.remove(self.h_line)
                 self.axes.lines.remove(self.h_line)
                 self.axes.lines.remove(self.v_line)
                 self.axes.lines.remove(self.v_line)
                 self.app.ui.axis_status_label.setStyleSheet("")
                 self.app.ui.axis_status_label.setStyleSheet("")
-                self.app.inform[str, bool].emit(_("Axis disabled."), False)
+                if silent is None:
+                    self.app.inform[str, bool].emit(_("Axis disabled."), False)
 
 
         self.canvas.draw()
         self.canvas.draw()
 
 
-    def on_toggle_hud(self, signal=None, state=None):
+    def on_toggle_hud(self, signal=None, state=None, silent=None):
         if state is None:
         if state is None:
             state = not self.hud_enabled
             state = not self.hud_enabled
 
 
@@ -371,13 +381,15 @@ class PlotCanvasLegacy(QtCore.QObject):
                                                     background-color: mediumpurple;
                                                     background-color: mediumpurple;
                                                 }
                                                 }
                                                 """)
                                                 """)
-            self.app.inform[str, bool].emit(_("HUD enabled."), False)
+            if silent is None:
+                self.app.inform[str, bool].emit(_("HUD enabled."), False)
         else:
         else:
             self.hud_enabled = False
             self.hud_enabled = False
             self.text_hud.remove_artist()
             self.text_hud.remove_artist()
             self.app.defaults['global_hud'] = False
             self.app.defaults['global_hud'] = False
             self.app.ui.hud_label.setStyleSheet("")
             self.app.ui.hud_label.setStyleSheet("")
-            self.app.inform[str, bool].emit(_("HUD disabled."), False)
+            if silent is None:
+                self.app.inform[str, bool].emit(_("HUD disabled."), False)
 
 
         self.canvas.draw()
         self.canvas.draw()
 
 
@@ -440,7 +452,7 @@ class PlotCanvasLegacy(QtCore.QObject):
             if self.hud_holder in self.p.axes.artists:
             if self.hud_holder in self.p.axes.artists:
                 self.p.axes.artists.remove(self.hud_holder)
                 self.p.axes.artists.remove(self.hud_holder)
 
 
-    def on_toggle_grid_lines(self):
+    def on_toggle_grid_lines(self, signal=None, silent=None):
         state = not self.grid_lines_enabled
         state = not self.grid_lines_enabled
 
 
         if state:
         if state:
@@ -451,7 +463,8 @@ class PlotCanvasLegacy(QtCore.QObject):
                 self.canvas.draw()
                 self.canvas.draw()
             except IndexError:
             except IndexError:
                 pass
                 pass
-            self.app.inform[str, bool].emit(_("Grid enabled."), False)
+            if silent is None:
+                self.app.inform[str, bool].emit(_("Grid enabled."), False)
         else:
         else:
             self.app.defaults['global_grid_lines'] = False
             self.app.defaults['global_grid_lines'] = False
             self.grid_lines_enabled = False
             self.grid_lines_enabled = False
@@ -460,7 +473,8 @@ class PlotCanvasLegacy(QtCore.QObject):
                 self.canvas.draw()
                 self.canvas.draw()
             except IndexError:
             except IndexError:
                 pass
                 pass
-            self.app.inform[str, bool].emit(_("Grid disabled."), False)
+            if silent is None:
+                self.app.inform[str, bool].emit(_("Grid disabled."), False)
 
 
     def draw_workspace(self, workspace_size):
     def draw_workspace(self, workspace_size):
         """
         """

+ 3 - 2
appGUI/VisPyTesselators.py

@@ -29,9 +29,10 @@ class GLUTess:
         pass
         pass
 
 
     def _on_combine(self, coords, data, weight):
     def _on_combine(self, coords, data, weight):
-        return (coords[0], coords[1], coords[2])
+        return coords[0], coords[1], coords[2]
 
 
-    def _on_error(self, errno):
+    @staticmethod
+    def _on_error(errno):
         print("GLUTess error:", errno)
         print("GLUTess error:", errno)
 
 
     def _on_end_primitive(self):
     def _on_end_primitive(self):

+ 3 - 2
appGUI/VisPyVisuals.py

@@ -339,7 +339,7 @@ class ShapeCollectionVisual(CompoundVisual):
         if update:
         if update:
             self.__update()
             self.__update()
 
 
-    def update_visibility(self, state:bool, indexes=None) -> None:
+    def update_visibility(self, state: bool, indexes=None) -> None:
         # Lock sub-visuals updates
         # Lock sub-visuals updates
         self.update_lock.acquire(True)
         self.update_lock.acquire(True)
         if indexes is None:
         if indexes is None:
@@ -540,8 +540,9 @@ class ShapeCollectionVisual(CompoundVisual):
     def redraw(self, indexes=None, update_colors=None):
     def redraw(self, indexes=None, update_colors=None):
         """
         """
         Redraws collection
         Redraws collection
-        :param indexes: list
+        :param indexes:     list
             Shape indexes to get from process pool
             Shape indexes to get from process pool
+        :param update_colors:
         """
         """
         # Only one thread can update data
         # Only one thread can update data
         self.results_lock.acquire(True)
         self.results_lock.acquire(True)

+ 22 - 4
appGUI/preferences/PreferencesUIManager.py

@@ -42,6 +42,8 @@ class PreferencesUIManager:
         # if Preferences are changed in the Edit -> Preferences tab the value will be set to True
         # if Preferences are changed in the Edit -> Preferences tab the value will be set to True
         self.preferences_changed_flag = False
         self.preferences_changed_flag = False
 
 
+        self.old_color = QtGui.QColor('black')
+
         # when adding entries here read the comments in the  method found below named:
         # when adding entries here read the comments in the  method found below named:
         # def app_obj.new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
         # def app_obj.new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
         self.defaults_form_fields = {
         self.defaults_form_fields = {
@@ -282,6 +284,14 @@ class PreferencesUIManager:
             "geometry_area_shape":      self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio,
             "geometry_area_shape":      self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio,
             "geometry_area_strategy":   self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio,
             "geometry_area_strategy":   self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio,
             "geometry_area_overz":      self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry,
             "geometry_area_overz":      self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry,
+            # Polish
+            "geometry_polish":          self.ui.geometry_defaults_form.geometry_adv_opt_group.polish_cb,
+            "geometry_polish_dia":      self.ui.geometry_defaults_form.geometry_adv_opt_group.polish_dia_entry,
+            "geometry_polish_pressure": self.ui.geometry_defaults_form.geometry_adv_opt_group.polish_pressure_entry,
+            "geometry_polish_travelz":  self.ui.geometry_defaults_form.geometry_adv_opt_group.polish_travelz_entry,
+            "geometry_polish_margin":   self.ui.geometry_defaults_form.geometry_adv_opt_group.polish_margin_entry,
+            "geometry_polish_overlap":  self.ui.geometry_defaults_form.geometry_adv_opt_group.polish_over_entry,
+            "geometry_polish_method":   self.ui.geometry_defaults_form.geometry_adv_opt_group.polish_method_combo,
 
 
             # Geometry Editor
             # Geometry Editor
             "geometry_editor_sel_limit":        self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,
             "geometry_editor_sel_limit":        self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry,
@@ -488,6 +498,7 @@ class PreferencesUIManager:
             "tools_calc_vshape_cut_z": self.ui.tools_defaults_form.tools_calculators_group.cut_z_entry,
             "tools_calc_vshape_cut_z": self.ui.tools_defaults_form.tools_calculators_group.cut_z_entry,
             "tools_calc_electro_length": self.ui.tools_defaults_form.tools_calculators_group.pcblength_entry,
             "tools_calc_electro_length": self.ui.tools_defaults_form.tools_calculators_group.pcblength_entry,
             "tools_calc_electro_width": self.ui.tools_defaults_form.tools_calculators_group.pcbwidth_entry,
             "tools_calc_electro_width": self.ui.tools_defaults_form.tools_calculators_group.pcbwidth_entry,
+            "tools_calc_electro_area": self.ui.tools_defaults_form.tools_calculators_group.area_entry,
             "tools_calc_electro_cdensity": self.ui.tools_defaults_form.tools_calculators_group.cdensity_entry,
             "tools_calc_electro_cdensity": self.ui.tools_defaults_form.tools_calculators_group.cdensity_entry,
             "tools_calc_electro_growth": self.ui.tools_defaults_form.tools_calculators_group.growth_entry,
             "tools_calc_electro_growth": self.ui.tools_defaults_form.tools_calculators_group.growth_entry,
 
 
@@ -530,13 +541,17 @@ class PreferencesUIManager:
             "tools_solderpaste_speedrev": self.ui.tools_defaults_form.tools_solderpaste_group.speedrev_entry,
             "tools_solderpaste_speedrev": self.ui.tools_defaults_form.tools_solderpaste_group.speedrev_entry,
             "tools_solderpaste_dwellrev": self.ui.tools_defaults_form.tools_solderpaste_group.dwellrev_entry,
             "tools_solderpaste_dwellrev": self.ui.tools_defaults_form.tools_solderpaste_group.dwellrev_entry,
             "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
             "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
+
+            # Subtractor Tool
             "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
             "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
+            "tools_sub_delete_sources":  self.ui.tools_defaults_form.tools_sub_group.delete_sources_cb,
 
 
             # Corner Markers Tool
             # Corner Markers Tool
-
+            "tools_corners_type": self.ui.tools_defaults_form.tools_corners_group.type_radio,
             "tools_corners_thickness": self.ui.tools_defaults_form.tools_corners_group.thick_entry,
             "tools_corners_thickness": self.ui.tools_defaults_form.tools_corners_group.thick_entry,
             "tools_corners_length": self.ui.tools_defaults_form.tools_corners_group.l_entry,
             "tools_corners_length": self.ui.tools_defaults_form.tools_corners_group.l_entry,
             "tools_corners_margin": self.ui.tools_defaults_form.tools_corners_group.margin_entry,
             "tools_corners_margin": self.ui.tools_defaults_form.tools_corners_group.margin_entry,
+            "tools_corners_drill_dia": self.ui.tools_defaults_form.tools_corners_group.drill_dia_entry,
 
 
             # #######################################################################################################
             # #######################################################################################################
             # ########################################## TOOLS 2 ####################################################
             # ########################################## TOOLS 2 ####################################################
@@ -582,6 +597,7 @@ class PreferencesUIManager:
             # Copper Thieving Tool
             # Copper Thieving Tool
             "tools_copper_thieving_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_entry,
             "tools_copper_thieving_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_entry,
             "tools_copper_thieving_margin": self.ui.tools2_defaults_form.tools2_cfill_group.margin_entry,
             "tools_copper_thieving_margin": self.ui.tools2_defaults_form.tools2_cfill_group.margin_entry,
+            "tools_copper_thieving_area": self.ui.tools2_defaults_form.tools2_cfill_group.area_entry,
             "tools_copper_thieving_reference": self.ui.tools2_defaults_form.tools2_cfill_group.reference_radio,
             "tools_copper_thieving_reference": self.ui.tools2_defaults_form.tools2_cfill_group.reference_radio,
             "tools_copper_thieving_box_type": self.ui.tools2_defaults_form.tools2_cfill_group.bbox_type_radio,
             "tools_copper_thieving_box_type": self.ui.tools2_defaults_form.tools2_cfill_group.bbox_type_radio,
             "tools_copper_thieving_circle_steps": self.ui.tools2_defaults_form.tools2_cfill_group.circlesteps_entry,
             "tools_copper_thieving_circle_steps": self.ui.tools2_defaults_form.tools2_cfill_group.circlesteps_entry,
@@ -596,6 +612,7 @@ class PreferencesUIManager:
             "tools_copper_thieving_rb_margin": self.ui.tools2_defaults_form.tools2_cfill_group.rb_margin_entry,
             "tools_copper_thieving_rb_margin": self.ui.tools2_defaults_form.tools2_cfill_group.rb_margin_entry,
             "tools_copper_thieving_rb_thickness": self.ui.tools2_defaults_form.tools2_cfill_group.rb_thickness_entry,
             "tools_copper_thieving_rb_thickness": self.ui.tools2_defaults_form.tools2_cfill_group.rb_thickness_entry,
             "tools_copper_thieving_mask_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_ppm_entry,
             "tools_copper_thieving_mask_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_ppm_entry,
+            "tools_copper_thieving_geo_choice": self.ui.tools2_defaults_form.tools2_cfill_group.ppm_choice_radio,
 
 
             # Fiducials Tool
             # Fiducials Tool
             "tools_fiducials_dia": self.ui.tools2_defaults_form.tools2_fiducials_group.dia_entry,
             "tools_fiducials_dia": self.ui.tools2_defaults_form.tools2_fiducials_group.dia_entry,
@@ -897,7 +914,7 @@ class PreferencesUIManager:
         # Preferences save, update the color of the Preferences Tab text
         # Preferences save, update the color of the Preferences Tab text
         for idx in range(self.ui.plot_tab_area.count()):
         for idx in range(self.ui.plot_tab_area.count()):
             if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
             if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
-                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, self.old_color)
 
 
         # restore the default stylesheet by setting a blank one
         # restore the default stylesheet by setting a blank one
         self.ui.pref_apply_button.setStyleSheet("")
         self.ui.pref_apply_button.setStyleSheet("")
@@ -990,7 +1007,7 @@ class PreferencesUIManager:
             # close the tab and delete it
             # close the tab and delete it
             for idx in range(self.ui.plot_tab_area.count()):
             for idx in range(self.ui.plot_tab_area.count()):
                 if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
                 if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
-                    self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+                    self.ui.plot_tab_area.tabBar.setTabTextColor(idx, self.old_color)
                     self.ui.plot_tab_area.closeTab(idx)
                     self.ui.plot_tab_area.closeTab(idx)
                     break
                     break
 
 
@@ -1018,7 +1035,7 @@ class PreferencesUIManager:
         # Preferences save, update the color of the Preferences Tab text
         # Preferences save, update the color of the Preferences Tab text
         for idx in range(self.ui.plot_tab_area.count()):
         for idx in range(self.ui.plot_tab_area.count()):
             if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
             if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
-                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
+                self.ui.plot_tab_area.tabBar.setTabTextColor(idx, self.old_color)
                 self.ui.plot_tab_area.closeTab(idx)
                 self.ui.plot_tab_area.closeTab(idx)
                 break
                 break
 
 
@@ -1123,6 +1140,7 @@ class PreferencesUIManager:
 
 
             for idx in range(self.ui.plot_tab_area.count()):
             for idx in range(self.ui.plot_tab_area.count()):
                 if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
                 if self.ui.plot_tab_area.tabText(idx) == _("Preferences"):
+                    self.old_color = self.ui.plot_tab_area.tabBar.tabTextColor(idx)
                     self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('red'))
                     self.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('red'))
 
 
             self.ui.pref_apply_button.setStyleSheet("QPushButton {color: red;}")
             self.ui.pref_apply_button.setStyleSheet("QPushButton {color: red;}")

+ 1 - 0
appGUI/preferences/__init__.py

@@ -1,4 +1,5 @@
 from appGUI.GUIElements import *
 from appGUI.GUIElements import *
+from PyQt5.QtCore import QSettings
 import gettext
 import gettext
 import appTranslation as fcTranslate
 import appTranslation as fcTranslate
 import builtins
 import builtins

+ 6 - 6
appGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py

@@ -122,7 +122,7 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
         )
         )
         self.ptravelz_entry = FCDoubleSpinner()
         self.ptravelz_entry = FCDoubleSpinner()
         self.ptravelz_entry.set_precision(self.decimals)
         self.ptravelz_entry.set_precision(self.decimals)
-        self.ptravelz_entry.set_range(0.0000, 9999.9999)
+        self.ptravelz_entry.set_range(0.0000, 10000.0000)
 
 
         grid0.addWidget(self.ptravelz_label, 14, 0)
         grid0.addWidget(self.ptravelz_label, 14, 0)
         grid0.addWidget(self.ptravelz_entry, 14, 1)
         grid0.addWidget(self.ptravelz_entry, 14, 1)
@@ -135,7 +135,7 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
         )
         )
         self.pdepth_entry = FCDoubleSpinner()
         self.pdepth_entry = FCDoubleSpinner()
         self.pdepth_entry.set_precision(self.decimals)
         self.pdepth_entry.set_precision(self.decimals)
-        self.pdepth_entry.set_range(-99999.9999, 0.0000)
+        self.pdepth_entry.set_range(-910000.0000, 0.0000)
 
 
         grid0.addWidget(self.pdepth_label, 16, 0)
         grid0.addWidget(self.pdepth_label, 16, 0)
         grid0.addWidget(self.pdepth_entry, 16, 1)
         grid0.addWidget(self.pdepth_entry, 16, 1)
@@ -147,7 +147,7 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
         )
         )
         self.feedrate_probe_entry = FCDoubleSpinner()
         self.feedrate_probe_entry = FCDoubleSpinner()
         self.feedrate_probe_entry.set_precision(self.decimals)
         self.feedrate_probe_entry.set_precision(self.decimals)
-        self.feedrate_probe_entry.set_range(0, 99999.9999)
+        self.feedrate_probe_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(self.feedrate_probe_label, 18, 0)
         grid0.addWidget(self.feedrate_probe_label, 18, 0)
         grid0.addWidget(self.feedrate_probe_entry, 18, 1)
         grid0.addWidget(self.feedrate_probe_entry, 18, 1)
@@ -176,7 +176,7 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
 
 
         self.jog_step_entry = FCDoubleSpinner()
         self.jog_step_entry = FCDoubleSpinner()
         self.jog_step_entry.set_precision(self.decimals)
         self.jog_step_entry.set_precision(self.decimals)
-        self.jog_step_entry.set_range(0, 99999.9999)
+        self.jog_step_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(self.jog_step_label, 24, 0)
         grid0.addWidget(self.jog_step_label, 24, 0)
         grid0.addWidget(self.jog_step_entry, 24, 1)
         grid0.addWidget(self.jog_step_entry, 24, 1)
@@ -189,7 +189,7 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
 
 
         self.jog_fr_entry = FCDoubleSpinner()
         self.jog_fr_entry = FCDoubleSpinner()
         self.jog_fr_entry.set_precision(self.decimals)
         self.jog_fr_entry.set_precision(self.decimals)
-        self.jog_fr_entry.set_range(0, 99999.9999)
+        self.jog_fr_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(self.jog_fr_label, 26, 0)
         grid0.addWidget(self.jog_fr_label, 26, 0)
         grid0.addWidget(self.jog_fr_entry, 26, 1)
         grid0.addWidget(self.jog_fr_entry, 26, 1)
@@ -202,7 +202,7 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
 
 
         self.jog_travelz_entry = FCDoubleSpinner()
         self.jog_travelz_entry = FCDoubleSpinner()
         self.jog_travelz_entry.set_precision(self.decimals)
         self.jog_travelz_entry.set_precision(self.decimals)
-        self.jog_travelz_entry.set_range(0, 99999.9999)
+        self.jog_travelz_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(self.jog_travelz_label, 28, 0)
         grid0.addWidget(self.jog_travelz_label, 28, 0)
         grid0.addWidget(self.jog_travelz_entry, 28, 1)
         grid0.addWidget(self.jog_travelz_entry, 28, 1)

+ 2 - 2
appGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py

@@ -107,8 +107,8 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
               "- Incremental G91 -> the reference is the previous position")
               "- Incremental G91 -> the reference is the previous position")
         )
         )
         self.coords_type_radio = RadioSet([
         self.coords_type_radio = RadioSet([
-            {"label": _("Absolute G90"), "value": "G90"},
-            {"label": _("Incremental G91"), "value": "G91"}
+            {"label": _("Absolute"), "value": "G90"},
+            {"label": _("Incremental"), "value": "G91"}
         ], orientation='vertical', stretch=False)
         ], orientation='vertical', stretch=False)
         grid0.addWidget(coords_type_label, 8, 0)
         grid0.addWidget(coords_type_label, 8, 0)
         grid0.addWidget(self.coords_type_radio, 8, 1)
         grid0.addWidget(self.coords_type_radio, 8, 1)

+ 1 - 1
appGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py

@@ -33,7 +33,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
 
 
         self.exc_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
         self.exc_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
         self.exc_label.setToolTip(
         self.exc_label.setToolTip(
-            _("A list of Excellon advanced parameters.\n"
+            _("A list of advanced parameters.\n"
               "Those parameters are available only for\n"
               "Those parameters are available only for\n"
               "Advanced App. Level.")
               "Advanced App. Level.")
         )
         )

+ 4 - 4
appGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py

@@ -103,7 +103,7 @@ class ExcellonEditorPrefGroupUI(OptionsGroupUI):
         )
         )
         # self.drill_pitch_label.setMinimumWidth(100)
         # self.drill_pitch_label.setMinimumWidth(100)
         self.drill_pitch_entry = FCDoubleSpinner()
         self.drill_pitch_entry = FCDoubleSpinner()
-        self.drill_pitch_entry.set_range(0, 99999.9999)
+        self.drill_pitch_entry.set_range(0, 910000.0000)
         self.drill_pitch_entry.set_precision(self.decimals)
         self.drill_pitch_entry.set_precision(self.decimals)
 
 
         grid0.addWidget(self.drill_pitch_label, 5, 0)
         grid0.addWidget(self.drill_pitch_label, 5, 0)
@@ -161,7 +161,7 @@ class ExcellonEditorPrefGroupUI(OptionsGroupUI):
         # Slot length
         # Slot length
         self.slot_length_label = QtWidgets.QLabel('%s:' % _('Length'))
         self.slot_length_label = QtWidgets.QLabel('%s:' % _('Length'))
         self.slot_length_label.setToolTip(
         self.slot_length_label.setToolTip(
-            _("Length = The length of the slot.")
+            _("Length. The length of the slot.")
         )
         )
         self.slot_length_label.setMinimumWidth(100)
         self.slot_length_label.setMinimumWidth(100)
 
 
@@ -195,8 +195,8 @@ class ExcellonEditorPrefGroupUI(OptionsGroupUI):
         self.slot_angle_label.setToolTip(
         self.slot_angle_label.setToolTip(
             _("Angle at which the slot is placed.\n"
             _("Angle at which the slot is placed.\n"
               "The precision is of max 2 decimals.\n"
               "The precision is of max 2 decimals.\n"
-              "Min value is: -359.99 degrees.\n"
-              "Max value is:  360.00 degrees.")
+              "Min value is: -360.00 degrees.\n"
+              "Max value is: 360.00 degrees.")
         )
         )
         self.slot_angle_label.setMinimumWidth(100)
         self.slot_angle_label.setMinimumWidth(100)
 
 

+ 2 - 2
appGUI/preferences/excellon/ExcellonExpPrefGroupUI.py

@@ -43,8 +43,8 @@ class ExcellonExpPrefGroupUI(OptionsGroupUI):
             _("The units used in the Excellon file.")
             _("The units used in the Excellon file.")
         )
         )
 
 
-        self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
-                                              {'label': _('MM'), 'value': 'METRIC'}])
+        self.excellon_units_radio = RadioSet([{'label': _('Inch'), 'value': 'INCH'},
+                                              {'label': _('mm'), 'value': 'METRIC'}])
         self.excellon_units_radio.setToolTip(
         self.excellon_units_radio.setToolTip(
             _("The units used in the Excellon file.")
             _("The units used in the Excellon file.")
         )
         )

+ 2 - 2
appGUI/preferences/excellon/ExcellonGenPrefGroupUI.py

@@ -179,8 +179,8 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
               "therefore this parameter will be used.")
               "therefore this parameter will be used.")
         )
         )
 
 
-        self.excellon_units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
-                                              {'label': _('MM'), 'value': 'METRIC'}])
+        self.excellon_units_radio = RadioSet([{'label': _('Inch'), 'value': 'INCH'},
+                                              {'label': _('mm'), 'value': 'METRIC'}])
         self.excellon_units_radio.setToolTip(
         self.excellon_units_radio.setToolTip(
             _("This sets the units of Excellon files.\n"
             _("This sets the units of Excellon files.\n"
               "Some Excellon files don't have an header\n"
               "Some Excellon files don't have an header\n"

+ 2 - 2
appGUI/preferences/excellon/ExcellonOptPrefGroupUI.py

@@ -30,7 +30,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
         self.decimals = decimals
         self.decimals = decimals
 
 
         # ## Create CNC Job
         # ## Create CNC Job
-        self.cncjob_label = QtWidgets.QLabel('<b>%s</b>' % _('Create CNC Job'))
+        self.cncjob_label = QtWidgets.QLabel('<b>%s</b>' % _('Create CNCJob'))
         self.cncjob_label.setToolTip(
         self.cncjob_label.setToolTip(
             _("Parameters used to create a CNC Job object\n"
             _("Parameters used to create a CNC Job object\n"
               "for this drill object.")
               "for this drill object.")
@@ -84,7 +84,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
 
 
         self.mill_dia_entry = FCDoubleSpinner()
         self.mill_dia_entry = FCDoubleSpinner()
         self.mill_dia_entry.set_precision(self.decimals)
         self.mill_dia_entry.set_precision(self.decimals)
-        self.mill_dia_entry.set_range(0.0000, 9999.9999)
+        self.mill_dia_entry.set_range(0.0000, 10000.0000)
 
 
         grid2.addWidget(self.mill_dia_label, 2, 0)
         grid2.addWidget(self.mill_dia_label, 2, 0)
         grid2.addWidget(self.mill_dia_entry, 2, 1)
         grid2.addWidget(self.mill_dia_entry, 2, 1)

+ 5 - 5
appGUI/preferences/general/GeneralAppPrefGroupUI.py

@@ -61,7 +61,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.precision_metric_entry, 1, 1)
         grid0.addWidget(self.precision_metric_entry, 1, 1)
 
 
         # Precision Inch
         # Precision Inch
-        self.precision_inch_label = QtWidgets.QLabel('%s:' % _('Precision INCH'))
+        self.precision_inch_label = QtWidgets.QLabel('%s:' % _('Precision Inch'))
         self.precision_inch_label.setToolTip(
         self.precision_inch_label.setToolTip(
             _("The number of decimals used throughout the application\n"
             _("The number of decimals used throughout the application\n"
               "when the set units are in INCH system.\n"
               "when the set units are in INCH system.\n"
@@ -316,7 +316,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         # Top Margin value
         # Top Margin value
         self.tmargin_entry = FCDoubleSpinner()
         self.tmargin_entry = FCDoubleSpinner()
         self.tmargin_entry.set_precision(self.decimals)
         self.tmargin_entry.set_precision(self.decimals)
-        self.tmargin_entry.set_range(0.0000, 9999.9999)
+        self.tmargin_entry.set_range(0.0000, 10000.0000)
 
 
         self.tmargin_label = QtWidgets.QLabel('%s:' % _("Top Margin"))
         self.tmargin_label = QtWidgets.QLabel('%s:' % _("Top Margin"))
         self.tmargin_label.setToolTip(
         self.tmargin_label.setToolTip(
@@ -329,7 +329,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         # Bottom Margin value
         # Bottom Margin value
         self.bmargin_entry = FCDoubleSpinner()
         self.bmargin_entry = FCDoubleSpinner()
         self.bmargin_entry.set_precision(self.decimals)
         self.bmargin_entry.set_precision(self.decimals)
-        self.bmargin_entry.set_range(0.0000, 9999.9999)
+        self.bmargin_entry.set_range(0.0000, 10000.0000)
 
 
         self.bmargin_label = QtWidgets.QLabel('%s:' % _("Bottom Margin"))
         self.bmargin_label = QtWidgets.QLabel('%s:' % _("Bottom Margin"))
         self.bmargin_label.setToolTip(
         self.bmargin_label.setToolTip(
@@ -342,7 +342,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         # Left Margin value
         # Left Margin value
         self.lmargin_entry = FCDoubleSpinner()
         self.lmargin_entry = FCDoubleSpinner()
         self.lmargin_entry.set_precision(self.decimals)
         self.lmargin_entry.set_precision(self.decimals)
-        self.lmargin_entry.set_range(0.0000, 9999.9999)
+        self.lmargin_entry.set_range(0.0000, 10000.0000)
 
 
         self.lmargin_label = QtWidgets.QLabel('%s:' % _("Left Margin"))
         self.lmargin_label = QtWidgets.QLabel('%s:' % _("Left Margin"))
         self.lmargin_label.setToolTip(
         self.lmargin_label.setToolTip(
@@ -355,7 +355,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         # Right Margin value
         # Right Margin value
         self.rmargin_entry = FCDoubleSpinner()
         self.rmargin_entry = FCDoubleSpinner()
         self.rmargin_entry.set_precision(self.decimals)
         self.rmargin_entry.set_precision(self.decimals)
-        self.rmargin_entry.set_range(0.0000, 9999.9999)
+        self.rmargin_entry.set_range(0.0000, 10000.0000)
 
 
         self.rmargin_label = QtWidgets.QLabel('%s:' % _("Right Margin"))
         self.rmargin_label = QtWidgets.QLabel('%s:' % _("Right Margin"))
         self.rmargin_label.setToolTip(
         self.rmargin_label.setToolTip(

+ 1 - 141
appGUI/preferences/general/GeneralGUIPrefGroupUI.py

@@ -326,7 +326,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.proj_color_entry.editingFinished.connect(self.on_proj_color_entry)
         self.proj_color_entry.editingFinished.connect(self.on_proj_color_entry)
         self.proj_color_dis_entry.editingFinished.connect(self.on_proj_color_dis_entry)
         self.proj_color_dis_entry.editingFinished.connect(self.on_proj_color_dis_entry)
 
 
-        self.layout_combo.activated.connect(self.on_layout)
+        self.layout_combo.activated.connect(self.app.on_layout)
 
 
     @staticmethod
     @staticmethod
     def handle_style(style):
     def handle_style(style):
@@ -407,143 +407,3 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
 
 
     def on_proj_color_dis_entry(self):
     def on_proj_color_dis_entry(self):
         self.app.defaults['global_proj_item_dis_color'] = self.proj_color_dis_entry.get_value()
         self.app.defaults['global_proj_item_dis_color'] = self.proj_color_dis_entry.get_value()
-
-    def on_layout(self, index=None, lay=None):
-        """
-        Set the toolbars layout (location)
-
-        :param index:
-        :param lay:     Type of layout to be set on the toolbard
-        :return:        None
-        """
-
-        self.app.defaults.report_usage("on_layout()")
-        if lay:
-            current_layout = lay
-        else:
-            current_layout = self.layout_combo.get_value()
-
-        lay_settings = QSettings("Open Source", "FlatCAM")
-        lay_settings.setValue('layout', current_layout)
-
-        # This will write the setting to the platform specific storage.
-        del lay_settings
-
-        # first remove the toolbars:
-        try:
-            self.app.ui.removeToolBar(self.app.ui.toolbarfile)
-            self.app.ui.removeToolBar(self.app.ui.toolbaredit)
-            self.app.ui.removeToolBar(self.app.ui.toolbarview)
-            self.app.ui.removeToolBar(self.app.ui.toolbarshell)
-            self.app.ui.removeToolBar(self.app.ui.toolbartools)
-            self.app.ui.removeToolBar(self.app.ui.exc_edit_toolbar)
-            self.app.ui.removeToolBar(self.app.ui.geo_edit_toolbar)
-            self.app.ui.removeToolBar(self.app.ui.grb_edit_toolbar)
-            self.app.ui.removeToolBar(self.app.ui.toolbarshell)
-        except Exception:
-            pass
-
-        if current_layout == 'compact':
-            # ## TOOLBAR INSTALLATION # ##
-            self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
-            self.app.ui.toolbarfile.setObjectName('File_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
-
-            self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
-            self.app.ui.toolbaredit.setObjectName('Edit_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbaredit)
-
-            self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
-            self.app.ui.toolbarshell.setObjectName('Shell_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarshell)
-
-            self.app.ui.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
-            self.app.ui.toolbartools.setObjectName('Tools_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbartools)
-
-            self.app.ui.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
-            # self.app.ui.geo_edit_toolbar.setVisible(False)
-            self.app.ui.geo_edit_toolbar.setObjectName('GeoEditor_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.geo_edit_toolbar)
-
-            self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
-            self.app.ui.toolbarview.setObjectName('View_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.toolbarview)
-
-            self.app.ui.addToolBarBreak(area=Qt.RightToolBarArea)
-
-            self.app.ui.grb_edit_toolbar = QtWidgets.QToolBar('Gerber Editor Toolbar')
-            # self.app.ui.grb_edit_toolbar.setVisible(False)
-            self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.grb_edit_toolbar)
-
-            self.app.ui.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
-            self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
-            self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar)
-
-        else:
-            # ## TOOLBAR INSTALLATION # ##
-            self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
-            self.app.ui.toolbarfile.setObjectName('File_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbarfile)
-
-            self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
-            self.app.ui.toolbaredit.setObjectName('Edit_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbaredit)
-
-            self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
-            self.app.ui.toolbarview.setObjectName('View_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbarview)
-
-            self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
-            self.app.ui.toolbarshell.setObjectName('Shell_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbarshell)
-
-            self.app.ui.toolbartools = QtWidgets.QToolBar('Tools Toolbar')
-            self.app.ui.toolbartools.setObjectName('Tools_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbartools)
-
-            self.app.ui.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar')
-            # self.app.ui.exc_edit_toolbar.setVisible(False)
-            self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
-            self.app.ui.addToolBar(self.app.ui.exc_edit_toolbar)
-
-            self.app.ui.addToolBarBreak()
-
-            self.app.ui.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar')
-            # self.app.ui.geo_edit_toolbar.setVisible(False)
-            self.app.ui.geo_edit_toolbar.setObjectName('GeoEditor_TB')
-            self.app.ui.addToolBar(self.app.ui.geo_edit_toolbar)
-
-            self.app.ui.grb_edit_toolbar = QtWidgets.QToolBar('Gerber Editor Toolbar')
-            # self.app.ui.grb_edit_toolbar.setVisible(False)
-            self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
-            self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar)
-
-        if current_layout == 'minimal':
-            self.app.ui.toolbarview.setVisible(False)
-            self.app.ui.toolbarshell.setVisible(False)
-            self.app.ui.geo_edit_toolbar.setVisible(False)
-            self.app.ui.grb_edit_toolbar.setVisible(False)
-            self.app.ui.exc_edit_toolbar.setVisible(False)
-            self.app.ui.lock_toolbar(lock=True)
-
-        # add all the actions to the toolbars
-        self.app.ui.populate_toolbars()
-
-        try:
-            # reconnect all the signals to the toolbar actions
-            self.app.connect_toolbar_signals(ui=self.app.ui)
-        except Exception as e:
-            self.app.log.debug(
-                "appGUI.preferences.general.GeneralGUIPrefGroupUI.on_layout() - connect toolbar signals -> %s" % str(e))
-
-        self.app.ui.grid_snap_btn.setChecked(True)
-
-        self.app.ui.corner_snap_btn.setVisible(False)
-        self.app.ui.snap_magnet.setVisible(False)
-
-        self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
-        self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))
-        self.app.ui.snap_max_dist_entry.setText(str(self.app.defaults["global_snap_max"]))
-        self.app.ui.grid_gap_link_cb.setChecked(True)

+ 113 - 16
appGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py

@@ -2,7 +2,7 @@ from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
 from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel, NumericalEvalTupleEntry, \
 from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel, NumericalEvalTupleEntry, \
-    NumericalEvalEntry
+    NumericalEvalEntry, FCComboBox2
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -31,9 +31,9 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         # ------------------------------
         # ------------------------------
         # ## Advanced Options
         # ## Advanced Options
         # ------------------------------
         # ------------------------------
-        self.geo_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
+        self.geo_label = FCLabel('<b>%s:</b>' % _('Advanced Options'))
         self.geo_label.setToolTip(
         self.geo_label.setToolTip(
-            _("A list of Geometry advanced parameters.\n"
+            _("A list of advanced parameters.\n"
               "Those parameters are available only for\n"
               "Those parameters are available only for\n"
               "Advanced App. Level.")
               "Advanced App. Level.")
         )
         )
@@ -43,7 +43,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         self.layout.addLayout(grid1)
         self.layout.addLayout(grid1)
 
 
         # Toolchange X,Y
         # Toolchange X,Y
-        toolchange_xy_label = QtWidgets.QLabel('%s:' % _('Toolchange X-Y'))
+        toolchange_xy_label = FCLabel('%s:' % _('Toolchange X-Y'))
         toolchange_xy_label.setToolTip(
         toolchange_xy_label.setToolTip(
             _("Toolchange X,Y position.")
             _("Toolchange X,Y position.")
         )
         )
@@ -53,7 +53,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.toolchangexy_entry, 1, 1)
         grid1.addWidget(self.toolchangexy_entry, 1, 1)
 
 
         # Start move Z
         # Start move Z
-        startzlabel = QtWidgets.QLabel('%s:' % _('Start Z'))
+        startzlabel = FCLabel('%s:' % _('Start Z'))
         startzlabel.setToolTip(
         startzlabel.setToolTip(
             _("Height of the tool just after starting the work.\n"
             _("Height of the tool just after starting the work.\n"
               "Delete the value if you don't need this feature.")
               "Delete the value if you don't need this feature.")
@@ -64,7 +64,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.gstartz_entry, 2, 1)
         grid1.addWidget(self.gstartz_entry, 2, 1)
 
 
         # Feedrate rapids
         # Feedrate rapids
-        fr_rapid_label = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
+        fr_rapid_label = FCLabel('%s:' % _('Feedrate Rapids'))
         fr_rapid_label.setToolTip(
         fr_rapid_label.setToolTip(
             _("Cutting speed in the XY plane\n"
             _("Cutting speed in the XY plane\n"
               "(in units per minute).\n"
               "(in units per minute).\n"
@@ -73,7 +73,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
               "ignore for any other cases.")
               "ignore for any other cases.")
         )
         )
         self.feedrate_rapid_entry = FCDoubleSpinner()
         self.feedrate_rapid_entry = FCDoubleSpinner()
-        self.feedrate_rapid_entry.set_range(0, 99999.9999)
+        self.feedrate_rapid_entry.set_range(0, 910000.0000)
         self.feedrate_rapid_entry.set_precision(self.decimals)
         self.feedrate_rapid_entry.set_precision(self.decimals)
         self.feedrate_rapid_entry.setSingleStep(0.1)
         self.feedrate_rapid_entry.setSingleStep(0.1)
         self.feedrate_rapid_entry.setWrapping(True)
         self.feedrate_rapid_entry.setWrapping(True)
@@ -105,7 +105,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.e_cut_entry, 5, 1)
         grid1.addWidget(self.e_cut_entry, 5, 1)
 
 
         # Probe depth
         # Probe depth
-        self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
+        self.pdepth_label = FCLabel('%s:' % _("Probe Z depth"))
         self.pdepth_label.setToolTip(
         self.pdepth_label.setToolTip(
             _("The maximum depth that the probe is allowed\n"
             _("The maximum depth that the probe is allowed\n"
               "to probe. Negative value, in current units.")
               "to probe. Negative value, in current units.")
@@ -120,12 +120,12 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.pdepth_entry, 6, 1)
         grid1.addWidget(self.pdepth_entry, 6, 1)
 
 
         # Probe feedrate
         # Probe feedrate
-        self.feedrate_probe_label = QtWidgets.QLabel('%s:' % _("Feedrate Probe"))
+        self.feedrate_probe_label = FCLabel('%s:' % _("Feedrate Probe"))
         self.feedrate_probe_label.setToolTip(
         self.feedrate_probe_label.setToolTip(
             _("The feedrate used while the probe is probing.")
             _("The feedrate used while the probe is probing.")
         )
         )
         self.feedrate_probe_entry = FCDoubleSpinner()
         self.feedrate_probe_entry = FCDoubleSpinner()
-        self.feedrate_probe_entry.set_range(0, 99999.9999)
+        self.feedrate_probe_entry.set_range(0, 910000.0000)
         self.feedrate_probe_entry.set_precision(self.decimals)
         self.feedrate_probe_entry.set_precision(self.decimals)
         self.feedrate_probe_entry.setSingleStep(0.1)
         self.feedrate_probe_entry.setSingleStep(0.1)
         self.feedrate_probe_entry.setWrapping(True)
         self.feedrate_probe_entry.setWrapping(True)
@@ -134,7 +134,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.feedrate_probe_entry, 7, 1)
         grid1.addWidget(self.feedrate_probe_entry, 7, 1)
 
 
         # Spindle direction
         # Spindle direction
-        spindle_dir_label = QtWidgets.QLabel('%s:' % _('Spindle direction'))
+        spindle_dir_label = FCLabel('%s:' % _('Spindle direction'))
         spindle_dir_label.setToolTip(
         spindle_dir_label.setToolTip(
             _("This sets the direction that the spindle is rotating.\n"
             _("This sets the direction that the spindle is rotating.\n"
               "It can be either:\n"
               "It can be either:\n"
@@ -158,7 +158,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.fplunge_cb, 9, 0, 1, 2)
         grid1.addWidget(self.fplunge_cb, 9, 0, 1, 2)
 
 
         # Size of trace segment on X axis
         # Size of trace segment on X axis
-        segx_label = QtWidgets.QLabel('%s:' % _("Segment X size"))
+        segx_label = FCLabel('%s:' % _("Segment X size"))
         segx_label.setToolTip(
         segx_label.setToolTip(
             _("The size of the trace segment on the X axis.\n"
             _("The size of the trace segment on the X axis.\n"
               "Useful for auto-leveling.\n"
               "Useful for auto-leveling.\n"
@@ -174,7 +174,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.segx_entry, 10, 1)
         grid1.addWidget(self.segx_entry, 10, 1)
 
 
         # Size of trace segment on Y axis
         # Size of trace segment on Y axis
-        segy_label = QtWidgets.QLabel('%s:' % _("Segment Y size"))
+        segy_label = FCLabel('%s:' % _("Segment Y size"))
         segy_label.setToolTip(
         segy_label.setToolTip(
             _("The size of the trace segment on the Y axis.\n"
             _("The size of the trace segment on the Y axis.\n"
               "Useful for auto-leveling.\n"
               "Useful for auto-leveling.\n"
@@ -197,7 +197,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         # -----------------------------
         # -----------------------------
         # --- Area Exclusion ----------
         # --- Area Exclusion ----------
         # -----------------------------
         # -----------------------------
-        self.area_exc_label = QtWidgets.QLabel('<b>%s:</b>' % _('Area Exclusion'))
+        self.area_exc_label = FCLabel('<b>%s:</b>' % _('Area Exclusion'))
         self.area_exc_label.setToolTip(
         self.area_exc_label.setToolTip(
             _("Area exclusion parameters.")
             _("Area exclusion parameters.")
         )
         )
@@ -215,7 +215,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(self.exclusion_cb, 14, 0, 1, 2)
         grid1.addWidget(self.exclusion_cb, 14, 0, 1, 2)
 
 
         # Area Selection shape
         # Area Selection shape
-        self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
+        self.area_shape_label = FCLabel('%s:' % _("Shape"))
         self.area_shape_label.setToolTip(
         self.area_shape_label.setToolTip(
             _("The kind of selection shape used for area selection.")
             _("The kind of selection shape used for area selection.")
         )
         )
@@ -243,10 +243,107 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
                                        "an interdiction area."))
                                        "an interdiction area."))
         self.over_z_entry = FCDoubleSpinner()
         self.over_z_entry = FCDoubleSpinner()
-        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_range(0.000, 10000.0000)
         self.over_z_entry.set_precision(self.decimals)
         self.over_z_entry.set_precision(self.decimals)
 
 
         grid1.addWidget(self.over_z_label, 18, 0)
         grid1.addWidget(self.over_z_label, 18, 0)
         grid1.addWidget(self.over_z_entry, 18, 1)
         grid1.addWidget(self.over_z_entry, 18, 1)
+        
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid1.addWidget(separator_line, 20, 0, 1, 2)
+        
+        # -----------------------------
+        # --- Area POLISH ----------
+        # -----------------------------
+        # Add Polish
+        self.polish_cb = FCCheckBox(label=_('Add Polish'))
+        self.polish_cb.setToolTip(_(
+            "Will add a Paint section at the end of the GCode.\n"
+            "A metallic brush will clean the material after milling."))
+        grid1.addWidget(self.polish_cb, 22, 0, 1, 2)
+
+        # Polish Tool Diameter
+        self.polish_dia_lbl = FCLabel('%s:' % _('Tool Dia'))
+        self.polish_dia_lbl.setToolTip(
+            _("Diameter for the polishing tool.")
+        )
+        self.polish_dia_entry = FCDoubleSpinner()
+        self.polish_dia_entry.set_precision(self.decimals)
+        self.polish_dia_entry.set_range(0.000, 10000.0000)
+
+        grid1.addWidget(self.polish_dia_lbl, 24, 0)
+        grid1.addWidget(self.polish_dia_entry, 24, 1)
+
+        # Polish Travel Z
+        self.polish_travelz_lbl = FCLabel('%s:' % _('Travel Z'))
+        self.polish_travelz_lbl.setToolTip(
+            _("Height of the tool when\n"
+              "moving without cutting.")
+        )
+        self.polish_travelz_entry = FCDoubleSpinner()
+        self.polish_travelz_entry.set_precision(self.decimals)
+        self.polish_travelz_entry.set_range(0.00000, 10000.00000)
+        self.polish_travelz_entry.setSingleStep(0.1)
+
+        grid1.addWidget(self.polish_travelz_lbl, 26, 0)
+        grid1.addWidget(self.polish_travelz_entry, 26, 1)
+
+        # Polish Pressure
+        self.polish_pressure_lbl = FCLabel('%s:' % _('Pressure'))
+        self.polish_pressure_lbl.setToolTip(
+            _("Negative value. The higher the absolute value\n"
+              "the stronger the pressure of the brush on the material.")
+        )
+        self.polish_pressure_entry = FCDoubleSpinner()
+        self.polish_pressure_entry.set_precision(self.decimals)
+        self.polish_pressure_entry.set_range(-10000.0000, 10000.0000)
+
+        grid1.addWidget(self.polish_pressure_lbl, 28, 0)
+        grid1.addWidget(self.polish_pressure_entry, 28, 1)
+
+        # Polish Margin
+        self.polish_margin_lbl = FCLabel('%s:' % _('Margin'))
+        self.polish_margin_lbl.setToolTip(
+            _("Bounding box margin.")
+        )
+        self.polish_margin_entry = FCDoubleSpinner()
+        self.polish_margin_entry.set_precision(self.decimals)
+        self.polish_margin_entry.set_range(-10000.0000, 10000.0000)
+
+        grid1.addWidget(self.polish_margin_lbl, 30, 0)
+        grid1.addWidget(self.polish_margin_entry, 30, 1)
+
+        # Polish Overlap
+        self.polish_over_lbl = FCLabel('%s:' % _('Overlap'))
+        self.polish_over_lbl.setToolTip(
+            _("How much (percentage) of the tool width to overlap each tool pass.")
+        )
+        self.polish_over_entry = FCDoubleSpinner(suffix='%')
+        self.polish_over_entry.set_precision(self.decimals)
+        self.polish_over_entry.setWrapping(True)
+        self.polish_over_entry.set_range(0.0000, 99.9999)
+        self.polish_over_entry.setSingleStep(0.1)
+
+        grid1.addWidget(self.polish_over_lbl, 32, 0)
+        grid1.addWidget(self.polish_over_entry, 32, 1)
+
+        # Polish Method
+        self.polish_method_lbl = FCLabel('%s:' % _('Method'))
+        self.polish_method_lbl.setToolTip(
+            _("Algorithm for polishing:\n"
+              "- Standard: Fixed step inwards.\n"
+              "- Seed-based: Outwards from seed.\n"
+              "- Line-based: Parallel lines.")
+        )
+
+        self.polish_method_combo = FCComboBox2()
+        self.polish_method_combo.addItems(
+            [_("Standard"), _("Seed"), _("Lines")]
+        )
+
+        grid1.addWidget(self.polish_method_lbl, 34, 0)
+        grid1.addWidget(self.polish_method_combo, 34, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()

+ 12 - 11
appGUI/preferences/geometry/GeometryOptPrefGroupUI.py

@@ -32,7 +32,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
         # ------------------------------
         # ------------------------------
         # ## Create CNC Job
         # ## Create CNC Job
         # ------------------------------
         # ------------------------------
-        self.cncjob_label = QtWidgets.QLabel('<b>%s:</b>' % _('Create CNC Job'))
+        self.cncjob_label = QtWidgets.QLabel('<b>%s:</b>' % _('Create CNCJob'))
         self.cncjob_label.setToolTip(
         self.cncjob_label.setToolTip(
             _("Create a CNC Job object\n"
             _("Create a CNC Job object\n"
               "tracing the contours of this\n"
               "tracing the contours of this\n"
@@ -54,9 +54,9 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry = FCDoubleSpinner()
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.cutz_entry.set_range(-9999.9999, 0.0000)
+            self.cutz_entry.set_range(-10000.0000, 0.0000)
         else:
         else:
-            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+            self.cutz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
@@ -107,9 +107,9 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
         self.travelz_entry = FCDoubleSpinner()
         self.travelz_entry = FCDoubleSpinner()
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.travelz_entry.set_range(0.0001, 9999.9999)
+            self.travelz_entry.set_range(0.0001, 10000.0000)
         else:
         else:
-            self.travelz_entry.set_range(-9999.9999, 9999.9999)
+            self.travelz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
@@ -139,9 +139,9 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
         self.toolchangez_entry = FCDoubleSpinner()
         self.toolchangez_entry = FCDoubleSpinner()
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.toolchangez_entry.set_range(0.000, 9999.9999)
+            self.toolchangez_entry.set_range(0.000, 10000.0000)
         else:
         else:
-            self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
+            self.toolchangez_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.toolchangez_entry.set_precision(self.decimals)
         self.toolchangez_entry.set_precision(self.decimals)
         self.toolchangez_entry.setSingleStep(0.1)
         self.toolchangez_entry.setSingleStep(0.1)
@@ -159,9 +159,9 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
         self.endz_entry = FCDoubleSpinner()
         self.endz_entry = FCDoubleSpinner()
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.endz_entry.set_range(0.000, 9999.9999)
+            self.endz_entry.set_range(0.000, 10000.0000)
         else:
         else:
-            self.endz_entry.set_range(-9999.9999, 9999.9999)
+            self.endz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.endz_entry.set_precision(self.decimals)
         self.endz_entry.set_precision(self.decimals)
         self.endz_entry.setSingleStep(0.1)
         self.endz_entry.setSingleStep(0.1)
@@ -189,7 +189,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
               "plane in units per minute")
               "plane in units per minute")
         )
         )
         self.cncfeedrate_entry = FCDoubleSpinner()
         self.cncfeedrate_entry = FCDoubleSpinner()
-        self.cncfeedrate_entry.set_range(0, 99999.9999)
+        self.cncfeedrate_entry.set_range(0, 910000.0000)
         self.cncfeedrate_entry.set_precision(self.decimals)
         self.cncfeedrate_entry.set_precision(self.decimals)
         self.cncfeedrate_entry.setSingleStep(0.1)
         self.cncfeedrate_entry.setSingleStep(0.1)
         self.cncfeedrate_entry.setWrapping(True)
         self.cncfeedrate_entry.setWrapping(True)
@@ -205,7 +205,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
               "It is called also Plunge.")
               "It is called also Plunge.")
         )
         )
         self.feedrate_z_entry = FCDoubleSpinner()
         self.feedrate_z_entry = FCDoubleSpinner()
-        self.feedrate_z_entry.set_range(0, 99999.9999)
+        self.feedrate_z_entry.set_range(0, 910000.0000)
         self.feedrate_z_entry.set_precision(self.decimals)
         self.feedrate_z_entry.set_precision(self.decimals)
         self.feedrate_z_entry.setSingleStep(0.1)
         self.feedrate_z_entry.setSingleStep(0.1)
         self.feedrate_z_entry.setWrapping(True)
         self.feedrate_z_entry.setWrapping(True)
@@ -259,6 +259,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
         )
         )
         self.pp_geometry_name_cb = FCComboBox()
         self.pp_geometry_name_cb = FCComboBox()
         self.pp_geometry_name_cb.setFocusPolicy(Qt.StrongFocus)
         self.pp_geometry_name_cb.setFocusPolicy(Qt.StrongFocus)
+        self.pp_geometry_name_cb.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
 
 
         grid1.addWidget(pp_label, 13, 0)
         grid1.addWidget(pp_label, 13, 0)
         grid1.addWidget(self.pp_geometry_name_cb, 13, 1)
         grid1.addWidget(self.pp_geometry_name_cb, 13, 1)

+ 1 - 1
appGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py

@@ -30,7 +30,7 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI):
         # ## Advanced Gerber Parameters
         # ## Advanced Gerber Parameters
         self.adv_param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
         self.adv_param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Advanced Options'))
         self.adv_param_label.setToolTip(
         self.adv_param_label.setToolTip(
-            _("A list of Gerber advanced parameters.\n"
+            _("A list of advanced parameters.\n"
               "Those parameters are available only for\n"
               "Those parameters are available only for\n"
               "Advanced App. Level.")
               "Advanced App. Level.")
         )
         )

+ 2 - 2
appGUI/preferences/gerber/GerberExpPrefGroupUI.py

@@ -44,8 +44,8 @@ class GerberExpPrefGroupUI(OptionsGroupUI):
             _("The units used in the Gerber file.")
             _("The units used in the Gerber file.")
         )
         )
 
 
-        self.gerber_units_radio = RadioSet([{'label': _('INCH'), 'value': 'IN'},
-                                            {'label': _('MM'), 'value': 'MM'}])
+        self.gerber_units_radio = RadioSet([{'label': _('Inch'), 'value': 'IN'},
+                                            {'label': _('mm'), 'value': 'MM'}])
         self.gerber_units_radio.setToolTip(
         self.gerber_units_radio.setToolTip(
             _("The units used in the Gerber file.")
             _("The units used in the Gerber file.")
         )
         )

+ 2 - 2
appGUI/preferences/gerber/GerberGenPrefGroupUI.py

@@ -84,8 +84,8 @@ class GerberGenPrefGroupUI(OptionsGroupUI):
             _("The units used in the Gerber file.")
             _("The units used in the Gerber file.")
         )
         )
 
 
-        self.gerber_units_radio = RadioSet([{'label': _('INCH'), 'value': 'IN'},
-                                            {'label': _('MM'), 'value': 'MM'}])
+        self.gerber_units_radio = RadioSet([{'label': _('Inch'), 'value': 'IN'},
+                                            {'label': _('mm'), 'value': 'MM'}])
         self.gerber_units_radio.setToolTip(
         self.gerber_units_radio.setToolTip(
             _("The units used in the Gerber file.")
             _("The units used in the Gerber file.")
         )
         )

+ 95 - 68
appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
+from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCLabel
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -34,7 +34,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
         grid_lay.setColumnStretch(1, 1)
         grid_lay.setColumnStretch(1, 1)
 
 
         # ## Parameters
         # ## Parameters
-        self.cflabel = QtWidgets.QLabel('<b>%s</b>' % _('Parameters'))
+        self.cflabel = FCLabel('<b>%s</b>' % _('Parameters'))
         self.cflabel.setToolTip(
         self.cflabel.setToolTip(
             _("A tool to generate a Copper Thieving that can be added\n"
             _("A tool to generate a Copper Thieving that can be added\n"
               "to a selected Gerber file.")
               "to a selected Gerber file.")
@@ -42,7 +42,7 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
         grid_lay.addWidget(self.cflabel, 0, 0, 1, 2)
         grid_lay.addWidget(self.cflabel, 0, 0, 1, 2)
 
 
         # CIRCLE STEPS - to be used when buffering
         # CIRCLE STEPS - to be used when buffering
-        self.circle_steps_lbl = QtWidgets.QLabel('%s:' % _("Circle Steps"))
+        self.circle_steps_lbl = FCLabel('%s:' % _("Circle Steps"))
         self.circle_steps_lbl.setToolTip(
         self.circle_steps_lbl.setToolTip(
             _("Number of steps (lines) used to interpolate circles.")
             _("Number of steps (lines) used to interpolate circles.")
         )
         )
@@ -50,11 +50,11 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
         self.circlesteps_entry = FCSpinner()
         self.circlesteps_entry = FCSpinner()
         self.circlesteps_entry.set_range(1, 9999)
         self.circlesteps_entry.set_range(1, 9999)
 
 
-        grid_lay.addWidget(self.circle_steps_lbl, 1, 0)
-        grid_lay.addWidget(self.circlesteps_entry, 1, 1)
+        grid_lay.addWidget(self.circle_steps_lbl, 2, 0)
+        grid_lay.addWidget(self.circlesteps_entry, 2, 1)
 
 
         # CLEARANCE #
         # CLEARANCE #
-        self.clearance_label = QtWidgets.QLabel('%s:' % _("Clearance"))
+        self.clearance_label = FCLabel('%s:' % _("Clearance"))
         self.clearance_label.setToolTip(
         self.clearance_label.setToolTip(
             _("This set the distance between the copper Thieving components\n"
             _("This set the distance between the copper Thieving components\n"
               "(the polygon fill may be split in multiple polygons)\n"
               "(the polygon fill may be split in multiple polygons)\n"
@@ -65,11 +65,11 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
         self.clearance_entry.set_precision(self.decimals)
         self.clearance_entry.set_precision(self.decimals)
         self.clearance_entry.setSingleStep(0.1)
         self.clearance_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.clearance_label, 2, 0)
-        grid_lay.addWidget(self.clearance_entry, 2, 1)
+        grid_lay.addWidget(self.clearance_label, 4, 0)
+        grid_lay.addWidget(self.clearance_entry, 4, 1)
 
 
         # MARGIN #
         # MARGIN #
-        self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+        self.margin_label = FCLabel('%s:' % _("Margin"))
         self.margin_label.setToolTip(
         self.margin_label.setToolTip(
             _("Bounding box margin.")
             _("Bounding box margin.")
         )
         )
@@ -78,41 +78,54 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.setSingleStep(0.1)
         self.margin_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.margin_label, 3, 0)
-        grid_lay.addWidget(self.margin_entry, 3, 1)
+        grid_lay.addWidget(self.margin_label, 6, 0)
+        grid_lay.addWidget(self.margin_entry, 6, 1)
 
 
+        # Area #
+        self.area_label = FCLabel('%s:' % _("Area"))
+        self.area_label.setToolTip(
+            _("Thieving areas with area less then this value will not be added.")
+        )
+        self.area_entry = FCDoubleSpinner()
+        self.area_entry.set_range(0.0, 10000.0000)
+        self.area_entry.set_precision(self.decimals)
+        self.area_entry.setSingleStep(0.1)
+
+        grid_lay.addWidget(self.area_label, 8, 0)
+        grid_lay.addWidget(self.area_entry, 8, 1)
+        
         # Reference #
         # Reference #
         self.reference_radio = RadioSet([
         self.reference_radio = RadioSet([
             {'label': _('Itself'), 'value': 'itself'},
             {'label': _('Itself'), 'value': 'itself'},
             {"label": _("Area Selection"), "value": "area"},
             {"label": _("Area Selection"), "value": "area"},
             {'label': _("Reference Object"), 'value': 'box'}
             {'label': _("Reference Object"), 'value': 'box'}
         ], orientation='vertical', stretch=False)
         ], orientation='vertical', stretch=False)
-        self.reference_label = QtWidgets.QLabel(_("Reference:"))
+        self.reference_label = FCLabel(_("Reference:"))
         self.reference_label.setToolTip(
         self.reference_label.setToolTip(
-            _("- 'Itself' - the copper Thieving extent is based on the object extent.\n"
+            _("- 'Itself' - the copper thieving extent is based on the object extent.\n"
               "- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
               "- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
               "- 'Reference Object' - will do copper thieving within the area specified by another object.")
               "- 'Reference Object' - will do copper thieving within the area specified by another object.")
         )
         )
-        grid_lay.addWidget(self.reference_label, 4, 0)
-        grid_lay.addWidget(self.reference_radio, 4, 1)
+        grid_lay.addWidget(self.reference_label, 10, 0)
+        grid_lay.addWidget(self.reference_radio, 10, 1)
 
 
         # Bounding Box Type #
         # Bounding Box Type #
         self.bbox_type_radio = RadioSet([
         self.bbox_type_radio = RadioSet([
             {'label': _('Rectangular'), 'value': 'rect'},
             {'label': _('Rectangular'), 'value': 'rect'},
             {"label": _("Minimal"), "value": "min"}
             {"label": _("Minimal"), "value": "min"}
         ], stretch=False)
         ], stretch=False)
-        self.bbox_type_label = QtWidgets.QLabel(_("Box Type:"))
+        self.bbox_type_label = FCLabel('%s:' % _("Box Type"))
         self.bbox_type_label.setToolTip(
         self.bbox_type_label.setToolTip(
             _("- 'Rectangular' - the bounding box will be of rectangular shape.\n"
             _("- 'Rectangular' - the bounding box will be of rectangular shape.\n"
               "- 'Minimal' - the bounding box will be the convex hull shape.")
               "- 'Minimal' - the bounding box will be the convex hull shape.")
         )
         )
-        grid_lay.addWidget(self.bbox_type_label, 5, 0)
-        grid_lay.addWidget(self.bbox_type_radio, 5, 1)
+        grid_lay.addWidget(self.bbox_type_label, 12, 0)
+        grid_lay.addWidget(self.bbox_type_radio, 12, 1)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line, 6, 0, 1, 2)
+        grid_lay.addWidget(separator_line, 14, 0, 1, 2)
 
 
         # Fill Type
         # Fill Type
         self.fill_type_radio = RadioSet([
         self.fill_type_radio = RadioSet([
@@ -121,154 +134,168 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI):
             {"label": _("Squares Grid"), "value": "square"},
             {"label": _("Squares Grid"), "value": "square"},
             {"label": _("Lines Grid"), "value": "line"}
             {"label": _("Lines Grid"), "value": "line"}
         ], orientation='vertical', stretch=False)
         ], orientation='vertical', stretch=False)
-        self.fill_type_label = QtWidgets.QLabel(_("Fill Type:"))
+        self.fill_type_label = FCLabel(_("Fill Type:"))
         self.fill_type_label.setToolTip(
         self.fill_type_label.setToolTip(
             _("- 'Solid' - copper thieving will be a solid polygon.\n"
             _("- 'Solid' - copper thieving will be a solid polygon.\n"
               "- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n"
               "- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n"
               "- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n"
               "- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n"
               "- 'Lines Grid' - the empty area will be filled with a pattern of lines.")
               "- 'Lines Grid' - the empty area will be filled with a pattern of lines.")
         )
         )
-        grid_lay.addWidget(self.fill_type_label, 7, 0)
-        grid_lay.addWidget(self.fill_type_radio, 7, 1)
+        grid_lay.addWidget(self.fill_type_label, 16, 0)
+        grid_lay.addWidget(self.fill_type_radio, 16, 1)
 
 
-        self.dots_label = QtWidgets.QLabel('<b>%s</b>:' % _("Dots Grid Parameters"))
-        grid_lay.addWidget(self.dots_label, 8, 0, 1, 2)
+        self.dots_label = FCLabel('<b>%s</b>:' % _("Dots Grid Parameters"))
+        grid_lay.addWidget(self.dots_label, 18, 0, 1, 2)
 
 
         # Dot diameter #
         # Dot diameter #
-        self.dotdia_label = QtWidgets.QLabel('%s:' % _("Dia"))
+        self.dotdia_label = FCLabel('%s:' % _("Dia"))
         self.dotdia_label.setToolTip(
         self.dotdia_label.setToolTip(
             _("Dot diameter in Dots Grid.")
             _("Dot diameter in Dots Grid.")
         )
         )
         self.dot_dia_entry = FCDoubleSpinner()
         self.dot_dia_entry = FCDoubleSpinner()
-        self.dot_dia_entry.set_range(0.0, 9999.9999)
+        self.dot_dia_entry.set_range(0.0, 10000.0000)
         self.dot_dia_entry.set_precision(self.decimals)
         self.dot_dia_entry.set_precision(self.decimals)
         self.dot_dia_entry.setSingleStep(0.1)
         self.dot_dia_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.dotdia_label, 9, 0)
-        grid_lay.addWidget(self.dot_dia_entry, 9, 1)
+        grid_lay.addWidget(self.dotdia_label, 20, 0)
+        grid_lay.addWidget(self.dot_dia_entry, 20, 1)
 
 
         # Dot spacing #
         # Dot spacing #
-        self.dotspacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+        self.dotspacing_label = FCLabel('%s:' % _("Spacing"))
         self.dotspacing_label.setToolTip(
         self.dotspacing_label.setToolTip(
             _("Distance between each two dots in Dots Grid.")
             _("Distance between each two dots in Dots Grid.")
         )
         )
         self.dot_spacing_entry = FCDoubleSpinner()
         self.dot_spacing_entry = FCDoubleSpinner()
-        self.dot_spacing_entry.set_range(0.0, 9999.9999)
+        self.dot_spacing_entry.set_range(0.0, 10000.0000)
         self.dot_spacing_entry.set_precision(self.decimals)
         self.dot_spacing_entry.set_precision(self.decimals)
         self.dot_spacing_entry.setSingleStep(0.1)
         self.dot_spacing_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.dotspacing_label, 10, 0)
-        grid_lay.addWidget(self.dot_spacing_entry, 10, 1)
+        grid_lay.addWidget(self.dotspacing_label, 22, 0)
+        grid_lay.addWidget(self.dot_spacing_entry, 22, 1)
 
 
-        self.squares_label = QtWidgets.QLabel('<b>%s</b>:' % _("Squares Grid Parameters"))
-        grid_lay.addWidget(self.squares_label, 11, 0, 1, 2)
+        self.squares_label = FCLabel('<b>%s</b>:' % _("Squares Grid Parameters"))
+        grid_lay.addWidget(self.squares_label, 24, 0, 1, 2)
 
 
         # Square Size #
         # Square Size #
-        self.square_size_label = QtWidgets.QLabel('%s:' % _("Size"))
+        self.square_size_label = FCLabel('%s:' % _("Size"))
         self.square_size_label.setToolTip(
         self.square_size_label.setToolTip(
             _("Square side size in Squares Grid.")
             _("Square side size in Squares Grid.")
         )
         )
         self.square_size_entry = FCDoubleSpinner()
         self.square_size_entry = FCDoubleSpinner()
-        self.square_size_entry.set_range(0.0, 9999.9999)
+        self.square_size_entry.set_range(0.0, 10000.0000)
         self.square_size_entry.set_precision(self.decimals)
         self.square_size_entry.set_precision(self.decimals)
         self.square_size_entry.setSingleStep(0.1)
         self.square_size_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.square_size_label, 12, 0)
-        grid_lay.addWidget(self.square_size_entry, 12, 1)
+        grid_lay.addWidget(self.square_size_label, 26, 0)
+        grid_lay.addWidget(self.square_size_entry, 26, 1)
 
 
         # Squares spacing #
         # Squares spacing #
-        self.squares_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+        self.squares_spacing_label = FCLabel('%s:' % _("Spacing"))
         self.squares_spacing_label.setToolTip(
         self.squares_spacing_label.setToolTip(
             _("Distance between each two squares in Squares Grid.")
             _("Distance between each two squares in Squares Grid.")
         )
         )
         self.squares_spacing_entry = FCDoubleSpinner()
         self.squares_spacing_entry = FCDoubleSpinner()
-        self.squares_spacing_entry.set_range(0.0, 9999.9999)
+        self.squares_spacing_entry.set_range(0.0, 10000.0000)
         self.squares_spacing_entry.set_precision(self.decimals)
         self.squares_spacing_entry.set_precision(self.decimals)
         self.squares_spacing_entry.setSingleStep(0.1)
         self.squares_spacing_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.squares_spacing_label, 13, 0)
-        grid_lay.addWidget(self.squares_spacing_entry, 13, 1)
+        grid_lay.addWidget(self.squares_spacing_label, 28, 0)
+        grid_lay.addWidget(self.squares_spacing_entry, 28, 1)
 
 
-        self.lines_label = QtWidgets.QLabel('<b>%s</b>:' % _("Lines Grid Parameters"))
-        grid_lay.addWidget(self.lines_label, 14, 0, 1, 2)
+        self.lines_label = FCLabel('<b>%s</b>:' % _("Lines Grid Parameters"))
+        grid_lay.addWidget(self.lines_label, 30, 0, 1, 2)
 
 
         # Square Size #
         # Square Size #
-        self.line_size_label = QtWidgets.QLabel('%s:' % _("Size"))
+        self.line_size_label = FCLabel('%s:' % _("Size"))
         self.line_size_label.setToolTip(
         self.line_size_label.setToolTip(
             _("Line thickness size in Lines Grid.")
             _("Line thickness size in Lines Grid.")
         )
         )
         self.line_size_entry = FCDoubleSpinner()
         self.line_size_entry = FCDoubleSpinner()
-        self.line_size_entry.set_range(0.0, 9999.9999)
+        self.line_size_entry.set_range(0.0, 10000.0000)
         self.line_size_entry.set_precision(self.decimals)
         self.line_size_entry.set_precision(self.decimals)
         self.line_size_entry.setSingleStep(0.1)
         self.line_size_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.line_size_label, 15, 0)
-        grid_lay.addWidget(self.line_size_entry, 15, 1)
+        grid_lay.addWidget(self.line_size_label, 32, 0)
+        grid_lay.addWidget(self.line_size_entry, 32, 1)
 
 
         # Lines spacing #
         # Lines spacing #
-        self.lines_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+        self.lines_spacing_label = FCLabel('%s:' % _("Spacing"))
         self.lines_spacing_label.setToolTip(
         self.lines_spacing_label.setToolTip(
             _("Distance between each two lines in Lines Grid.")
             _("Distance between each two lines in Lines Grid.")
         )
         )
         self.lines_spacing_entry = FCDoubleSpinner()
         self.lines_spacing_entry = FCDoubleSpinner()
-        self.lines_spacing_entry.set_range(0.0, 9999.9999)
+        self.lines_spacing_entry.set_range(0.0, 10000.0000)
         self.lines_spacing_entry.set_precision(self.decimals)
         self.lines_spacing_entry.set_precision(self.decimals)
         self.lines_spacing_entry.setSingleStep(0.1)
         self.lines_spacing_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.lines_spacing_label, 16, 0)
-        grid_lay.addWidget(self.lines_spacing_entry, 16, 1)
+        grid_lay.addWidget(self.lines_spacing_label, 34, 0)
+        grid_lay.addWidget(self.lines_spacing_entry, 34, 1)
 
 
-        self.robber_bar_label = QtWidgets.QLabel('<b>%s</b>' % _('Robber Bar Parameters'))
+        self.robber_bar_label = FCLabel('<b>%s</b>' % _('Robber Bar Parameters'))
         self.robber_bar_label.setToolTip(
         self.robber_bar_label.setToolTip(
             _("Parameters used for the robber bar.\n"
             _("Parameters used for the robber bar.\n"
               "Robber bar = copper border to help in pattern hole plating.")
               "Robber bar = copper border to help in pattern hole plating.")
         )
         )
-        grid_lay.addWidget(self.robber_bar_label, 17, 0, 1, 2)
+        grid_lay.addWidget(self.robber_bar_label, 36, 0, 1, 2)
 
 
         # ROBBER BAR MARGIN #
         # ROBBER BAR MARGIN #
-        self.rb_margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+        self.rb_margin_label = FCLabel('%s:' % _("Margin"))
         self.rb_margin_label.setToolTip(
         self.rb_margin_label.setToolTip(
             _("Bounding box margin for robber bar.")
             _("Bounding box margin for robber bar.")
         )
         )
         self.rb_margin_entry = FCDoubleSpinner()
         self.rb_margin_entry = FCDoubleSpinner()
-        self.rb_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.rb_margin_entry.set_range(-10000.0000, 10000.0000)
         self.rb_margin_entry.set_precision(self.decimals)
         self.rb_margin_entry.set_precision(self.decimals)
         self.rb_margin_entry.setSingleStep(0.1)
         self.rb_margin_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.rb_margin_label, 18, 0)
-        grid_lay.addWidget(self.rb_margin_entry, 18, 1)
+        grid_lay.addWidget(self.rb_margin_label, 38, 0)
+        grid_lay.addWidget(self.rb_margin_entry, 38, 1)
 
 
         # THICKNESS #
         # THICKNESS #
-        self.rb_thickness_label = QtWidgets.QLabel('%s:' % _("Thickness"))
+        self.rb_thickness_label = FCLabel('%s:' % _("Thickness"))
         self.rb_thickness_label.setToolTip(
         self.rb_thickness_label.setToolTip(
             _("The robber bar thickness.")
             _("The robber bar thickness.")
         )
         )
         self.rb_thickness_entry = FCDoubleSpinner()
         self.rb_thickness_entry = FCDoubleSpinner()
-        self.rb_thickness_entry.set_range(0.0000, 9999.9999)
+        self.rb_thickness_entry.set_range(0.0000, 10000.0000)
         self.rb_thickness_entry.set_precision(self.decimals)
         self.rb_thickness_entry.set_precision(self.decimals)
         self.rb_thickness_entry.setSingleStep(0.1)
         self.rb_thickness_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.rb_thickness_label, 19, 0)
-        grid_lay.addWidget(self.rb_thickness_entry, 19, 1)
+        grid_lay.addWidget(self.rb_thickness_label, 40, 0)
+        grid_lay.addWidget(self.rb_thickness_entry, 40, 1)
 
 
-        self.patern_mask_label = QtWidgets.QLabel('<b>%s</b>' % _('Pattern Plating Mask'))
+        self.patern_mask_label = FCLabel('<b>%s</b>' % _('Pattern Plating Mask'))
         self.patern_mask_label.setToolTip(
         self.patern_mask_label.setToolTip(
             _("Generate a mask for pattern plating.")
             _("Generate a mask for pattern plating.")
         )
         )
-        grid_lay.addWidget(self.patern_mask_label, 20, 0, 1, 2)
+        grid_lay.addWidget(self.patern_mask_label, 42, 0, 1, 2)
 
 
         # Openings CLEARANCE #
         # Openings CLEARANCE #
-        self.clearance_ppm_label = QtWidgets.QLabel('%s:' % _("Clearance"))
+        self.clearance_ppm_label = FCLabel('%s:' % _("Clearance"))
         self.clearance_ppm_label.setToolTip(
         self.clearance_ppm_label.setToolTip(
             _("The distance between the possible copper thieving elements\n"
             _("The distance between the possible copper thieving elements\n"
               "and/or robber bar and the actual openings in the mask.")
               "and/or robber bar and the actual openings in the mask.")
         )
         )
         self.clearance_ppm_entry = FCDoubleSpinner()
         self.clearance_ppm_entry = FCDoubleSpinner()
-        self.clearance_ppm_entry.set_range(-9999.9999, 9999.9999)
+        self.clearance_ppm_entry.set_range(-10000.0000, 10000.0000)
         self.clearance_ppm_entry.set_precision(self.decimals)
         self.clearance_ppm_entry.set_precision(self.decimals)
         self.clearance_ppm_entry.setSingleStep(0.1)
         self.clearance_ppm_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.clearance_ppm_label, 21, 0)
-        grid_lay.addWidget(self.clearance_ppm_entry, 21, 1)
+        grid_lay.addWidget(self.clearance_ppm_label, 44, 0)
+        grid_lay.addWidget(self.clearance_ppm_entry, 44, 1)
+
+        # Include geometry
+        self.ppm_choice_label = FCLabel('%s:' % _("Add"))
+        self.ppm_choice_label.setToolTip(
+            _("Choose which additional geometry to include, if available.")
+        )
+        self.ppm_choice_radio = RadioSet([
+            {"label": _("Both"), "value": "b"},
+            {'label': _('Thieving'), 'value': 't'},
+            {"label": _("Robber bar"), "value": "r"},
+            {"label": _("None"), "value": "n"}
+        ], orientation='vertical', stretch=False)
+        grid_lay.addWidget(self.ppm_choice_label, 46, 0)
+        grid_lay.addWidget(self.ppm_choice_radio, 46, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()

+ 5 - 5
appGUI/preferences/tools/Tools2CalPrefGroupUI.py

@@ -64,7 +64,7 @@ class Tools2CalPrefGroupUI(OptionsGroupUI):
         )
         )
 
 
         self.travelz_entry = FCDoubleSpinner()
         self.travelz_entry = FCDoubleSpinner()
-        self.travelz_entry.set_range(-9999.9999, 9999.9999)
+        self.travelz_entry.set_range(-10000.0000, 10000.0000)
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
 
 
@@ -78,7 +78,7 @@ class Tools2CalPrefGroupUI(OptionsGroupUI):
         )
         )
 
 
         self.verz_entry = FCDoubleSpinner()
         self.verz_entry = FCDoubleSpinner()
-        self.verz_entry.set_range(-9999.9999, 9999.9999)
+        self.verz_entry.set_range(-10000.0000, 10000.0000)
         self.verz_entry.set_precision(self.decimals)
         self.verz_entry.set_precision(self.decimals)
         self.verz_entry.setSingleStep(0.1)
         self.verz_entry.setSingleStep(0.1)
 
 
@@ -101,7 +101,7 @@ class Tools2CalPrefGroupUI(OptionsGroupUI):
         )
         )
 
 
         self.toolchangez_entry = FCDoubleSpinner()
         self.toolchangez_entry = FCDoubleSpinner()
-        self.toolchangez_entry.set_range(0.0000, 9999.9999)
+        self.toolchangez_entry.set_range(0.0000, 10000.0000)
         self.toolchangez_entry.set_precision(self.decimals)
         self.toolchangez_entry.set_precision(self.decimals)
         self.toolchangez_entry.setSingleStep(0.1)
         self.toolchangez_entry.setSingleStep(0.1)
 
 
@@ -128,8 +128,8 @@ class Tools2CalPrefGroupUI(OptionsGroupUI):
               "- top-left -> the user will align the PCB vertically\n"
               "- top-left -> the user will align the PCB vertically\n"
               "- bottom-right -> the user will align the PCB horizontally")
               "- bottom-right -> the user will align the PCB horizontally")
         )
         )
-        self.second_point_radio = RadioSet([{'label': _('Top-Left'), 'value': 'tl'},
-                                            {'label': _('Bottom-Right'), 'value': 'br'}],
+        self.second_point_radio = RadioSet([{'label': _('Top Left'), 'value': 'tl'},
+                                            {'label': _('Bottom Right'), 'value': 'br'}],
                                            orientation='vertical')
                                            orientation='vertical')
 
 
         grid_lay.addWidget(second_point_lbl, 8, 0)
         grid_lay.addWidget(second_point_lbl, 8, 0)

+ 6 - 6
appGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py

@@ -126,7 +126,7 @@ class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
         # Diameter value
         # Diameter value
         self.dia_entry = FCDoubleSpinner()
         self.dia_entry = FCDoubleSpinner()
         self.dia_entry.set_precision(self.decimals)
         self.dia_entry.set_precision(self.decimals)
-        self.dia_entry.set_range(0.0000, 9999.9999)
+        self.dia_entry.set_range(0.0000, 10000.0000)
 
 
         self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
         self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
         self.dia_label.setToolTip(
         self.dia_label.setToolTip(
@@ -153,7 +153,7 @@ class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
 
 
         self.circular_ring_entry = FCDoubleSpinner()
         self.circular_ring_entry = FCDoubleSpinner()
         self.circular_ring_entry.set_precision(self.decimals)
         self.circular_ring_entry.set_precision(self.decimals)
-        self.circular_ring_entry.set_range(0.0000, 9999.9999)
+        self.circular_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.circular_ring_label, 14, 0)
         grid_lay.addWidget(self.circular_ring_label, 14, 0)
         grid_lay.addWidget(self.circular_ring_entry, 14, 1)
         grid_lay.addWidget(self.circular_ring_entry, 14, 1)
@@ -166,7 +166,7 @@ class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
 
 
         self.oblong_ring_entry = FCDoubleSpinner()
         self.oblong_ring_entry = FCDoubleSpinner()
         self.oblong_ring_entry.set_precision(self.decimals)
         self.oblong_ring_entry.set_precision(self.decimals)
-        self.oblong_ring_entry.set_range(0.0000, 9999.9999)
+        self.oblong_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.oblong_ring_label, 15, 0)
         grid_lay.addWidget(self.oblong_ring_label, 15, 0)
         grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
         grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
@@ -179,7 +179,7 @@ class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
 
 
         self.square_ring_entry = FCDoubleSpinner()
         self.square_ring_entry = FCDoubleSpinner()
         self.square_ring_entry.set_precision(self.decimals)
         self.square_ring_entry.set_precision(self.decimals)
-        self.square_ring_entry.set_range(0.0000, 9999.9999)
+        self.square_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.square_ring_label, 16, 0)
         grid_lay.addWidget(self.square_ring_label, 16, 0)
         grid_lay.addWidget(self.square_ring_entry, 16, 1)
         grid_lay.addWidget(self.square_ring_entry, 16, 1)
@@ -192,7 +192,7 @@ class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
 
 
         self.rectangular_ring_entry = FCDoubleSpinner()
         self.rectangular_ring_entry = FCDoubleSpinner()
         self.rectangular_ring_entry.set_precision(self.decimals)
         self.rectangular_ring_entry.set_precision(self.decimals)
-        self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
+        self.rectangular_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
         grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
         grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
         grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
@@ -205,7 +205,7 @@ class Tools2EDrillsPrefGroupUI(OptionsGroupUI):
 
 
         self.other_ring_entry = FCDoubleSpinner()
         self.other_ring_entry = FCDoubleSpinner()
         self.other_ring_entry.set_precision(self.decimals)
         self.other_ring_entry.set_precision(self.decimals)
-        self.other_ring_entry.set_range(0.0000, 9999.9999)
+        self.other_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.other_ring_label, 18, 0)
         grid_lay.addWidget(self.other_ring_label, 18, 0)
         grid_lay.addWidget(self.other_ring_entry, 18, 1)
         grid_lay.addWidget(self.other_ring_entry, 18, 1)

+ 3 - 3
appGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py

@@ -61,7 +61,7 @@ class Tools2FiducialsPrefGroupUI(OptionsGroupUI):
             _("Bounding box margin.")
             _("Bounding box margin.")
         )
         )
         self.margin_entry = FCDoubleSpinner()
         self.margin_entry = FCDoubleSpinner()
-        self.margin_entry.set_range(-9999.9999, 9999.9999)
+        self.margin_entry.set_range(-10000.0000, 10000.0000)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.setSingleStep(0.1)
         self.margin_entry.setSingleStep(0.1)
 
 
@@ -73,7 +73,7 @@ class Tools2FiducialsPrefGroupUI(OptionsGroupUI):
             {'label': _('Auto'), 'value': 'auto'},
             {'label': _('Auto'), 'value': 'auto'},
             {"label": _("Manual"), "value": "manual"}
             {"label": _("Manual"), "value": "manual"}
         ], stretch=False)
         ], stretch=False)
-        self.mode_label = QtWidgets.QLabel(_("Mode:"))
+        self.mode_label = QtWidgets.QLabel('%s:' % _("Mode"))
         self.mode_label.setToolTip(
         self.mode_label.setToolTip(
             _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n"
             _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n"
               "- 'Manual' - manual placement of fiducials.")
               "- 'Manual' - manual placement of fiducials.")
@@ -125,7 +125,7 @@ class Tools2FiducialsPrefGroupUI(OptionsGroupUI):
             _("Bounding box margin.")
             _("Bounding box margin.")
         )
         )
         self.line_thickness_entry = FCDoubleSpinner()
         self.line_thickness_entry = FCDoubleSpinner()
-        self.line_thickness_entry.set_range(0.00001, 9999.9999)
+        self.line_thickness_entry.set_range(0.00001, 10000.0000)
         self.line_thickness_entry.set_precision(self.decimals)
         self.line_thickness_entry.set_precision(self.decimals)
         self.line_thickness_entry.setSingleStep(0.1)
         self.line_thickness_entry.setSingleStep(0.1)
 
 

+ 1 - 1
appGUI/preferences/tools/Tools2InvertPrefGroupUI.py

@@ -49,7 +49,7 @@ class Tools2InvertPrefGroupUI(OptionsGroupUI):
         )
         )
         self.margin_entry = FCDoubleSpinner()
         self.margin_entry = FCDoubleSpinner()
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.set_precision(self.decimals)
-        self.margin_entry.set_range(0.0000, 9999.9999)
+        self.margin_entry.set_range(0.0000, 10000.0000)
         self.margin_entry.setObjectName(_("Margin"))
         self.margin_entry.setObjectName(_("Margin"))
 
 
         grid0.addWidget(self.margin_label, 2, 0, 1, 2)
         grid0.addWidget(self.margin_label, 2, 0, 1, 2)

+ 6 - 6
appGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py

@@ -128,7 +128,7 @@ class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
         # Diameter value
         # Diameter value
         self.dia_entry = FCDoubleSpinner()
         self.dia_entry = FCDoubleSpinner()
         self.dia_entry.set_precision(self.decimals)
         self.dia_entry.set_precision(self.decimals)
-        self.dia_entry.set_range(0.0000, 9999.9999)
+        self.dia_entry.set_range(0.0000, 10000.0000)
 
 
         self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
         self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
         self.dia_label.setToolTip(
         self.dia_label.setToolTip(
@@ -155,7 +155,7 @@ class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
 
 
         self.circular_ring_entry = FCDoubleSpinner()
         self.circular_ring_entry = FCDoubleSpinner()
         self.circular_ring_entry.set_precision(self.decimals)
         self.circular_ring_entry.set_precision(self.decimals)
-        self.circular_ring_entry.set_range(0.0000, 9999.9999)
+        self.circular_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.circular_ring_label, 14, 0)
         grid_lay.addWidget(self.circular_ring_label, 14, 0)
         grid_lay.addWidget(self.circular_ring_entry, 14, 1)
         grid_lay.addWidget(self.circular_ring_entry, 14, 1)
@@ -168,7 +168,7 @@ class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
 
 
         self.oblong_ring_entry = FCDoubleSpinner()
         self.oblong_ring_entry = FCDoubleSpinner()
         self.oblong_ring_entry.set_precision(self.decimals)
         self.oblong_ring_entry.set_precision(self.decimals)
-        self.oblong_ring_entry.set_range(0.0000, 9999.9999)
+        self.oblong_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.oblong_ring_label, 15, 0)
         grid_lay.addWidget(self.oblong_ring_label, 15, 0)
         grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
         grid_lay.addWidget(self.oblong_ring_entry, 15, 1)
@@ -181,7 +181,7 @@ class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
 
 
         self.square_ring_entry = FCDoubleSpinner()
         self.square_ring_entry = FCDoubleSpinner()
         self.square_ring_entry.set_precision(self.decimals)
         self.square_ring_entry.set_precision(self.decimals)
-        self.square_ring_entry.set_range(0.0000, 9999.9999)
+        self.square_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.square_ring_label, 16, 0)
         grid_lay.addWidget(self.square_ring_label, 16, 0)
         grid_lay.addWidget(self.square_ring_entry, 16, 1)
         grid_lay.addWidget(self.square_ring_entry, 16, 1)
@@ -194,7 +194,7 @@ class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
 
 
         self.rectangular_ring_entry = FCDoubleSpinner()
         self.rectangular_ring_entry = FCDoubleSpinner()
         self.rectangular_ring_entry.set_precision(self.decimals)
         self.rectangular_ring_entry.set_precision(self.decimals)
-        self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
+        self.rectangular_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
         grid_lay.addWidget(self.rectangular_ring_label, 17, 0)
         grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
         grid_lay.addWidget(self.rectangular_ring_entry, 17, 1)
@@ -207,7 +207,7 @@ class Tools2PunchGerberPrefGroupUI(OptionsGroupUI):
 
 
         self.other_ring_entry = FCDoubleSpinner()
         self.other_ring_entry = FCDoubleSpinner()
         self.other_ring_entry.set_precision(self.decimals)
         self.other_ring_entry.set_precision(self.decimals)
-        self.other_ring_entry.set_range(0.0000, 9999.9999)
+        self.other_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid_lay.addWidget(self.other_ring_label, 18, 0)
         grid_lay.addWidget(self.other_ring_label, 18, 0)
         grid_lay.addWidget(self.other_ring_entry, 18, 1)
         grid_lay.addWidget(self.other_ring_entry, 18, 1)

+ 4 - 4
appGUI/preferences/tools/Tools2sidedPrefGroupUI.py

@@ -24,7 +24,7 @@ class Tools2sidedPrefGroupUI(OptionsGroupUI):
         # OptionsGroupUI.__init__(self, "2sided Tool Options", parent=parent)
         # OptionsGroupUI.__init__(self, "2sided Tool Options", parent=parent)
         super(Tools2sidedPrefGroupUI, self).__init__(self, parent=parent)
         super(Tools2sidedPrefGroupUI, self).__init__(self, parent=parent)
 
 
-        self.setTitle(str(_("2Sided Tool Options")))
+        self.setTitle(str(_("2-Sided Tool Options")))
         self.decimals = decimals
         self.decimals = decimals
 
 
         # ## Board cuttout
         # ## Board cuttout
@@ -40,11 +40,11 @@ class Tools2sidedPrefGroupUI(OptionsGroupUI):
 
 
         # ## Drill diameter for alignment holes
         # ## Drill diameter for alignment holes
         self.drill_dia_entry = FCDoubleSpinner()
         self.drill_dia_entry = FCDoubleSpinner()
-        self.drill_dia_entry.set_range(0.000001, 9999.9999)
+        self.drill_dia_entry.set_range(0.000001, 10000.0000)
         self.drill_dia_entry.set_precision(self.decimals)
         self.drill_dia_entry.set_precision(self.decimals)
         self.drill_dia_entry.setSingleStep(0.1)
         self.drill_dia_entry.setSingleStep(0.1)
 
 
-        self.dd_label = QtWidgets.QLabel('%s:' % _("Drill dia"))
+        self.dd_label = QtWidgets.QLabel('%s:' % _("Drill Dia"))
         self.dd_label.setToolTip(
         self.dd_label.setToolTip(
             _("Diameter of the drill for the "
             _("Diameter of the drill for the "
               "alignment holes.")
               "alignment holes.")
@@ -66,7 +66,7 @@ class Tools2sidedPrefGroupUI(OptionsGroupUI):
         # ## Axis
         # ## Axis
         self.mirror_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
         self.mirror_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
                                            {'label': 'Y', 'value': 'Y'}])
                                            {'label': 'Y', 'value': 'Y'}])
-        self.mirax_label = QtWidgets.QLabel(_("Mirror Axis:"))
+        self.mirax_label = QtWidgets.QLabel('%s:' % _("Mirror Axis"))
         self.mirax_label.setToolTip(
         self.mirax_label.setToolTip(
             _("Mirror vertically (X) or horizontally (Y).")
             _("Mirror vertically (X) or horizontally (Y).")
         )
         )

+ 40 - 29
appGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from appGUI.GUIElements import FCDoubleSpinner
+from appGUI.GUIElements import FCDoubleSpinner, FCLabel
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -28,7 +28,7 @@ class ToolsCalculatorsPrefGroupUI(OptionsGroupUI):
         self.decimals = decimals
         self.decimals = decimals
 
 
         # ## V-shape Calculator Tool
         # ## V-shape Calculator Tool
-        self.vshape_tool_label = QtWidgets.QLabel("<b>%s:</b>" % _("V-Shape Tool Calculator"))
+        self.vshape_tool_label = FCLabel("<b>%s:</b>" % _("V-Shape Tool Calculator"))
         self.vshape_tool_label.setToolTip(
         self.vshape_tool_label.setToolTip(
             _("Calculate the tool diameter for a given V-shape tool,\n"
             _("Calculate the tool diameter for a given V-shape tool,\n"
               "having the tip diameter, tip angle and\n"
               "having the tip diameter, tip angle and\n"
@@ -43,11 +43,11 @@ class ToolsCalculatorsPrefGroupUI(OptionsGroupUI):
 
 
         # ## Tip Diameter
         # ## Tip Diameter
         self.tip_dia_entry = FCDoubleSpinner()
         self.tip_dia_entry = FCDoubleSpinner()
-        self.tip_dia_entry.set_range(0.000001, 9999.9999)
+        self.tip_dia_entry.set_range(0.000001, 10000.0000)
         self.tip_dia_entry.set_precision(self.decimals)
         self.tip_dia_entry.set_precision(self.decimals)
         self.tip_dia_entry.setSingleStep(0.1)
         self.tip_dia_entry.setSingleStep(0.1)
 
 
-        self.tip_dia_label = QtWidgets.QLabel('%s:' % _("Tip Diameter"))
+        self.tip_dia_label = FCLabel('%s:' % _("Tip Diameter"))
         self.tip_dia_label.setToolTip(
         self.tip_dia_label.setToolTip(
             _("This is the tool tip diameter.\n"
             _("This is the tool tip diameter.\n"
               "It is specified by manufacturer.")
               "It is specified by manufacturer.")
@@ -61,30 +61,30 @@ class ToolsCalculatorsPrefGroupUI(OptionsGroupUI):
         self.tip_angle_entry.set_precision(self.decimals)
         self.tip_angle_entry.set_precision(self.decimals)
         self.tip_angle_entry.setSingleStep(5)
         self.tip_angle_entry.setSingleStep(5)
 
 
-        self.tip_angle_label = QtWidgets.QLabel('%s:' % _("Tip Angle"))
+        self.tip_angle_label = FCLabel('%s:' % _("Tip Angle"))
         self.tip_angle_label.setToolTip(
         self.tip_angle_label.setToolTip(
             _("This is the angle on the tip of the tool.\n"
             _("This is the angle on the tip of the tool.\n"
               "It is specified by manufacturer.")
               "It is specified by manufacturer.")
         )
         )
-        grid0.addWidget(self.tip_angle_label, 1, 0)
-        grid0.addWidget(self.tip_angle_entry, 1, 1)
+        grid0.addWidget(self.tip_angle_label, 2, 0)
+        grid0.addWidget(self.tip_angle_entry, 2, 1)
 
 
         # ## Depth-of-cut Cut Z
         # ## Depth-of-cut Cut Z
         self.cut_z_entry = FCDoubleSpinner()
         self.cut_z_entry = FCDoubleSpinner()
-        self.cut_z_entry.set_range(-9999.9999, 0.0000)
+        self.cut_z_entry.set_range(-10000.0000, 0.0000)
         self.cut_z_entry.set_precision(self.decimals)
         self.cut_z_entry.set_precision(self.decimals)
         self.cut_z_entry.setSingleStep(0.01)
         self.cut_z_entry.setSingleStep(0.01)
 
 
-        self.cut_z_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
+        self.cut_z_label = FCLabel('%s:' % _("Cut Z"))
         self.cut_z_label.setToolTip(
         self.cut_z_label.setToolTip(
             _("This is depth to cut into material.\n"
             _("This is depth to cut into material.\n"
               "In the CNCJob object it is the CutZ parameter.")
               "In the CNCJob object it is the CutZ parameter.")
         )
         )
-        grid0.addWidget(self.cut_z_label, 2, 0)
-        grid0.addWidget(self.cut_z_entry, 2, 1)
+        grid0.addWidget(self.cut_z_label, 4, 0)
+        grid0.addWidget(self.cut_z_entry, 4, 1)
 
 
         # ## Electroplating Calculator Tool
         # ## Electroplating Calculator Tool
-        self.plate_title_label = QtWidgets.QLabel("<b>%s:</b>" % _("ElectroPlating Calculator"))
+        self.plate_title_label = FCLabel("<b>%s:</b>" % _("ElectroPlating Calculator"))
         self.plate_title_label.setToolTip(
         self.plate_title_label.setToolTip(
             _("This calculator is useful for those who plate the via/pad/drill holes,\n"
             _("This calculator is useful for those who plate the via/pad/drill holes,\n"
               "using a method like graphite ink or calcium hypophosphite ink or palladium chloride.")
               "using a method like graphite ink or calcium hypophosphite ink or palladium chloride.")
@@ -93,50 +93,61 @@ class ToolsCalculatorsPrefGroupUI(OptionsGroupUI):
 
 
         # ## PCB Length
         # ## PCB Length
         self.pcblength_entry = FCDoubleSpinner()
         self.pcblength_entry = FCDoubleSpinner()
-        self.pcblength_entry.set_range(0.000001, 9999.9999)
+        self.pcblength_entry.set_range(0.000001, 10000.0000)
         self.pcblength_entry.set_precision(self.decimals)
         self.pcblength_entry.set_precision(self.decimals)
         self.pcblength_entry.setSingleStep(0.1)
         self.pcblength_entry.setSingleStep(0.1)
 
 
-        self.pcblengthlabel = QtWidgets.QLabel('%s:' % _("Board Length"))
+        self.pcblengthlabel = FCLabel('%s:' % _("Board Length"))
 
 
         self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
         self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
-        grid0.addWidget(self.pcblengthlabel, 4, 0)
-        grid0.addWidget(self.pcblength_entry, 4, 1)
+        grid0.addWidget(self.pcblengthlabel, 6, 0)
+        grid0.addWidget(self.pcblength_entry, 6, 1)
 
 
         # ## PCB Width
         # ## PCB Width
         self.pcbwidth_entry = FCDoubleSpinner()
         self.pcbwidth_entry = FCDoubleSpinner()
-        self.pcbwidth_entry.set_range(0.000001, 9999.9999)
+        self.pcbwidth_entry.set_range(0.000001, 10000.0000)
         self.pcbwidth_entry.set_precision(self.decimals)
         self.pcbwidth_entry.set_precision(self.decimals)
         self.pcbwidth_entry.setSingleStep(0.1)
         self.pcbwidth_entry.setSingleStep(0.1)
 
 
-        self.pcbwidthlabel = QtWidgets.QLabel('%s:' % _("Board Width"))
+        self.pcbwidthlabel = FCLabel('%s:' % _("Board Width"))
 
 
         self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
         self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
-        grid0.addWidget(self.pcbwidthlabel, 5, 0)
-        grid0.addWidget(self.pcbwidth_entry, 5, 1)
-
+        grid0.addWidget(self.pcbwidthlabel, 8, 0)
+        grid0.addWidget(self.pcbwidth_entry, 8, 1)
+        
+        # AREA
+        self.area_label = FCLabel('%s:' % _("Area"))
+        self.area_label.setToolTip(_('This is the board area.'))
+        self.area_entry = FCDoubleSpinner()
+        self.area_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+        self.area_entry.set_precision(self.decimals)
+        self.area_entry.set_range(0.0, 10000.0000)
+        
+        grid0.addWidget(self.area_label, 10, 0)
+        grid0.addWidget(self.area_entry, 10, 1)
+        
         # ## Current Density
         # ## Current Density
-        self.cdensity_label = QtWidgets.QLabel('%s:' % _("Current Density"))
+        self.cdensity_label = FCLabel('%s:' % _("Current Density"))
         self.cdensity_entry = FCDoubleSpinner()
         self.cdensity_entry = FCDoubleSpinner()
-        self.cdensity_entry.set_range(0.000001, 9999.9999)
+        self.cdensity_entry.set_range(0.000001, 10000.0000)
         self.cdensity_entry.set_precision(self.decimals)
         self.cdensity_entry.set_precision(self.decimals)
         self.cdensity_entry.setSingleStep(0.1)
         self.cdensity_entry.setSingleStep(0.1)
 
 
         self.cdensity_label.setToolTip(_("Current density to pass through the board. \n"
         self.cdensity_label.setToolTip(_("Current density to pass through the board. \n"
                                          "In Amps per Square Feet ASF."))
                                          "In Amps per Square Feet ASF."))
-        grid0.addWidget(self.cdensity_label, 6, 0)
-        grid0.addWidget(self.cdensity_entry, 6, 1)
+        grid0.addWidget(self.cdensity_label, 12, 0)
+        grid0.addWidget(self.cdensity_entry, 12, 1)
 
 
         # ## PCB Copper Growth
         # ## PCB Copper Growth
-        self.growth_label = QtWidgets.QLabel('%s:' % _("Copper Growth"))
+        self.growth_label = FCLabel('%s:' % _("Copper Growth"))
         self.growth_entry = FCDoubleSpinner()
         self.growth_entry = FCDoubleSpinner()
-        self.growth_entry.set_range(0.000001, 9999.9999)
+        self.growth_entry.set_range(0.000001, 10000.0000)
         self.growth_entry.set_precision(self.decimals)
         self.growth_entry.set_precision(self.decimals)
         self.growth_entry.setSingleStep(0.01)
         self.growth_entry.setSingleStep(0.01)
 
 
         self.growth_label.setToolTip(_("How thick the copper growth is intended to be.\n"
         self.growth_label.setToolTip(_("How thick the copper growth is intended to be.\n"
                                        "In microns."))
                                        "In microns."))
-        grid0.addWidget(self.growth_label, 7, 0)
-        grid0.addWidget(self.growth_entry, 7, 1)
+        grid0.addWidget(self.growth_label, 14, 0)
+        grid0.addWidget(self.growth_entry, 14, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()

+ 47 - 20
appGUI/preferences/tools/ToolsCornersPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from appGUI.GUIElements import FCDoubleSpinner
+from appGUI.GUIElements import FCDoubleSpinner, FCLabel, RadioSet
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -32,14 +32,28 @@ class ToolsCornersPrefGroupUI(OptionsGroupUI):
         grid0.setColumnStretch(1, 1)
         grid0.setColumnStretch(1, 1)
         self.layout.addLayout(grid0)
         self.layout.addLayout(grid0)
 
 
-        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
+        self.param_label = FCLabel('<b>%s:</b>' % _('Parameters'))
         self.param_label.setToolTip(
         self.param_label.setToolTip(
             _("Parameters used for this tool.")
             _("Parameters used for this tool.")
         )
         )
         grid0.addWidget(self.param_label, 0, 0, 1, 2)
         grid0.addWidget(self.param_label, 0, 0, 1, 2)
 
 
+        # Type of Marker
+        self.type_label = FCLabel('%s:' % _("Type"))
+        self.type_label.setToolTip(
+            _("Shape of the marker.")
+        )
+
+        self.type_radio = RadioSet([
+            {"label": _("Semi-Cross"), "value": "s"},
+            {"label": _("Cross"), "value": "c"},
+        ])
+
+        grid0.addWidget(self.type_label, 2, 0)
+        grid0.addWidget(self.type_radio, 2, 1)
+        
         # Thickness #
         # Thickness #
-        self.thick_label = QtWidgets.QLabel('%s:' % _("Thickness"))
+        self.thick_label = FCLabel('%s:' % _("Thickness"))
         self.thick_label.setToolTip(
         self.thick_label.setToolTip(
             _("The thickness of the line that makes the corner marker.")
             _("The thickness of the line that makes the corner marker.")
         )
         )
@@ -49,33 +63,46 @@ class ToolsCornersPrefGroupUI(OptionsGroupUI):
         self.thick_entry.setWrapping(True)
         self.thick_entry.setWrapping(True)
         self.thick_entry.setSingleStep(10 ** -self.decimals)
         self.thick_entry.setSingleStep(10 ** -self.decimals)
 
 
-        grid0.addWidget(self.thick_label, 1, 0)
-        grid0.addWidget(self.thick_entry, 1, 1)
+        grid0.addWidget(self.thick_label, 4, 0)
+        grid0.addWidget(self.thick_entry, 4, 1)
+
+        # Margin #
+        self.margin_label = FCLabel('%s:' % _("Margin"))
+        self.margin_label.setToolTip(
+            _("Bounding box margin.")
+        )
+        self.margin_entry = FCDoubleSpinner()
+        self.margin_entry.set_range(-10000.0000, 10000.0000)
+        self.margin_entry.set_precision(self.decimals)
+        self.margin_entry.setSingleStep(0.1)
+
+        grid0.addWidget(self.margin_label, 6, 0)
+        grid0.addWidget(self.margin_entry, 6, 1)
 
 
         # Length #
         # Length #
-        self.l_label = QtWidgets.QLabel('%s:' % _("Length"))
+        self.l_label = FCLabel('%s:' % _("Length"))
         self.l_label.setToolTip(
         self.l_label.setToolTip(
             _("The length of the line that makes the corner marker.")
             _("The length of the line that makes the corner marker.")
         )
         )
         self.l_entry = FCDoubleSpinner()
         self.l_entry = FCDoubleSpinner()
-        self.l_entry.set_range(-9999.9999, 9999.9999)
+        self.l_entry.set_range(-10000.0000, 10000.0000)
         self.l_entry.set_precision(self.decimals)
         self.l_entry.set_precision(self.decimals)
         self.l_entry.setSingleStep(10 ** -self.decimals)
         self.l_entry.setSingleStep(10 ** -self.decimals)
 
 
-        # Margin #
-        self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
-        self.margin_label.setToolTip(
-            _("Bounding box margin.")
-        )
-        self.margin_entry = FCDoubleSpinner()
-        self.margin_entry.set_range(-9999.9999, 9999.9999)
-        self.margin_entry.set_precision(self.decimals)
-        self.margin_entry.setSingleStep(0.1)
+        grid0.addWidget(self.l_label, 8, 0)
+        grid0.addWidget(self.l_entry, 8, 1)
 
 
-        grid0.addWidget(self.margin_label, 2, 0)
-        grid0.addWidget(self.margin_entry, 2, 1)
+        # Drill Tool Diameter
+        self.drill_dia_label = FCLabel('%s:' % _("Drill Dia"))
+        self.drill_dia_label.setToolTip(
+            '%s.' % _("Drill Diameter")
+        )
+        self.drill_dia_entry = FCDoubleSpinner()
+        self.drill_dia_entry.set_range(0.0000, 100.0000)
+        self.drill_dia_entry.set_precision(self.decimals)
+        self.drill_dia_entry.setWrapping(True)
 
 
-        grid0.addWidget(self.l_label, 4, 0)
-        grid0.addWidget(self.l_entry, 4, 1)
+        grid0.addWidget(self.drill_dia_label, 10, 0)
+        grid0.addWidget(self.drill_dia_entry, 10, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()

+ 12 - 12
appGUI/preferences/tools/ToolsCutoutPrefGroupUI.py

@@ -47,7 +47,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         )
         )
 
 
         self.cutout_tooldia_entry = FCDoubleSpinner()
         self.cutout_tooldia_entry = FCDoubleSpinner()
-        self.cutout_tooldia_entry.set_range(0.000001, 9999.9999)
+        self.cutout_tooldia_entry.set_range(0.000001, 10000.0000)
         self.cutout_tooldia_entry.set_precision(self.decimals)
         self.cutout_tooldia_entry.set_precision(self.decimals)
         self.cutout_tooldia_entry.setSingleStep(0.1)
         self.cutout_tooldia_entry.setSingleStep(0.1)
 
 
@@ -66,9 +66,9 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.cutz_entry.setRange(-9999.9999, 0.0000)
+            self.cutz_entry.setRange(-10000.0000, 0.0000)
         else:
         else:
-            self.cutz_entry.setRange(-9999.9999, 9999.9999)
+            self.cutz_entry.setRange(-10000.0000, 10000.0000)
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
 
 
@@ -88,7 +88,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
 
 
         self.maxdepth_entry = FCDoubleSpinner()
         self.maxdepth_entry = FCDoubleSpinner()
         self.maxdepth_entry.set_precision(self.decimals)
         self.maxdepth_entry.set_precision(self.decimals)
-        self.maxdepth_entry.setRange(0, 9999.9999)
+        self.maxdepth_entry.setRange(0, 10000.0000)
         self.maxdepth_entry.setSingleStep(0.1)
         self.maxdepth_entry.setSingleStep(0.1)
 
 
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
@@ -97,11 +97,11 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.maxdepth_entry, 2, 1)
         grid0.addWidget(self.maxdepth_entry, 2, 1)
 
 
         # Object kind
         # Object kind
-        kindlabel = FCLabel('%s:' % _('Object kind'))
+        kindlabel = FCLabel('%s:' % _('Kind'))
         kindlabel.setToolTip(
         kindlabel.setToolTip(
-            _("Choice of what kind the object we want to cutout is.<BR>"
-              "- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
-              "- <B>Panel</B>: a panel PCB Gerber object, which is made\n"
+            _("Choice of what kind the object we want to cutout is.\n"
+              "- Single: contain a single PCB Gerber outline object.\n"
+              "- Panel: a panel PCB Gerber object, which is made\n"
               "out of many individual PCB outlines.")
               "out of many individual PCB outlines.")
         )
         )
 
 
@@ -120,7 +120,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         )
         )
 
 
         self.cutout_margin_entry = FCDoubleSpinner()
         self.cutout_margin_entry = FCDoubleSpinner()
-        self.cutout_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.cutout_margin_entry.set_range(-10000.0000, 10000.0000)
         self.cutout_margin_entry.set_precision(self.decimals)
         self.cutout_margin_entry.set_precision(self.decimals)
         self.cutout_margin_entry.setSingleStep(0.1)
         self.cutout_margin_entry.setSingleStep(0.1)
 
 
@@ -137,7 +137,7 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         )
         )
 
 
         self.cutout_gap_entry = FCDoubleSpinner()
         self.cutout_gap_entry = FCDoubleSpinner()
-        self.cutout_gap_entry.set_range(0.000001, 9999.9999)
+        self.cutout_gap_entry.set_range(0.000001, 10000.0000)
         self.cutout_gap_entry.set_precision(self.decimals)
         self.cutout_gap_entry.set_precision(self.decimals)
         self.cutout_gap_entry.setSingleStep(0.1)
         self.cutout_gap_entry.setSingleStep(0.1)
 
 
@@ -174,9 +174,9 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
         self.thin_depth_entry = FCDoubleSpinner()
         self.thin_depth_entry = FCDoubleSpinner()
         self.thin_depth_entry.set_precision(self.decimals)
         self.thin_depth_entry.set_precision(self.decimals)
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.thin_depth_entry.setRange(-9999.9999, -0.00001)
+            self.thin_depth_entry.setRange(-10000.0000, -0.00001)
         else:
         else:
-            self.thin_depth_entry.setRange(-9999.9999, 9999.9999)
+            self.thin_depth_entry.setRange(-10000.0000, 10000.0000)
         self.thin_depth_entry.setSingleStep(0.1)
         self.thin_depth_entry.setSingleStep(0.1)
 
 
         grid0.addWidget(self.thin_depth_label, 9, 0)
         grid0.addWidget(self.thin_depth_label, 9, 0)

+ 18 - 17
appGUI/preferences/tools/ToolsDrillPrefGroupUI.py

@@ -63,9 +63,9 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry = FCDoubleSpinner()
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.cutz_entry.set_range(-9999.9999, 0.0000)
+            self.cutz_entry.set_range(-10000.0000, 0.0000)
         else:
         else:
-            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+            self.cutz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
@@ -86,7 +86,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
 
 
         self.maxdepth_entry = FCDoubleSpinner()
         self.maxdepth_entry = FCDoubleSpinner()
         self.maxdepth_entry.set_precision(self.decimals)
         self.maxdepth_entry.set_precision(self.decimals)
-        self.maxdepth_entry.set_range(0, 9999.9999)
+        self.maxdepth_entry.set_range(0, 10000.0000)
         self.maxdepth_entry.setSingleStep(0.1)
         self.maxdepth_entry.setSingleStep(0.1)
 
 
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
@@ -105,9 +105,9 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.travelz_entry.set_range(0.0001, 9999.9999)
+            self.travelz_entry.set_range(0.0001, 10000.0000)
         else:
         else:
-            self.travelz_entry.set_range(-9999.9999, 9999.9999)
+            self.travelz_entry.set_range(-10000.0000, 10000.0000)
 
 
         grid0.addWidget(travelzlabel, 5, 0)
         grid0.addWidget(travelzlabel, 5, 0)
         grid0.addWidget(self.travelz_entry, 5, 1, 1, 2)
         grid0.addWidget(self.travelz_entry, 5, 1, 1, 2)
@@ -131,9 +131,9 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         self.toolchangez_entry.set_precision(self.decimals)
         self.toolchangez_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.toolchangez_entry.set_range(0.0001, 9999.9999)
+            self.toolchangez_entry.set_range(0.0001, 10000.0000)
         else:
         else:
-            self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
+            self.toolchangez_entry.set_range(-10000.0000, 10000.0000)
 
 
         grid0.addWidget(toolchangezlabel, 7, 0)
         grid0.addWidget(toolchangezlabel, 7, 0)
         grid0.addWidget(self.toolchangez_entry, 7, 1, 1, 2)
         grid0.addWidget(self.toolchangez_entry, 7, 1, 1, 2)
@@ -148,9 +148,9 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         self.endz_entry.set_precision(self.decimals)
         self.endz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.endz_entry.set_range(0.0000, 9999.9999)
+            self.endz_entry.set_range(0.0000, 10000.0000)
         else:
         else:
-            self.endz_entry.set_range(-9999.9999, 9999.9999)
+            self.endz_entry.set_range(-10000.0000, 10000.0000)
 
 
         grid0.addWidget(endz_label, 8, 0)
         grid0.addWidget(endz_label, 8, 0)
         grid0.addWidget(self.endz_entry, 8, 1, 1, 2)
         grid0.addWidget(self.endz_entry, 8, 1, 1, 2)
@@ -177,7 +177,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         )
         )
         self.feedrate_z_entry = FCDoubleSpinner()
         self.feedrate_z_entry = FCDoubleSpinner()
         self.feedrate_z_entry.set_precision(self.decimals)
         self.feedrate_z_entry.set_precision(self.decimals)
-        self.feedrate_z_entry.set_range(0, 99999.9999)
+        self.feedrate_z_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(frlabel, 10, 0)
         grid0.addWidget(frlabel, 10, 0)
         grid0.addWidget(self.feedrate_z_entry, 10, 1, 1, 2)
         grid0.addWidget(self.feedrate_z_entry, 10, 1, 1, 2)
@@ -210,7 +210,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         dwelltime.setToolTip(_("Number of time units for spindle to dwell."))
         dwelltime.setToolTip(_("Number of time units for spindle to dwell."))
         self.dwelltime_entry = FCDoubleSpinner()
         self.dwelltime_entry = FCDoubleSpinner()
         self.dwelltime_entry.set_precision(self.decimals)
         self.dwelltime_entry.set_precision(self.decimals)
-        self.dwelltime_entry.set_range(0, 99999.9999)
+        self.dwelltime_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(dwelltime, 13, 0)
         grid0.addWidget(dwelltime, 13, 0)
         grid0.addWidget(self.dwelltime_entry, 13, 1, 1, 2)
         grid0.addWidget(self.dwelltime_entry, 13, 1, 1, 2)
@@ -226,6 +226,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
 
 
         self.pp_excellon_name_cb = FCComboBox()
         self.pp_excellon_name_cb = FCComboBox()
         self.pp_excellon_name_cb.setFocusPolicy(Qt.StrongFocus)
         self.pp_excellon_name_cb.setFocusPolicy(Qt.StrongFocus)
+        self.pp_excellon_name_cb.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
 
 
         grid0.addWidget(pp_excellon_label, 14, 0)
         grid0.addWidget(pp_excellon_label, 14, 0)
         grid0.addWidget(self.pp_excellon_name_cb, 14, 1, 1, 2)
         grid0.addWidget(self.pp_excellon_name_cb, 14, 1, 1, 2)
@@ -254,7 +255,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
 
 
         self.drill_overlap_entry = FCDoubleSpinner()
         self.drill_overlap_entry = FCDoubleSpinner()
         self.drill_overlap_entry.set_precision(self.decimals)
         self.drill_overlap_entry.set_precision(self.decimals)
-        self.drill_overlap_entry.set_range(0.0, 9999.9999)
+        self.drill_overlap_entry.set_range(0.0, 10000.0000)
         self.drill_overlap_entry.setSingleStep(0.1)
         self.drill_overlap_entry.setSingleStep(0.1)
 
 
         grid0.addWidget(self.drill_overlap_label, 22, 0)
         grid0.addWidget(self.drill_overlap_label, 22, 0)
@@ -305,7 +306,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         # Start Z
         # Start Z
         startzlabel = FCLabel('%s:' % _('Start Z'))
         startzlabel = FCLabel('%s:' % _('Start Z'))
         startzlabel.setToolTip(
         startzlabel.setToolTip(
-            _("Height of the tool just after start.\n"
+            _("Height of the tool just after starting the work.\n"
               "Delete the value if you don't need this feature.")
               "Delete the value if you don't need this feature.")
         )
         )
         self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
         self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
@@ -324,7 +325,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         )
         )
         self.feedrate_rapid_entry = FCDoubleSpinner()
         self.feedrate_rapid_entry = FCDoubleSpinner()
         self.feedrate_rapid_entry.set_precision(self.decimals)
         self.feedrate_rapid_entry.set_precision(self.decimals)
-        self.feedrate_rapid_entry.set_range(0, 99999.9999)
+        self.feedrate_rapid_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(fr_rapid_label, 35, 0)
         grid0.addWidget(fr_rapid_label, 35, 0)
         grid0.addWidget(self.feedrate_rapid_entry, 35, 1, 1, 2)
         grid0.addWidget(self.feedrate_rapid_entry, 35, 1, 1, 2)
@@ -337,7 +338,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         )
         )
         self.pdepth_entry = FCDoubleSpinner()
         self.pdepth_entry = FCDoubleSpinner()
         self.pdepth_entry.set_precision(self.decimals)
         self.pdepth_entry.set_precision(self.decimals)
-        self.pdepth_entry.set_range(-99999.9999, 0.0000)
+        self.pdepth_entry.set_range(-910000.0000, 0.0000)
 
 
         grid0.addWidget(self.pdepth_label, 37, 0)
         grid0.addWidget(self.pdepth_label, 37, 0)
         grid0.addWidget(self.pdepth_entry, 37, 1, 1, 2)
         grid0.addWidget(self.pdepth_entry, 37, 1, 1, 2)
@@ -349,7 +350,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         )
         )
         self.feedrate_probe_entry = FCDoubleSpinner()
         self.feedrate_probe_entry = FCDoubleSpinner()
         self.feedrate_probe_entry.set_precision(self.decimals)
         self.feedrate_probe_entry.set_precision(self.decimals)
-        self.feedrate_probe_entry.set_range(0, 99999.9999)
+        self.feedrate_probe_entry.set_range(0, 910000.0000)
 
 
         grid0.addWidget(self.feedrate_probe_label, 38, 0)
         grid0.addWidget(self.feedrate_probe_label, 38, 0)
         grid0.addWidget(self.feedrate_probe_entry, 38, 1, 1, 2)
         grid0.addWidget(self.feedrate_probe_entry, 38, 1, 1, 2)
@@ -443,7 +444,7 @@ class ToolsDrillPrefGroupUI(OptionsGroupUI):
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
                                        "an interdiction area."))
                                        "an interdiction area."))
         self.over_z_entry = FCDoubleSpinner()
         self.over_z_entry = FCDoubleSpinner()
-        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_range(0.000, 10000.0000)
         self.over_z_entry.set_precision(self.decimals)
         self.over_z_entry.set_precision(self.decimals)
 
 
         grid0.addWidget(self.over_z_label, 55, 0)
         grid0.addWidget(self.over_z_label, 55, 0)

+ 4 - 4
appGUI/preferences/tools/ToolsFilmPrefGroupUI.py

@@ -65,7 +65,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         # Film Border
         # Film Border
         self.film_boundary_entry = FCDoubleSpinner()
         self.film_boundary_entry = FCDoubleSpinner()
         self.film_boundary_entry.set_precision(self.decimals)
         self.film_boundary_entry.set_precision(self.decimals)
-        self.film_boundary_entry.set_range(0, 9999.9999)
+        self.film_boundary_entry.set_range(0, 10000.0000)
         self.film_boundary_entry.setSingleStep(0.1)
         self.film_boundary_entry.setSingleStep(0.1)
 
 
         self.film_boundary_label = FCLabel('%s:' % _("Border"))
         self.film_boundary_label = FCLabel('%s:' % _("Border"))
@@ -84,7 +84,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
 
 
         self.film_scale_stroke_entry = FCDoubleSpinner()
         self.film_scale_stroke_entry = FCDoubleSpinner()
         self.film_scale_stroke_entry.set_precision(self.decimals)
         self.film_scale_stroke_entry.set_precision(self.decimals)
-        self.film_scale_stroke_entry.set_range(0, 9999.9999)
+        self.film_scale_stroke_entry.set_range(0, 10000.0000)
         self.film_scale_stroke_entry.setSingleStep(0.1)
         self.film_scale_stroke_entry.setSingleStep(0.1)
 
 
         self.film_scale_stroke_label = FCLabel('%s:' % _("Scale Stroke"))
         self.film_scale_stroke_label = FCLabel('%s:' % _("Scale Stroke"))
@@ -198,7 +198,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
                                           {'label': _('Y'), 'value': 'y'},
                                           {'label': _('Y'), 'value': 'y'},
                                           {'label': _('Both'), 'value': 'both'}],
                                           {'label': _('Both'), 'value': 'both'}],
                                          stretch=False)
                                          stretch=False)
-        self.film_mirror_axis_label = FCLabel('%s:' % _("Mirror axis"))
+        self.film_mirror_axis_label = FCLabel('%s:' % _("Mirror Axis"))
 
 
         grid0.addWidget(self.film_mirror_axis_label, 13, 0)
         grid0.addWidget(self.film_mirror_axis_label, 13, 0)
         grid0.addWidget(self.film_mirror_axis, 13, 1)
         grid0.addWidget(self.film_mirror_axis, 13, 1)
@@ -213,7 +213,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
                                          {'label': _('PDF'), 'value': 'pdf'}
                                          {'label': _('PDF'), 'value': 'pdf'}
                                          ], stretch=False)
                                          ], stretch=False)
 
 
-        self.file_type_label = FCLabel(_("Film Type:"))
+        self.file_type_label = FCLabel('%s:' % _("Film Type"))
         self.file_type_label.setToolTip(
         self.file_type_label.setToolTip(
             _("The file type of the saved film. Can be:\n"
             _("The file type of the saved film. Can be:\n"
               "- 'SVG' -> open-source vectorial format\n"
               "- 'SVG' -> open-source vectorial format\n"

+ 13 - 13
appGUI/preferences/tools/ToolsISOPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, NumericalEvalTupleEntry
+from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox2, FCCheckBox, FCSpinner, NumericalEvalTupleEntry
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -115,16 +115,16 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
         cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
         cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
         cutzlabel.setToolTip(
         cutzlabel.setToolTip(
            _("Depth of cut into material. Negative value.\n"
            _("Depth of cut into material. Negative value.\n"
-             "In FlatCAM units.")
+             "In application units.")
         )
         )
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.set_range(-9999.9999, 0.0000)
+        self.cutz_entry.set_range(-10000.0000, 0.0000)
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
 
 
         self.cutz_entry.setToolTip(
         self.cutz_entry.setToolTip(
            _("Depth of cut into material. Negative value.\n"
            _("Depth of cut into material. Negative value.\n"
-             "In FlatCAM units.")
+             "In application units.")
         )
         )
 
 
         grid0.addWidget(cutzlabel, 5, 0)
         grid0.addWidget(cutzlabel, 5, 0)
@@ -139,7 +139,7 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
         )
         )
         self.newdia_entry = FCDoubleSpinner()
         self.newdia_entry = FCDoubleSpinner()
         self.newdia_entry.set_precision(self.decimals)
         self.newdia_entry.set_precision(self.decimals)
-        self.newdia_entry.set_range(0.0001, 9999.9999)
+        self.newdia_entry.set_range(0.0001, 10000.0000)
         self.newdia_entry.setSingleStep(0.1)
         self.newdia_entry.setSingleStep(0.1)
 
 
         grid0.addWidget(self.newdialabel, 6, 0)
         grid0.addWidget(self.newdialabel, 6, 0)
@@ -181,7 +181,7 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
         # Milling Type Radio Button
         # Milling Type Radio Button
         self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
         self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
         self.milling_type_label.setToolTip(
         self.milling_type_label.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -189,7 +189,7 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
                                             {'label': _('Conventional'), 'value': 'cv'}])
                                             {'label': _('Conventional'), 'value': 'cv'}])
         self.milling_type_radio.setToolTip(
         self.milling_type_radio.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -244,11 +244,11 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
         self.rest_cb.setObjectName("i_rest_machining")
         self.rest_cb.setObjectName("i_rest_machining")
         self.rest_cb.setToolTip(
         self.rest_cb.setToolTip(
             _("If checked, use 'rest machining'.\n"
             _("If checked, use 'rest machining'.\n"
-              "Basically it will isolate outside PCB features,\n"
+              "Basically it will process copper outside PCB features,\n"
               "using the biggest tool and continue with the next tools,\n"
               "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to isolate the copper features that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper features to isolate or there are no more tools.\n"
+              "from bigger to smaller, to process the copper features that\n"
+              "could not be processed by previous tool, until there is\n"
+              "nothing left to process or there are no more tools.\n\n"
               "If not checked, use the standard algorithm.")
               "If not checked, use the standard algorithm.")
         )
         )
 
 
@@ -290,7 +290,7 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
               "- 'Polygon Selection' -> Isolate a selection of polygons.\n"
               "- 'Polygon Selection' -> Isolate a selection of polygons.\n"
               "- 'Reference Object' - will process the area specified by another object.")
               "- 'Reference Object' - will process the area specified by another object.")
         )
         )
-        self.select_combo = FCComboBox()
+        self.select_combo = FCComboBox2()
         self.select_combo.addItems(
         self.select_combo.addItems(
             [_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")]
             [_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")]
         )
         )
@@ -338,7 +338,7 @@ class ToolsISOPrefGroupUI(OptionsGroupUI):
                                         {"label": _("Progressive"), "value": "progressive"}])
                                         {"label": _("Progressive"), "value": "progressive"}])
         plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
         plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
         plotting_label.setToolTip(
         plotting_label.setToolTip(
-            _("- 'Normal' -  normal plotting, done at the end of the job\n"
+            _("- 'Normal' - normal plotting, done at the end of the job\n"
               "- 'Progressive' - each shape is plotted after it is generated")
               "- 'Progressive' - each shape is plotted after it is generated")
         )
         )
         grid0.addWidget(plotting_label, 25, 0)
         grid0.addWidget(plotting_label, 25, 0)

+ 19 - 21
appGUI/preferences/tools/ToolsNCCPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, NumericalEvalTupleEntry
+from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, NumericalEvalTupleEntry, FCComboBox2
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -98,16 +98,16 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
         cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
         cutzlabel.setToolTip(
         cutzlabel.setToolTip(
            _("Depth of cut into material. Negative value.\n"
            _("Depth of cut into material. Negative value.\n"
-             "In FlatCAM units.")
+             "In application units.")
         )
         )
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.set_range(-9999.9999, 0.0000)
+        self.cutz_entry.set_range(-10000.0000, 0.0000)
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
 
 
         self.cutz_entry.setToolTip(
         self.cutz_entry.setToolTip(
            _("Depth of cut into material. Negative value.\n"
            _("Depth of cut into material. Negative value.\n"
-             "In FlatCAM units.")
+             "In application units.")
         )
         )
 
 
         grid0.addWidget(cutzlabel, 4, 0)
         grid0.addWidget(cutzlabel, 4, 0)
@@ -122,7 +122,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         )
         )
         self.newdia_entry = FCDoubleSpinner()
         self.newdia_entry = FCDoubleSpinner()
         self.newdia_entry.set_precision(self.decimals)
         self.newdia_entry.set_precision(self.decimals)
-        self.newdia_entry.set_range(0.0001, 9999.9999)
+        self.newdia_entry.set_range(0.0001, 10000.0000)
         self.newdia_entry.setSingleStep(0.1)
         self.newdia_entry.setSingleStep(0.1)
 
 
         grid0.addWidget(self.newdialabel, 5, 0)
         grid0.addWidget(self.newdialabel, 5, 0)
@@ -136,7 +136,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         # Milling Type Radio Button
         # Milling Type Radio Button
         self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
         self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
         self.milling_type_label.setToolTip(
         self.milling_type_label.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -144,7 +144,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
                                             {'label': _('Conventional'), 'value': 'cv'}])
                                             {'label': _('Conventional'), 'value': 'cv'}])
         self.milling_type_radio.setToolTip(
         self.milling_type_radio.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -183,8 +183,8 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         nccoverlabel.setToolTip(
         nccoverlabel.setToolTip(
            _("How much (percentage) of the tool width to overlap each tool pass.\n"
            _("How much (percentage) of the tool width to overlap each tool pass.\n"
              "Adjust the value starting with lower values\n"
              "Adjust the value starting with lower values\n"
-             "and increasing it if areas that should be cleared are still \n"
-             "not cleared.\n"
+             "and increasing it if areas that should be processed are still \n"
+             "not processed.\n"
              "Lower values = faster processing, faster execution on CNC.\n"
              "Lower values = faster processing, faster execution on CNC.\n"
              "Higher values = slow processing and slow execution on CNC\n"
              "Higher values = slow processing and slow execution on CNC\n"
              "due of too many paths.")
              "due of too many paths.")
@@ -225,7 +225,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         #     {"label": _("Seed-based"), "value": "seed"},
         #     {"label": _("Seed-based"), "value": "seed"},
         #     {"label": _("Straight lines"), "value": "lines"}
         #     {"label": _("Straight lines"), "value": "lines"}
         # ], orientation='vertical', stretch=False)
         # ], orientation='vertical', stretch=False)
-        self.ncc_method_combo = FCComboBox()
+        self.ncc_method_combo = FCComboBox2()
         self.ncc_method_combo.addItems(
         self.ncc_method_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
             [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
         )
         )
@@ -256,8 +256,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         self.ncc_choice_offset_cb.setToolTip(
         self.ncc_choice_offset_cb.setToolTip(
             _("If used, it will add an offset to the copper features.\n"
             _("If used, it will add an offset to the copper features.\n"
               "The copper clearing will finish to a distance\n"
               "The copper clearing will finish to a distance\n"
-              "from the copper features.\n"
-              "The value can be between 0 and 10 FlatCAM units.")
+              "from the copper features.")
         )
         )
 
 
         grid0.addWidget(self.ncc_choice_offset_cb, 14, 0, 1, 2)
         grid0.addWidget(self.ncc_choice_offset_cb, 14, 0, 1, 2)
@@ -267,11 +266,10 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         self.ncc_offset_label.setToolTip(
         self.ncc_offset_label.setToolTip(
             _("If used, it will add an offset to the copper features.\n"
             _("If used, it will add an offset to the copper features.\n"
               "The copper clearing will finish to a distance\n"
               "The copper clearing will finish to a distance\n"
-              "from the copper features.\n"
-              "The value can be between 0.0 and 9999.9 FlatCAM units.")
+              "from the copper features.")
         )
         )
         self.ncc_offset_spinner = FCDoubleSpinner()
         self.ncc_offset_spinner = FCDoubleSpinner()
-        self.ncc_offset_spinner.set_range(0.00, 9999.9999)
+        self.ncc_offset_spinner.set_range(0.00, 10000.0000)
         self.ncc_offset_spinner.set_precision(self.decimals)
         self.ncc_offset_spinner.set_precision(self.decimals)
         self.ncc_offset_spinner.setWrapping(True)
         self.ncc_offset_spinner.setWrapping(True)
         self.ncc_offset_spinner.setSingleStep(0.1)
         self.ncc_offset_spinner.setSingleStep(0.1)
@@ -288,11 +286,11 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         self.ncc_rest_cb = FCCheckBox('%s' % _("Rest"))
         self.ncc_rest_cb = FCCheckBox('%s' % _("Rest"))
         self.ncc_rest_cb.setToolTip(
         self.ncc_rest_cb.setToolTip(
             _("If checked, use 'rest machining'.\n"
             _("If checked, use 'rest machining'.\n"
-              "Basically it will clear copper outside PCB features,\n"
+              "Basically it will process copper outside PCB features,\n"
               "using the biggest tool and continue with the next tools,\n"
               "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to clear areas of copper that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper to clear or there are no more tools.\n"
+              "from bigger to smaller, to process the copper features that\n"
+              "could not be processed by previous tool, until there is\n"
+              "nothing left to process or there are no more tools.\n\n"
               "If not checked, use the standard algorithm.")
               "If not checked, use the standard algorithm.")
         )
         )
 
 
@@ -304,7 +302,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         #                                  {'label': _('Reference Object'), 'value': 'box'}],
         #                                  {'label': _('Reference Object'), 'value': 'box'}],
         #                                 orientation='vertical',
         #                                 orientation='vertical',
         #                                 stretch=None)
         #                                 stretch=None)
-        self.select_combo = FCComboBox()
+        self.select_combo = FCComboBox2()
         self.select_combo.addItems(
         self.select_combo.addItems(
             [_("Itself"), _("Area Selection"), _("Reference Object")]
             [_("Itself"), _("Area Selection"), _("Reference Object")]
         )
         )
@@ -340,7 +338,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
                                         {"label": _("Progressive"), "value": "progressive"}])
                                         {"label": _("Progressive"), "value": "progressive"}])
         plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
         plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
         plotting_label.setToolTip(
         plotting_label.setToolTip(
-            _("- 'Normal' -  normal plotting, done at the end of the job\n"
+            _("- 'Normal' - normal plotting, done at the end of the job\n"
               "- 'Progressive' - each shape is plotted after it is generated")
               "- 'Progressive' - each shape is plotted after it is generated")
         )
         )
         grid0.addWidget(plotting_label, 21, 0)
         grid0.addWidget(plotting_label, 21, 0)

+ 16 - 16
appGUI/preferences/tools/ToolsPaintPrefGroupUI.py

@@ -1,7 +1,7 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, NumericalEvalTupleEntry
+from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox2, FCCheckBox, NumericalEvalTupleEntry
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 from appGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
@@ -30,7 +30,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         # ------------------------------
         # ------------------------------
         # ## Paint area
         # ## Paint area
         # ------------------------------
         # ------------------------------
-        self.paint_label = QtWidgets.QLabel(_('<b>Parameters:</b>'))
+        self.paint_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
         self.paint_label.setToolTip(
         self.paint_label.setToolTip(
             _("Creates tool paths to cover the\n"
             _("Creates tool paths to cover the\n"
               "whole area of a polygon.")
               "whole area of a polygon.")
@@ -78,7 +78,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
             _("The tip diameter for V-Shape Tool"))
             _("The tip diameter for V-Shape Tool"))
         self.tipdia_entry = FCDoubleSpinner()
         self.tipdia_entry = FCDoubleSpinner()
         self.tipdia_entry.set_precision(self.decimals)
         self.tipdia_entry.set_precision(self.decimals)
-        self.tipdia_entry.set_range(0.0000, 9999.9999)
+        self.tipdia_entry.set_range(0.0000, 10000.0000)
         self.tipdia_entry.setSingleStep(0.1)
         self.tipdia_entry.setSingleStep(0.1)
         self.tipdia_entry.setObjectName(_("V-Tip Dia"))
         self.tipdia_entry.setObjectName(_("V-Tip Dia"))
 
 
@@ -107,7 +107,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         )
         )
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry = FCDoubleSpinner()
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
-        self.cutz_entry.set_range(-99999.9999, 0.0000)
+        self.cutz_entry.set_range(-910000.0000, 0.0000)
         self.cutz_entry.setObjectName(_("Cut Z"))
         self.cutz_entry.setObjectName(_("Cut Z"))
 
 
         self.cutz_entry.setToolTip(
         self.cutz_entry.setToolTip(
@@ -126,7 +126,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         )
         )
         self.newdia_entry = FCDoubleSpinner()
         self.newdia_entry = FCDoubleSpinner()
         self.newdia_entry.set_precision(self.decimals)
         self.newdia_entry.set_precision(self.decimals)
-        self.newdia_entry.set_range(0.000, 9999.9999)
+        self.newdia_entry.set_range(0.000, 10000.0000)
         self.newdia_entry.setObjectName(_("Tool Dia"))
         self.newdia_entry.setObjectName(_("Tool Dia"))
 
 
         grid0.addWidget(self.newdialabel, 5, 0)
         grid0.addWidget(self.newdialabel, 5, 0)
@@ -162,8 +162,8 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         ovlabel.setToolTip(
         ovlabel.setToolTip(
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
               "Adjust the value starting with lower values\n"
               "Adjust the value starting with lower values\n"
-              "and increasing it if areas that should be painted are still \n"
-              "not painted.\n"
+              "and increasing it if areas that should be processed are still \n"
+              "not processed.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "due of too many paths.")
               "due of too many paths.")
@@ -185,7 +185,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
               "be painted.")
               "be painted.")
         )
         )
         self.paintmargin_entry = FCDoubleSpinner()
         self.paintmargin_entry = FCDoubleSpinner()
-        self.paintmargin_entry.set_range(-9999.9999, 9999.9999)
+        self.paintmargin_entry.set_range(-10000.0000, 10000.0000)
         self.paintmargin_entry.set_precision(self.decimals)
         self.paintmargin_entry.set_precision(self.decimals)
         self.paintmargin_entry.setSingleStep(0.1)
         self.paintmargin_entry.setSingleStep(0.1)
 
 
@@ -210,7 +210,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         #     {"label": _("Seed-based"), "value": "seed"},
         #     {"label": _("Seed-based"), "value": "seed"},
         #     {"label": _("Straight lines"), "value": "lines"}
         #     {"label": _("Straight lines"), "value": "lines"}
         # ], orientation='vertical', stretch=False)
         # ], orientation='vertical', stretch=False)
-        self.paintmethod_combo = FCComboBox()
+        self.paintmethod_combo = FCComboBox2()
         self.paintmethod_combo.addItems(
         self.paintmethod_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
             [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
         )
         )
@@ -243,11 +243,11 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         self.rest_cb.setObjectName(_("Rest"))
         self.rest_cb.setObjectName(_("Rest"))
         self.rest_cb.setToolTip(
         self.rest_cb.setToolTip(
             _("If checked, use 'rest machining'.\n"
             _("If checked, use 'rest machining'.\n"
-              "Basically it will clear copper outside PCB features,\n"
+              "Basically it will process copper outside PCB features,\n"
               "using the biggest tool and continue with the next tools,\n"
               "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to clear areas of copper that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper to clear or there are no more tools.\n\n"
+              "from bigger to smaller, to process the copper features that\n"
+              "could not be processed by previous tool, until there is\n"
+              "nothing left to process or there are no more tools.\n\n"
               "If not checked, use the standard algorithm.")
               "If not checked, use the standard algorithm.")
         )
         )
         grid0.addWidget(self.rest_cb, 14, 0, 1, 2)
         grid0.addWidget(self.rest_cb, 14, 0, 1, 2)
@@ -273,9 +273,9 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         #     orientation='vertical',
         #     orientation='vertical',
         #     stretch=None
         #     stretch=None
         # )
         # )
-        self.selectmethod_combo = FCComboBox()
+        self.selectmethod_combo = FCComboBox2()
         self.selectmethod_combo.addItems(
         self.selectmethod_combo.addItems(
-            [_("Polygon Selection"), _("Area Selection"), _("All"), _("Reference Object")]
+            [_("All"), _("Polygon Selection"), _("Area Selection"), _("Reference Object")]
         )
         )
 
 
         grid0.addWidget(selectlabel, 15, 0)
         grid0.addWidget(selectlabel, 15, 0)
@@ -302,7 +302,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
                                               {"label": _("Progressive"), "value": "progressive"}])
                                               {"label": _("Progressive"), "value": "progressive"}])
         plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
         plotting_label = QtWidgets.QLabel('%s:' % _("Plotting"))
         plotting_label.setToolTip(
         plotting_label.setToolTip(
-            _("- 'Normal' -  normal plotting, done at the end of the job\n"
+            _("- 'Normal' - normal plotting, done at the end of the job\n"
               "- 'Progressive' - each shape is plotted after it is generated")
               "- 'Progressive' - each shape is plotted after it is generated")
         )
         )
         grid0.addWidget(plotting_label, 20, 0)
         grid0.addWidget(plotting_label, 20, 0)

+ 4 - 4
appGUI/preferences/tools/ToolsPanelizePrefGroupUI.py

@@ -43,7 +43,7 @@ class ToolsPanelizePrefGroupUI(OptionsGroupUI):
 
 
         # ## Spacing Columns
         # ## Spacing Columns
         self.pspacing_columns = FCDoubleSpinner()
         self.pspacing_columns = FCDoubleSpinner()
-        self.pspacing_columns.set_range(0.000001, 9999.9999)
+        self.pspacing_columns.set_range(0.000001, 10000.0000)
         self.pspacing_columns.set_precision(self.decimals)
         self.pspacing_columns.set_precision(self.decimals)
         self.pspacing_columns.setSingleStep(0.1)
         self.pspacing_columns.setSingleStep(0.1)
 
 
@@ -57,7 +57,7 @@ class ToolsPanelizePrefGroupUI(OptionsGroupUI):
 
 
         # ## Spacing Rows
         # ## Spacing Rows
         self.pspacing_rows = FCDoubleSpinner()
         self.pspacing_rows = FCDoubleSpinner()
-        self.pspacing_rows.set_range(0.000001, 9999.9999)
+        self.pspacing_rows.set_range(0.000001, 10000.0000)
         self.pspacing_rows.set_precision(self.decimals)
         self.pspacing_rows.set_precision(self.decimals)
         self.pspacing_rows.setSingleStep(0.1)
         self.pspacing_rows.setSingleStep(0.1)
 
 
@@ -128,7 +128,7 @@ class ToolsPanelizePrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.pconstrain_cb, 10, 0, 1, 2)
         grid0.addWidget(self.pconstrain_cb, 10, 0, 1, 2)
 
 
         self.px_width_entry = FCDoubleSpinner()
         self.px_width_entry = FCDoubleSpinner()
-        self.px_width_entry.set_range(0.000001, 9999.9999)
+        self.px_width_entry.set_range(0.000001, 10000.0000)
         self.px_width_entry.set_precision(self.decimals)
         self.px_width_entry.set_precision(self.decimals)
         self.px_width_entry.setSingleStep(0.1)
         self.px_width_entry.setSingleStep(0.1)
 
 
@@ -141,7 +141,7 @@ class ToolsPanelizePrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.px_width_entry, 12, 1)
         grid0.addWidget(self.px_width_entry, 12, 1)
 
 
         self.py_height_entry = FCDoubleSpinner()
         self.py_height_entry = FCDoubleSpinner()
-        self.py_height_entry.set_range(0.000001, 9999.9999)
+        self.py_height_entry.set_range(0.000001, 10000.0000)
         self.py_height_entry.set_precision(self.decimals)
         self.py_height_entry.set_precision(self.decimals)
         self.py_height_entry.setSingleStep(0.1)
         self.py_height_entry.setSingleStep(0.1)
 
 

+ 8 - 8
appGUI/preferences/tools/ToolsPreferencesUI.py

@@ -81,26 +81,26 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
         self.vlay = QtWidgets.QVBoxLayout()
         self.vlay = QtWidgets.QVBoxLayout()
 
 
         self.vlay.addWidget(self.tools_iso_group)
         self.vlay.addWidget(self.tools_iso_group)
-        self.vlay.addWidget(self.tools_drill_group)
+        self.vlay.addWidget(self.tools_2sided_group)
+        self.vlay.addWidget(self.tools_cutout_group)
 
 
         self.vlay1 = QtWidgets.QVBoxLayout()
         self.vlay1 = QtWidgets.QVBoxLayout()
-        self.vlay1.addWidget(self.tools_ncc_group)
-        self.vlay1.addWidget(self.tools_2sided_group)
-        self.vlay1.addWidget(self.tools_cutout_group)
-        self.vlay1.addWidget(self.tools_sub_group)
+        self.vlay1.addWidget(self.tools_drill_group)
+        self.vlay1.addWidget(self.tools_panelize_group)
 
 
         self.vlay2 = QtWidgets.QVBoxLayout()
         self.vlay2 = QtWidgets.QVBoxLayout()
+        self.vlay2.addWidget(self.tools_ncc_group)
         self.vlay2.addWidget(self.tools_paint_group)
         self.vlay2.addWidget(self.tools_paint_group)
-        self.vlay2.addWidget(self.tools_transform_group)
 
 
         self.vlay3 = QtWidgets.QVBoxLayout()
         self.vlay3 = QtWidgets.QVBoxLayout()
         self.vlay3.addWidget(self.tools_film_group)
         self.vlay3.addWidget(self.tools_film_group)
-        self.vlay3.addWidget(self.tools_calculators_group)
+        self.vlay3.addWidget(self.tools_transform_group)
 
 
         self.vlay4 = QtWidgets.QVBoxLayout()
         self.vlay4 = QtWidgets.QVBoxLayout()
         self.vlay4.addWidget(self.tools_solderpaste_group)
         self.vlay4.addWidget(self.tools_solderpaste_group)
         self.vlay4.addWidget(self.tools_corners_group)
         self.vlay4.addWidget(self.tools_corners_group)
-        self.vlay4.addWidget(self.tools_panelize_group)
+        self.vlay4.addWidget(self.tools_calculators_group)
+        self.vlay4.addWidget(self.tools_sub_group)
 
 
         self.layout.addLayout(self.vlay)
         self.layout.addLayout(self.vlay)
         self.layout.addLayout(self.vlay1)
         self.layout.addLayout(self.vlay1)

+ 12 - 12
appGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py

@@ -53,11 +53,11 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # New Nozzle Tool Dia
         # New Nozzle Tool Dia
         self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('New Nozzle Dia'))
         self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('New Nozzle Dia'))
         self.addtool_entry_lbl.setToolTip(
         self.addtool_entry_lbl.setToolTip(
-            _("Diameter for the new Nozzle tool to add in the Tool Table")
+            _("Diameter for the new tool to add in the Tool Table")
         )
         )
         self.addtool_entry = FCDoubleSpinner()
         self.addtool_entry = FCDoubleSpinner()
         self.addtool_entry.set_precision(self.decimals)
         self.addtool_entry.set_precision(self.decimals)
-        self.addtool_entry.set_range(0.0000001, 9999.9999)
+        self.addtool_entry.set_range(0.0000001, 10000.0000)
         self.addtool_entry.setSingleStep(0.1)
         self.addtool_entry.setSingleStep(0.1)
 
 
         grid0.addWidget(self.addtool_entry_lbl, 1, 0)
         grid0.addWidget(self.addtool_entry_lbl, 1, 0)
@@ -66,7 +66,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Z dispense start
         # Z dispense start
         self.z_start_entry = FCDoubleSpinner()
         self.z_start_entry = FCDoubleSpinner()
         self.z_start_entry.set_precision(self.decimals)
         self.z_start_entry.set_precision(self.decimals)
-        self.z_start_entry.set_range(0.0000001, 9999.9999)
+        self.z_start_entry.set_range(0.0000001, 10000.0000)
         self.z_start_entry.setSingleStep(0.1)
         self.z_start_entry.setSingleStep(0.1)
 
 
         self.z_start_label = QtWidgets.QLabel('%s:' % _("Z Dispense Start"))
         self.z_start_label = QtWidgets.QLabel('%s:' % _("Z Dispense Start"))
@@ -79,7 +79,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Z dispense
         # Z dispense
         self.z_dispense_entry = FCDoubleSpinner()
         self.z_dispense_entry = FCDoubleSpinner()
         self.z_dispense_entry.set_precision(self.decimals)
         self.z_dispense_entry.set_precision(self.decimals)
-        self.z_dispense_entry.set_range(0.0000001, 9999.9999)
+        self.z_dispense_entry.set_range(0.0000001, 10000.0000)
         self.z_dispense_entry.setSingleStep(0.1)
         self.z_dispense_entry.setSingleStep(0.1)
 
 
         self.z_dispense_label = QtWidgets.QLabel('%s:' % _("Z Dispense"))
         self.z_dispense_label = QtWidgets.QLabel('%s:' % _("Z Dispense"))
@@ -92,7 +92,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Z dispense stop
         # Z dispense stop
         self.z_stop_entry = FCDoubleSpinner()
         self.z_stop_entry = FCDoubleSpinner()
         self.z_stop_entry.set_precision(self.decimals)
         self.z_stop_entry.set_precision(self.decimals)
-        self.z_stop_entry.set_range(0.0000001, 9999.9999)
+        self.z_stop_entry.set_range(0.0000001, 10000.0000)
         self.z_stop_entry.setSingleStep(0.1)
         self.z_stop_entry.setSingleStep(0.1)
 
 
         self.z_stop_label = QtWidgets.QLabel('%s:' % _("Z Dispense Stop"))
         self.z_stop_label = QtWidgets.QLabel('%s:' % _("Z Dispense Stop"))
@@ -105,7 +105,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Z travel
         # Z travel
         self.z_travel_entry = FCDoubleSpinner()
         self.z_travel_entry = FCDoubleSpinner()
         self.z_travel_entry.set_precision(self.decimals)
         self.z_travel_entry.set_precision(self.decimals)
-        self.z_travel_entry.set_range(0.0000001, 9999.9999)
+        self.z_travel_entry.set_range(0.0000001, 10000.0000)
         self.z_travel_entry.setSingleStep(0.1)
         self.z_travel_entry.setSingleStep(0.1)
 
 
         self.z_travel_label = QtWidgets.QLabel('%s:' % _("Z Travel"))
         self.z_travel_label = QtWidgets.QLabel('%s:' % _("Z Travel"))
@@ -119,7 +119,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Z toolchange location
         # Z toolchange location
         self.z_toolchange_entry = FCDoubleSpinner()
         self.z_toolchange_entry = FCDoubleSpinner()
         self.z_toolchange_entry.set_precision(self.decimals)
         self.z_toolchange_entry.set_precision(self.decimals)
-        self.z_toolchange_entry.set_range(0.0000001, 9999.9999)
+        self.z_toolchange_entry.set_range(0.0000001, 10000.0000)
         self.z_toolchange_entry.setSingleStep(0.1)
         self.z_toolchange_entry.setSingleStep(0.1)
 
 
         self.z_toolchange_label = QtWidgets.QLabel('%s:' % _("Z Toolchange"))
         self.z_toolchange_label = QtWidgets.QLabel('%s:' % _("Z Toolchange"))
@@ -142,7 +142,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Feedrate X-Y
         # Feedrate X-Y
         self.frxy_entry = FCDoubleSpinner()
         self.frxy_entry = FCDoubleSpinner()
         self.frxy_entry.set_precision(self.decimals)
         self.frxy_entry.set_precision(self.decimals)
-        self.frxy_entry.set_range(0.0000001, 99999.9999)
+        self.frxy_entry.set_range(0.0000001, 910000.0000)
         self.frxy_entry.setSingleStep(0.1)
         self.frxy_entry.setSingleStep(0.1)
 
 
         self.frxy_label = QtWidgets.QLabel('%s:' % _("Feedrate X-Y"))
         self.frxy_label = QtWidgets.QLabel('%s:' % _("Feedrate X-Y"))
@@ -155,7 +155,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Feedrate Z
         # Feedrate Z
         self.frz_entry = FCDoubleSpinner()
         self.frz_entry = FCDoubleSpinner()
         self.frz_entry.set_precision(self.decimals)
         self.frz_entry.set_precision(self.decimals)
-        self.frz_entry.set_range(0.0000001, 99999.9999)
+        self.frz_entry.set_range(0.0000001, 910000.0000)
         self.frz_entry.setSingleStep(0.1)
         self.frz_entry.setSingleStep(0.1)
 
 
         self.frz_label = QtWidgets.QLabel('%s:' % _("Feedrate Z"))
         self.frz_label = QtWidgets.QLabel('%s:' % _("Feedrate Z"))
@@ -169,7 +169,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Feedrate Z Dispense
         # Feedrate Z Dispense
         self.frz_dispense_entry = FCDoubleSpinner()
         self.frz_dispense_entry = FCDoubleSpinner()
         self.frz_dispense_entry.set_precision(self.decimals)
         self.frz_dispense_entry.set_precision(self.decimals)
-        self.frz_dispense_entry.set_range(0.0000001, 99999.9999)
+        self.frz_dispense_entry.set_range(0.0000001, 910000.0000)
         self.frz_dispense_entry.setSingleStep(0.1)
         self.frz_dispense_entry.setSingleStep(0.1)
 
 
         self.frz_dispense_label = QtWidgets.QLabel('%s:' % _("Feedrate Z Dispense"))
         self.frz_dispense_label = QtWidgets.QLabel('%s:' % _("Feedrate Z Dispense"))
@@ -196,7 +196,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Dwell Forward
         # Dwell Forward
         self.dwellfwd_entry = FCDoubleSpinner()
         self.dwellfwd_entry = FCDoubleSpinner()
         self.dwellfwd_entry.set_precision(self.decimals)
         self.dwellfwd_entry.set_precision(self.decimals)
-        self.dwellfwd_entry.set_range(0.0000001, 9999.9999)
+        self.dwellfwd_entry.set_range(0.0000001, 10000.0000)
         self.dwellfwd_entry.setSingleStep(0.1)
         self.dwellfwd_entry.setSingleStep(0.1)
 
 
         self.dwellfwd_label = QtWidgets.QLabel('%s:' % _("Dwell FWD"))
         self.dwellfwd_label = QtWidgets.QLabel('%s:' % _("Dwell FWD"))
@@ -222,7 +222,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
         # Dwell Reverse
         # Dwell Reverse
         self.dwellrev_entry = FCDoubleSpinner()
         self.dwellrev_entry = FCDoubleSpinner()
         self.dwellrev_entry.set_precision(self.decimals)
         self.dwellrev_entry.set_precision(self.decimals)
-        self.dwellrev_entry.set_range(0.0000001, 9999.9999)
+        self.dwellrev_entry.set_range(0.0000001, 10000.0000)
         self.dwellrev_entry.setSingleStep(0.1)
         self.dwellrev_entry.setSingleStep(0.1)
 
 
         self.dwellrev_label = QtWidgets.QLabel('%s:' % _("Dwell REV"))
         self.dwellrev_label = QtWidgets.QLabel('%s:' % _("Dwell REV"))

+ 7 - 1
appGUI/preferences/tools/ToolsSubPrefGroupUI.py

@@ -36,7 +36,13 @@ class ToolsSubPrefGroupUI(OptionsGroupUI):
         self.layout.addWidget(self.sublabel)
         self.layout.addWidget(self.sublabel)
 
 
         self.close_paths_cb = FCCheckBox(_("Close paths"))
         self.close_paths_cb = FCCheckBox(_("Close paths"))
-        self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the Geometry substractor object."))
+        self.close_paths_cb.setToolTip(_("Checking this will close the paths cut by the subtractor object."))
         self.layout.addWidget(self.close_paths_cb)
         self.layout.addWidget(self.close_paths_cb)
 
 
+        self.delete_sources_cb = FCCheckBox(_("Delete source"))
+        self.delete_sources_cb.setToolTip(
+            _("When checked will delete the source objects\n"
+              "after a successful operation.")
+        )
+        self.layout.addWidget(self.delete_sources_cb)
         self.layout.addStretch()
         self.layout.addStretch()

+ 8 - 8
appGUI/preferences/tools/ToolsTransformPrefGroupUI.py

@@ -95,7 +95,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
 
 
         self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
         self.rotate_label = QtWidgets.QLabel('%s:' % _("Angle"))
         self.rotate_label.setToolTip(
         self.rotate_label.setToolTip(
-            _("Angle for Rotation action, in degrees.\n"
+            _("Angle, in degrees.\n"
               "Float number between -360 and 359.\n"
               "Float number between -360 and 359.\n"
               "Positive numbers for CW motion.\n"
               "Positive numbers for CW motion.\n"
               "Negative numbers for CCW motion.")
               "Negative numbers for CCW motion.")
@@ -123,7 +123,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
 
 
         self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
         self.skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
         self.skewx_label.setToolTip(
         self.skewx_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
+            _("Angle, in degrees.\n"
               "Float number between -360 and 359.")
               "Float number between -360 and 359.")
         )
         )
         grid0.addWidget(self.skewx_label, 9, 0)
         grid0.addWidget(self.skewx_label, 9, 0)
@@ -137,7 +137,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
 
 
         self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
         self.skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
         self.skewy_label.setToolTip(
         self.skewy_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
+            _("Angle, in degrees.\n"
               "Float number between -360 and 359.")
               "Float number between -360 and 359.")
         )
         )
         grid0.addWidget(self.skewy_label, 10, 0)
         grid0.addWidget(self.skewy_label, 10, 0)
@@ -155,7 +155,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.scale_link_cb, 12, 1)
         grid0.addWidget(self.scale_link_cb, 12, 1)
 
 
         self.scalex_entry = FCDoubleSpinner()
         self.scalex_entry = FCDoubleSpinner()
-        self.scalex_entry.set_range(0, 9999.9999)
+        self.scalex_entry.set_range(0, 10000.0000)
         self.scalex_entry.set_precision(self.decimals)
         self.scalex_entry.set_precision(self.decimals)
         self.scalex_entry.setSingleStep(0.1)
         self.scalex_entry.setSingleStep(0.1)
 
 
@@ -168,7 +168,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
 
 
         # ## Scale factor on X axis
         # ## Scale factor on X axis
         self.scaley_entry = FCDoubleSpinner()
         self.scaley_entry = FCDoubleSpinner()
-        self.scaley_entry.set_range(0, 9999.9999)
+        self.scaley_entry.set_range(0, 10000.0000)
         self.scaley_entry.set_precision(self.decimals)
         self.scaley_entry.set_precision(self.decimals)
         self.scaley_entry.setSingleStep(0.1)
         self.scaley_entry.setSingleStep(0.1)
 
 
@@ -184,7 +184,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(offset_title_lbl, 20, 0, 1, 2)
         grid0.addWidget(offset_title_lbl, 20, 0, 1, 2)
 
 
         self.offx_entry = FCDoubleSpinner()
         self.offx_entry = FCDoubleSpinner()
-        self.offx_entry.set_range(-9999.9999, 9999.9999)
+        self.offx_entry.set_range(-10000.0000, 10000.0000)
         self.offx_entry.set_precision(self.decimals)
         self.offx_entry.set_precision(self.decimals)
         self.offx_entry.setSingleStep(0.1)
         self.offx_entry.setSingleStep(0.1)
 
 
@@ -197,7 +197,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
 
 
         # ## Offset distance on Y axis
         # ## Offset distance on Y axis
         self.offy_entry = FCDoubleSpinner()
         self.offy_entry = FCDoubleSpinner()
-        self.offy_entry.set_range(-9999.9999, 9999.9999)
+        self.offy_entry.set_range(-10000.0000, 10000.0000)
         self.offy_entry.set_precision(self.decimals)
         self.offy_entry.set_precision(self.decimals)
         self.offy_entry.setSingleStep(0.1)
         self.offy_entry.setSingleStep(0.1)
 
 
@@ -235,7 +235,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
         self.buffer_entry.set_precision(self.decimals)
         self.buffer_entry.set_precision(self.decimals)
         self.buffer_entry.setSingleStep(0.1)
         self.buffer_entry.setSingleStep(0.1)
         self.buffer_entry.setWrapping(True)
         self.buffer_entry.setWrapping(True)
-        self.buffer_entry.set_range(-9999.9999, 9999.9999)
+        self.buffer_entry.set_range(-10000.0000, 10000.0000)
 
 
         grid0.addWidget(self.buffer_label, 28, 0)
         grid0.addWidget(self.buffer_label, 28, 0)
         grid0.addWidget(self.buffer_entry, 28, 1)
         grid0.addWidget(self.buffer_entry, 28, 1)

+ 4 - 5
appObjects/AppObject.py

@@ -168,7 +168,7 @@ class AppObject(QtCore.QObject):
             return "fail"
             return "fail"
 
 
         t2 = time.time()
         t2 = time.time()
-        msg = "New object with name: %s. %f seconds executing initialize()." % (name, (t2 - t1))
+        msg = "%s %s. %f seconds executing initialize()." % (_("New object with name:"), name, (t2 - t1))
         log.debug(msg)
         log.debug(msg)
         self.app.inform_shell.emit(msg)
         self.app.inform_shell.emit(msg)
 
 
@@ -256,7 +256,7 @@ class AppObject(QtCore.QObject):
                     'tooldia': float(app.defaults["geometry_cnctooldia"]),
                     'tooldia': float(app.defaults["geometry_cnctooldia"]),
                     'offset': 'Path',
                     'offset': 'Path',
                     'offset_value': 0.0,
                     'offset_value': 0.0,
-                    'type': _('Rough'),
+                    'type': 'Rough',
                     'tool_type': 'C1',
                     'tool_type': 'C1',
                     'data': deepcopy(default_data),
                     'data': deepcopy(default_data),
                     'solid_geometry': []
                     'solid_geometry': []
@@ -401,7 +401,6 @@ class AppObject(QtCore.QObject):
                 name=str(obj.options['name']), tx=_("created/selected"))
                 name=str(obj.options['name']), tx=_("created/selected"))
             )
             )
 
 
-
         # ############################################################################################################
         # ############################################################################################################
         # Set the colors for the objects that have geometry
         # Set the colors for the objects that have geometry
         # ############################################################################################################
         # ############################################################################################################
@@ -439,7 +438,7 @@ class AppObject(QtCore.QObject):
         # #############################################################################################################
         # #############################################################################################################
         # update the SHELL auto-completer model with the name of the new object
         # update the SHELL auto-completer model with the name of the new object
         # #############################################################################################################
         # #############################################################################################################
-        self.app.shell._edit.set_model_data(self.app.myKeywords)
+        self.app.shell.command_line().set_model_data(self.app.myKeywords)
 
 
         if auto_select or self.app.ui.notebook.currentWidget() is self.app.ui.properties_tab:
         if auto_select or self.app.ui.notebook.currentWidget() is self.app.ui.properties_tab:
             # select the just opened object but deselect the previous ones
             # select the just opened object but deselect the previous ones
@@ -450,7 +449,7 @@ class AppObject(QtCore.QObject):
 
 
         # here it is done the object plotting
         # here it is done the object plotting
         def plotting_task(t_obj):
         def plotting_task(t_obj):
-            with self.app.proc_container.new(_("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 if t_obj.kind == 'cncjob':
                 if t_obj.kind == 'cncjob':
                     t_obj.plot(kind=self.app.defaults["cncjob_plot_kind"])
                     t_obj.plot(kind=self.app.defaults["cncjob_plot_kind"])
                 if t_obj.kind == 'gerber':
                 if t_obj.kind == 'gerber':

+ 86 - 59
appObjects/FlatCAMCNCJob.py

@@ -188,8 +188,8 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.mm = None
         self.mm = None
         self.mr = None
         self.mr = None
 
 
-        self.append_snippet = ''
         self.prepend_snippet = ''
         self.prepend_snippet = ''
+        self.append_snippet = ''
         self.gc_header = self.gcode_header()
         self.gc_header = self.gcode_header()
         self.gc_start = ''
         self.gc_start = ''
         self.gc_end = ''
         self.gc_end = ''
@@ -372,7 +372,12 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia_key)))
             dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia_key)))
             nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_drills']))
             nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_drills']))
             nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_slots']))
             nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_slots']))
-            cutz_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(dia_value['offset']) + self.z_cut))
+            try:
+                offset_val = self.app.dec_format(float(dia_value['offset']), self.decimals) + self.z_cut
+            except KeyError:
+                offset_val = self.app.dec_format(float(dia_value['offset_z']), self.decimals) + self.z_cut
+
+            cutz_item = QtWidgets.QTableWidgetItem('%f' % offset_val)
 
 
             t_id.setFlags(QtCore.Qt.ItemIsEnabled)
             t_id.setFlags(QtCore.Qt.ItemIsEnabled)
             dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
             dia_item.setFlags(QtCore.Qt.ItemIsEnabled)
@@ -527,7 +532,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.append_snippet = self.app.defaults['cncjob_append']
         self.append_snippet = self.app.defaults['cncjob_append']
         self.prepend_snippet = self.app.defaults['cncjob_prepend']
         self.prepend_snippet = self.app.defaults['cncjob_prepend']
 
 
-        if self.append_snippet != '' or self.prepend_snippet:
+        if self.append_snippet != '' or self.prepend_snippet != '':
             self.ui.snippets_cb.set_value(True)
             self.ui.snippets_cb.set_value(True)
 
 
         # Fill form fields only on object create
         # Fill form fields only on object create
@@ -638,16 +643,13 @@ class CNCJobObject(FlatCAMObj, CNCjob):
 
 
         # Show/Hide Advanced Options
         # Show/Hide Advanced Options
         if self.app.defaults["global_app_level"] == 'b':
         if self.app.defaults["global_app_level"] == 'b':
-            self.ui.level.setText(_(
-                '<span style="color:green;"><b>Basic</b></span>'
-            ))
+            self.ui.level.setText('<span style="color:green;"><b>%s</b></span>' % _("Basic"))
 
 
             self.ui.sal_btn.hide()
             self.ui.sal_btn.hide()
             self.ui.sal_btn.setChecked(False)
             self.ui.sal_btn.setChecked(False)
         else:
         else:
-            self.ui.level.setText(_(
-                '<span style="color:red;"><b>Advanced</b></span>'
-            ))
+            self.ui.level.setText('<span style="color:red;"><b>%s</b></span>' % _("Advanced"))
+
             if 'Roland' in self.pp_excellon_name or 'Roland' in self.pp_geometry_name or 'hpgl' in \
             if 'Roland' in self.pp_excellon_name or 'Roland' in self.pp_geometry_name or 'hpgl' in \
                     self.pp_geometry_name:
                     self.pp_geometry_name:
                 self.ui.sal_btn.hide()
                 self.ui.sal_btn.hide()
@@ -656,8 +658,8 @@ class CNCJobObject(FlatCAMObj, CNCjob):
                 self.ui.sal_btn.show()
                 self.ui.sal_btn.show()
                 self.ui.sal_btn.setChecked(self.app.defaults["cncjob_al_status"])
                 self.ui.sal_btn.setChecked(self.app.defaults["cncjob_al_status"])
 
 
-        preamble = self.append_snippet
-        postamble = self.prepend_snippet
+        preamble = self.prepend_snippet
+        postamble = self.append_snippet
         gc = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
         gc = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
         self.source_file = gc.getvalue()
         self.source_file = gc.getvalue()
 
 
@@ -1648,7 +1650,9 @@ class CNCJobObject(FlatCAMObj, CNCjob):
                 ext_filter=_filter_
                 ext_filter=_filter_
             )
             )
         except TypeError:
         except TypeError:
-            filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)
+            filename, _f = FCFileSaveDialog.get_saved_filename(
+                caption=_("Export Code ..."),
+                ext_filter=_filter_)
 
 
         if filename == '':
         if filename == '':
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export cancelled ..."))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export cancelled ..."))
@@ -1675,7 +1679,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
                 return 'fail'
                 return 'fail'
 
 
     def on_edit_probing_gcode(self):
     def on_edit_probing_gcode(self):
-        self.app.proc_container.view.set_busy(_("Loading..."))
+        self.app.proc_container.view.set_busy('%s...' % _("Loading"))
 
 
         gco = self.probing_gcode_text
         gco = self.probing_gcode_text
         if gco is None or gco == '':
         if gco is None or gco == '':
@@ -1838,7 +1842,9 @@ class CNCJobObject(FlatCAMObj, CNCjob):
                     ext_filter=_filter_
                     ext_filter=_filter_
                 )
                 )
             except TypeError:
             except TypeError:
-                filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)
+                filename, _f = FCFileSaveDialog.get_saved_filename(
+                    caption=_("Export Code ..."),
+                    ext_filter=_filter_)
 
 
             if filename == '':
             if filename == '':
                 self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export cancelled ..."))
                 self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export cancelled ..."))
@@ -1882,7 +1888,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         kind = self.ui.cncplot_method_combo.get_value()
         kind = self.ui.cncplot_method_combo.get_value()
 
 
         def worker_task():
         def worker_task():
-            with self.app.proc_container.new(_("Plotting...")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 self.plot(kind=kind)
                 self.plot(kind=kind)
 
 
         self.app.worker_task.emit({'fcn': worker_task, 'params': []})
         self.app.worker_task.emit({'fcn': worker_task, 'params': []})
@@ -1916,7 +1922,9 @@ class CNCJobObject(FlatCAMObj, CNCjob):
                 ext_filter=_filter_
                 ext_filter=_filter_
             )
             )
         except TypeError:
         except TypeError:
-            filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)
+            filename, _f = FCFileSaveDialog.get_saved_filename(
+                caption=_("Export Code ..."),
+                ext_filter=_filter_)
 
 
         self.export_gcode_handler(filename, is_gcode=save_gcode)
         self.export_gcode_handler(filename, is_gcode=save_gcode)
 
 
@@ -1939,8 +1947,8 @@ class CNCJobObject(FlatCAMObj, CNCjob):
 
 
         try:
         try:
             if self.ui.snippets_cb.get_value():
             if self.ui.snippets_cb.get_value():
-                preamble = self.append_snippet
-                postamble = self.prepend_snippet
+                preamble = self.prepend_snippet
+                postamble = self.append_snippet
             gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
             gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
         except Exception as err:
         except Exception as err:
             log.debug("CNCJobObject.export_gcode_handler() --> %s" % str(err))
             log.debug("CNCJobObject.export_gcode_handler() --> %s" % str(err))
@@ -1954,18 +1962,17 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         self.app.file_saved.emit("gcode", filename)
         self.app.file_saved.emit("gcode", filename)
         self.app.inform.emit('[success] %s: %s' % (_("File saved to"), filename))
         self.app.inform.emit('[success] %s: %s' % (_("File saved to"), filename))
 
 
-    def on_review_code_click(self, *args):
+    def on_review_code_click(self):
         """
         """
         Handler activated by a button clicked when reviewing GCode.
         Handler activated by a button clicked when reviewing GCode.
 
 
-        :param args:
         :return:
         :return:
         """
         """
 
 
-        self.app.proc_container.view.set_busy(_("Loading..."))
+        self.app.proc_container.view.set_busy('%s...' % _("Loading"))
 
 
-        preamble = self.append_snippet
-        postamble = self.prepend_snippet
+        preamble = self.prepend_snippet
+        postamble = self.append_snippet
 
 
         gco = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
         gco = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
         if gco == 'fail':
         if gco == 'fail':
@@ -2154,7 +2161,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
         if preamble == '':
         if preamble == '':
             preamble = self.app.defaults["cncjob_prepend"]
             preamble = self.app.defaults["cncjob_prepend"]
         if postamble == '':
         if postamble == '':
-            preamble = self.app.defaults["cncjob_append"]
+            postamble = self.app.defaults["cncjob_append"]
 
 
         try:
         try:
             if self.special_group:
             if self.special_group:
@@ -2180,7 +2187,6 @@ class CNCJobObject(FlatCAMObj, CNCjob):
 
 
         gcode = ''
         gcode = ''
         if include_header is False:
         if include_header is False:
-            g = preamble
             # detect if using multi-tool and make the Gcode summation correctly for each case
             # detect if using multi-tool and make the Gcode summation correctly for each case
             if self.multitool is True:
             if self.multitool is True:
                 for tooluid_key in self.cnc_tools:
                 for tooluid_key in self.cnc_tools:
@@ -2191,7 +2197,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             else:
             else:
                 gcode += self.gcode
                 gcode += self.gcode
 
 
-            g = g + gcode + postamble
+            g = preamble + '\n' + gcode + '\n' + postamble
         else:
         else:
             # search for the GCode beginning which is usually a G20 or G21
             # search for the GCode beginning which is usually a G20 or G21
             # fix so the preamble gets inserted in between the comments header and the actual start of GCODE
             # fix so the preamble gets inserted in between the comments header and the actual start of GCODE
@@ -2241,39 +2247,52 @@ class CNCJobObject(FlatCAMObj, CNCjob):
                             break
                             break
 
 
             if hpgl:
             if hpgl:
-                processed_gcode = ''
+                processed_body_gcode = ''
                 pa_re = re.compile(r"^PA\s*(-?\d+\.\d*),?\s*(-?\d+\.\d*)*;?$")
                 pa_re = re.compile(r"^PA\s*(-?\d+\.\d*),?\s*(-?\d+\.\d*)*;?$")
+
+                # process body gcode
                 for gline in gcode.splitlines():
                 for gline in gcode.splitlines():
                     match = pa_re.search(gline)
                     match = pa_re.search(gline)
                     if match:
                     if match:
                         x_int = int(float(match.group(1)))
                         x_int = int(float(match.group(1)))
                         y_int = int(float(match.group(2)))
                         y_int = int(float(match.group(2)))
                         new_line = 'PA%d,%d;\n' % (x_int, y_int)
                         new_line = 'PA%d,%d;\n' % (x_int, y_int)
-                        processed_gcode += new_line
+                        processed_body_gcode += new_line
                     else:
                     else:
-                        processed_gcode += gline + '\n'
+                        processed_body_gcode += gline + '\n'
 
 
-                gcode = processed_gcode
-                g = self.gc_header + '\n' + preamble + '\n' + gcode + postamble + end_gcode
+                gcode = processed_body_gcode
+                g = self.gc_header + '\n' + self.gc_start + '\n' + preamble + '\n' + \
+                    gcode + '\n' + postamble + end_gcode
             else:
             else:
-                try:
-                    g_idx = gcode.index('G94')
-                    if preamble != '' and postamble != '':
-                        g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
-                            gcode[(g_idx + 3):] + postamble + end_gcode
-                    elif preamble == '':
-                        g = self.gc_header + gcode[:g_idx + 3] + '\n' + \
-                            gcode[(g_idx + 3):] + postamble + end_gcode
-                    elif postamble == '':
-                        g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
-                            gcode[(g_idx + 3):] + end_gcode
-                    else:
-                        g = self.gc_header + gcode[:g_idx + 3] + gcode[(g_idx + 3):] + end_gcode
-                except ValueError:
-                    self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                         _("G-code does not have a G94 code.\n"
-                                           "Append Code snippet will not be used.."))
-                    g = self.gc_header + '\n' + gcode + postamble + end_gcode
+                # try:
+                #     g_idx = gcode.index('G94')
+                #     if preamble != '' and postamble != '':
+                #         g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
+                #             gcode[(g_idx + 3):] + postamble + end_gcode
+                #     elif preamble == '':
+                #         g = self.gc_header + gcode[:g_idx + 3] + '\n' + \
+                #             gcode[(g_idx + 3):] + postamble + end_gcode
+                #     elif postamble == '':
+                #         g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
+                #             gcode[(g_idx + 3):] + end_gcode
+                #     else:
+                #         g = self.gc_header + gcode[:g_idx + 3] + gcode[(g_idx + 3):] + end_gcode
+                # except ValueError:
+                #     self.app.inform.emit('[ERROR_NOTCL] %s' %
+                #                          _("G-code does not have a G94 code.\n"
+                #                            "Append Code snippet will not be used.."))
+                #     g = self.gc_header + '\n' + gcode + postamble + end_gcode
+                g = ''
+                if preamble != '' and postamble != '':
+                    g = self.gc_header + self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + \
+                        postamble + '\n' + end_gcode
+                if preamble == '':
+                    g = self.gc_header + self.gc_start + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
+                if postamble == '':
+                    g = self.gc_header + self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + end_gcode
+                if preamble == '' and postamble == '':
+                    g = self.gc_header + self.gc_start + '\n' + gcode + '\n' + end_gcode
 
 
         # if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box
         # if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box
         # if self.ui.toolchange_cb.get_value() is True:
         # if self.ui.toolchange_cb.get_value() is True:
@@ -2490,10 +2509,10 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             if self.app.is_legacy is False:
             if self.app.is_legacy is False:
                 self.annotation.clear(update=True)
                 self.annotation.clear(update=True)
 
 
-        # Annotaions shapes plotting
+        # Annotations shapes plotting
         try:
         try:
             if self.app.is_legacy is False:
             if self.app.is_legacy is False:
-                if self.ui.annotation_cb.get_value() and self.ui.plot_cb.get_value():
+                if self.ui.annotation_cb.get_value() and visible:
                     self.plot_annotations(obj=self, visible=True)
                     self.plot_annotations(obj=self, visible=True)
                 else:
                 else:
                     self.plot_annotations(obj=self, visible=False)
                     self.plot_annotations(obj=self, visible=False)
@@ -2502,20 +2521,28 @@ class CNCJobObject(FlatCAMObj, CNCjob):
             if self.app.is_legacy is False:
             if self.app.is_legacy is False:
                 self.annotation.clear(update=True)
                 self.annotation.clear(update=True)
 
 
-    def on_annotation_change(self):
+    def on_annotation_change(self, val):
         """
         """
         Handler for toggling the annotation display by clicking a checkbox.
         Handler for toggling the annotation display by clicking a checkbox.
         :return:
         :return:
         """
         """
 
 
         if self.app.is_legacy is False:
         if self.app.is_legacy is False:
-            if self.ui.annotation_cb.get_value():
-                self.text_col.enabled = True
-            else:
-                self.text_col.enabled = False
-            # kind = self.ui.cncplot_method_combo.get_value()
-            # self.plot(kind=kind)
-            self.annotation.redraw()
+            # self.text_col.visible = True if val == 2 else False
+            # self.plot(kind=self.ui.cncplot_method_combo.get_value())
+            # Annotations shapes plotting
+            try:
+                if self.app.is_legacy is False:
+                    if val and self.ui.plot_cb.get_value():
+                        self.plot_annotations(obj=self, visible=True)
+                    else:
+                        self.plot_annotations(obj=self, visible=False)
+
+            except (ObjectDeleted, AttributeError):
+                if self.app.is_legacy is False:
+                    self.annotation.clear(update=True)
+
+            # self.annotation.redraw()
         else:
         else:
             kind = self.ui.cncplot_method_combo.get_value()
             kind = self.ui.cncplot_method_combo.get_value()
             self.plot(kind=kind)
             self.plot(kind=kind)

+ 2 - 6
appObjects/FlatCAMDocument.py

@@ -67,13 +67,9 @@ class DocumentObject(FlatCAMObj):
 
 
         # Show/Hide Advanced Options
         # Show/Hide Advanced Options
         if self.app.defaults["global_app_level"] == 'b':
         if self.app.defaults["global_app_level"] == 'b':
-            self.ui.level.setText(_(
-                '<span style="color:green;"><b>Basic</b></span>'
-            ))
+            self.ui.level.setText('<span style="color:green;"><b>%s</b></span>' % _("Basic"))
         else:
         else:
-            self.ui.level.setText(_(
-                '<span style="color:red;"><b>Advanced</b></span>'
-            ))
+            self.ui.level.setText('<span style="color:red;"><b>%s</b></span>' % _("Advanced"))
 
 
         self.document_editor_tab = AppTextEditor(app=self.app)
         self.document_editor_tab = AppTextEditor(app=self.app)
         stylesheet = """
         stylesheet = """

+ 11 - 14
appObjects/FlatCAMExcellon.py

@@ -11,16 +11,13 @@
 # ##########################################################
 # ##########################################################
 
 
 
 
-from shapely.geometry import Point, LineString
-
-from copy import deepcopy
+from shapely.geometry import LineString
 
 
 from appParsers.ParseExcellon import Excellon
 from appParsers.ParseExcellon import Excellon
 from appObjects.FlatCAMObj import *
 from appObjects.FlatCAMObj import *
 
 
 import itertools
 import itertools
 import numpy as np
 import numpy as np
-from collections import defaultdict
 
 
 import gettext
 import gettext
 import appTranslation as fcTranslate
 import appTranslation as fcTranslate
@@ -1016,10 +1013,10 @@ class ExcellonObject(FlatCAMObj, Excellon):
             # in case that the tool used has the same diameter with the hole, and since the maximum resolution
             # in case that the tool used has the same diameter with the hole, and since the maximum resolution
             # for FlatCAM is 6 decimals,
             # for FlatCAM is 6 decimals,
             # we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero"
             # we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero"
-            for tool in tools:
-                for slot in self.tools[tool]['slots']:
+            for m_tool in tools:
+                for slot in self.tools[m_tool]['slots']:
                     toolstable_tool = float('%.*f' % (self.decimals, float(tooldia)))
                     toolstable_tool = float('%.*f' % (self.decimals, float(tooldia)))
-                    file_tool = float('%.*f' % (self.decimals, float(self.tools[tool]["tooldia"])))
+                    file_tool = float('%.*f' % (self.decimals, float(self.tools[m_tool]["tooldia"])))
 
 
                     # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
                     # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse
                     # for the file_tool (tooldia actually)
                     # for the file_tool (tooldia actually)
@@ -1164,8 +1161,8 @@ class ExcellonObject(FlatCAMObj, Excellon):
                     r_color[3] = 1
                     r_color[3] = 1
 
 
                     new_color = '#'
                     new_color = '#'
-                    for idx in range(len(r_color)):
-                        new_color += '%x' % int(r_color[idx] * 255)
+                    for idx_c in range(len(r_color)):
+                        new_color += '%x' % int(r_color[idx_c] * 255)
                     # do it until a valid color is generated
                     # do it until a valid color is generated
                     # a valid color has the # symbol, another 6 chars for the color and the last 2 chars for alpha
                     # a valid color has the # symbol, another 6 chars for the color and the last 2 chars for alpha
                     # for a total of 9 chars
                     # for a total of 9 chars
@@ -1278,21 +1275,21 @@ class ExcellonObject(FlatCAMObj, Excellon):
             for option in exc.options:
             for option in exc.options:
                 if option != 'name':
                 if option != 'name':
                     try:
                     try:
-                        exc_final.options[option] = exc.options[option]
+                        exc_final.options[option] = deepcopy(exc.options[option])
                     except Exception:
                     except Exception:
                         exc.app.log.warning("Failed to copy option.", option)
                         exc.app.log.warning("Failed to copy option.", option)
 
 
             for tool in exc.tools:
             for tool in exc.tools:
                 toolid += 1
                 toolid += 1
-                new_tools[toolid] = exc.tools[tool]
+                new_tools[toolid] = deepcopy(exc.tools[tool])
 
 
             exc_final.tools = deepcopy(new_tools)
             exc_final.tools = deepcopy(new_tools)
             # add the zeros and units to the exc_final object
             # add the zeros and units to the exc_final object
-            exc_final.zeros = exc.zeros
-            exc_final.units = exc.units
+            exc_final.zeros = deepcopy(exc.zeros)
+            exc_final.units = deepcopy(exc.units)
             total_geo += exc.solid_geometry
             total_geo += exc.solid_geometry
 
 
-        exc_final.solid_geometry = total_geo
+        exc_final.solid_geometry = deepcopy(total_geo)
 
 
         fused_tools_dict = {}
         fused_tools_dict = {}
         if exc_final.tools and fuse_tools:
         if exc_final.tools and fuse_tools:

+ 362 - 108
appObjects/FlatCAMGeometry.py

@@ -10,10 +10,10 @@
 # File modified by: Marius Stanciu                         #
 # File modified by: Marius Stanciu                         #
 # ##########################################################
 # ##########################################################
 
 
-from shapely.geometry import Polygon, MultiPolygon, MultiLineString, LineString, LinearRing
+from shapely.geometry import MultiLineString, LineString, LinearRing, box
 import shapely.affinity as affinity
 import shapely.affinity as affinity
 
 
-from camlib import Geometry
+from camlib import Geometry, grace
 
 
 from appObjects.FlatCAMObj import *
 from appObjects.FlatCAMObj import *
 
 
@@ -25,6 +25,8 @@ import traceback
 from collections import defaultdict
 from collections import defaultdict
 from functools import reduce
 from functools import reduce
 
 
+import simplejson as json
+
 import gettext
 import gettext
 import appTranslation as fcTranslate
 import appTranslation as fcTranslate
 import builtins
 import builtins
@@ -41,6 +43,7 @@ class GeometryObject(FlatCAMObj, Geometry):
     """
     """
     optionChanged = QtCore.pyqtSignal(str)
     optionChanged = QtCore.pyqtSignal(str)
     builduiSig = QtCore.pyqtSignal()
     builduiSig = QtCore.pyqtSignal()
+    launch_job = QtCore.pyqtSignal()
 
 
     ui_type = GeometryObjectUI
     ui_type = GeometryObjectUI
 
 
@@ -125,7 +128,7 @@ class GeometryObject(FlatCAMObj, Geometry):
         self.sel_tools = {}
         self.sel_tools = {}
 
 
         self.offset_item_options = ["Path", "In", "Out", "Custom"]
         self.offset_item_options = ["Path", "In", "Out", "Custom"]
-        self.type_item_options = [_("Iso"), _("Rough"), _("Finish")]
+        self.type_item_options = ['Iso', 'Rough', 'Finish']
         self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
         self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
 
 
         # flag to store if the V-Shape tool is selected in self.ui.geo_tools_table
         # flag to store if the V-Shape tool is selected in self.ui.geo_tools_table
@@ -200,27 +203,39 @@ class GeometryObject(FlatCAMObj, Geometry):
             self.ui.geo_tools_table.setItem(row_idx, 1, dia_item)  # Diameter
             self.ui.geo_tools_table.setItem(row_idx, 1, dia_item)  # Diameter
 
 
             # -------------------- OFFSET   ------------------------------------- #
             # -------------------- OFFSET   ------------------------------------- #
-            offset_item = FCComboBox()
+            offset_item = FCComboBox(policy=False)
             for item in self.offset_item_options:
             for item in self.offset_item_options:
                 offset_item.addItem(item)
                 offset_item.addItem(item)
             idx = offset_item.findText(tooluid_value['offset'])
             idx = offset_item.findText(tooluid_value['offset'])
-            offset_item.setCurrentIndex(idx)
+            # protection against having this translated or loading a project with translated values
+            if idx == -1:
+                offset_item.setCurrentIndex(0)
+            else:
+                offset_item.setCurrentIndex(idx)
             self.ui.geo_tools_table.setCellWidget(row_idx, 2, offset_item)
             self.ui.geo_tools_table.setCellWidget(row_idx, 2, offset_item)
 
 
             # -------------------- TYPE     ------------------------------------- #
             # -------------------- TYPE     ------------------------------------- #
-            type_item = FCComboBox()
+            type_item = FCComboBox(policy=False)
             for item in self.type_item_options:
             for item in self.type_item_options:
                 type_item.addItem(item)
                 type_item.addItem(item)
             idx = type_item.findText(tooluid_value['type'])
             idx = type_item.findText(tooluid_value['type'])
-            type_item.setCurrentIndex(idx)
+            # protection against having this translated or loading a project with translated values
+            if idx == -1:
+                type_item.setCurrentIndex(0)
+            else:
+                type_item.setCurrentIndex(idx)
             self.ui.geo_tools_table.setCellWidget(row_idx, 3, type_item)
             self.ui.geo_tools_table.setCellWidget(row_idx, 3, type_item)
 
 
             # -------------------- TOOL TYPE ------------------------------------- #
             # -------------------- TOOL TYPE ------------------------------------- #
-            tool_type_item = FCComboBox()
+            tool_type_item = FCComboBox(policy=False)
             for item in self.tool_type_item_options:
             for item in self.tool_type_item_options:
                 tool_type_item.addItem(item)
                 tool_type_item.addItem(item)
             idx = tool_type_item.findText(tooluid_value['tool_type'])
             idx = tool_type_item.findText(tooluid_value['tool_type'])
-            tool_type_item.setCurrentIndex(idx)
+            # protection against having this translated or loading a project with translated values
+            if idx == -1:
+                tool_type_item.setCurrentIndex(0)
+            else:
+                tool_type_item.setCurrentIndex(idx)
             self.ui.geo_tools_table.setCellWidget(row_idx, 4, tool_type_item)
             self.ui.geo_tools_table.setCellWidget(row_idx, 4, tool_type_item)
 
 
             # -------------------- TOOL UID   ------------------------------------- #
             # -------------------- TOOL UID   ------------------------------------- #
@@ -442,6 +457,8 @@ class GeometryObject(FlatCAMObj, Geometry):
             "polish": self.ui.polish_cb,
             "polish": self.ui.polish_cb,
             "polish_dia": self.ui.polish_dia_entry,
             "polish_dia": self.ui.polish_dia_entry,
             "polish_pressure": self.ui.polish_pressure_entry,
             "polish_pressure": self.ui.polish_pressure_entry,
+            "polish_travelz": self.ui.polish_travelz_entry,
+            "polish_margin": self.ui.polish_margin_entry,
             "polish_overlap": self.ui.polish_over_entry,
             "polish_overlap": self.ui.polish_over_entry,
             "polish_method": self.ui.polish_method_combo,
             "polish_method": self.ui.polish_method_combo,
         })
         })
@@ -514,10 +531,10 @@ class GeometryObject(FlatCAMObj, Geometry):
                 new_data = deepcopy(self.default_data)
                 new_data = deepcopy(self.default_data)
                 self.tools.update({
                 self.tools.update({
                     self.tooluid: {
                     self.tooluid: {
-                        'tooldia': float('%.*f' % (self.decimals, float(toold))),
+                        'tooldia': self.app.dec_format(float(toold), self.decimals),
                         'offset': 'Path',
                         'offset': 'Path',
                         'offset_value': 0.0,
                         'offset_value': 0.0,
-                        'type': _('Rough'),
+                        'type': 'Rough',
                         'tool_type': self.tool_type,
                         'tool_type': self.tool_type,
                         'data': new_data,
                         'data': new_data,
                         'solid_geometry': self.solid_geometry
                         'solid_geometry': self.solid_geometry
@@ -552,14 +569,14 @@ class GeometryObject(FlatCAMObj, Geometry):
 
 
         self.ui.geo_tools_table.setupContextMenu()
         self.ui.geo_tools_table.setupContextMenu()
         self.ui.geo_tools_table.addContextMenu(
         self.ui.geo_tools_table.addContextMenu(
-            _("Add from Tool DB"), self.on_tool_add_from_db_clicked,
+            _("Pick from DB"), self.on_tool_add_from_db_clicked,
             icon=QtGui.QIcon(self.app.resource_location + "/plus16.png"))
             icon=QtGui.QIcon(self.app.resource_location + "/plus16.png"))
         self.ui.geo_tools_table.addContextMenu(
         self.ui.geo_tools_table.addContextMenu(
             _("Copy"), self.on_tool_copy,
             _("Copy"), self.on_tool_copy,
             icon=QtGui.QIcon(self.app.resource_location + "/copy16.png"))
             icon=QtGui.QIcon(self.app.resource_location + "/copy16.png"))
         self.ui.geo_tools_table.addContextMenu(
         self.ui.geo_tools_table.addContextMenu(
             _("Delete"), lambda: self.on_tool_delete(all_tools=None),
             _("Delete"), lambda: self.on_tool_delete(all_tools=None),
-            icon=QtGui.QIcon(self.app.resource_location + "/delete32.png"))
+            icon=QtGui.QIcon(self.app.resource_location + "/trash16.png"))
 
 
         # Show/Hide Advanced Options
         # Show/Hide Advanced Options
         if self.app.defaults["global_app_level"] == 'b':
         if self.app.defaults["global_app_level"] == 'b':
@@ -570,8 +587,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             # self.ui.geo_tools_table.setColumnHidden(4, True)
             # self.ui.geo_tools_table.setColumnHidden(4, True)
             self.ui.addtool_entry_lbl.hide()
             self.ui.addtool_entry_lbl.hide()
             self.ui.addtool_entry.hide()
             self.ui.addtool_entry.hide()
-            self.ui.addtool_btn.hide()
-            self.ui.copytool_btn.hide()
+            self.ui.search_and_add_btn.hide()
             self.ui.deltool_btn.hide()
             self.ui.deltool_btn.hide()
             # self.ui.endz_label.hide()
             # self.ui.endz_label.hide()
             # self.ui.endz_entry.hide()
             # self.ui.endz_entry.hide()
@@ -625,6 +641,8 @@ class GeometryObject(FlatCAMObj, Geometry):
 
 
         self.ui.geo_tools_table.drag_drop_sig.connect(self.rebuild_ui)
         self.ui.geo_tools_table.drag_drop_sig.connect(self.rebuild_ui)
 
 
+        self.launch_job.connect(self.mtool_gen_cncjob)
+
     def on_properties(self, state):
     def on_properties(self, state):
         if state:
         if state:
             self.ui.properties_frame.show()
             self.ui.properties_frame.show()
@@ -729,11 +747,9 @@ class GeometryObject(FlatCAMObj, Geometry):
                 self.ui.geo_tools_table.cellWidget(row, col).currentIndexChanged.connect(
                 self.ui.geo_tools_table.cellWidget(row, col).currentIndexChanged.connect(
                     self.on_tooltable_cellwidget_change)
                     self.on_tooltable_cellwidget_change)
 
 
-        # I use lambda's because the connected functions have parameters that could be used in certain scenarios
-        self.ui.addtool_btn.clicked.connect(lambda: self.on_tool_add())
+        self.ui.search_and_add_btn.clicked.connect(self.on_tool_add)
 
 
-        self.ui.copytool_btn.clicked.connect(lambda: self.on_tool_copy())
-        self.ui.deltool_btn.clicked.connect(lambda: self.on_tool_delete())
+        self.ui.deltool_btn.clicked.connect(self.on_tool_delete)
 
 
         self.ui.geo_tools_table.clicked.connect(self.on_row_selection_change)
         self.ui.geo_tools_table.clicked.connect(self.on_row_selection_change)
         self.ui.geo_tools_table.horizontalHeader().sectionClicked.connect(self.on_toggle_all_rows)
         self.ui.geo_tools_table.horizontalHeader().sectionClicked.connect(self.on_toggle_all_rows)
@@ -793,12 +809,7 @@ class GeometryObject(FlatCAMObj, Geometry):
                     pass
                     pass
 
 
         try:
         try:
-            self.ui.addtool_btn.clicked.disconnect()
-        except (TypeError, AttributeError):
-            pass
-
-        try:
-            self.ui.copytool_btn.clicked.disconnect()
+            self.ui.search_and_add_btn.clicked.disconnect()
         except (TypeError, AttributeError):
         except (TypeError, AttributeError):
             pass
             pass
 
 
@@ -1004,19 +1015,156 @@ class GeometryObject(FlatCAMObj, Geometry):
 
 
         self.ui_connect()
         self.ui_connect()
 
 
-    def on_tool_add(self, dia=None, new_geo=None):
+    def on_tool_add(self, clicked_state, dia=None, new_geo=None):
+        log.debug("GeometryObject.on_add_tool()")
+
         self.ui_disconnect()
         self.ui_disconnect()
 
 
-        self.units = self.app.defaults['units'].upper()
+        filename = self.app.tools_database_path()
+
+        tool_dia = dia if dia is not None else self.ui.addtool_entry.get_value()
+
+        # construct a list of all 'tooluid' in the self.iso_tools
+        tool_uid_list = [int(tooluid_key) for tooluid_key in self.tools]
+
+        # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
+        max_uid = 0 if not tool_uid_list else max(tool_uid_list)
+        tooluid = int(max_uid) + 1
+
+        new_tools_dict = deepcopy(self.default_data)
+        updated_tooldia = None
+
+        # determine the new tool diameter
+        if tool_dia is None or tool_dia == 0:
+            self.build_ui()
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, "
+                                                          "in Float format."))
+            self.ui_connect()
+            return
+        truncated_tooldia = self.app.dec_format(tool_dia, self.decimals)
+
+        # load the database tools from the file
+        try:
+            with open(filename) as f:
+                tools = f.read()
+        except IOError:
+            self.app.log.error("Could not load tools DB file.")
+            self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
+            self.ui_connect()
+            self.on_tool_default_add(dia=tool_dia)
+            return
+
+        try:
+            # store here the tools from Tools Database when searching in Tools Database
+            tools_db_dict = json.loads(tools)
+        except Exception:
+            e = sys.exc_info()[0]
+            self.app.log.error(str(e))
+            self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
+            self.ui_connect()
+            self.on_tool_default_add(dia=tool_dia)
+            return
+
+        tool_found = 0
+
+        offset = 'Path'
+        offset_val = 0.0
+        typ = 'Rough'
+        tool_type = 'C1'
+        # look in database tools
+        for db_tool, db_tool_val in tools_db_dict.items():
+            offset = db_tool_val['offset']
+            offset_val = db_tool_val['offset_value']
+            typ = db_tool_val['type']
+            tool_type = db_tool_val['tool_type']
+
+            db_tooldia = db_tool_val['tooldia']
+            low_limit = float(db_tool_val['data']['tol_min'])
+            high_limit = float(db_tool_val['data']['tol_max'])
+
+            # we need only tool marked for Milling Tool (Geometry Object)
+            if db_tool_val['data']['tool_target'] != 1:     # _('Milling')
+                continue
+
+            # if we find a tool with the same diameter in the Tools DB just update it's data
+            if truncated_tooldia == db_tooldia:
+                tool_found += 1
+                for d in db_tool_val['data']:
+                    if d.find('tools_mill_') == 0:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+                    elif d.find('tools_') == 0:
+                        # don't need data for other App Tools; this tests after 'tools_mill_'
+                        continue
+                    else:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+            # search for a tool that has a tolerance that the tool fits in
+            elif high_limit >= truncated_tooldia >= low_limit:
+                tool_found += 1
+                updated_tooldia = db_tooldia
+                for d in db_tool_val['data']:
+                    if d.find('tools_iso') == 0:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+                    elif d.find('tools_') == 0:
+                        # don't need data for other App Tools; this tests after 'tools_drill_'
+                        continue
+                    else:
+                        new_tools_dict[d] = db_tool_val['data'][d]
+
+        # test we found a suitable tool in Tools Database or if multiple ones
+        if tool_found == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Tool not in Tools Database. Adding a default tool."))
+            self.on_tool_default_add(dia=tool_dia, new_geo=new_geo)
+            self.ui_connect()
+            return
+
+        if tool_found > 1:
+            self.app.inform.emit(
+                '[WARNING_NOTCL] %s' % _("Cancelled.\n"
+                                         "Multiple tools for one tool diameter found in Tools Database."))
+            self.ui_connect()
+            return
+
+        new_tdia = deepcopy(updated_tooldia) if updated_tooldia is not None else deepcopy(truncated_tooldia)
+        self.tools.update({
+            tooluid: {
+                'tooldia': new_tdia,
+                'offset': deepcopy(offset),
+                'offset_value': deepcopy(offset_val),
+                'type': deepcopy(typ),
+                'tool_type': deepcopy(tool_type),
+                'data': deepcopy(new_tools_dict),
+                'solid_geometry': self.solid_geometry
+            }
+        })
+        self.ui_connect()
+        self.build_ui()
+
+        # select the tool just added
+        for row in range(self.ui.geo_tools_table.rowCount()):
+            if int(self.ui.geo_tools_table.item(row, 5).text()) == tooluid:
+                self.ui.geo_tools_table.selectRow(row)
+                break
+
+        # update the UI form
+        self.update_ui()
+
+        # if there is at least one tool left in the Tools Table, enable the parameters GUI
+        if self.ui.geo_tools_table.rowCount() != 0:
+            self.ui.geo_param_frame.setDisabled(False)
+
+        self.app.inform.emit('[success] %s' % _("New tool added to Tool Table from Tools Database."))
+
+    def on_tool_default_add(self, dia=None, new_geo=None, muted=None):
+        self.ui_disconnect()
 
 
-        tooldia = dia if dia is not None else float(self.ui.addtool_entry.get_value())
+        tooldia = dia if dia is not None else self.ui.addtool_entry.get_value()
         tool_uid_list = [int(tooluid_key) for tooluid_key in self.tools]
         tool_uid_list = [int(tooluid_key) for tooluid_key in self.tools]
 
 
         # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
         # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
         max_uid = max(tool_uid_list) if tool_uid_list else 0
         max_uid = max(tool_uid_list) if tool_uid_list else 0
-        self.tooluid = max_uid + 1
+        self.tooluid = int(max_uid) + 1
 
 
-        tooldia = float('%.*f' % (self.decimals, tooldia))
+        tooldia = self.app.dec_format(tooldia, self.decimals)
 
 
         # here we actually add the new tool; if there is no tool in the tool table we add a tool with default data
         # here we actually add the new tool; if there is no tool in the tool table we add a tool with default data
         # otherwise we add a tool with data copied from last tool
         # otherwise we add a tool with data copied from last tool
@@ -1051,7 +1199,7 @@ class GeometryObject(FlatCAMObj, Geometry):
                     'tooldia': tooldia,
                     'tooldia': tooldia,
                     'offset': 'Path',
                     'offset': 'Path',
                     'offset_value': 0.0,
                     'offset_value': 0.0,
-                    'type': _('Rough'),
+                    'type': 'Rough',
                     'tool_type': 'C1',
                     'tool_type': 'C1',
                     'data': deepcopy(self.default_data),
                     'data': deepcopy(self.default_data),
                     'solid_geometry': self.solid_geometry
                     'solid_geometry': self.solid_geometry
@@ -1070,7 +1218,8 @@ class GeometryObject(FlatCAMObj, Geometry):
             pass
             pass
         self.ser_attrs.append('tools')
         self.ser_attrs.append('tools')
 
 
-        self.app.inform.emit('[success] %s' % _("Tool added in Tool Table."))
+        if muted is None:
+            self.app.inform.emit('[success] %s' % _("Tool added in Tool Table."))
         self.ui_connect()
         self.ui_connect()
         self.build_ui()
         self.build_ui()
 
 
@@ -1090,7 +1239,9 @@ class GeometryObject(FlatCAMObj, Geometry):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 break
                 break
-        self.app.on_tools_database()
+        ret_val = self.app.on_tools_database()
+        if ret_val == 'fail':
+            return
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
@@ -1251,7 +1402,11 @@ class GeometryObject(FlatCAMObj, Geometry):
         self.ui_connect()
         self.ui_connect()
         self.builduiSig.emit()
         self.builduiSig.emit()
 
 
-    def on_tool_delete(self, all_tools=None):
+    def on_tool_delete(self, clicked_signal, all_tools=None):
+        """
+        It's important to keep the not clicked_signal parameter otherwise the signal will go to the all_tools
+        parameter and I might get all the tool deleted
+        """
         self.ui_disconnect()
         self.ui_disconnect()
 
 
         if all_tools is None:
         if all_tools is None:
@@ -1432,8 +1587,8 @@ class GeometryObject(FlatCAMObj, Geometry):
                 elif cw_col == 3:
                 elif cw_col == 3:
                     # force toolpath type as 'Iso' if the tool type is V-Shape
                     # force toolpath type as 'Iso' if the tool type is V-Shape
                     if self.ui.geo_tools_table.cellWidget(cw_row, 4).currentText() == 'V':
                     if self.ui.geo_tools_table.cellWidget(cw_row, 4).currentText() == 'V':
-                        tooluid_value['type'] = _('Iso')
-                        idx = self.ui.geo_tools_table.cellWidget(cw_row, 3).findText(_('Iso'))
+                        tooluid_value['type'] = 'Iso'
+                        idx = self.ui.geo_tools_table.cellWidget(cw_row, 3).findText('Iso')
                         self.ui.geo_tools_table.cellWidget(cw_row, 3).setCurrentIndex(idx)
                         self.ui.geo_tools_table.cellWidget(cw_row, 3).setCurrentIndex(idx)
                     else:
                     else:
                         tooluid_value['type'] = cb_txt
                         tooluid_value['type'] = cb_txt
@@ -1442,7 +1597,7 @@ class GeometryObject(FlatCAMObj, Geometry):
 
 
                     # if the tool_type selected is V-Shape then autoselect the toolpath type as Iso
                     # if the tool_type selected is V-Shape then autoselect the toolpath type as Iso
                     if cb_txt == 'V':
                     if cb_txt == 'V':
-                        idx = self.ui.geo_tools_table.cellWidget(cw_row, 3).findText(_('Iso'))
+                        idx = self.ui.geo_tools_table.cellWidget(cw_row, 3).findText('Iso')
                         self.ui.geo_tools_table.cellWidget(cw_row, 3).setCurrentIndex(idx)
                         self.ui.geo_tools_table.cellWidget(cw_row, 3).setCurrentIndex(idx)
                     else:
                     else:
                         self.ui.cutz_entry.set_value(self.old_cutz)
                         self.ui.cutz_entry.set_value(self.old_cutz)
@@ -1833,7 +1988,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             except AttributeError:
             except AttributeError:
                 pass
                 pass
 
 
-    def on_generatecnc_button_click(self, *args):
+    def on_generatecnc_button_click(self):
         log.debug("Generating CNCJob from Geometry ...")
         log.debug("Generating CNCJob from Geometry ...")
         self.app.defaults.report_usage("geometry_on_generatecnc_button")
         self.app.defaults.report_usage("geometry_on_generatecnc_button")
 
 
@@ -1846,7 +2001,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             if self.special_group:
             if self.special_group:
                 self.app.inform.emit(
                 self.app.inform.emit(
                     '[WARNING_NOTCL] %s %s %s.' %
                     '[WARNING_NOTCL] %s %s %s.' %
-                    (_("This Geometry can't be processed because it is"), str(self.special_group), _("geometry"))
+                    (_("This Geometry can't be processed because it is"), str(self.special_group), _("Geometry"))
                 )
                 )
                 return
                 return
         except AttributeError:
         except AttributeError:
@@ -1862,7 +2017,11 @@ class GeometryObject(FlatCAMObj, Geometry):
                         self.sel_tools.update({
                         self.sel_tools.update({
                             tooluid: deepcopy(tooluid_value)
                             tooluid: deepcopy(tooluid_value)
                         })
                         })
-            self.mtool_gen_cncjob()
+
+            if self.ui.polish_cb.get_value():
+                self.on_polish()
+            else:
+                self.mtool_gen_cncjob()
             self.ui.geo_tools_table.clearSelection()
             self.ui.geo_tools_table.clearSelection()
 
 
         elif self.ui.geo_tools_table.rowCount() == 1:
         elif self.ui.geo_tools_table.rowCount() == 1:
@@ -1873,9 +2032,11 @@ class GeometryObject(FlatCAMObj, Geometry):
                     self.sel_tools.update({
                     self.sel_tools.update({
                         tooluid: deepcopy(tooluid_value)
                         tooluid: deepcopy(tooluid_value)
                     })
                     })
-            self.mtool_gen_cncjob()
+            if self.ui.polish_cb.get_value():
+                self.on_polish()
+            else:
+                self.mtool_gen_cncjob()
             self.ui.geo_tools_table.clearSelection()
             self.ui.geo_tools_table.clearSelection()
-
         else:
         else:
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed. No tool selected in the tool table ..."))
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed. No tool selected in the tool table ..."))
 
 
@@ -1955,7 +2116,7 @@ class GeometryObject(FlatCAMObj, Geometry):
                 tool_cnt += 1
                 tool_cnt += 1
 
 
                 dia_cnc_dict = deepcopy(tools_dict[tooluid_key])
                 dia_cnc_dict = deepcopy(tools_dict[tooluid_key])
-                tooldia_val = float('%.*f' % (self.decimals, float(tools_dict[tooluid_key]['tooldia'])))
+                tooldia_val = app_obj.dec_format(float(tools_dict[tooluid_key]['tooldia']), self.decimals)
                 dia_cnc_dict.update({
                 dia_cnc_dict.update({
                     'tooldia': tooldia_val
                     'tooldia': tooldia_val
                 })
                 })
@@ -1972,12 +2133,12 @@ class GeometryObject(FlatCAMObj, Geometry):
                         try:
                         try:
                             offset_value = float(self.ui.tool_offset_entry.get_value().replace(',', '.'))
                             offset_value = float(self.ui.tool_offset_entry.get_value().replace(',', '.'))
                         except ValueError:
                         except ValueError:
-                            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number."))
+                            app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number."))
                             return
                             return
                     if offset_value:
                     if offset_value:
                         tool_offset = float(offset_value)
                         tool_offset = float(offset_value)
                     else:
                     else:
-                        self.app.inform.emit(
+                        app_obj.inform.emit(
                             '[WARNING] %s' % _("Tool Offset is selected in Tool Table but no value is provided.\n"
                             '[WARNING] %s' % _("Tool Offset is selected in Tool Table but no value is provided.\n"
                                                "Add a Tool Offset or change the Offset Type.")
                                                "Add a Tool Offset or change the Offset Type.")
                         )
                         )
@@ -2020,10 +2181,13 @@ class GeometryObject(FlatCAMObj, Geometry):
                 job_obj.options['type'] = 'Geometry'
                 job_obj.options['type'] = 'Geometry'
                 job_obj.options['tool_dia'] = tooldia_val
                 job_obj.options['tool_dia'] = tooldia_val
 
 
+                tool_lst = list(tools_dict.keys())
+                is_first = True if tooluid_key == tool_lst[0] else False
+
                 # it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
                 # it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
                 # to a value of 0.0005 which is 20 times less than 0.01
                 # to a value of 0.0005 which is 20 times less than 0.01
                 tol = float(self.app.defaults['global_tolerance']) / 20
                 tol = float(self.app.defaults['global_tolerance']) / 20
-                res = job_obj.generate_from_geometry_2(
+                res, start_gcode = job_obj.generate_from_geometry_2(
                     self, tooldia=tooldia_val, offset=tool_offset, tolerance=tol,
                     self, tooldia=tooldia_val, offset=tool_offset, tolerance=tol,
                     z_cut=z_cut, z_move=z_move,
                     z_cut=z_cut, z_move=z_move,
                     feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
                     feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid,
@@ -2032,13 +2196,15 @@ class GeometryObject(FlatCAMObj, Geometry):
                     extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz, endxy=endxy,
                     extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz, endxy=endxy,
                     toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy,
                     toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy,
                     pp_geometry_name=pp_geometry_name,
                     pp_geometry_name=pp_geometry_name,
-                    tool_no=tool_cnt)
+                    tool_no=tool_cnt, is_first=is_first)
 
 
                 if res == 'fail':
                 if res == 'fail':
                     log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed")
                     log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed")
                     return 'fail'
                     return 'fail'
-                else:
-                    dia_cnc_dict['gcode'] = res
+
+                dia_cnc_dict['gcode'] = res
+                if start_gcode != '':
+                    job_obj.gc_start = start_gcode
 
 
                 total_gcode += res
                 total_gcode += res
 
 
@@ -2048,24 +2214,23 @@ class GeometryObject(FlatCAMObj, Geometry):
 
 
                 self.app.inform.emit('[success] %s' % _("G-Code parsing in progress..."))
                 self.app.inform.emit('[success] %s' % _("G-Code parsing in progress..."))
                 dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
                 dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
-                self.app.inform.emit('[success] %s' % _("G-Code parsing finished..."))
+                app_obj.inform.emit('[success] %s' % _("G-Code parsing finished..."))
 
 
-                # TODO this serve for bounding box creation only; should be optimized
                 # commented this; there is no need for the actual GCode geometry - the original one will serve as well
                 # commented this; there is no need for the actual GCode geometry - the original one will serve as well
                 # for bounding box values
                 # for bounding box values
                 # dia_cnc_dict['solid_geometry'] = unary_union([geo['geom'] for geo in dia_cnc_dict['gcode_parsed']])
                 # dia_cnc_dict['solid_geometry'] = unary_union([geo['geom'] for geo in dia_cnc_dict['gcode_parsed']])
                 try:
                 try:
                     dia_cnc_dict['solid_geometry'] = tool_solid_geometry
                     dia_cnc_dict['solid_geometry'] = tool_solid_geometry
-                    self.app.inform.emit('[success] %s...' % _("Finished G-Code processing"))
+                    app_obj.inform.emit('[success] %s...' % _("Finished G-Code processing"))
                 except Exception as er:
                 except Exception as er:
-                    self.app.inform.emit('[ERROR] %s: %s' % (_("G-Code processing failed with error"), str(er)))
+                    app_obj.inform.emit('[ERROR] %s: %s' % (_("G-Code processing failed with error"), str(er)))
 
 
                 job_obj.cnc_tools.update({
                 job_obj.cnc_tools.update({
                     tooluid_key: deepcopy(dia_cnc_dict)
                     tooluid_key: deepcopy(dia_cnc_dict)
                 })
                 })
                 dia_cnc_dict.clear()
                 dia_cnc_dict.clear()
 
 
-            job_obj.source_file = total_gcode
+            job_obj.source_file = job_obj.gc_start + total_gcode
 
 
         # Object initialization function for app.app_obj.new_object()
         # Object initialization function for app.app_obj.new_object()
         # RUNNING ON SEPARATE THREAD!
         # RUNNING ON SEPARATE THREAD!
@@ -2102,15 +2267,14 @@ class GeometryObject(FlatCAMObj, Geometry):
                     if self.tools[tooluid_key]['solid_geometry'] is None:
                     if self.tools[tooluid_key]['solid_geometry'] is None:
                         a += 1
                         a += 1
                 if a == len(self.tools):
                 if a == len(self.tools):
-                    self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry'))
+                    app_obj.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry'))
                     return 'fail'
                     return 'fail'
 
 
             total_gcode = ''
             total_gcode = ''
             for tooluid_key in list(tools_dict.keys()):
             for tooluid_key in list(tools_dict.keys()):
                 tool_cnt += 1
                 tool_cnt += 1
                 dia_cnc_dict = deepcopy(tools_dict[tooluid_key])
                 dia_cnc_dict = deepcopy(tools_dict[tooluid_key])
-                tooldia_val = float('%.*f' % (self.decimals, float(tools_dict[tooluid_key]['tooldia'])))
-
+                tooldia_val = app_obj.dec_format(float(tools_dict[tooluid_key]['tooldia']), self.decimals)
                 dia_cnc_dict.update({
                 dia_cnc_dict.update({
                     'tooldia': tooldia_val
                     'tooldia': tooldia_val
                 })
                 })
@@ -2123,7 +2287,7 @@ class GeometryObject(FlatCAMObj, Geometry):
                 #         current_uid = int(k)
                 #         current_uid = int(k)
                 #         break
                 #         break
 
 
-                if dia_cnc_dict['offset'] == 'in':
+                if dia_cnc_dict['offset'].lower() == 'in':
                     tool_offset = -tooldia_val / 2
                     tool_offset = -tooldia_val / 2
                 elif dia_cnc_dict['offset'].lower() == 'out':
                 elif dia_cnc_dict['offset'].lower() == 'out':
                     tool_offset = tooldia_val / 2
                     tool_offset = tooldia_val / 2
@@ -2144,27 +2308,27 @@ class GeometryObject(FlatCAMObj, Geometry):
                     'offset_value': tool_offset
                     'offset_value': tool_offset
                 })
                 })
 
 
-                z_cut = tools_dict[tooluid_key]['data']["cutz"]
-                z_move = tools_dict[tooluid_key]['data']["travelz"]
-                feedrate = tools_dict[tooluid_key]['data']["feedrate"]
-                feedrate_z = tools_dict[tooluid_key]['data']["feedrate_z"]
-                feedrate_rapid = tools_dict[tooluid_key]['data']["feedrate_rapid"]
-                multidepth = tools_dict[tooluid_key]['data']["multidepth"]
-                extracut = tools_dict[tooluid_key]['data']["extracut"]
-                extracut_length = tools_dict[tooluid_key]['data']["extracut_length"]
-                depthpercut = tools_dict[tooluid_key]['data']["depthperpass"]
-                toolchange = tools_dict[tooluid_key]['data']["toolchange"]
-                toolchangez = tools_dict[tooluid_key]['data']["toolchangez"]
-                toolchangexy = tools_dict[tooluid_key]['data']["toolchangexy"]
-                startz = tools_dict[tooluid_key]['data']["startz"]
-                endz = tools_dict[tooluid_key]['data']["endz"]
-                endxy = self.options["endxy"]
-                spindlespeed = tools_dict[tooluid_key]['data']["spindlespeed"]
-                dwell = tools_dict[tooluid_key]['data']["dwell"]
-                dwelltime = tools_dict[tooluid_key]['data']["dwelltime"]
-                pp_geometry_name = tools_dict[tooluid_key]['data']["ppname_g"]
-
-                spindledir = self.app.defaults['geometry_spindledir']
+                # z_cut = tools_dict[tooluid_key]['data']["cutz"]
+                # z_move = tools_dict[tooluid_key]['data']["travelz"]
+                # feedrate = tools_dict[tooluid_key]['data']["feedrate"]
+                # feedrate_z = tools_dict[tooluid_key]['data']["feedrate_z"]
+                # feedrate_rapid = tools_dict[tooluid_key]['data']["feedrate_rapid"]
+                # multidepth = tools_dict[tooluid_key]['data']["multidepth"]
+                # extracut = tools_dict[tooluid_key]['data']["extracut"]
+                # extracut_length = tools_dict[tooluid_key]['data']["extracut_length"]
+                # depthpercut = tools_dict[tooluid_key]['data']["depthperpass"]
+                # toolchange = tools_dict[tooluid_key]['data']["toolchange"]
+                # toolchangez = tools_dict[tooluid_key]['data']["toolchangez"]
+                # toolchangexy = tools_dict[tooluid_key]['data']["toolchangexy"]
+                # startz = tools_dict[tooluid_key]['data']["startz"]
+                # endz = tools_dict[tooluid_key]['data']["endz"]
+                # endxy = self.options["endxy"]
+                # spindlespeed = tools_dict[tooluid_key]['data']["spindlespeed"]
+                # dwell = tools_dict[tooluid_key]['data']["dwell"]
+                # dwelltime = tools_dict[tooluid_key]['data']["dwelltime"]
+                # pp_geometry_name = tools_dict[tooluid_key]['data']["ppname_g"]
+                #
+                # spindledir = self.app.defaults['geometry_spindledir']
                 tool_solid_geometry = self.tools[tooluid_key]['solid_geometry']
                 tool_solid_geometry = self.tools[tooluid_key]['solid_geometry']
 
 
                 job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"]
                 job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"]
@@ -2196,11 +2360,10 @@ class GeometryObject(FlatCAMObj, Geometry):
                 if start_gcode != '':
                 if start_gcode != '':
                     job_obj.gc_start = start_gcode
                     job_obj.gc_start = start_gcode
 
 
-                self.app.inform.emit('[success] %s' % _("G-Code parsing in progress..."))
+                app_obj.inform.emit('[success] %s' % _("G-Code parsing in progress..."))
                 dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
                 dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse()
-                self.app.inform.emit('[success] %s' % _("G-Code parsing finished..."))
+                app_obj.inform.emit('[success] %s' % _("G-Code parsing finished..."))
 
 
-                # TODO this serve for bounding box creation only; should be optimized
                 # commented this; there is no need for the actual GCode geometry - the original one will serve as well
                 # commented this; there is no need for the actual GCode geometry - the original one will serve as well
                 # for bounding box values
                 # for bounding box values
                 # geo_for_bound_values = unary_union([
                 # geo_for_bound_values = unary_union([
@@ -2208,9 +2371,9 @@ class GeometryObject(FlatCAMObj, Geometry):
                 # ])
                 # ])
                 try:
                 try:
                     dia_cnc_dict['solid_geometry'] = deepcopy(tool_solid_geometry)
                     dia_cnc_dict['solid_geometry'] = deepcopy(tool_solid_geometry)
-                    self.app.inform.emit('[success] %s' % _("Finished G-Code processing..."))
+                    app_obj.inform.emit('[success] %s...' % _("Finished G-Code processing"))
                 except Exception as ee:
                 except Exception as ee:
-                    self.app.inform.emit('[ERROR] %s: %s' % (_("G-Code processing failed with error"), str(ee)))
+                    app_obj.inform.emit('[ERROR] %s: %s' % (_("G-Code processing failed with error"), str(ee)))
 
 
                 # tell gcode_parse from which point to start drawing the lines depending on what kind of
                 # tell gcode_parse from which point to start drawing the lines depending on what kind of
                 # object is the source of gcode
                 # object is the source of gcode
@@ -2228,11 +2391,13 @@ class GeometryObject(FlatCAMObj, Geometry):
             def job_thread(a_obj):
             def job_thread(a_obj):
                 if self.multigeo is False:
                 if self.multigeo is False:
                     with self.app.proc_container.new(_("Generating CNC Code")):
                     with self.app.proc_container.new(_("Generating CNC Code")):
-                        if a_obj.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) != 'fail':
+                        ret_val = a_obj.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot)
+                        if ret_val != 'fail':
                             a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
                             a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
                 else:
                 else:
                     with self.app.proc_container.new(_("Generating CNC Code")):
                     with self.app.proc_container.new(_("Generating CNC Code")):
-                        if a_obj.app_obj.new_object("cncjob", outname, job_init_multi_geometry) != 'fail':
+                        ret_val = a_obj.app_obj.new_object("cncjob", outname, job_init_multi_geometry, plot=plot)
+                        if ret_val != 'fail':
                             a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
                             a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname))
 
 
             # Create a promise with the name
             # Create a promise with the name
@@ -2362,23 +2527,22 @@ class GeometryObject(FlatCAMObj, Geometry):
             # it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
             # it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially
             # to a value of 0.0005 which is 20 times less than 0.01
             # to a value of 0.0005 which is 20 times less than 0.01
             tol = float(self.app.defaults['global_tolerance']) / 20
             tol = float(self.app.defaults['global_tolerance']) / 20
-            res = 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,
-                                                   multidepth=multidepth, depthpercut=depthperpass,
-                                                   toolchange=toolchange, toolchangez=toolchangez,
-                                                   toolchangexy=toolchangexy,
-                                                   extracut=extracut, extracut_length=extracut_length,
-                                                   startz=startz, endz=endz, endxy=endxy,
-                                                   pp_geometry_name=ppname_g)
-
-            job_obj.source_file = res
+            res, start_gcode = 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, multidepth=multidepth, depthpercut=depthperpass, toolchange=toolchange,
+                toolchangez=toolchangez, toolchangexy=toolchangexy, extracut=extracut, extracut_length=extracut_length,
+                startz=startz, endz=endz, endxy=endxy, pp_geometry_name=ppname_g, is_first=True)
+
+            if start_gcode != '':
+                job_obj.gc_start = start_gcode
+
+            job_obj.source_file = start_gcode + res
             # tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
             # tell gcode_parse from which point to start drawing the lines depending on what kind of object is the
             # source of gcode
             # source of gcode
             job_obj.toolchange_xy_type = "geometry"
             job_obj.toolchange_xy_type = "geometry"
             job_obj.gcode_parse()
             job_obj.gcode_parse()
-            self.app.inform.emit('[success] %s' % _("Finished G-Code processing..."))
+            app_obj.inform.emit('[success] %s...' % _("Finished G-Code processing"))
 
 
         if use_thread:
         if use_thread:
             # To be run in separate thread
             # To be run in separate thread
@@ -2394,10 +2558,100 @@ class GeometryObject(FlatCAMObj, Geometry):
         else:
         else:
             self.app.app_obj.new_object("cncjob", outname, job_init, plot=plot)
             self.app.app_obj.new_object("cncjob", outname, job_init, plot=plot)
 
 
-    # def on_plot_cb_click(self, *args):
-    #     if self.muted_ui:
-    #         return
-    #     self.read_form_item('plot')
+    def on_polish(self):
+
+        def job_thread(obj):
+            with obj.app.proc_container.new(_("Working ...")):
+                tooldia = obj.ui.polish_dia_entry.get_value()
+                depth = obj.ui.polish_pressure_entry.get_value()
+                travelz = obj.ui.polish_travelz_entry.get_value()
+                margin = obj.ui.polish_margin_entry.get_value()
+                overlap = obj.ui.polish_over_entry.get_value() / 100
+                paint_method = obj.ui.polish_method_combo.get_value()
+
+                # calculate the max uid form the keys of the self.tools
+                max_uid = max(list(obj.tools.keys()))
+                new_uid = max_uid + 1
+
+                # add a new key in the dict
+                new_data = deepcopy(obj.default_data)
+                new_data["travelz"] = travelz
+                new_data["cutz"] = depth
+                new_dict = {
+                    new_uid: {
+                        'tooldia': obj.app.dec_format(float(tooldia), obj.decimals),
+                        'offset': 'Path',
+                        'offset_value': 0.0,
+                        'type': _('Polish'),
+                        'tool_type': 'C1',
+                        'data': new_data,
+                        'solid_geometry': []
+                    }
+                }
+                obj.tools.update(new_dict)
+                obj.sel_tools.update(new_dict)
+
+                # make a box polygon out of the bounds of the current object
+                # apply the margin
+                xmin, ymin, xmax, ymax = obj.bounds()
+                bbox = box(xmin-margin, ymin-margin, xmax+margin, ymax+margin)
+
+                # paint the box
+                try:
+                    # provide the app with a way to process the GUI events when in a blocking loop
+                    QtWidgets.QApplication.processEvents()
+                    if self.app.abort_flag:
+                        # graceful abort requested by the user
+                        raise grace
+
+                    # Type(cpoly) == FlatCAMRTreeStorage | None
+                    cpoly = None
+                    if paint_method == 0:       # Standard
+                        cpoly = self.clear_polygon(bbox,
+                                                   tooldia=tooldia,
+                                                   steps_per_circle=obj.circle_steps,
+                                                   overlap=overlap,
+                                                   contour=True,
+                                                   connect=True,
+                                                   prog_plot=False)
+                    elif paint_method == 1:     # Seed
+                        cpoly = self.clear_polygon2(bbox,
+                                                    tooldia=tooldia,
+                                                    steps_per_circle=obj.circle_steps,
+                                                    overlap=overlap,
+                                                    contour=True,
+                                                    connect=True,
+                                                    prog_plot=False)
+                    elif paint_method == 2:     # Lines
+                        cpoly = self.clear_polygon3(bbox,
+                                                    tooldia=tooldia,
+                                                    steps_per_circle=obj.circle_steps,
+                                                    overlap=overlap,
+                                                    contour=True,
+                                                    connect=True,
+                                                    prog_plot=False)
+
+                    if not cpoly or not cpoly.objects:
+                        obj.app.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be painted completely'))
+                        return
+
+                    paint_geo = [g for g in cpoly.get_objects() if g and not g.is_empty]
+                except grace:
+                    return "fail"
+                except Exception as e:
+                    log.debug("Could not Paint the polygons. %s" % str(e))
+                    mssg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
+                                                 "Or a different method of Paint"), str(e))
+                    self.app.inform.emit(mssg)
+                    return
+
+                obj.sel_tools[new_uid]['solid_geometry'] = paint_geo
+
+                # and now create the CNCJob
+                obj.launch_job.emit()
+
+        # Send to worker
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self]})
 
 
     def scale(self, xfactor, yfactor=None, point=None):
     def scale(self, xfactor, yfactor=None, point=None):
         """
         """
@@ -2488,7 +2742,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             return
             return
 
 
         self.app.proc_container.new_text = ''
         self.app.proc_container.new_text = ''
-        self.app.inform.emit('[success] %s' % _("Geometry Scale done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
 
 
     def offset(self, vect):
     def offset(self, vect):
         """
         """
@@ -2561,7 +2815,7 @@ class GeometryObject(FlatCAMObj, Geometry):
         self.solid_geometry = translate_recursion(self.solid_geometry)
         self.solid_geometry = translate_recursion(self.solid_geometry)
 
 
         self.app.proc_container.new_text = ''
         self.app.proc_container.new_text = ''
-        self.app.inform.emit('[success] %s' % _("Geometry Offset done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
 
 
     def convert_units(self, units):
     def convert_units(self, units):
         log.debug("FlatCAMObj.GeometryObject.convert_units()")
         log.debug("FlatCAMObj.GeometryObject.convert_units()")
@@ -2675,7 +2929,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             tooldia = float('%.*f' % (self.decimals, tooldia))
             tooldia = float('%.*f' % (self.decimals, tooldia))
 
 
             self.ui.addtool_entry.set_value(tooldia)
             self.ui.addtool_entry.set_value(tooldia)
-        self.ui.addtool_entry.returnPressed.connect(self.on_tool_add)
+        self.ui.addtool_entry.returnPressed.connect(self.on_tool_default_add)
 
 
         return factor
         return factor
 
 
@@ -2874,7 +3128,7 @@ class GeometryObject(FlatCAMObj, Geometry):
         except (ObjectDeleted, AttributeError):
         except (ObjectDeleted, AttributeError):
             self.shapes.clear(update=True)
             self.shapes.clear(update=True)
 
 
-    def on_plot_cb_click(self, *args):
+    def on_plot_cb_click(self):
         if self.muted_ui:
         if self.muted_ui:
             return
             return
         self.read_form_item('plot')
         self.read_form_item('plot')
@@ -2933,7 +3187,7 @@ class GeometryObject(FlatCAMObj, Geometry):
             self.ui.plot_cb.setChecked(True)
             self.ui.plot_cb.setChecked(True)
         self.ui_connect()
         self.ui_connect()
 
 
-    def on_multicolored_cb_click(self, *args):
+    def on_multicolored_cb_click(self):
         if self.muted_ui:
         if self.muted_ui:
             return
             return
         self.read_form_item('multicolored')
         self.read_form_item('multicolored')

+ 57 - 54
appObjects/FlatCAMGerber.py

@@ -11,13 +11,11 @@
 # ##########################################################
 # ##########################################################
 
 
 
 
-from shapely.geometry import Point, Polygon, MultiPolygon, MultiLineString, LineString, LinearRing
-from shapely.ops import unary_union
+from shapely.geometry import Point, MultiLineString, LineString, LinearRing
 
 
 from appParsers.ParseGerber import Gerber
 from appParsers.ParseGerber import Gerber
 from appObjects.FlatCAMObj import *
 from appObjects.FlatCAMObj import *
 
 
-import math
 import numpy as np
 import numpy as np
 from copy import deepcopy
 from copy import deepcopy
 
 
@@ -210,9 +208,9 @@ class GerberObject(FlatCAMObj, Gerber):
                 pass
                 pass
 
 
             self.apertures_row = 0
             self.apertures_row = 0
-            aper_no = self.apertures_row + 1
+
             sort = []
             sort = []
-            for k, v in list(self.apertures.items()):
+            for k in list(self.apertures.keys()):
                 sort.append(int(k))
                 sort.append(int(k))
             sorted_apertures = sorted(sort)
             sorted_apertures = sorted(sort)
 
 
@@ -367,11 +365,11 @@ class GerberObject(FlatCAMObj, Gerber):
         self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Buffering solid geometry"))
         self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Buffering solid geometry"))
 
 
         def buffer_task():
         def buffer_task():
-            with self.app.proc_container.new('%s...' % _("Buffering")):
+            with self.app.proc_container.new('%s ...' % _("Buffering")):
                 output = self.app.pool.apply_async(self.buffer_handler, args=([self.solid_geometry]))
                 output = self.app.pool.apply_async(self.buffer_handler, args=([self.solid_geometry]))
                 self.solid_geometry = output.get()
                 self.solid_geometry = output.get()
 
 
-                self.app.inform.emit('[success] %s.' % _("Done"))
+                self.app.inform.emit('[success] %s' % _("Done."))
                 self.plot_single_object.emit()
                 self.plot_single_object.emit()
 
 
         self.app.worker_task.emit({'fcn': buffer_task, 'params': []})
         self.app.worker_task.emit({'fcn': buffer_task, 'params': []})
@@ -397,7 +395,7 @@ class GerberObject(FlatCAMObj, Gerber):
             non_copper = bounding_box.difference(self.solid_geometry)
             non_copper = bounding_box.difference(self.solid_geometry)
 
 
             if non_copper is None or non_copper.is_empty:
             if non_copper is None or non_copper.is_empty:
-                self.app.inform.emit("[ERROR_NOTCL] %s" % _("Operation could not be done."))
+                app_obj.inform.emit("[ERROR_NOTCL] %s" % _("Operation could not be done."))
                 return "fail"
                 return "fail"
             geo_obj.solid_geometry = non_copper
             geo_obj.solid_geometry = non_copper
 
 
@@ -423,7 +421,7 @@ class GerberObject(FlatCAMObj, Gerber):
                 bounding_box = bounding_box.envelope
                 bounding_box = bounding_box.envelope
 
 
             if bounding_box is None or bounding_box.is_empty:
             if bounding_box is None or bounding_box.is_empty:
-                self.app.inform.emit("[ERROR_NOTCL] %s" % _("Operation could not be done."))
+                app_obj.inform.emit("[ERROR_NOTCL] %s" % _("Operation could not be done."))
                 return "fail"
                 return "fail"
             geo_obj.solid_geometry = bounding_box
             geo_obj.solid_geometry = bounding_box
 
 
@@ -493,7 +491,7 @@ class GerberObject(FlatCAMObj, Gerber):
 
 
                 geo_obj.solid_geometry = []
                 geo_obj.solid_geometry = []
 
 
-                # transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI
+                # transfer the Cut Z and Vtip and Vangle values in case that we use the V-Shape tool in Gerber UI
                 if geo_obj.tool_type.lower() == 'v':
                 if geo_obj.tool_type.lower() == 'v':
                     new_cutz = self.app.defaults["tools_iso_tool_cutz"]
                     new_cutz = self.app.defaults["tools_iso_tool_cutz"]
                     new_vtipdia = self.app.defaults["tools_iso_tool_vtipdia"]
                     new_vtipdia = self.app.defaults["tools_iso_tool_vtipdia"]
@@ -532,17 +530,16 @@ class GerberObject(FlatCAMObj, Gerber):
                     "startz": self.app.defaults['geometry_startz']
                     "startz": self.app.defaults['geometry_startz']
                 })
                 })
 
 
-                geo_obj.tools = {}
-                geo_obj.tools['1'] = {}
+                geo_obj.tools = {'1': {}}
                 geo_obj.tools.update({
                 geo_obj.tools.update({
                     '1': {
                     '1': {
-                        'tooldia': dia,
-                        'offset': 'Path',
-                        'offset_value': 0.0,
-                        'type': _('Rough'),
-                        'tool_type': tool_type,
-                        'data': default_data,
-                        'solid_geometry': geo_obj.solid_geometry
+                        'tooldia':          dia,
+                        'offset':           'Path',
+                        'offset_value':     0.0,
+                        'type':             'Rough',
+                        'tool_type':        tool_type,
+                        'data':             default_data,
+                        'solid_geometry':   geo_obj.solid_geometry
                     }
                     }
                 })
                 })
 
 
@@ -555,7 +552,8 @@ class GerberObject(FlatCAMObj, Gerber):
                                                   follow=follow, nr_passes=nr_pass)
                                                   follow=follow, nr_passes=nr_pass)
 
 
                     if geom == 'fail':
                     if geom == 'fail':
-                        app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
+                        if plot:
+                            app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
                         return 'fail'
                         return 'fail'
                     geo_obj.solid_geometry.append(geom)
                     geo_obj.solid_geometry.append(geom)
 
 
@@ -580,7 +578,9 @@ class GerberObject(FlatCAMObj, Gerber):
                 if empty_cnt == len(geo_obj.solid_geometry):
                 if empty_cnt == len(geo_obj.solid_geometry):
                     raise ValidationError("Empty Geometry", None)
                     raise ValidationError("Empty Geometry", None)
                 else:
                 else:
-                    app_obj.inform.emit('[success] %s" %s' % (_("Isolation geometry created"), geo_obj.options["name"]))
+                    if plot:
+                        app_obj.inform.emit('[success] %s: %s' %
+                                            (_("Isolation geometry created"), geo_obj.options["name"]))
 
 
                 # even if combine is checked, one pass is still single-geo
                 # even if combine is checked, one pass is still single-geo
                 geo_obj.multigeo = True if passes > 1 else False
                 geo_obj.multigeo = True if passes > 1 else False
@@ -629,7 +629,8 @@ class GerberObject(FlatCAMObj, Gerber):
                                                   follow=follow, nr_passes=i)
                                                   follow=follow, nr_passes=i)
 
 
                     if geom == 'fail':
                     if geom == 'fail':
-                        app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
+                        if plot:
+                            app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated."))
                         return 'fail'
                         return 'fail'
 
 
                     geo_obj.solid_geometry = geom
                     geo_obj.solid_geometry = geom
@@ -675,17 +676,16 @@ class GerberObject(FlatCAMObj, Gerber):
                         "startz": self.app.defaults['geometry_startz']
                         "startz": self.app.defaults['geometry_startz']
                     })
                     })
 
 
-                    geo_obj.tools = {}
-                    geo_obj.tools['1'] = {}
+                    geo_obj.tools = {'1': {}}
                     geo_obj.tools.update({
                     geo_obj.tools.update({
                         '1': {
                         '1': {
-                            'tooldia': dia,
-                            'offset': 'Path',
-                            'offset_value': 0.0,
-                            'type': _('Rough'),
-                            'tool_type': tool_type,
-                            'data': default_data,
-                            'solid_geometry': geo_obj.solid_geometry
+                            'tooldia':          dia,
+                            'offset':           'Path',
+                            'offset_value':     0.0,
+                            'type':             'Rough',
+                            'tool_type':        tool_type,
+                            'data':             default_data,
+                            'solid_geometry':   geo_obj.solid_geometry
                         }
                         }
                     })
                     })
 
 
@@ -706,8 +706,9 @@ class GerberObject(FlatCAMObj, Gerber):
                     if empty_cnt == len(geo_obj.solid_geometry):
                     if empty_cnt == len(geo_obj.solid_geometry):
                         raise ValidationError("Empty Geometry", None)
                         raise ValidationError("Empty Geometry", None)
                     else:
                     else:
-                        app_obj.inform.emit('[success] %s: %s' %
-                                            (_("Isolation geometry created"), geo_obj.options["name"]))
+                        if plot:
+                            app_obj.inform.emit('[success] %s: %s' %
+                                                (_("Isolation geometry created"), geo_obj.options["name"]))
                     geo_obj.multigeo = False
                     geo_obj.multigeo = False
 
 
                     # ############################################################
                     # ############################################################
@@ -771,7 +772,7 @@ class GerberObject(FlatCAMObj, Gerber):
         else:
         else:
             follow_name = outname
             follow_name = outname
 
 
-        def follow_init(follow_obj, app):
+        def follow_init(follow_obj, app_obj):
             # Propagate options
             # Propagate options
             follow_obj.options["cnctooldia"] = str(self.app.defaults["tools_iso_tooldia"])
             follow_obj.options["cnctooldia"] = str(self.app.defaults["tools_iso_tooldia"])
             follow_obj.solid_geometry = self.follow_geometry
             follow_obj.solid_geometry = self.follow_geometry
@@ -849,7 +850,7 @@ class GerberObject(FlatCAMObj, Gerber):
 
 
         log.debug("FlatCAMObj.GerberObject.convert_units()")
         log.debug("FlatCAMObj.GerberObject.convert_units()")
 
 
-        factor = Gerber.convert_units(self, units)
+        Gerber.convert_units(self, units)
 
 
         # self.options['isotooldia'] = float(self.options['isotooldia']) * factor
         # self.options['isotooldia'] = float(self.options['isotooldia']) * factor
         # self.options['bboxmargin'] = float(self.options['bboxmargin']) * factor
         # self.options['bboxmargin'] = float(self.options['bboxmargin']) * factor
@@ -956,11 +957,12 @@ class GerberObject(FlatCAMObj, Gerber):
             log.debug("GerberObject.plot() --> %s" % str(e))
             log.debug("GerberObject.plot() --> %s" % str(e))
 
 
     # experimental plot() when the solid_geometry is stored in the self.apertures
     # experimental plot() when the solid_geometry is stored in the self.apertures
-    def plot_aperture(self, run_thread=False, **kwargs):
+    def plot_aperture(self, only_flashes=False, run_thread=False, **kwargs):
         """
         """
 
 
-        :param run_thread: if True run the aperture plot as a thread in a worker
-        :param kwargs: color and face_color
+        :param only_flashes:    plot only flashed
+        :param run_thread:      if True run the aperture plot as a thread in a worker
+        :param kwargs:          color and face_color
         :return:
         :return:
         """
         """
 
 
@@ -989,35 +991,36 @@ class GerberObject(FlatCAMObj, Gerber):
         else:
         else:
             visibility = kwargs['visible']
             visibility = kwargs['visible']
 
 
-        with self.app.proc_container.new(_("Plotting Apertures")):
-
-            def job_thread(app_obj):
+        def job_thread(app_obj):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 try:
                 try:
                     if aperture_to_plot_mark in self.apertures:
                     if aperture_to_plot_mark in self.apertures:
-                        for elem in self.apertures[aperture_to_plot_mark]['geometry']:
+                        for elem in app_obj.apertures[aperture_to_plot_mark]['geometry']:
                             if 'solid' in elem:
                             if 'solid' in elem:
+                                if only_flashes and not isinstance(elem['follow'], Point):
+                                    continue
                                 geo = elem['solid']
                                 geo = elem['solid']
                                 try:
                                 try:
                                     for el in geo:
                                     for el in geo:
-                                        shape_key = self.add_mark_shape(shape=el, color=color, face_color=color,
-                                                                        visible=visibility)
-                                        self.mark_shapes_storage[aperture_to_plot_mark].append(shape_key)
+                                        shape_key = app_obj.add_mark_shape(shape=el, color=color, face_color=color,
+                                                                           visible=visibility)
+                                        app_obj.mark_shapes_storage[aperture_to_plot_mark].append(shape_key)
                                 except TypeError:
                                 except TypeError:
-                                    shape_key = self.add_mark_shape(shape=geo, color=color, face_color=color,
-                                                                    visible=visibility)
-                                    self.mark_shapes_storage[aperture_to_plot_mark].append(shape_key)
+                                    shape_key = app_obj.add_mark_shape(shape=geo, color=color, face_color=color,
+                                                                       visible=visibility)
+                                    app_obj.mark_shapes_storage[aperture_to_plot_mark].append(shape_key)
 
 
-                    self.mark_shapes.redraw()
+                    app_obj.mark_shapes.redraw()
 
 
                 except (ObjectDeleted, AttributeError):
                 except (ObjectDeleted, AttributeError):
-                    self.clear_plot_apertures()
+                    app_obj.clear_plot_apertures()
                 except Exception as e:
                 except Exception as e:
                     log.debug("GerberObject.plot_aperture() --> %s" % str(e))
                     log.debug("GerberObject.plot_aperture() --> %s" % str(e))
 
 
-            if run_thread:
-                self.app.worker_task.emit({'fcn': job_thread, 'params': [self]})
-            else:
-                job_thread(self)
+        if run_thread:
+            self.app.worker_task.emit({'fcn': job_thread, 'params': [self]})
+        else:
+            job_thread(self)
 
 
     def clear_plot_apertures(self, aperture='all'):
     def clear_plot_apertures(self, aperture='all'):
         """
         """

+ 4 - 4
appObjects/FlatCAMObj.py

@@ -263,7 +263,7 @@ class FlatCAMObj(QtCore.QObject):
             with self.app.proc_container.new(_("Offsetting...")):
             with self.app.proc_container.new(_("Offsetting...")):
                 self.offset(vector_val)
                 self.offset(vector_val)
             self.app.proc_container.update_view_text('')
             self.app.proc_container.update_view_text('')
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 self.plot()
                 self.plot()
             self.app.app_obj.object_changed.emit(self)
             self.app.app_obj.object_changed.emit(self)
 
 
@@ -294,7 +294,7 @@ class FlatCAMObj(QtCore.QObject):
                 self.app.inform.emit('[success] %s' % _("Scale done."))
                 self.app.inform.emit('[success] %s' % _("Scale done."))
 
 
             self.app.proc_container.update_view_text('')
             self.app.proc_container.update_view_text('')
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 self.plot()
                 self.plot()
             self.app.app_obj.object_changed.emit(self)
             self.app.app_obj.object_changed.emit(self)
 
 
@@ -310,7 +310,7 @@ class FlatCAMObj(QtCore.QObject):
             with self.app.proc_container.new(_("Skewing...")):
             with self.app.proc_container.new(_("Skewing...")):
                 self.skew(x_angle, y_angle)
                 self.skew(x_angle, y_angle)
             self.app.proc_container.update_view_text('')
             self.app.proc_container.update_view_text('')
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 self.plot()
                 self.plot()
             self.app.app_obj.object_changed.emit(self)
             self.app.app_obj.object_changed.emit(self)
 
 
@@ -390,7 +390,7 @@ class FlatCAMObj(QtCore.QObject):
 
 
     def single_object_plot(self):
     def single_object_plot(self):
         def plot_task():
         def plot_task():
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 self.plot()
                 self.plot()
             self.app.app_obj.object_changed.emit(self)
             self.app.app_obj.object_changed.emit(self)
 
 

+ 4 - 9
appObjects/FlatCAMScript.py

@@ -16,7 +16,6 @@ from appGUI.ObjectUI import *
 
 
 import tkinter as tk
 import tkinter as tk
 import sys
 import sys
-from copy import deepcopy
 
 
 import gettext
 import gettext
 import appTranslation as fcTranslate
 import appTranslation as fcTranslate
@@ -78,13 +77,9 @@ class ScriptObject(FlatCAMObj):
 
 
         # Show/Hide Advanced Options
         # Show/Hide Advanced Options
         if self.app.defaults["global_app_level"] == 'b':
         if self.app.defaults["global_app_level"] == 'b':
-            self.ui.level.setText(_(
-                '<span style="color:green;"><b>Basic</b></span>'
-            ))
+            self.ui.level.setText('<span style="color:green;"><b>%s</b></span>' % _("Basic"))
         else:
         else:
-            self.ui.level.setText(_(
-                '<span style="color:red;"><b>Advanced</b></span>'
-            ))
+            self.ui.level.setText('<span style="color:red;"><b>%s</b></span>' % _("Advanced"))
 
 
         self.script_editor_tab = AppTextEditor(app=self.app, plain_text=True, parent=self.app.ui)
         self.script_editor_tab = AppTextEditor(app=self.app, plain_text=True, parent=self.app.ui)
 
 
@@ -131,7 +126,7 @@ class ScriptObject(FlatCAMObj):
         # ---------------------------------------------------- #
         # ---------------------------------------------------- #
         # ----------- LOAD THE TEXT SOURCE FILE -------------- #
         # ----------- LOAD THE TEXT SOURCE FILE -------------- #
         # ---------------------------------------------------- #
         # ---------------------------------------------------- #
-        self.app.proc_container.view.set_busy(_("Loading..."))
+        self.app.proc_container.view.set_busy('%s...' % _("Loading"))
         self.script_editor_tab.t_frame.hide()
         self.script_editor_tab.t_frame.hide()
 
 
         try:
         try:
@@ -219,7 +214,7 @@ class ScriptObject(FlatCAMObj):
             # it means that the script finished with an error
             # it means that the script finished with an error
             result = self.app.shell.tcl.eval("set errorInfo")
             result = self.app.shell.tcl.eval("set errorInfo")
             log.error("Exec command Exception: %s\n" % result)
             log.error("Exec command Exception: %s\n" % result)
-            self.app.shell.append_error('ERROR: %s\n '% result)
+            self.app.shell.append_error('ERROR: %s\n' % result)
 
 
         self.app.ui.fcinfo.lock_pmaps = False
         self.app.ui.fcinfo.lock_pmaps = False
         self.app.shell.close_processing()
         self.app.shell.close_processing()

+ 12 - 12
appObjects/ObjectCollection.py

@@ -229,12 +229,12 @@ class ObjectCollection(QtCore.QAbstractItemModel):
     """
     """
 
 
     groups = [
     groups = [
-        ("gerber", "Gerber"),
-        ("excellon", "Excellon"),
-        ("geometry", "Geometry"),
+        ("gerber", _("Gerber")),
+        ("excellon", _("Excellon")),
+        ("geometry", _("Geometry")),
         ("cncjob", "CNC Job"),
         ("cncjob", "CNC Job"),
-        ("script", "Scripts"),
-        ("document", "Document"),
+        ("script", _("Script")),
+        ("document", _("Document")),
     ]
     ]
 
 
     classdict = {
     classdict = {
@@ -1090,14 +1090,14 @@ class ObjectCollection(QtCore.QAbstractItemModel):
 
 
             def add_act(o_name):
             def add_act(o_name):
                 obj_for_icon = self.get_by_name(o_name)
                 obj_for_icon = self.get_by_name(o_name)
-                add_action = QtWidgets.QAction(parent=self.app.ui.menuobjects)
-                add_action.setCheckable(True)
-                add_action.setText(o_name)
-                add_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind]))
-                add_action.triggered.connect(
-                    lambda: self.set_active(o_name) if add_action.isChecked() is True else
+                menu_action = QtWidgets.QAction(parent=self.app.ui.menuobjects)
+                menu_action.setCheckable(True)
+                menu_action.setText(o_name)
+                menu_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind]))
+                menu_action.triggered.connect(
+                    lambda: self.set_active(o_name) if menu_action.isChecked() is True else
                     self.set_inactive(o_name))
                     self.set_inactive(o_name))
-                self.app.ui.menuobjects.addAction(add_action)
+                self.app.ui.menuobjects.addAction(menu_action)
 
 
             for name in gerber_list:
             for name in gerber_list:
                 add_act(name)
                 add_act(name)

+ 10 - 10
appParsers/ParseDXF.py

@@ -9,13 +9,12 @@ from shapely.geometry import LineString
 from shapely.affinity import rotate
 from shapely.affinity import rotate
 from ezdxf.math.vector import Vector as ezdxf_vector
 from ezdxf.math.vector import Vector as ezdxf_vector
 
 
+from appParsers.ParseFont import *
+from appParsers.ParseDXF_Spline import *
 import logging
 import logging
 
 
 log = logging.getLogger('base2')
 log = logging.getLogger('base2')
 
 
-from appParsers.ParseFont import *
-from appParsers.ParseDXF_Spline import *
-
 
 
 def distance(pt1, pt2):
 def distance(pt1, pt2):
     return math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
     return math.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
@@ -107,12 +106,12 @@ def dxfarc2shapely(arc, n_points=100):
         arc_center = ocs.to_wcs(arc.dxf.center)
         arc_center = ocs.to_wcs(arc.dxf.center)
         start_angle = arc.dxf.start_angle + 180
         start_angle = arc.dxf.start_angle + 180
         end_angle = arc.dxf.end_angle + 180
         end_angle = arc.dxf.end_angle + 180
-        dir = 'CW'
+        direction = 'CW'
     else:
     else:
         arc_center = arc.dxf.center
         arc_center = arc.dxf.center
         start_angle = arc.dxf.start_angle
         start_angle = arc.dxf.start_angle
         end_angle = arc.dxf.end_angle
         end_angle = arc.dxf.end_angle
-        dir = 'CCW'
+        direction = 'CCW'
 
 
     center_x = arc_center[0]
     center_x = arc_center[0]
     center_y = arc_center[1]
     center_y = arc_center[1]
@@ -127,7 +126,7 @@ def dxfarc2shapely(arc, n_points=100):
     step_angle = float(abs(end_angle - start_angle) / n_points)
     step_angle = float(abs(end_angle - start_angle) / n_points)
 
 
     while angle <= end_angle:
     while angle <= end_angle:
-        if dir == 'CCW':
+        if direction == 'CCW':
             x = center_x + radius * math.cos(math.radians(angle))
             x = center_x + radius * math.cos(math.radians(angle))
             y = center_y + radius * math.sin(math.radians(angle))
             y = center_y + radius * math.sin(math.radians(angle))
         else:
         else:
@@ -138,7 +137,7 @@ def dxfarc2shapely(arc, n_points=100):
 
 
     # in case the number of segments do not cover everything until the end of the arc
     # in case the number of segments do not cover everything until the end of the arc
     if angle != end_angle:
     if angle != end_angle:
-        if dir == 'CCW':
+        if direction == 'CCW':
             x = center_x + radius * math.cos(math.radians(end_angle))
             x = center_x + radius * math.cos(math.radians(end_angle))
             y = center_y + radius * math.sin(math.radians(end_angle))
             y = center_y + radius * math.sin(math.radians(end_angle))
         else:
         else:
@@ -164,12 +163,12 @@ def dxfellipse2shapely(ellipse, ellipse_segments=100):
         center = ocs.to_wcs(ellipse.dxf.center)
         center = ocs.to_wcs(ellipse.dxf.center)
         start_angle = ocs.to_wcs(ellipse.dxf.start_param)
         start_angle = ocs.to_wcs(ellipse.dxf.start_param)
         end_angle = ocs.to_wcs(ellipse.dxf.end_param)
         end_angle = ocs.to_wcs(ellipse.dxf.end_param)
-        dir = 'CW'
+        direction = 'CW'
     else:
     else:
         center = ellipse.dxf.center
         center = ellipse.dxf.center
         start_angle = ellipse.dxf.start_param
         start_angle = ellipse.dxf.start_param
         end_angle = ellipse.dxf.end_param
         end_angle = ellipse.dxf.end_param
-        dir = 'CCW'
+        direction = 'CCW'
 
 
     # print("Dir = %s" % dir)
     # print("Dir = %s" % dir)
     major_axis = ellipse.dxf.major_axis
     major_axis = ellipse.dxf.major_axis
@@ -189,7 +188,7 @@ def dxfellipse2shapely(ellipse, ellipse_segments=100):
 
 
     angle = start_angle
     angle = start_angle
     for step in range(line_seg + 1):
     for step in range(line_seg + 1):
-        if dir == 'CW':
+        if direction == 'CW':
             major_dim = normalize_2(major_axis)
             major_dim = normalize_2(major_axis)
             minor_dim = normalize_2(Vector([ratio * k for k in major_axis]))
             minor_dim = normalize_2(Vector([ratio * k for k in major_axis]))
             vx = (major_dim[0] + major_dim[1]) * math.cos(angle)
             vx = (major_dim[0] + major_dim[1]) * math.cos(angle)
@@ -381,6 +380,7 @@ def get_geo(dxf_object, container):
 
 
     return geo
     return geo
 
 
+
 def getdxftext(exf_object, object_type, units=None):
 def getdxftext(exf_object, object_type, units=None):
     pass
     pass
 
 

+ 1 - 2
appParsers/ParseDXF_Spline.py

@@ -400,7 +400,7 @@ class Vector(list):
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     def __str__(self):
     def __str__(self):
-        return "[%s]" % ", ".join([("%15g" % (x)).strip() for x in self])
+        return "[%s]" % ", ".join([("%15g" % x).strip() for x in self])
 
 
     # ----------------------------------------------------------------------
     # ----------------------------------------------------------------------
     def eq(self, v, acc=_accuracy):
     def eq(self, v, acc=_accuracy):
@@ -805,7 +805,6 @@ class Vector(list):
 #
 #
 #     #-----------------------------------------------------------------------
 #     #-----------------------------------------------------------------------
 #     def __call__(self, i, x):
 #     def __call__(self, i, x):
-#         # FIXME should interpolate to find the interval
 #         C = self.coefficients(i)
 #         C = self.coefficients(i)
 #         return ((C[0]*x + C[1])*x + C[2])*x + C[3]
 #         return ((C[0]*x + C[1])*x + C[2])*x + C[3]
 #
 #

+ 16 - 14
appParsers/ParseFont.py

@@ -10,13 +10,15 @@
 # ## and made it work with Python 3                                    #
 # ## and made it work with Python 3                                    #
 # ######################################################################
 # ######################################################################
 
 
-import re, os, sys, glob
+import re
+import os
+import sys
+import glob
 
 
-from shapely.geometry import Point, Polygon
+from shapely.geometry import Polygon
 from shapely.affinity import translate, scale
 from shapely.affinity import translate, scale
 from shapely.geometry import MultiPolygon
 from shapely.geometry import MultiPolygon
 
 
-
 import freetype as ft
 import freetype as ft
 from fontTools import ttLib
 from fontTools import ttLib
 
 
@@ -32,7 +34,7 @@ if '_' not in builtins.__dict__:
 log = logging.getLogger('base2')
 log = logging.getLogger('base2')
 
 
 
 
-class ParseFont():
+class ParseFont:
 
 
     FONT_SPECIFIER_NAME_ID = 4
     FONT_SPECIFIER_NAME_ID = 4
     FONT_SPECIFIER_FAMILY_ID = 1
     FONT_SPECIFIER_FAMILY_ID = 1
@@ -69,12 +71,12 @@ class ParseFont():
         if os.path.isfile(executable):
         if os.path.isfile(executable):
             data = os.popen(executable).readlines()
             data = os.popen(executable).readlines()
             match = re.compile('\d+: (.+)')
             match = re.compile('\d+: (.+)')
-            set = []
+            set_lst = []
             for line in data:
             for line in data:
                 result = match.match(line)
                 result = match.match(line)
                 if result:
                 if result:
-                    set.append(result.group(1))
-            return set
+                    set_lst.append(result.group(1))
+            return set_lst
         else:
         else:
             directories = [
             directories = [
                 # what seems to be the standard installation point
                 # what seems to be the standard installation point
@@ -91,7 +93,7 @@ class ParseFont():
             dir_set = []
             dir_set = []
 
 
             for directory in directories:
             for directory in directories:
-                directory = directory = os.path.expanduser(os.path.expandvars(directory))
+                directory = os.path.expanduser(os.path.expandvars(directory))
                 try:
                 try:
                     if os.path.isdir(directory):
                     if os.path.isdir(directory):
                         for path, children, files in os.walk(directory):
                         for path, children, files in os.walk(directory):
@@ -116,7 +118,7 @@ class ParseFont():
         dir_set = []
         dir_set = []
 
 
         for directory in directories:
         for directory in directories:
-            directory = directory = os.path.expanduser(os.path.expandvars(directory))
+            directory = os.path.expanduser(os.path.expandvars(directory))
             try:
             try:
                 if os.path.isdir(directory):
                 if os.path.isdir(directory):
                     for path, children, files in os.walk(directory):
                     for path, children, files in os.walk(directory):
@@ -144,7 +146,7 @@ class ParseFont():
                     winreg.HKEY_LOCAL_MACHINE,
                     winreg.HKEY_LOCAL_MACHINE,
                     keyName
                     keyName
                 )
                 )
-            except OSError as err:
+            except OSError:
                 pass
                 pass
 
 
         if not k:
         if not k:
@@ -195,7 +197,7 @@ class ParseFont():
                 break
                 break
         return name, family
         return name, family
 
 
-    def __init__(self, app, parent=None):
+    def __init__(self, app):
         super(ParseFont, self).__init__()
         super(ParseFont, self).__init__()
 
 
         self.app = app
         self.app = app
@@ -217,7 +219,7 @@ class ParseFont():
         if paths is None:
         if paths is None:
             if sys.platform == 'win32':
             if sys.platform == 'win32':
                 font_directory = ParseFont.get_win32_font_path()
                 font_directory = ParseFont.get_win32_font_path()
-                paths = [font_directory,]
+                paths = [font_directory, ]
 
 
                 # now get all installed fonts directly...
                 # now get all installed fonts directly...
                 for f in self.get_win32_fonts(font_directory):
                 for f in self.get_win32_fonts(font_directory):
@@ -275,7 +277,7 @@ class ParseFont():
             else:
             else:
                 try:
                 try:
                     name = name.replace(" Regular", '')
                     name = name.replace(" Regular", '')
-                except Exception as e:
+                except Exception:
                     pass
                     pass
                 self.regular_f.update({name: font})
                 self.regular_f.update({name: font})
         log.debug("Font parsing is finished.")
         log.debug("Font parsing is finished.")
@@ -318,7 +320,7 @@ class ParseFont():
                 if previous > 0 and glyph_index > 0:
                 if previous > 0 and glyph_index > 0:
                     delta = face.get_kerning(previous, glyph_index)
                     delta = face.get_kerning(previous, glyph_index)
                     pen_x += delta.x
                     pen_x += delta.x
-            except Exception as e:
+            except Exception:
                 pass
                 pass
 
 
             face.load_glyph(glyph_index)
             face.load_glyph(glyph_index)

+ 61 - 64
appParsers/ParseGerber.py

@@ -2,16 +2,13 @@ from PyQt5 import QtWidgets
 from camlib import Geometry, arc, arc_angle, ApertureMacro, grace
 from camlib import Geometry, arc, arc_angle, ApertureMacro, grace
 
 
 import numpy as np
 import numpy as np
-# import re
-# import logging
 import traceback
 import traceback
 from copy import deepcopy
 from copy import deepcopy
-# import sys
 
 
 from shapely.ops import unary_union, linemerge
 from shapely.ops import unary_union, linemerge
-# from shapely.affinity import scale, translate
 import shapely.affinity as affinity
 import shapely.affinity as affinity
 from shapely.geometry import box as shply_box
 from shapely.geometry import box as shply_box
+from shapely.geometry import Point
 
 
 from lxml import etree as ET
 from lxml import etree as ET
 import ezdxf
 import ezdxf
@@ -296,6 +293,7 @@ class Gerber(Geometry):
 
 
         if apertureType in self.aperture_macros:
         if apertureType in self.aperture_macros:
             self.apertures[apid] = {"type": "AM",
             self.apertures[apid] = {"type": "AM",
+                                    # "size": 0.0,
                                     "macro": self.aperture_macros[apertureType],
                                     "macro": self.aperture_macros[apertureType],
                                     "modifiers": paramList}
                                     "modifiers": paramList}
             return apid
             return apid
@@ -315,12 +313,12 @@ class Gerber(Geometry):
 
 
         First is ``G54D11*`` and seconds is ``G36*``.
         First is ``G54D11*`` and seconds is ``G36*``.
 
 
-        :param filename: Gerber file to parse.
-        :type filename: str
-        :param follow: If true, will not create polygons, just lines
-            following the gerber path.
-        :type follow: bool
-        :return: None
+        :param filename:        Gerber file to parse.
+        :type filename:         str
+        :param follow:          If true, will not create polygons, just lines
+                                following the gerber path.
+        :type follow:           bool
+        :return:                None
         """
         """
 
 
         with open(filename, 'r') as gfile:
         with open(filename, 'r') as gfile:
@@ -423,7 +421,7 @@ class Gerber(Geometry):
 
 
         s_tol = float(self.app.defaults["gerber_simp_tolerance"])
         s_tol = float(self.app.defaults["gerber_simp_tolerance"])
 
 
-        self.app.inform.emit('%s %d %s.' % (_("Gerber processing. Parsing"), len(glines), _("lines")))
+        self.app.inform.emit('%s %d %s.' % (_("Gerber processing. Parsing"), len(glines), _("Lines").lower()))
         try:
         try:
             for gline in glines:
             for gline in glines:
                 if self.app.abort_flag:
                 if self.app.abort_flag:
@@ -955,36 +953,41 @@ class Gerber(Geometry):
                                 # Reset path starting point
                                 # Reset path starting point
                                 path = [[current_x, current_y]]
                                 path = [[current_x, current_y]]
 
 
-                                # --- BUFFERED ---
-                                # Draw the flash
-                                # this treats the case when we are storing geometry as paths
-                                geo_dict = {}
-                                geo_flash = Point([current_x, current_y])
-                                follow_buffer.append(geo_flash)
-                                geo_dict['follow'] = geo_flash
-
-                                # this treats the case when we are storing geometry as solids
-                                flash = self.create_flash_geometry(
-                                    Point([current_x, current_y]),
-                                    self.apertures[current_aperture],
-                                    self.steps_per_circle
-                                )
-                                if not flash.is_empty:
-                                    if self.app.defaults['gerber_simplification']:
-                                        poly_buffer.append(flash.simplify(s_tol))
-                                    else:
-                                        poly_buffer.append(flash)
+                                # treat the case when there is a flash inside a Gerber Region when the current_aperture
+                                # is None
+                                if current_aperture is None:
+                                    pass
+                                else:
+                                    # --- BUFFERED ---
+                                    # Draw the flash
+                                    # this treats the case when we are storing geometry as paths
+                                    geo_dict = {}
+                                    geo_flash = Point([current_x, current_y])
+                                    follow_buffer.append(geo_flash)
+                                    geo_dict['follow'] = geo_flash
+
+                                    # this treats the case when we are storing geometry as solids
+                                    flash = self.create_flash_geometry(
+                                        Point([current_x, current_y]),
+                                        self.apertures[current_aperture],
+                                        self.steps_per_circle
+                                    )
+                                    if not flash.is_empty:
+                                        if self.app.defaults['gerber_simplification']:
+                                            poly_buffer.append(flash.simplify(s_tol))
+                                        else:
+                                            poly_buffer.append(flash)
 
 
-                                    if self.is_lpc is True:
-                                        geo_dict['clear'] = flash
-                                    else:
-                                        geo_dict['solid'] = flash
+                                        if self.is_lpc is True:
+                                            geo_dict['clear'] = flash
+                                        else:
+                                            geo_dict['solid'] = flash
 
 
-                                if current_aperture not in self.apertures:
-                                    self.apertures[current_aperture] = {}
-                                if 'geometry' not in self.apertures[current_aperture]:
-                                    self.apertures[current_aperture]['geometry'] = []
-                                self.apertures[current_aperture]['geometry'].append(deepcopy(geo_dict))
+                                    if current_aperture not in self.apertures:
+                                        self.apertures[current_aperture] = {}
+                                    if 'geometry' not in self.apertures[current_aperture]:
+                                        self.apertures[current_aperture]['geometry'] = []
+                                    self.apertures[current_aperture]['geometry'].append(deepcopy(geo_dict))
 
 
                             if making_region is False:
                             if making_region is False:
                                 # if the aperture is rectangle then add a rectangular shape having as parameters the
                                 # if the aperture is rectangle then add a rectangular shape having as parameters the
@@ -1837,12 +1840,10 @@ class Gerber(Geometry):
                 geos_length = 1
                 geos_length = 1
 
 
             if geos_length == 1:
             if geos_length == 1:
-                geo_qrcode = []
-                geo_qrcode.append(Polygon(geos[0].exterior))
+                geo_qrcode = [Polygon(geos[0].exterior)]
                 for i_el in geos[0].interiors:
                 for i_el in geos[0].interiors:
                     geo_qrcode.append(Polygon(i_el).buffer(0, resolution=res))
                     geo_qrcode.append(Polygon(i_el).buffer(0, resolution=res))
-                for poly in geo_qrcode:
-                    geos.append(poly)
+                geos = [poly for poly in geo_qrcode]
 
 
             if type(self.solid_geometry) == list:
             if type(self.solid_geometry) == list:
                 self.solid_geometry += geos
                 self.solid_geometry += geos
@@ -1864,15 +1865,14 @@ class Gerber(Geometry):
             self.solid_geometry = [self.solid_geometry]
             self.solid_geometry = [self.solid_geometry]
 
 
         if '0' not in self.apertures:
         if '0' not in self.apertures:
-            self.apertures['0'] = {}
-            self.apertures['0']['type'] = 'REG'
-            self.apertures['0']['size'] = 0.0
-            self.apertures['0']['geometry'] = []
+            self.apertures['0'] = {
+                'type': 'REG',
+                'size': 0.0,
+                'geometry': []
+            }
 
 
         for pol in self.solid_geometry:
         for pol in self.solid_geometry:
-            new_el = {}
-            new_el['solid'] = pol
-            new_el['follow'] = pol.exterior
+            new_el = {'solid': pol, 'follow': pol.exterior}
             self.apertures['0']['geometry'].append(new_el)
             self.apertures['0']['geometry'].append(new_el)
 
 
     def import_dxf_as_gerber(self, filename, units='MM'):
     def import_dxf_as_gerber(self, filename, units='MM'):
@@ -1914,15 +1914,14 @@ class Gerber(Geometry):
 
 
         # create the self.apertures data structure
         # create the self.apertures data structure
         if '0' not in self.apertures:
         if '0' not in self.apertures:
-            self.apertures['0'] = {}
-            self.apertures['0']['type'] = 'REG'
-            self.apertures['0']['size'] = 0.0
-            self.apertures['0']['geometry'] = []
+            self.apertures['0'] = {
+                'type': 'REG',
+                'size': 0.0,
+                'geometry': []
+            }
 
 
         for pol in flat_geo:
         for pol in flat_geo:
-            new_el = {}
-            new_el['solid'] = pol
-            new_el['follow'] = pol
+            new_el = {'solid': pol, 'follow': pol}
             self.apertures['0']['geometry'].append(deepcopy(new_el))
             self.apertures['0']['geometry'].append(deepcopy(new_el))
 
 
     def scale(self, xfactor, yfactor=None, point=None):
     def scale(self, xfactor, yfactor=None, point=None):
@@ -2043,7 +2042,7 @@ class Gerber(Geometry):
             log.debug('camlib.Gerber.scale() Exception --> %s' % str(e))
             log.debug('camlib.Gerber.scale() Exception --> %s' % str(e))
             return 'fail'
             return 'fail'
 
 
-        self.app.inform.emit('[success] %s' % _("Gerber Scale done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
         self.app.proc_container.new_text = ''
         self.app.proc_container.new_text = ''
 
 
         # ## solid_geometry ???
         # ## solid_geometry ???
@@ -2134,8 +2133,7 @@ class Gerber(Geometry):
             log.debug('camlib.Gerber.offset() Exception --> %s' % str(e))
             log.debug('camlib.Gerber.offset() Exception --> %s' % str(e))
             return 'fail'
             return 'fail'
 
 
-        self.app.inform.emit('[success] %s' %
-                             _("Gerber Offset done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
         self.app.proc_container.new_text = ''
         self.app.proc_container.new_text = ''
 
 
     def mirror(self, axis, point):
     def mirror(self, axis, point):
@@ -2210,8 +2208,7 @@ class Gerber(Geometry):
             log.debug('camlib.Gerber.mirror() Exception --> %s' % str(e))
             log.debug('camlib.Gerber.mirror() Exception --> %s' % str(e))
             return 'fail'
             return 'fail'
 
 
-        self.app.inform.emit('[success] %s' %
-                             _("Gerber Mirror done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
         self.app.proc_container.new_text = ''
         self.app.proc_container.new_text = ''
 
 
     def skew(self, angle_x, angle_y, point):
     def skew(self, angle_x, angle_y, point):
@@ -2285,7 +2282,7 @@ class Gerber(Geometry):
             log.debug('camlib.Gerber.skew() Exception --> %s' % str(e))
             log.debug('camlib.Gerber.skew() Exception --> %s' % str(e))
             return 'fail'
             return 'fail'
 
 
-        self.app.inform.emit('[success] %s' % _("Gerber Skew done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
         self.app.proc_container.new_text = ''
         self.app.proc_container.new_text = ''
 
 
     def rotate(self, angle, point):
     def rotate(self, angle, point):
@@ -2347,7 +2344,7 @@ class Gerber(Geometry):
         except Exception as e:
         except Exception as e:
             log.debug('camlib.Gerber.rotate() Exception --> %s' % str(e))
             log.debug('camlib.Gerber.rotate() Exception --> %s' % str(e))
             return 'fail'
             return 'fail'
-        self.app.inform.emit('[success] %s' % _("Gerber Rotate done."))
+        self.app.inform.emit('[success] %s' % _("Done."))
         self.app.proc_container.new_text = ''
         self.app.proc_container.new_text = ''
 
 
     def buffer(self, distance, join=2, factor=None):
     def buffer(self, distance, join=2, factor=None):

+ 1 - 1
appParsers/ParseHPGL2.py

@@ -198,7 +198,7 @@ class HPGL2:
         line_num = 0
         line_num = 0
         gline = ""
         gline = ""
 
 
-        self.app.inform.emit('%s %d %s.' % (_("HPGL2 processing. Parsing"), len(glines), _("lines")))
+        self.app.inform.emit('%s %d %s.' % (_("HPGL2 processing. Parsing"), len(glines), _("Lines").lower()))
         try:
         try:
             for gline in glines:
             for gline in glines:
                 if self.app.abort_flag:
                 if self.app.abort_flag:

+ 71 - 112
appParsers/ParsePDF.py

@@ -82,12 +82,10 @@ class PdfParser(QtCore.QObject):
         self.restore_gs_re = re.compile(r'^.*Q.*$')
         self.restore_gs_re = re.compile(r'^.*Q.*$')
 
 
         # graphic stack where we save parameters like transformation, line_width
         # graphic stack where we save parameters like transformation, line_width
-        self.gs = {}
         # each element is a list composed of sublist elements
         # each element is a list composed of sublist elements
         # (each sublist has 2 lists each having 2 elements: first is offset like:
         # (each sublist has 2 lists each having 2 elements: first is offset like:
         # offset_geo = [off_x, off_y], second element is scale list with 2 elements, like: scale_geo = [sc_x, sc_yy])
         # offset_geo = [off_x, off_y], second element is scale list with 2 elements, like: scale_geo = [sc_x, sc_yy])
-        self.gs['transform'] = []
-        self.gs['line_width'] = []  # each element is a float
+        self.gs = {'transform': [], 'line_width': []}
 
 
         # conversion factor to INCH
         # conversion factor to INCH
         self.point_to_unit_factor = 0.01388888888
         self.point_to_unit_factor = 0.01388888888
@@ -102,15 +100,17 @@ class PdfParser(QtCore.QObject):
             # 1 inch = 72 points => 1 point = 1 / 72 = 0.01388888888 inch
             # 1 inch = 72 points => 1 point = 1 / 72 = 0.01388888888 inch
             self.point_to_unit_factor = 1 / 72
             self.point_to_unit_factor = 1 / 72
 
 
-        path = {}
-        path['lines'] = []  # it's a list of lines subpaths
-        path['bezier'] = []  # it's a list of bezier arcs subpaths
-        path['rectangle'] = []  # it's a list of rectangle subpaths
+        path = {
+            'lines': [],        # it's a list of lines subpaths
+            'bezier': [],       # it's a list of bezier arcs subpaths
+            'rectangle': []     # it's a list of rectangle subpaths
+        }
 
 
-        subpath = {}
-        subpath['lines'] = []  # it's a list of points
-        subpath['bezier'] = []  # it's a list of sublists each like this [start, c1, c2, stop]
-        subpath['rectangle'] = []  # it's a list of sublists of points
+        subpath = {
+            'lines': [],        # it's a list of points
+            'bezier': [],       # it's a list of sublists each like this [start, c1, c2, stop]
+            'rectangle': []     # it's a list of sublists of points
+        }
 
 
         # store the start point (when 'm' command is encountered)
         # store the start point (when 'm' command is encountered)
         current_subpath = None
         current_subpath = None
@@ -141,12 +141,14 @@ class PdfParser(QtCore.QObject):
 
 
         # store the apertures with clear geometry here
         # store the apertures with clear geometry here
         # we are interested only in the circular geometry (drill holes) therefore we target only Bezier subpaths
         # we are interested only in the circular geometry (drill holes) therefore we target only Bezier subpaths
-        clear_apertures_dict = {}
         # everything will be stored in the '0' aperture since we are dealing with clear polygons not strokes
         # everything will be stored in the '0' aperture since we are dealing with clear polygons not strokes
-        clear_apertures_dict['0'] = {}
-        clear_apertures_dict['0']['size'] = 0.0
-        clear_apertures_dict['0']['type'] = 'C'
-        clear_apertures_dict['0']['geometry'] = []
+        clear_apertures_dict = {
+            '0': {
+                'size': 0.0,
+                'type': 'C',
+                'geometry': []
+            }
+        }
 
 
         # on stroke color change we create a new apertures dictionary and store the old one in a storage from where
         # on stroke color change we create a new apertures dictionary and store the old one in a storage from where
         # it will be transformed into Gerber object
         # it will be transformed into Gerber object
@@ -527,14 +529,10 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                                     apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                                 apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                     else:
                     else:
                         if str(aperture) in apertures_dict.keys():
                         if str(aperture) in apertures_dict.keys():
@@ -546,14 +544,10 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                                     apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                 else:
                 else:
                     apertures_dict[str(aperture)] = {}
                     apertures_dict[str(aperture)] = {}
@@ -563,14 +557,10 @@ class PdfParser(QtCore.QObject):
                     for pdf_geo in path_geo:
                     for pdf_geo in path_geo:
                         if isinstance(pdf_geo, MultiPolygon):
                         if isinstance(pdf_geo, MultiPolygon):
                             for poly in pdf_geo:
                             for poly in pdf_geo:
-                                new_el = {}
-                                new_el['solid'] = poly
-                                new_el['follow'] = poly.exterior
+                                new_el = {'solid': poly, 'follow': poly.exterior}
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                         else:
                         else:
-                            new_el = {}
-                            new_el['solid'] = pdf_geo
-                            new_el['follow'] = pdf_geo.exterior
+                            new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                             apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                             apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
 
 
                 continue
                 continue
@@ -675,12 +665,10 @@ class PdfParser(QtCore.QObject):
                         if path_geo:
                         if path_geo:
                             try:
                             try:
                                 for g in path_geo:
                                 for g in path_geo:
-                                    new_el = {}
-                                    new_el['clear'] = g
+                                    new_el = {'clear': g}
                                     clear_apertures_dict['0']['geometry'].append(new_el)
                                     clear_apertures_dict['0']['geometry'].append(new_el)
                             except TypeError:
                             except TypeError:
-                                new_el = {}
-                                new_el['clear'] = path_geo
+                                new_el = {'clear': path_geo}
                                 clear_apertures_dict['0']['geometry'].append(new_el)
                                 clear_apertures_dict['0']['geometry'].append(new_el)
 
 
                     # now that we finished searching for drill holes (this is not very precise because holes in the
                     # now that we finished searching for drill holes (this is not very precise because holes in the
@@ -690,12 +678,10 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['clear'] = poly
+                                    new_el = {'clear': poly}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['clear'] = pdf_geo
+                                new_el = {'clear': pdf_geo}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                     except KeyError:
                     except KeyError:
                         # in case there is no stroke width yet therefore no aperture
                         # in case there is no stroke width yet therefore no aperture
@@ -706,12 +692,10 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['clear'] = poly
+                                    new_el = {'clear': poly}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['clear'] = pdf_geo
+                                new_el = {'clear': pdf_geo}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                 else:
                 else:
                     # else, add the geometry as usual
                     # else, add the geometry as usual
@@ -719,14 +703,10 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                     except KeyError:
                     except KeyError:
                         # in case there is no stroke width yet therefore no aperture
                         # in case there is no stroke width yet therefore no aperture
@@ -737,14 +717,10 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                     continue
                     continue
 
 
@@ -890,50 +866,41 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                                     apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                                 apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
                     else:
                     else:
                         if str(aperture) in apertures_dict.keys():
                         if str(aperture) in apertures_dict.keys():
                             aperture += 1
                             aperture += 1
-                        apertures_dict[str(aperture)] = {}
-                        apertures_dict[str(aperture)]['size'] = round(applied_size, 5)
-                        apertures_dict[str(aperture)]['type'] = 'C'
-                        apertures_dict[str(aperture)]['geometry'] = []
+                        apertures_dict[str(aperture)] = {
+                            'size': round(applied_size, 5),
+                            'type': 'C',
+                            'geometry': []
+                        }
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                                     apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                 else:
                 else:
-                    apertures_dict[str(aperture)] = {}
-                    apertures_dict[str(aperture)]['size'] = round(applied_size, 5)
-                    apertures_dict[str(aperture)]['type'] = 'C'
-                    apertures_dict[str(aperture)]['geometry'] = []
+                    apertures_dict[str(aperture)] = {
+                        'size': round(applied_size, 5),
+                        'type': 'C',
+                        'geometry': []
+                    }
+
                     for pdf_geo in path_geo:
                     for pdf_geo in path_geo:
                         if isinstance(pdf_geo, MultiPolygon):
                         if isinstance(pdf_geo, MultiPolygon):
                             for poly in pdf_geo:
                             for poly in pdf_geo:
-                                new_el = {}
-                                new_el['solid'] = poly
-                                new_el['follow'] = poly.exterior
+                                new_el = {'solid': poly, 'follow': poly.exterior}
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                                 apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                         else:
                         else:
-                            new_el = {}
-                            new_el['solid'] = pdf_geo
-                            new_el['follow'] = pdf_geo.exterior
+                            new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                             apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
                             apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
 
 
                 # ############################################# ##
                 # ############################################# ##
@@ -946,60 +913,52 @@ class PdfParser(QtCore.QObject):
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in fill_geo:
                                 for poly in fill_geo:
-                                    new_el = {}
-                                    new_el['clear'] = poly
+                                    new_el = {'clear': poly}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['clear'] = pdf_geo
+                                new_el = {'clear': pdf_geo}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                     except KeyError:
                     except KeyError:
                         # in case there is no stroke width yet therefore no aperture
                         # in case there is no stroke width yet therefore no aperture
-                        apertures_dict['0'] = {}
-                        apertures_dict['0']['size'] = round(applied_size, 5)
-                        apertures_dict['0']['type'] = 'C'
-                        apertures_dict['0']['geometry'] = []
+                        apertures_dict['0'] = {
+                            'size': round(applied_size, 5),
+                            'type': 'C',
+                            'geometry': []
+                        }
+
                         for pdf_geo in fill_geo:
                         for pdf_geo in fill_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['clear'] = poly
+                                    new_el = {'clear': poly}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['clear'] = pdf_geo
+                                new_el = {'clear': pdf_geo}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                 else:
                 else:
                     try:
                     try:
                         for pdf_geo in path_geo:
                         for pdf_geo in path_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in fill_geo:
                                 for poly in fill_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                     except KeyError:
                     except KeyError:
                         # in case there is no stroke width yet therefore no aperture
                         # in case there is no stroke width yet therefore no aperture
-                        apertures_dict['0'] = {}
-                        apertures_dict['0']['size'] = round(applied_size, 5)
-                        apertures_dict['0']['type'] = 'C'
-                        apertures_dict['0']['geometry'] = []
+                        apertures_dict['0'] = {
+                            'size': round(applied_size, 5),
+                            'type': 'C',
+                            'geometry': []
+                        }
+
                         for pdf_geo in fill_geo:
                         for pdf_geo in fill_geo:
                             if isinstance(pdf_geo, MultiPolygon):
                             if isinstance(pdf_geo, MultiPolygon):
                                 for poly in pdf_geo:
                                 for poly in pdf_geo:
-                                    new_el = {}
-                                    new_el['solid'] = poly
-                                    new_el['follow'] = poly.exterior
+                                    new_el = {'solid': poly, 'follow': poly.exterior}
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                     apertures_dict['0']['geometry'].append(deepcopy(new_el))
                             else:
                             else:
-                                new_el = {}
-                                new_el['solid'] = pdf_geo
-                                new_el['follow'] = pdf_geo.exterior
+                                new_el = {'solid': pdf_geo, 'follow': pdf_geo.exterior}
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
                                 apertures_dict['0']['geometry'].append(deepcopy(new_el))
 
 
                 continue
                 continue

+ 5 - 1
appParsers/ParseSVG.py

@@ -24,7 +24,7 @@ from svg.path import Line, Arc, CubicBezier, QuadraticBezier, parse_path
 # from svg.path.path import Move
 # from svg.path.path import Move
 # from svg.path.path import Close
 # from svg.path.path import Close
 import svg.path
 import svg.path
-from shapely.geometry import LineString, MultiLineString
+from shapely.geometry import LineString, MultiLineString, Point
 from shapely.affinity import skew, affine_transform, rotate
 from shapely.affinity import skew, affine_transform, rotate
 import numpy as np
 import numpy as np
 
 
@@ -286,6 +286,8 @@ def svgcircle2shapely(circle, n_points=64, factor=1.0):
     :type circle:       xml.etree.ElementTree.Element
     :type circle:       xml.etree.ElementTree.Element
     :param n_points:    circle resolution; nr of points to b e used to approximate a circle
     :param n_points:    circle resolution; nr of points to b e used to approximate a circle
     :type n_points:     int
     :type n_points:     int
+    :param factor:
+    :type factor:       float
     :return:            Shapely representation of the circle.
     :return:            Shapely representation of the circle.
     :rtype:             shapely.geometry.polygon.LinearRing
     :rtype:             shapely.geometry.polygon.LinearRing
     """
     """
@@ -310,6 +312,8 @@ def svgellipse2shapely(ellipse, n_points=64, factor=1.0):
     :type ellipse:      xml.etree.ElementTree.Element
     :type ellipse:      xml.etree.ElementTree.Element
     :param n_points:    Number of discrete points in output.
     :param n_points:    Number of discrete points in output.
     :type n_points:     int
     :type n_points:     int
+    :param factor:
+    :type factor:       float
     :return:            Shapely representation of the ellipse.
     :return:            Shapely representation of the ellipse.
     :rtype:             shapely.geometry.polygon.LinearRing
     :rtype:             shapely.geometry.polygon.LinearRing
     """
     """

+ 4 - 2
appPreProcessor.py

@@ -147,8 +147,10 @@ class AppPreProcTools(object, metaclass=ABCPreProcRegister):
 
 
 
 
 def load_preprocessors(app):
 def load_preprocessors(app):
-    preprocessors_path_search = [os.path.join(app.data_path, 'preprocessors', '*.py'),
-                                  os.path.join('preprocessors', '*.py')]
+    preprocessors_path_search = [
+        os.path.join(app.data_path, 'preprocessors', '*.py'),
+        os.path.join('preprocessors', '*.py')
+    ]
     import glob
     import glob
     for path_search in preprocessors_path_search:
     for path_search in preprocessors_path_search:
         for file in glob.glob(path_search):
         for file in glob.glob(path_search):

+ 5 - 4
appTools/ToolAlignObjects.py

@@ -242,7 +242,7 @@ class AlignObjects(AppTool):
     def check_points(self):
     def check_points(self):
         if len(self.clicked_points) == 1:
         if len(self.clicked_points) == 1:
             self.app.inform.emit('%s: %s. %s' % (
             self.app.inform.emit('%s: %s. %s' % (
-                _("First Point"), _("Click on the DESTINATION point."), _("Or right click to cancel.")))
+                _("First Point"), _("Click on the DESTINATION point ..."), _("Or right click to cancel.")))
             self.target_obj = self.aligner_obj
             self.target_obj = self.aligner_obj
             self.reset_color()
             self.reset_color()
             self.set_color()
             self.set_color()
@@ -264,7 +264,7 @@ class AlignObjects(AppTool):
 
 
         if len(self.clicked_points) == 3:
         if len(self.clicked_points) == 3:
             self.app.inform.emit('%s: %s. %s' % (
             self.app.inform.emit('%s: %s. %s' % (
-                _("Second Point"), _("Click on the DESTINATION point."), _("Or right click to cancel.")))
+                _("Second Point"), _("Click on the DESTINATION point ..."), _("Or right click to cancel.")))
             self.target_obj = self.aligner_obj
             self.target_obj = self.aligner_obj
             self.reset_color()
             self.reset_color()
             self.set_color()
             self.set_color()
@@ -383,7 +383,7 @@ class AlignUI:
         grid0.setColumnStretch(1, 1)
         grid0.setColumnStretch(1, 1)
         self.layout.addLayout(grid0)
         self.layout.addLayout(grid0)
 
 
-        self.aligned_label =FCLabel('<b>%s:</b>' % _("MOVING object"))
+        self.aligned_label = FCLabel('<b>%s:</b>' % _("MOVING object"))
         grid0.addWidget(self.aligned_label, 0, 0, 1, 2)
         grid0.addWidget(self.aligned_label, 0, 0, 1, 2)
 
 
         self.aligned_label.setToolTip(
         self.aligned_label.setToolTip(
@@ -478,7 +478,8 @@ class AlignUI:
         grid0.addWidget(separator_line, 14, 0, 1, 2)
         grid0.addWidget(separator_line, 14, 0, 1, 2)
 
 
         # Buttons
         # Buttons
-        self.align_object_button =FCButton(_("Align Object"))
+        self.align_object_button = FCButton(_("Align Object"))
+        self.align_object_button.setIcon(QtGui.QIcon(self.app.resource_location + '/align16.png'))
         self.align_object_button.setToolTip(
         self.align_object_button.setToolTip(
             _("Align the specified object to the aligner object.\n"
             _("Align the specified object to the aligner object.\n"
               "If only one point is used then it assumes translation.\n"
               "If only one point is used then it assumes translation.\n"

+ 197 - 68
appTools/ToolCalculators.py

@@ -7,7 +7,7 @@
 
 
 from PyQt5 import QtWidgets, QtGui
 from PyQt5 import QtWidgets, QtGui
 from appTool import AppTool
 from appTool import AppTool
-from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, NumericalEvalEntry
+from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, NumericalEvalEntry, FCLabel, RadioSet, FCButton
 import math
 import math
 
 
 import gettext
 import gettext
@@ -48,6 +48,8 @@ class ToolCalculator(AppTool):
         self.ui.calculate_plate_button.clicked.connect(self.on_calculate_eplate)
         self.ui.calculate_plate_button.clicked.connect(self.on_calculate_eplate)
         self.ui.reset_button.clicked.connect(self.set_tool_ui)
         self.ui.reset_button.clicked.connect(self.set_tool_ui)
 
 
+        self.ui.area_sel_radio.activated_custom.connect(self.on_area_calculation_radio)
+
     def run(self, toggle=True):
     def run(self, toggle=True):
         self.app.defaults.report_usage("ToolCalculators()")
         self.app.defaults.report_usage("ToolCalculators()")
 
 
@@ -80,7 +82,7 @@ class ToolCalculator(AppTool):
         AppTool.install(self, icon, separator, shortcut='Alt+C', **kwargs)
         AppTool.install(self, icon, separator, shortcut='Alt+C', **kwargs)
 
 
     def set_tool_ui(self):
     def set_tool_ui(self):
-        self.units = self.app.defaults['units'].upper()
+        self.units = self.app.defaults['units'].lower()
 
 
         # ## Initialize form
         # ## Initialize form
         self.ui.mm_entry.set_value('%.*f' % (self.decimals, 0))
         self.ui.mm_entry.set_value('%.*f' % (self.decimals, 0))
@@ -90,8 +92,10 @@ class ToolCalculator(AppTool):
         width = self.app.defaults["tools_calc_electro_width"]
         width = self.app.defaults["tools_calc_electro_width"]
         density = self.app.defaults["tools_calc_electro_cdensity"]
         density = self.app.defaults["tools_calc_electro_cdensity"]
         growth = self.app.defaults["tools_calc_electro_growth"]
         growth = self.app.defaults["tools_calc_electro_growth"]
+
         self.ui.pcblength_entry.set_value(length)
         self.ui.pcblength_entry.set_value(length)
         self.ui.pcbwidth_entry.set_value(width)
         self.ui.pcbwidth_entry.set_value(width)
+        self.ui.area_entry.set_value(self.app.defaults["tools_calc_electro_area"])
         self.ui.cdensity_entry.set_value(density)
         self.ui.cdensity_entry.set_value(density)
         self.ui.growth_entry.set_value(growth)
         self.ui.growth_entry.set_value(growth)
         self.ui.cvalue_entry.set_value(0.00)
         self.ui.cvalue_entry.set_value(0.00)
@@ -106,6 +110,35 @@ class ToolCalculator(AppTool):
         self.ui.cutDepth_entry.set_value(cut_z)
         self.ui.cutDepth_entry.set_value(cut_z)
         self.ui.effectiveToolDia_entry.set_value('0.0000')
         self.ui.effectiveToolDia_entry.set_value('0.0000')
 
 
+        self.ui.area_sel_radio.set_value('d')
+        self.on_area_calculation_radio(val='d')
+
+    def on_area_calculation_radio(self, val):
+        if val == 'a':
+            self.ui.pcbwidthlabel.hide()
+            self.ui.pcbwidth_entry.hide()
+            self.ui.width_unit.hide()
+
+            self.ui.pcblengthlabel.hide()
+            self.ui.pcblength_entry.hide()
+            self.ui.length_unit.hide()
+
+            self.ui.area_label.show()
+            self.ui.area_entry.show()
+            self.ui.area_unit.show()
+        else:
+            self.ui.pcbwidthlabel.show()
+            self.ui.pcbwidth_entry.show()
+            self.ui.width_unit.show()
+
+            self.ui.pcblengthlabel.show()
+            self.ui.pcblength_entry.show()
+            self.ui.length_unit.show()
+
+            self.ui.area_label.hide()
+            self.ui.area_entry.hide()
+            self.ui.area_unit.hide()
+
     def on_calculate_tool_dia(self):
     def on_calculate_tool_dia(self):
         # Calculation:
         # Calculation:
         # Manufacturer gives total angle of the the tip but we need only half of it
         # Manufacturer gives total angle of the the tip but we need only half of it
@@ -123,23 +156,29 @@ class ToolCalculator(AppTool):
         cut_depth = -cut_depth if cut_depth < 0 else cut_depth
         cut_depth = -cut_depth if cut_depth < 0 else cut_depth
 
 
         tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle)))
         tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle)))
-        self.ui.effectiveToolDia_entry.set_value("%.*f" % (self.decimals, tool_diameter))
+        self.ui.effectiveToolDia_entry.set_value(self.app.dec_format(tool_diameter, self.decimals))
 
 
     def on_calculate_inch_units(self):
     def on_calculate_inch_units(self):
-        mm_val = float(self.mm_entry.get_value())
+        mm_val = float(self.ui.mm_entry.get_value())
         self.ui.inch_entry.set_value('%.*f' % (self.decimals, (mm_val / 25.4)))
         self.ui.inch_entry.set_value('%.*f' % (self.decimals, (mm_val / 25.4)))
 
 
     def on_calculate_mm_units(self):
     def on_calculate_mm_units(self):
-        inch_val = float(self.inch_entry.get_value())
+        inch_val = float(self.ui.inch_entry.get_value())
         self.ui.mm_entry.set_value('%.*f' % (self.decimals, (inch_val * 25.4)))
         self.ui.mm_entry.set_value('%.*f' % (self.decimals, (inch_val * 25.4)))
 
 
     def on_calculate_eplate(self):
     def on_calculate_eplate(self):
-        length = float(self.ui.pcblength_entry.get_value())
-        width = float(self.ui.pcbwidth_entry.get_value())
-        density = float(self.ui.cdensity_entry.get_value())
-        copper = float(self.ui.growth_entry.get_value())
+        area_calc_sel = self.ui.area_sel_radio.get_value()
+        length = self.ui.pcblength_entry.get_value()
+        width = self.ui.pcbwidth_entry.get_value()
+        area = self.ui.area_entry.get_value()
+
+        density = self.ui.cdensity_entry.get_value()
+        copper = self.ui.growth_entry.get_value()
 
 
-        calculated_current = (length * width * density) * 0.0021527820833419
+        if area_calc_sel == 'd':
+            calculated_current = (length * width * density) * 0.0021527820833419
+        else:
+            calculated_current = (area * density) * 0.0021527820833419
         calculated_time = copper * 2.142857142857143 * float(20 / density)
         calculated_time = copper * 2.142857142857143 * float(20 / density)
 
 
         self.ui.cvalue_entry.set_value('%.2f' % calculated_current)
         self.ui.cvalue_entry.set_value('%.2f' % calculated_current)
@@ -157,9 +196,10 @@ class CalcUI:
         self.app = app
         self.app = app
         self.decimals = self.app.decimals
         self.decimals = self.app.decimals
         self.layout = layout
         self.layout = layout
+        self.units = self.app.defaults['units'].lower()
 
 
         # ## Title
         # ## Title
-        title_label = QtWidgets.QLabel("%s" % self.toolName)
+        title_label = FCLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
         title_label.setStyleSheet("""
                                 QLabel
                                 QLabel
                                 {
                                 {
@@ -173,19 +213,19 @@ class CalcUI:
         # ## Units Calculator #
         # ## Units Calculator #
         # #####################
         # #####################
 
 
-        self.unists_spacer_label = QtWidgets.QLabel(" ")
+        self.unists_spacer_label = FCLabel(" ")
         self.layout.addWidget(self.unists_spacer_label)
         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)
+        units_label = FCLabel("<font size=3><b>%s</b></font>" % self.unitsName)
         self.layout.addWidget(units_label)
         self.layout.addWidget(units_label)
 
 
         # Grid Layout
         # Grid Layout
         grid_units_layout = QtWidgets.QGridLayout()
         grid_units_layout = QtWidgets.QGridLayout()
         self.layout.addLayout(grid_units_layout)
         self.layout.addLayout(grid_units_layout)
 
 
-        inch_label = QtWidgets.QLabel(_("INCH"))
-        mm_label = QtWidgets.QLabel(_("MM"))
+        inch_label = FCLabel(_("INCH"))
+        mm_label = FCLabel(_("MM"))
         grid_units_layout.addWidget(mm_label, 0, 0)
         grid_units_layout.addWidget(mm_label, 0, 0)
         grid_units_layout.addWidget(inch_label, 0, 1)
         grid_units_layout.addWidget(inch_label, 0, 1)
 
 
@@ -206,21 +246,21 @@ class CalcUI:
         # ##############################
         # ##############################
         # ## V-shape Tool Calculator ###
         # ## V-shape Tool Calculator ###
         # ##############################
         # ##############################
-        self.v_shape_spacer_label = QtWidgets.QLabel(" ")
+        self.v_shape_spacer_label = FCLabel(" ")
         self.layout.addWidget(self.v_shape_spacer_label)
         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)
+        v_shape_title_label = FCLabel("<font size=3><b>%s</b></font>" % self.v_shapeName)
         self.layout.addWidget(v_shape_title_label)
         self.layout.addWidget(v_shape_title_label)
 
 
         # ## Form Layout
         # ## Form Layout
         form_layout = QtWidgets.QFormLayout()
         form_layout = QtWidgets.QFormLayout()
         self.layout.addLayout(form_layout)
         self.layout.addLayout(form_layout)
 
 
-        self.tipDia_label = QtWidgets.QLabel('%s:' % _("Tip Diameter"))
+        self.tipDia_label = FCLabel('%s:' % _("Tip Diameter"))
         self.tipDia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.tipDia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.tipDia_entry.set_precision(self.decimals)
         self.tipDia_entry.set_precision(self.decimals)
-        self.tipDia_entry.set_range(0.0, 9999.9999)
+        self.tipDia_entry.set_range(0.0, 10000.0000)
         self.tipDia_entry.setSingleStep(0.1)
         self.tipDia_entry.setSingleStep(0.1)
 
 
         # self.tipDia_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         # self.tipDia_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
@@ -228,7 +268,7 @@ class CalcUI:
             _("This is the tool tip diameter.\n"
             _("This is the tool tip diameter.\n"
               "It is specified by manufacturer.")
               "It is specified by manufacturer.")
         )
         )
-        self.tipAngle_label = QtWidgets.QLabel('%s:' % _("Tip Angle"))
+        self.tipAngle_label = FCLabel('%s:' % _("Tip Angle"))
         self.tipAngle_entry = FCSpinner(callback=self.confirmation_message_int)
         self.tipAngle_entry = FCSpinner(callback=self.confirmation_message_int)
         self.tipAngle_entry.set_range(0, 180)
         self.tipAngle_entry.set_range(0, 180)
         self.tipAngle_entry.set_step(5)
         self.tipAngle_entry.set_step(5)
@@ -237,16 +277,16 @@ class CalcUI:
         self.tipAngle_label.setToolTip(_("This is the angle of the tip of the tool.\n"
         self.tipAngle_label.setToolTip(_("This is the angle of the tip of the tool.\n"
                                          "It is specified by manufacturer."))
                                          "It is specified by manufacturer."))
 
 
-        self.cutDepth_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
+        self.cutDepth_label = FCLabel('%s:' % _("Cut Z"))
         self.cutDepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.cutDepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.cutDepth_entry.set_range(-9999.9999, 9999.9999)
+        self.cutDepth_entry.set_range(-10000.0000, 10000.0000)
         self.cutDepth_entry.set_precision(self.decimals)
         self.cutDepth_entry.set_precision(self.decimals)
 
 
         # self.cutDepth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         # self.cutDepth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         self.cutDepth_label.setToolTip(_("This is the depth to cut into the material.\n"
         self.cutDepth_label.setToolTip(_("This is the depth to cut into the material.\n"
                                          "In the CNCJob is the CutZ parameter."))
                                          "In the CNCJob is the CutZ parameter."))
 
 
-        self.effectiveToolDia_label = QtWidgets.QLabel('%s:' % _("Tool Diameter"))
+        self.effectiveToolDia_label = FCLabel('%s:' % _("Tool Diameter"))
         self.effectiveToolDia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.effectiveToolDia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.effectiveToolDia_entry.set_precision(self.decimals)
         self.effectiveToolDia_entry.set_precision(self.decimals)
 
 
@@ -262,8 +302,9 @@ class CalcUI:
         form_layout.addRow(self.effectiveToolDia_label, self.effectiveToolDia_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 = FCButton(_("Calculate"))
+        self.calculate_vshape_button.setIcon(QtGui.QIcon(self.app.resource_location + '/calculator16.png'))
+
         self.calculate_vshape_button.setToolTip(
         self.calculate_vshape_button.setToolTip(
             _("Calculate either the Cut Z or the effective tool diameter,\n  "
             _("Calculate either the Cut Z or the effective tool diameter,\n  "
               "depending on which is desired and which is known. ")
               "depending on which is desired and which is known. ")
@@ -275,11 +316,10 @@ class CalcUI:
         # ## ElectroPlating Tool Calculator ##
         # ## ElectroPlating Tool Calculator ##
         # ####################################
         # ####################################
 
 
-        self.plate_spacer_label = QtWidgets.QLabel(" ")
-        self.layout.addWidget(self.plate_spacer_label)
+        self.layout.addWidget(FCLabel(""))
 
 
         # ## 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 = FCLabel("<font size=3><b>%s</b></font>" % self.eplateName)
         plate_title_label.setToolTip(
         plate_title_label.setToolTip(
             _("This calculator is useful for those who plate the via/pad/drill holes,\n"
             _("This calculator is useful for those who plate the via/pad/drill holes,\n"
               "using a method like graphite ink or calcium hypophosphite ink or palladium chloride.")
               "using a method like graphite ink or calcium hypophosphite ink or palladium chloride.")
@@ -287,79 +327,168 @@ class CalcUI:
         self.layout.addWidget(plate_title_label)
         self.layout.addWidget(plate_title_label)
 
 
         # ## Plate Form Layout
         # ## Plate Form Layout
-        plate_form_layout = QtWidgets.QFormLayout()
-        self.layout.addLayout(plate_form_layout)
+        grid2 = QtWidgets.QGridLayout()
+        grid2.setColumnStretch(0, 0)
+        grid2.setColumnStretch(1, 1)
+        self.layout.addLayout(grid2)
+
+        # Area Calculation
+        self.area_sel_label = FCLabel('%s:' % _("Area Calculation"))
+        self.area_sel_label.setToolTip(
+            _("Choose how to calculate the board area.")
+        )
+        self.area_sel_radio = RadioSet([
+            {'label': _('Dimensions'), 'value': 'd'},
+            {"label": _("Area"), "value": "a"}
+        ], stretch=False)
 
 
-        self.pcblengthlabel = QtWidgets.QLabel('%s:' % _("Board Length"))
+        grid2.addWidget(self.area_sel_label, 0, 0)
+        grid2.addWidget(self.area_sel_radio, 1, 0, 1, 2)
+
+        # BOARD LENGTH
+        self.pcblengthlabel = FCLabel('%s:' % _("Board Length"))
+        self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
         self.pcblength_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.pcblength_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.pcblength_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
         self.pcblength_entry.set_precision(self.decimals)
         self.pcblength_entry.set_precision(self.decimals)
-        self.pcblength_entry.set_range(0.0, 9999.9999)
+        self.pcblength_entry.set_range(0.0, 10000.0000)
 
 
-        # self.pcblength_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
+        self.length_unit = FCLabel('%s' % _("cm"))
+        self.length_unit.setMinimumWidth(25)
 
 
-        self.pcbwidthlabel = QtWidgets.QLabel('%s:' % _("Board Width"))
+        l_hlay = QtWidgets.QHBoxLayout()
+        l_hlay.addWidget(self.pcblength_entry)
+        l_hlay.addWidget(self.length_unit)
+
+        grid2.addWidget(self.pcblengthlabel, 2, 0)
+        grid2.addLayout(l_hlay, 2, 1)
+
+        # BOARD WIDTH
+        self.pcbwidthlabel = FCLabel('%s:' % _("Board Width"))
+        self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
         self.pcbwidth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.pcbwidth_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.pcbwidth_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
         self.pcbwidth_entry.set_precision(self.decimals)
         self.pcbwidth_entry.set_precision(self.decimals)
-        self.pcbwidth_entry.set_range(0.0, 9999.9999)
+        self.pcbwidth_entry.set_range(0.0, 10000.0000)
 
 
-        # self.pcbwidth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
+        self.width_unit = FCLabel('%s' % _("cm"))
+        self.width_unit.setMinimumWidth(25)
+
+        w_hlay = QtWidgets.QHBoxLayout()
+        w_hlay.addWidget(self.pcbwidth_entry)
+        w_hlay.addWidget(self.width_unit)
+
+        grid2.addWidget(self.pcbwidthlabel, 4, 0)
+        grid2.addLayout(w_hlay, 4, 1)
+
+        # AREA
+        self.area_label = FCLabel('%s:' % _("Area"))
+        self.area_label.setToolTip(_('This is the board area.'))
+        self.area_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.area_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
+        self.area_entry.set_precision(self.decimals)
+        self.area_entry.set_range(0.0, 10000.0000)
+
+        self.area_unit = FCLabel('%s<sup>2</sup>' % _("cm"))
+        self.area_unit.setMinimumWidth(25)
+
+        a_hlay = QtWidgets.QHBoxLayout()
+        a_hlay.addWidget(self.area_entry)
+        a_hlay.addWidget(self.area_unit)
 
 
-        self.cdensity_label = QtWidgets.QLabel('%s:' % _("Current Density"))
+        grid2.addWidget(self.area_label, 6, 0)
+        grid2.addLayout(a_hlay, 6, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid2.addWidget(separator_line, 7, 0, 1, 2)
+
+        # DENSITY
+        self.cdensity_label = FCLabel('%s:' % _("Current Density"))
+        self.cdensity_label.setToolTip(_("Current density to pass through the board. \n"
+                                         "In Amps per Square Feet ASF."))
         self.cdensity_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.cdensity_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.cdensity_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
         self.cdensity_entry.set_precision(self.decimals)
         self.cdensity_entry.set_precision(self.decimals)
-        self.cdensity_entry.set_range(0.0, 9999.9999)
+        self.cdensity_entry.set_range(0.0, 10000.0000)
         self.cdensity_entry.setSingleStep(0.1)
         self.cdensity_entry.setSingleStep(0.1)
 
 
-        # self.cdensity_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.cdensity_label.setToolTip(_("Current density to pass through the board. \n"
-                                         "In Amps per Square Feet ASF."))
+        density_unit = FCLabel('%s' % "ASF")
+        density_unit.setMinimumWidth(25)
 
 
-        self.growth_label = QtWidgets.QLabel('%s:' % _("Copper Growth"))
+        d_hlay = QtWidgets.QHBoxLayout()
+        d_hlay.addWidget(self.cdensity_entry)
+        d_hlay.addWidget(density_unit)
+
+        grid2.addWidget(self.cdensity_label, 8, 0)
+        grid2.addLayout(d_hlay, 8, 1)
+
+        # COPPER GROWTH
+        self.growth_label = FCLabel('%s:' % _("Copper Growth"))
+        self.growth_label.setToolTip(_("How thick the copper growth is intended to be.\n"
+                                       "In microns."))
         self.growth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.growth_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.growth_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
         self.growth_entry.set_precision(self.decimals)
         self.growth_entry.set_precision(self.decimals)
-        self.growth_entry.set_range(0.0, 9999.9999)
+        self.growth_entry.set_range(0.0, 10000.0000)
         self.growth_entry.setSingleStep(0.01)
         self.growth_entry.setSingleStep(0.01)
 
 
-        # self.growth_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.growth_label.setToolTip(_("How thick the copper growth is intended to be.\n"
-                                       "In microns."))
+        growth_unit = FCLabel('%s' % _("um"))
+        growth_unit.setMinimumWidth(25)
 
 
-        # self.growth_entry.setEnabled(False)
+        g_hlay = QtWidgets.QHBoxLayout()
+        g_hlay.addWidget(self.growth_entry)
+        g_hlay.addWidget(growth_unit)
 
 
-        self.cvaluelabel = QtWidgets.QLabel('%s:' % _("Current Value"))
+        grid2.addWidget(self.growth_label, 10, 0)
+        grid2.addLayout(g_hlay, 10, 1)
+
+        # CURRENT
+        self.cvaluelabel = FCLabel('%s:' % _("Current Value"))
+        self.cvaluelabel.setToolTip(_('This is the current intensity value\n'
+                                      'to be set on the Power Supply. In Amps.'))
         self.cvalue_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.cvalue_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.cvalue_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
         self.cvalue_entry.set_precision(self.decimals)
         self.cvalue_entry.set_precision(self.decimals)
-        self.cvalue_entry.set_range(0.0, 9999.9999)
+        self.cvalue_entry.set_range(0.0, 10000.0000)
         self.cvalue_entry.setSingleStep(0.1)
         self.cvalue_entry.setSingleStep(0.1)
 
 
-        # self.cvalue_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.cvaluelabel.setToolTip(_('This is the current intensity value\n'
-                                      'to be set on the Power Supply. In Amps.'))
+        current_unit = FCLabel('%s' % "A")
+        current_unit.setMinimumWidth(25)
         self.cvalue_entry.setReadOnly(True)
         self.cvalue_entry.setReadOnly(True)
 
 
-        self.timelabel = QtWidgets.QLabel('%s:' % _("Time"))
+        c_hlay = QtWidgets.QHBoxLayout()
+        c_hlay.addWidget(self.cvalue_entry)
+        c_hlay.addWidget(current_unit)
+
+        grid2.addWidget(self.cvaluelabel, 12, 0)
+        grid2.addLayout(c_hlay, 12, 1)
+
+        # TIME
+        self.timelabel = FCLabel('%s:' % _("Time"))
+        self.timelabel.setToolTip(_('This is the calculated time required for the procedure.\n'
+                                    'In minutes.'))
         self.time_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.time_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.time_entry.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Preferred)
         self.time_entry.set_precision(self.decimals)
         self.time_entry.set_precision(self.decimals)
-        self.time_entry.set_range(0.0, 9999.9999)
+        self.time_entry.set_range(0.0, 10000.0000)
         self.time_entry.setSingleStep(0.1)
         self.time_entry.setSingleStep(0.1)
 
 
-        # self.time_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
-        self.timelabel.setToolTip(_('This is the calculated time required for the procedure.\n'
-                                    'In minutes.'))
+        time_unit = FCLabel('%s' % "min")
+        time_unit.setMinimumWidth(25)
         self.time_entry.setReadOnly(True)
         self.time_entry.setReadOnly(True)
 
 
-        plate_form_layout.addRow(self.pcblengthlabel, self.pcblength_entry)
-        plate_form_layout.addRow(self.pcbwidthlabel, self.pcbwidth_entry)
-        plate_form_layout.addRow(self.cdensity_label, self.cdensity_entry)
-        plate_form_layout.addRow(self.growth_label, self.growth_entry)
-        plate_form_layout.addRow(self.cvaluelabel, self.cvalue_entry)
-        plate_form_layout.addRow(self.timelabel, self.time_entry)
+        t_hlay = QtWidgets.QHBoxLayout()
+        t_hlay.addWidget(self.time_entry)
+        t_hlay.addWidget(time_unit)
+
+        grid2.addWidget(self.timelabel, 14, 0)
+        grid2.addLayout(t_hlay, 14, 1)
 
 
         # ## Buttons
         # ## Buttons
-        self.calculate_plate_button = QtWidgets.QPushButton(_("Calculate"))
-        # self.calculate_button.setFixedWidth(70)
+        self.calculate_plate_button = FCButton(_("Calculate"))
+        self.calculate_plate_button.setIcon(QtGui.QIcon(self.app.resource_location + '/calculator16.png'))
         self.calculate_plate_button.setToolTip(
         self.calculate_plate_button.setToolTip(
             _("Calculate the current intensity value and the procedure time,\n"
             _("Calculate the current intensity value and the procedure time,\n"
               "depending on the parameters above")
               "depending on the parameters above")
@@ -369,7 +498,7 @@ class CalcUI:
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         # ## Reset Tool
         # ## Reset Tool
-        self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
+        self.reset_button = FCButton(_("Reset Tool"))
         self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
         self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
         self.reset_button.setToolTip(
         self.reset_button.setToolTip(
             _("Will reset the tool parameters.")
             _("Will reset the tool parameters.")

+ 21 - 21
appTools/ToolCalibration.py

@@ -291,7 +291,7 @@ class ToolCalibration(AppTool):
         elif len(self.click_points) == 4:
         elif len(self.click_points) == 4:
             self.ui.top_right_coordx_tgt.set_value(self.click_points[3][0])
             self.ui.top_right_coordx_tgt.set_value(self.click_points[3][0])
             self.ui.top_right_coordy_tgt.set_value(self.click_points[3][1])
             self.ui.top_right_coordy_tgt.set_value(self.click_points[3][1])
-            self.app.inform.emit('[success] %s' % _("Done. All four points have been acquired."))
+            self.app.inform.emit('[success] %s' % _("Done."))
             self.disconnect_cal_events()
             self.disconnect_cal_events()
 
 
     def reset_calibration_points(self):
     def reset_calibration_points(self):
@@ -600,7 +600,7 @@ class ToolCalibration(AppTool):
             self.cal_object = model_index.internalPointer().obj
             self.cal_object = model_index.internalPointer().obj
         except Exception as e:
         except Exception as e:
             log.debug("ToolCalibration.on_cal_button_click() --> %s" % str(e))
             log.debug("ToolCalibration.on_cal_button_click() --> %s" % str(e))
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object is selected."))
             return 'fail'
             return 'fail'
 
 
         obj_name = self.cal_object.options["name"] + "_calibrated"
         obj_name = self.cal_object.options["name"] + "_calibrated"
@@ -639,7 +639,7 @@ class ToolCalibration(AppTool):
                 if obj.tools:
                 if obj.tools:
                     obj_init.tools = deepcopy(obj.tools)
                     obj_init.tools = deepcopy(obj.tools)
             except Exception as ee:
             except Exception as ee:
-                log.debug("ToolCalibration.new_calibrated_object.initialize_geometry() --> %s" % str(ee))
+                app.log.debug("ToolCalibration.new_calibrated_object.initialize_geometry() --> %s" % str(ee))
 
 
             obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
             obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
             obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
             obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
@@ -649,7 +649,7 @@ class ToolCalibration(AppTool):
             except (AttributeError, TypeError):
             except (AttributeError, TypeError):
                 pass
                 pass
 
 
-        def initialize_gerber(obj_init, app):
+        def initialize_gerber(obj_init, app_obj):
             obj_init.solid_geometry = deepcopy(obj.solid_geometry)
             obj_init.solid_geometry = deepcopy(obj.solid_geometry)
             try:
             try:
                 obj_init.follow_geometry = deepcopy(obj.follow_geometry)
                 obj_init.follow_geometry = deepcopy(obj.follow_geometry)
@@ -671,12 +671,12 @@ class ToolCalibration(AppTool):
             obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
             obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
 
 
             try:
             try:
-                obj_init.source_file = self.app.f_handlers.export_gerber(obj_name=obj_name, filename=None,
-                                                                         local_use=obj_init, use_thread=False)
+                obj_init.source_file = app_obj.f_handlers.export_gerber(obj_name=obj_name, filename=None,
+                                                                        local_use=obj_init, use_thread=False)
             except (AttributeError, TypeError):
             except (AttributeError, TypeError):
                 pass
                 pass
 
 
-        def initialize_excellon(obj_init, app):
+        def initialize_excellon(obj_init, app_obj):
             obj_init.tools = deepcopy(obj.tools)
             obj_init.tools = deepcopy(obj.tools)
 
 
             # drills are offset, so they need to be deep copied
             # drills are offset, so they need to be deep copied
@@ -689,14 +689,14 @@ class ToolCalibration(AppTool):
 
 
             obj_init.create_geometry()
             obj_init.create_geometry()
 
 
-            obj_init.source_file = self.app.export.export_excellon(obj_name=obj_name, local_use=obj, filename=None,
-                                                                   use_thread=False)
+            obj_init.source_file = app_obj.f_handlers.export_excellon(obj_name=obj_name, local_use=obj, filename=None,
+                                                                      use_thread=False)
 
 
         obj = self.cal_object
         obj = self.cal_object
         obj_name = obj_name
         obj_name = obj_name
 
 
         if obj is None:
         if obj is None:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object is selected."))
             log.debug("ToolCalibration.new_calibrated_object() --> No object to calibrate")
             log.debug("ToolCalibration.new_calibrated_object() --> No object to calibrate")
             return 'fail'
             return 'fail'
 
 
@@ -772,7 +772,7 @@ class CalibUI:
         )
         )
 
 
         self.travelz_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.travelz_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.travelz_entry.set_range(-9999.9999, 9999.9999)
+        self.travelz_entry.set_range(-10000.0000, 10000.0000)
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
 
 
@@ -786,7 +786,7 @@ class CalibUI:
         )
         )
 
 
         self.verz_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.verz_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.verz_entry.set_range(-9999.9999, 9999.9999)
+        self.verz_entry.set_range(-10000.0000, 10000.0000)
         self.verz_entry.set_precision(self.decimals)
         self.verz_entry.set_precision(self.decimals)
         self.verz_entry.setSingleStep(0.1)
         self.verz_entry.setSingleStep(0.1)
 
 
@@ -809,7 +809,7 @@ class CalibUI:
         )
         )
 
 
         self.toolchangez_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.toolchangez_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.toolchangez_entry.set_range(0.0000, 9999.9999)
+        self.toolchangez_entry.set_range(0.0000, 10000.0000)
         self.toolchangez_entry.set_precision(self.decimals)
         self.toolchangez_entry.set_precision(self.decimals)
         self.toolchangez_entry.setSingleStep(0.1)
         self.toolchangez_entry.setSingleStep(0.1)
 
 
@@ -851,8 +851,8 @@ class CalibUI:
               "- top-left -> the user will align the PCB vertically\n"
               "- top-left -> the user will align the PCB vertically\n"
               "- bottom-right -> the user will align the PCB horizontally")
               "- bottom-right -> the user will align the PCB horizontally")
         )
         )
-        self.second_point_radio = RadioSet([{'label': _('Top-Left'), 'value': 'tl'},
-                                            {'label': _('Bottom-Right'), 'value': 'br'}],
+        self.second_point_radio = RadioSet([{'label': _('Top Left'), 'value': 'tl'},
+                                            {'label': _('Bottom Right'), 'value': 'br'}],
                                            orientation='vertical')
                                            orientation='vertical')
 
 
         grid_lay.addWidget(second_point_lbl, 7, 0)
         grid_lay.addWidget(second_point_lbl, 7, 0)
@@ -1164,7 +1164,7 @@ class CalibUI:
             _("Factor for Scale action over X axis.")
             _("Factor for Scale action over X axis.")
         )
         )
         self.scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.scalex_entry.set_range(0, 9999.9999)
+        self.scalex_entry.set_range(0, 10000.0000)
         self.scalex_entry.set_precision(self.decimals)
         self.scalex_entry.set_precision(self.decimals)
         self.scalex_entry.setSingleStep(0.1)
         self.scalex_entry.setSingleStep(0.1)
 
 
@@ -1176,7 +1176,7 @@ class CalibUI:
             _("Factor for Scale action over Y axis.")
             _("Factor for Scale action over Y axis.")
         )
         )
         self.scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.scaley_entry.set_range(0, 9999.9999)
+        self.scaley_entry.set_range(0, 10000.0000)
         self.scaley_entry.set_precision(self.decimals)
         self.scaley_entry.set_precision(self.decimals)
         self.scaley_entry.setSingleStep(0.1)
         self.scaley_entry.setSingleStep(0.1)
 
 
@@ -1197,7 +1197,7 @@ class CalibUI:
 
 
         self.skewx_label = QtWidgets.QLabel(_("Skew Angle X:"))
         self.skewx_label = QtWidgets.QLabel(_("Skew Angle X:"))
         self.skewx_label.setToolTip(
         self.skewx_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
+            _("Angle, in degrees.\n"
               "Float number between -360 and 359.")
               "Float number between -360 and 359.")
         )
         )
         self.skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
@@ -1210,7 +1210,7 @@ class CalibUI:
 
 
         self.skewy_label = QtWidgets.QLabel(_("Skew Angle Y:"))
         self.skewy_label = QtWidgets.QLabel(_("Skew Angle Y:"))
         self.skewy_label.setToolTip(
         self.skewy_label.setToolTip(
-            _("Angle for Skew action, in degrees.\n"
+            _("Angle, in degrees.\n"
               "Float number between -360 and 359.")
               "Float number between -360 and 359.")
         )
         )
         self.skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
@@ -1245,7 +1245,7 @@ class CalibUI:
         #     _("Final factor for Scale action over X axis.")
         #     _("Final factor for Scale action over X axis.")
         # )
         # )
         # self.fin_scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
         # self.fin_scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.fin_scalex_entry.set_range(0, 9999.9999)
+        # self.fin_scalex_entry.set_range(0, 10000.0000)
         # self.fin_scalex_entry.set_precision(self.decimals)
         # self.fin_scalex_entry.set_precision(self.decimals)
         # self.fin_scalex_entry.setSingleStep(0.1)
         # self.fin_scalex_entry.setSingleStep(0.1)
         #
         #
@@ -1257,7 +1257,7 @@ class CalibUI:
         #     _("Final factor for Scale action over Y axis.")
         #     _("Final factor for Scale action over Y axis.")
         # )
         # )
         # self.fin_scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
         # self.fin_scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        # self.fin_scaley_entry.set_range(0, 9999.9999)
+        # self.fin_scaley_entry.set_range(0, 10000.0000)
         # self.fin_scaley_entry.set_precision(self.decimals)
         # self.fin_scaley_entry.set_precision(self.decimals)
         # self.fin_scaley_entry.setSingleStep(0.1)
         # self.fin_scaley_entry.setSingleStep(0.1)
         #
         #

File diff suppressed because it is too large
+ 339 - 280
appTools/ToolCopperThieving.py


+ 310 - 98
appTools/ToolCorners.py

@@ -8,9 +8,10 @@
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5 import QtWidgets, QtCore, QtGui
 
 
 from appTool import AppTool
 from appTool import AppTool
-from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, FCButton
+from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, FCButton, RadioSet, FCLabel
 
 
-from shapely.geometry import MultiPolygon, LineString
+from shapely.geometry import MultiPolygon, LineString, Point
+from shapely.ops import unary_union
 
 
 from copy import deepcopy
 from copy import deepcopy
 import logging
 import logging
@@ -37,6 +38,9 @@ class ToolCorners(AppTool):
         self.decimals = self.app.decimals
         self.decimals = self.app.decimals
         self.units = ''
         self.units = ''
 
 
+        # here we store the locations of the selected corners
+        self.points = {}
+
         # #############################################################################
         # #############################################################################
         # ######################### Tool GUI ##########################################
         # ######################### Tool GUI ##########################################
         # #############################################################################
         # #############################################################################
@@ -57,6 +61,7 @@ class ToolCorners(AppTool):
         # SIGNALS
         # SIGNALS
         self.ui.add_marker_button.clicked.connect(self.add_markers)
         self.ui.add_marker_button.clicked.connect(self.add_markers)
         self.ui.toggle_all_cb.toggled.connect(self.on_toggle_all)
         self.ui.toggle_all_cb.toggled.connect(self.on_toggle_all)
+        self.ui.drill_button.clicked.connect(self.on_create_drill_object)
 
 
     def run(self, toggle=True):
     def run(self, toggle=True):
         self.app.defaults.report_usage("ToolCorners()")
         self.app.defaults.report_usage("ToolCorners()")
@@ -95,6 +100,8 @@ class ToolCorners(AppTool):
         self.ui.l_entry.set_value(float(self.app.defaults["tools_corners_length"]))
         self.ui.l_entry.set_value(float(self.app.defaults["tools_corners_length"]))
         self.ui.margin_entry.set_value(float(self.app.defaults["tools_corners_margin"]))
         self.ui.margin_entry.set_value(float(self.app.defaults["tools_corners_margin"]))
         self.ui.toggle_all_cb.set_value(False)
         self.ui.toggle_all_cb.set_value(False)
+        self.ui.type_radio.set_value(self.app.defaults["tools_corners_type"])
+        self.ui.drill_dia_entry.set_value(self.app.defaults["tools_corners_drill_dia"])
 
 
     def on_toggle_all(self, val):
     def on_toggle_all(self, val):
         self.ui.bl_cb.set_value(val)
         self.ui.bl_cb.set_value(val)
@@ -118,26 +125,28 @@ class ToolCorners(AppTool):
         except Exception as e:
         except Exception as e:
             log.debug("ToolCorners.add_markers() --> %s" % str(e))
             log.debug("ToolCorners.add_markers() --> %s" % str(e))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
+            self.app.call_source = "app"
             return
             return
 
 
         xmin, ymin, xmax, ymax = self.grb_object.bounds()
         xmin, ymin, xmax, ymax = self.grb_object.bounds()
-        points = {}
+        self.points = {}
         if tl_state:
         if tl_state:
-            points['tl'] = (xmin, ymax)
+            self.points['tl'] = (xmin, ymax)
         if tr_state:
         if tr_state:
-            points['tr'] = (xmax, ymax)
+            self.points['tr'] = (xmax, ymax)
         if bl_state:
         if bl_state:
-            points['bl'] = (xmin, ymin)
+            self.points['bl'] = (xmin, ymin)
         if br_state:
         if br_state:
-            points['br'] = (xmax, ymin)
+            self.points['br'] = (xmax, ymin)
 
 
-        self.add_corners_geo(points, g_obj=self.grb_object)
+        ret_val = self.add_corners_geo(self.points, g_obj=self.grb_object)
+        self.app.call_source = "app"
+        if ret_val == 'fail':
+            self.app.call_source = "app"
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
+            return
 
 
-        self.grb_object.source_file = self.app.f_handlers.export_gerber(obj_name=self.grb_object.options['name'],
-                                                                        filename=None,
-                                                                        local_use=self.grb_object,
-                                                                        use_thread=False)
-        self.on_exit()
+        self.on_exit(ret_val)
 
 
     def add_corners_geo(self, points_storage, g_obj):
     def add_corners_geo(self, points_storage, g_obj):
         """
         """
@@ -148,68 +157,103 @@ class ToolCorners(AppTool):
         :return:                None
         :return:                None
         """
         """
 
 
+        marker_type = self.ui.type_radio.get_value()
         line_thickness = self.ui.thick_entry.get_value()
         line_thickness = self.ui.thick_entry.get_value()
-        line_length = self.ui.l_entry.get_value()
         margin = self.ui.margin_entry.get_value()
         margin = self.ui.margin_entry.get_value()
+        line_length = self.ui.l_entry.get_value() / 2.0
 
 
         geo_list = []
         geo_list = []
 
 
         if not points_storage:
         if not points_storage:
             self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
             self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
-            return
+            return 'fail'
 
 
         for key in points_storage:
         for key in points_storage:
             if key == 'tl':
             if key == 'tl':
                 pt = points_storage[key]
                 pt = points_storage[key]
                 x = pt[0] - margin - line_thickness / 2.0
                 x = pt[0] - margin - line_thickness / 2.0
                 y = pt[1] + margin + line_thickness / 2.0
                 y = pt[1] + margin + line_thickness / 2.0
-                line_geo_hor = LineString([
-                    (x, y), (x + line_length, y)
-                ])
-                line_geo_vert = LineString([
-                    (x, y), (x, y - line_length)
-                ])
+                if marker_type == 's':
+                    line_geo_hor = LineString([
+                        (x, y), (x + line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y), (x, y - line_length)
+                    ])
+                else:
+                    line_geo_hor = LineString([
+                        (x - line_length, y), (x + line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y + line_length), (x, y - line_length)
+                    ])
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_vert)
                 geo_list.append(line_geo_vert)
             if key == 'tr':
             if key == 'tr':
                 pt = points_storage[key]
                 pt = points_storage[key]
                 x = pt[0] + margin + line_thickness / 2.0
                 x = pt[0] + margin + line_thickness / 2.0
                 y = pt[1] + margin + line_thickness / 2.0
                 y = pt[1] + margin + line_thickness / 2.0
-                line_geo_hor = LineString([
-                    (x, y), (x - line_length, y)
-                ])
-                line_geo_vert = LineString([
-                    (x, y), (x, y - line_length)
-                ])
+                if marker_type == 's':
+                    line_geo_hor = LineString([
+                        (x, y), (x - line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y), (x, y - line_length)
+                    ])
+                else:
+                    line_geo_hor = LineString([
+                        (x + line_length, y), (x - line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y + line_length), (x, y - line_length)
+                    ])
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_vert)
                 geo_list.append(line_geo_vert)
             if key == 'bl':
             if key == 'bl':
                 pt = points_storage[key]
                 pt = points_storage[key]
                 x = pt[0] - margin - line_thickness / 2.0
                 x = pt[0] - margin - line_thickness / 2.0
                 y = pt[1] - margin - line_thickness / 2.0
                 y = pt[1] - margin - line_thickness / 2.0
-                line_geo_hor = LineString([
-                    (x, y), (x + line_length, y)
-                ])
-                line_geo_vert = LineString([
-                    (x, y), (x, y + line_length)
-                ])
+                if marker_type == 's':
+                    line_geo_hor = LineString([
+                        (x, y), (x + line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y), (x, y + line_length)
+                    ])
+                else:
+                    line_geo_hor = LineString([
+                        (x - line_length, y), (x + line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y - line_length), (x, y + line_length)
+                    ])
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_vert)
                 geo_list.append(line_geo_vert)
             if key == 'br':
             if key == 'br':
                 pt = points_storage[key]
                 pt = points_storage[key]
                 x = pt[0] + margin + line_thickness / 2.0
                 x = pt[0] + margin + line_thickness / 2.0
                 y = pt[1] - margin - line_thickness / 2.0
                 y = pt[1] - margin - line_thickness / 2.0
-                line_geo_hor = LineString([
-                    (x, y), (x - line_length, y)
-                ])
-                line_geo_vert = LineString([
-                    (x, y), (x, y + line_length)
-                ])
+                if marker_type == 's':
+                    line_geo_hor = LineString([
+                        (x, y), (x - line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y), (x, y + line_length)
+                    ])
+                else:
+                    line_geo_hor = LineString([
+                        (x + line_length, y), (x - line_length, y)
+                    ])
+                    line_geo_vert = LineString([
+                        (x, y - line_length), (x, y + line_length)
+                    ])
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_hor)
                 geo_list.append(line_geo_vert)
                 geo_list.append(line_geo_vert)
 
 
+        new_apertures = deepcopy(g_obj.apertures)
+
         aperture_found = None
         aperture_found = None
-        for ap_id, ap_val in g_obj.apertures.items():
+        for ap_id, ap_val in new_apertures.items():
             if ap_val['type'] == 'C' and ap_val['size'] == line_thickness:
             if ap_val['type'] == 'C' and ap_val['size'] == line_thickness:
                 aperture_found = ap_id
                 aperture_found = ap_id
                 break
                 break
@@ -220,30 +264,26 @@ class ToolCorners(AppTool):
                 geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=2)
                 geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=2)
                 geo_buff_list.append(geo_buff)
                 geo_buff_list.append(geo_buff)
 
 
-                dict_el = {}
-                dict_el['follow'] = geo
-                dict_el['solid'] = geo_buff
-                g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+                dict_el = {'follow': geo, 'solid': geo_buff}
+                new_apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
         else:
         else:
-            ap_keys = list(g_obj.apertures.keys())
+            ap_keys = list(new_apertures.keys())
             if ap_keys:
             if ap_keys:
                 new_apid = str(int(max(ap_keys)) + 1)
                 new_apid = str(int(max(ap_keys)) + 1)
             else:
             else:
                 new_apid = '10'
                 new_apid = '10'
 
 
-            g_obj.apertures[new_apid] = {}
-            g_obj.apertures[new_apid]['type'] = 'C'
-            g_obj.apertures[new_apid]['size'] = line_thickness
-            g_obj.apertures[new_apid]['geometry'] = []
+            new_apertures[new_apid] = {}
+            new_apertures[new_apid]['type'] = 'C'
+            new_apertures[new_apid]['size'] = line_thickness
+            new_apertures[new_apid]['geometry'] = []
 
 
             for geo in geo_list:
             for geo in geo_list:
                 geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=3)
                 geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=3)
                 geo_buff_list.append(geo_buff)
                 geo_buff_list.append(geo_buff)
 
 
-                dict_el = {}
-                dict_el['follow'] = geo
-                dict_el['solid'] = geo_buff
-                g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+                dict_el = {'follow': geo, 'solid': geo_buff}
+                new_apertures[new_apid]['geometry'].append(deepcopy(dict_el))
 
 
         s_list = []
         s_list = []
         if g_obj.solid_geometry:
         if g_obj.solid_geometry:
@@ -255,26 +295,146 @@ class ToolCorners(AppTool):
 
 
         geo_buff_list = MultiPolygon(geo_buff_list)
         geo_buff_list = MultiPolygon(geo_buff_list)
         geo_buff_list = geo_buff_list.buffer(0)
         geo_buff_list = geo_buff_list.buffer(0)
-        for poly in geo_buff_list:
-            s_list.append(poly)
-        g_obj.solid_geometry = MultiPolygon(s_list)
+        try:
+            for poly in geo_buff_list:
+                s_list.append(poly)
+        except TypeError:
+            s_list.append(geo_buff_list)
+
+        outname = '%s_%s' % (str(self.grb_object.options['name']), 'corners')
+
+        def initialize(grb_obj, app_obj):
+            grb_obj.options = {}
+            for opt in g_obj.options:
+                if opt != 'name':
+                    grb_obj.options[opt] = deepcopy(g_obj.options[opt])
+            grb_obj.options['name'] = outname
+            grb_obj.multitool = False
+            grb_obj.multigeo = False
+            grb_obj.follow = deepcopy(g_obj.follow)
+            grb_obj.apertures = new_apertures
+            grb_obj.solid_geometry = unary_union(s_list)
+            grb_obj.follow_geometry = deepcopy(g_obj.follow_geometry) + geo_list
+
+            grb_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None, local_use=grb_obj,
+                                                                   use_thread=False)
+
+        ret = self.app.app_obj.new_object('gerber', outname, initialize, plot=True)
+
+        return ret
+
+    def on_create_drill_object(self):
+        self.app.call_source = "corners_tool"
+
+        tooldia = self.ui.drill_dia_entry.get_value()
+
+        if tooldia == 0:
+            self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("The tool diameter is zero.")))
+            return
+
+        line_thickness = self.ui.thick_entry.get_value()
+        margin = self.ui.margin_entry.get_value()
+        tl_state = self.ui.tl_cb.get_value()
+        tr_state = self.ui.tr_cb.get_value()
+        bl_state = self.ui.bl_cb.get_value()
+        br_state = self.ui.br_cb.get_value()
+
+        # get the Gerber object on which the corner marker will be inserted
+        selection_index = self.ui.object_combo.currentIndex()
+        model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
+
+        try:
+            self.grb_object = model_index.internalPointer().obj
+        except Exception as e:
+            log.debug("ToolCorners.add_markers() --> %s" % str(e))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
+            self.app.call_source = "app"
+            return
+
+        if tl_state is False and tr_state is False and bl_state is False and br_state is False:
+            self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
+            self.app.call_source = "app"
+            return
+
+        xmin, ymin, xmax, ymax = self.grb_object.bounds()
+
+        # list of (x,y) tuples. Store here the drill coordinates
+        drill_list = []
+
+        if tl_state:
+            x = xmin - margin - line_thickness / 2.0
+            y = ymax + margin + line_thickness / 2.0
+            drill_list.append(
+                Point((x, y))
+            )
+
+        if tr_state:
+            x = xmax + margin + line_thickness / 2.0
+            y = ymax + margin + line_thickness / 2.0
+            drill_list.append(
+                Point((x, y))
+            )
+
+        if bl_state:
+            x = xmin - margin - line_thickness / 2.0
+            y = ymin - margin - line_thickness / 2.0
+            drill_list.append(
+                Point((x, y))
+            )
+
+        if br_state:
+            x = xmax + margin + line_thickness / 2.0
+            y = ymin - margin - line_thickness / 2.0
+            drill_list.append(
+                Point((x, y))
+            )
+
+        tools = {1: {}}
+        tools[1]["tooldia"] = tooldia
+        tools[1]['drills'] = drill_list
+        tools[1]['solid_geometry'] = []
+
+        def obj_init(obj_inst, app_inst):
+            obj_inst.options.update({
+                'name': outname
+            })
+            obj_inst.tools = deepcopy(tools)
+            obj_inst.create_geometry()
+            obj_inst.source_file = app_inst.f_handlers.export_excellon(obj_name=obj_inst.options['name'],
+                                                                       local_use=obj_inst,
+                                                                       filename=None,
+                                                                       use_thread=False)
+
+        outname = '%s_%s' % (str(self.grb_object.options['name']), 'corner_drills')
+        ret_val = self.app.app_obj.new_object("excellon", outname, obj_init)
+
+        self.app.call_source = "app"
+        if ret_val == 'fail':
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
+        else:
+            self.app.inform.emit('[success] %s' % _("Excellon object with corner drills created."))
 
 
     def replot(self, obj, run_thread=True):
     def replot(self, obj, run_thread=True):
         def worker_task():
         def worker_task():
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 obj.plot()
                 obj.plot()
+                self.app.app_obj.object_plotted.emit(obj)
 
 
         if run_thread:
         if run_thread:
             self.app.worker_task.emit({'fcn': worker_task, 'params': []})
             self.app.worker_task.emit({'fcn': worker_task, 'params': []})
         else:
         else:
             worker_task()
             worker_task()
 
 
-    def on_exit(self):
+    def on_exit(self, corner_gerber_obj=None):
         # plot the object
         # plot the object
-        try:
-            self.replot(obj=self.grb_object)
-        except (AttributeError, TypeError):
-            return
+        if corner_gerber_obj:
+            try:
+                for ob in corner_gerber_obj:
+                    self.replot(obj=ob)
+            except (AttributeError, TypeError):
+                self.replot(obj=corner_gerber_obj)
+            except Exception:
+                return
 
 
         # update the bounding box values
         # update the bounding box values
         try:
         try:
@@ -286,11 +446,8 @@ class ToolCorners(AppTool):
         except Exception as e:
         except Exception as e:
             log.debug("ToolCorners.on_exit() copper_obj bounds error --> %s" % str(e))
             log.debug("ToolCorners.on_exit() copper_obj bounds error --> %s" % str(e))
 
 
-        # reset the variables
-        self.grb_object = None
-
         self.app.call_source = "app"
         self.app.call_source = "app"
-        self.app.inform.emit('[success] %s' % _("Corners Tool exit."))
+        self.app.inform.emit('[success] %s' % _("A Gerber object with corner markers was created."))
 
 
 
 
 class CornersUI:
 class CornersUI:
@@ -303,7 +460,7 @@ class CornersUI:
         self.layout = layout
         self.layout = layout
 
 
         # ## Title
         # ## Title
-        title_label = QtWidgets.QLabel("%s" % self.toolName)
+        title_label = FCLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
         title_label.setStyleSheet("""
                                 QLabel
                                 QLabel
                                 {
                                 {
@@ -312,10 +469,10 @@ class CornersUI:
                                 }
                                 }
                                 """)
                                 """)
         self.layout.addWidget(title_label)
         self.layout.addWidget(title_label)
-        self.layout.addWidget(QtWidgets.QLabel(""))
+        self.layout.addWidget(FCLabel(""))
 
 
         # Gerber object #
         # Gerber object #
-        self.object_label = QtWidgets.QLabel('<b>%s:</b>' % _("GERBER"))
+        self.object_label = FCLabel('<b>%s:</b>' % _("GERBER"))
         self.object_label.setToolTip(
         self.object_label.setToolTip(
             _("The Gerber object to which will be added corner markers.")
             _("The Gerber object to which will be added corner markers.")
         )
         )
@@ -333,27 +490,31 @@ class CornersUI:
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         self.layout.addWidget(separator_line)
         self.layout.addWidget(separator_line)
 
 
-        self.points_label = QtWidgets.QLabel('<b>%s:</b>' % _('Locations'))
+        self.points_label = FCLabel('<b>%s:</b>' % _('Locations'))
         self.points_label.setToolTip(
         self.points_label.setToolTip(
             _("Locations where to place corner markers.")
             _("Locations where to place corner markers.")
         )
         )
         self.layout.addWidget(self.points_label)
         self.layout.addWidget(self.points_label)
 
 
-        # BOTTOM LEFT
-        self.bl_cb = FCCheckBox(_("Bottom Left"))
-        self.layout.addWidget(self.bl_cb)
-
-        # BOTTOM RIGHT
-        self.br_cb = FCCheckBox(_("Bottom Right"))
-        self.layout.addWidget(self.br_cb)
+        # ## Grid Layout
+        grid_loc = QtWidgets.QGridLayout()
+        self.layout.addLayout(grid_loc)
 
 
         # TOP LEFT
         # TOP LEFT
         self.tl_cb = FCCheckBox(_("Top Left"))
         self.tl_cb = FCCheckBox(_("Top Left"))
-        self.layout.addWidget(self.tl_cb)
+        grid_loc.addWidget(self.tl_cb, 0, 0)
 
 
         # TOP RIGHT
         # TOP RIGHT
         self.tr_cb = FCCheckBox(_("Top Right"))
         self.tr_cb = FCCheckBox(_("Top Right"))
-        self.layout.addWidget(self.tr_cb)
+        grid_loc.addWidget(self.tr_cb, 0, 1)
+
+        # BOTTOM LEFT
+        self.bl_cb = FCCheckBox(_("Bottom Left"))
+        grid_loc.addWidget(self.bl_cb, 1, 0)
+
+        # BOTTOM RIGHT
+        self.br_cb = FCCheckBox(_("Bottom Right"))
+        grid_loc.addWidget(self.br_cb, 1, 1)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -375,59 +536,74 @@ class CornersUI:
         grid_lay.setColumnStretch(0, 0)
         grid_lay.setColumnStretch(0, 0)
         grid_lay.setColumnStretch(1, 1)
         grid_lay.setColumnStretch(1, 1)
 
 
-        self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
+        self.param_label = FCLabel('<b>%s:</b>' % _('Parameters'))
         self.param_label.setToolTip(
         self.param_label.setToolTip(
             _("Parameters used for this tool.")
             _("Parameters used for this tool.")
         )
         )
         grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
         grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
 
 
+        # Type of Marker
+        self.type_label = FCLabel('%s:' % _("Type"))
+        self.type_label.setToolTip(
+            _("Shape of the marker.")
+        )
+
+        self.type_radio = RadioSet([
+            {"label": _("Semi-Cross"), "value": "s"},
+            {"label": _("Cross"), "value": "c"},
+        ])
+
+        grid_lay.addWidget(self.type_label, 2, 0)
+        grid_lay.addWidget(self.type_radio, 2, 1)
+
         # Thickness #
         # Thickness #
-        self.thick_label = QtWidgets.QLabel('%s:' % _("Thickness"))
+        self.thick_label = FCLabel('%s:' % _("Thickness"))
         self.thick_label.setToolTip(
         self.thick_label.setToolTip(
             _("The thickness of the line that makes the corner marker.")
             _("The thickness of the line that makes the corner marker.")
         )
         )
         self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.thick_entry.set_range(0.0000, 9.9999)
+        self.thick_entry.set_range(0.0000, 10.0000)
         self.thick_entry.set_precision(self.decimals)
         self.thick_entry.set_precision(self.decimals)
         self.thick_entry.setWrapping(True)
         self.thick_entry.setWrapping(True)
         self.thick_entry.setSingleStep(10 ** -self.decimals)
         self.thick_entry.setSingleStep(10 ** -self.decimals)
 
 
-        grid_lay.addWidget(self.thick_label, 1, 0)
-        grid_lay.addWidget(self.thick_entry, 1, 1)
+        grid_lay.addWidget(self.thick_label, 4, 0)
+        grid_lay.addWidget(self.thick_entry, 4, 1)
 
 
         # Length #
         # Length #
-        self.l_label = QtWidgets.QLabel('%s:' % _("Length"))
+        self.l_label = FCLabel('%s:' % _("Length"))
         self.l_label.setToolTip(
         self.l_label.setToolTip(
             _("The length of the line that makes the corner marker.")
             _("The length of the line that makes the corner marker.")
         )
         )
         self.l_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.l_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.l_entry.set_range(-9999.9999, 9999.9999)
+        self.l_entry.set_range(-10000.0000, 10000.0000)
         self.l_entry.set_precision(self.decimals)
         self.l_entry.set_precision(self.decimals)
         self.l_entry.setSingleStep(10 ** -self.decimals)
         self.l_entry.setSingleStep(10 ** -self.decimals)
 
 
-        grid_lay.addWidget(self.l_label, 2, 0)
-        grid_lay.addWidget(self.l_entry, 2, 1)
+        grid_lay.addWidget(self.l_label, 6, 0)
+        grid_lay.addWidget(self.l_entry, 6, 1)
 
 
         # Margin #
         # Margin #
-        self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+        self.margin_label = FCLabel('%s:' % _("Margin"))
         self.margin_label.setToolTip(
         self.margin_label.setToolTip(
             _("Bounding box margin.")
             _("Bounding box margin.")
         )
         )
         self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.margin_entry.set_range(-9999.9999, 9999.9999)
+        self.margin_entry.set_range(-10000.0000, 10000.0000)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.setSingleStep(0.1)
         self.margin_entry.setSingleStep(0.1)
 
 
-        grid_lay.addWidget(self.margin_label, 3, 0)
-        grid_lay.addWidget(self.margin_entry, 3, 1)
+        grid_lay.addWidget(self.margin_label, 8, 0)
+        grid_lay.addWidget(self.margin_entry, 8, 1)
 
 
-        separator_line_2 = QtWidgets.QFrame()
-        separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid_lay.addWidget(separator_line_2, 4, 0, 1, 2)
+        # separator_line_2 = QtWidgets.QFrame()
+        # separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
+        # separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+        # grid_lay.addWidget(separator_line_2, 10, 0, 1, 2)
 
 
         # ## Insert Corner Marker
         # ## Insert Corner Marker
         self.add_marker_button = FCButton(_("Add Marker"))
         self.add_marker_button = FCButton(_("Add Marker"))
+        self.add_marker_button.setIcon(QtGui.QIcon(self.app.resource_location + '/corners_32.png'))
         self.add_marker_button.setToolTip(
         self.add_marker_button.setToolTip(
             _("Will add corner markers to the selected Gerber file.")
             _("Will add corner markers to the selected Gerber file.")
         )
         )
@@ -437,7 +613,43 @@ class CornersUI:
                                     font-weight: bold;
                                     font-weight: bold;
                                 }
                                 }
                                 """)
                                 """)
-        grid_lay.addWidget(self.add_marker_button, 11, 0, 1, 2)
+        grid_lay.addWidget(self.add_marker_button, 12, 0, 1, 2)
+
+        separator_line_2 = QtWidgets.QFrame()
+        separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid_lay.addWidget(separator_line_2, 14, 0, 1, 2)
+
+        # Drill is corners
+        self.drills_label = FCLabel('<b>%s:</b>' % _('Drills in Corners'))
+        grid_lay.addWidget(self.drills_label, 16, 0, 1, 2)
+
+        # Drill Tooldia #
+        self.drill_dia_label = FCLabel('%s:' % _("Drill Dia"))
+        self.drill_dia_label.setToolTip(
+            '%s.' % _("Drill Diameter")
+        )
+        self.drill_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
+        self.drill_dia_entry.set_range(0.0000, 100.0000)
+        self.drill_dia_entry.set_precision(self.decimals)
+        self.drill_dia_entry.setWrapping(True)
+
+        grid_lay.addWidget(self.drill_dia_label, 18, 0)
+        grid_lay.addWidget(self.drill_dia_entry, 18, 1)
+
+        # ## Create an Excellon object
+        self.drill_button = FCButton(_("Create Excellon Object"))
+        self.drill_button.setIcon(QtGui.QIcon(self.app.resource_location + '/drill32.png'))
+        self.drill_button.setToolTip(
+            _("Will add drill holes in the center of the markers.")
+        )
+        self.drill_button.setStyleSheet("""
+                                        QPushButton
+                                        {
+                                            font-weight: bold;
+                                        }
+                                        """)
+        grid_lay.addWidget(self.drill_button, 20, 0, 1, 2)
 
 
         self.layout.addStretch()
         self.layout.addStretch()
 
 
@@ -473,4 +685,4 @@ class CornersUI:
             self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
             self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
                                             (_("Edited value is out of range"), minval, maxval), False)
                                             (_("Edited value is out of range"), minval, maxval), False)
         else:
         else:
-            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

+ 56 - 43
appTools/ToolCutOut.py

@@ -266,7 +266,7 @@ class CutOut(AppTool):
     def on_tool_add(self, custom_dia=None):
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
         self.blockSignals(True)
 
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
 
         new_tools_dict = deepcopy(self.default_data)
         new_tools_dict = deepcopy(self.default_data)
         updated_tooldia = None
         updated_tooldia = None
@@ -311,7 +311,7 @@ class CutOut(AppTool):
 
 
         offset = 'Path'
         offset = 'Path'
         offset_val = 0.0
         offset_val = 0.0
-        typ = "Rough"
+        typ = 'Rough'
         tool_type = 'V'
         tool_type = 'V'
         # look in database tools
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -389,7 +389,7 @@ class CutOut(AppTool):
 
 
     def on_tool_default_add(self, dia=None, muted=None):
     def on_tool_default_add(self, dia=None, muted=None):
 
 
-        dia = dia
+        dia = dia if dia else str(self.app.defaults["tools_cutout_tooldia"])
         self.default_data.update({
         self.default_data.update({
             "plot": True,
             "plot": True,
 
 
@@ -442,10 +442,10 @@ class CutOut(AppTool):
         })
         })
 
 
         self.cut_tool_dict.update({
         self.cut_tool_dict.update({
-            'tooldia': str(self.app.defaults["tools_cutout_tooldia"]),
+            'tooldia': dia,
             'offset': 'Path',
             'offset': 'Path',
             'offset_value': 0.0,
             'offset_value': 0.0,
-            'type': _('Rough'),
+            'type': 'Rough',
             'tool_type': 'C1',
             'tool_type': 'C1',
             'data': deepcopy(self.default_data),
             'data': deepcopy(self.default_data),
             'solid_geometry': []
             'solid_geometry': []
@@ -462,7 +462,12 @@ class CutOut(AppTool):
         :return:
         :return:
         """
         """
 
 
-        if tool['data']['tool_target'] != _("Cutout"):
+        if tool['data']['tool_target'] not in [0, 6]:   # [General, Cutout Tool]
+            for idx in range(self.app.ui.plot_tab_area.count()):
+                if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                    wdg = self.app.ui.plot_tab_area.widget(idx)
+                    wdg.deleteLater()
+                    self.app.ui.plot_tab_area.removeTab(idx)
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
             return
         tool_from_db = deepcopy(self.default_data)
         tool_from_db = deepcopy(self.default_data)
@@ -525,7 +530,9 @@ class CutOut(AppTool):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 break
                 break
-        self.app.on_tools_database(source='cutout')
+        ret_val = self.app.on_tools_database(source='cutout')
+        if ret_val == 'fail':
+            return
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
@@ -570,8 +577,8 @@ class CutOut(AppTool):
 
 
         if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
         if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
             self.app.inform.emit('[WARNING_NOTCL] %s' %
             self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                 _("Gaps value can be only one of: 'None', 'lr', 'tb', '2lr', '2tb', 4 or 8. "
-                                   "Fill in a correct value and retry. "))
+                                 _("Gaps value can be only one of: 'None', 'lr', 'tb', '2lr', '2tb', 4 or 8.\n"
+                                   "Fill in a correct value and retry."))
             return
             return
 
 
         # if cutout_obj.multigeo is True:
         # if cutout_obj.multigeo is True:
@@ -867,16 +874,18 @@ class CutOut(AppTool):
                     if not holes:
                     if not holes:
                         return 'fail'
                         return 'fail'
 
 
-                    tools = {}
-                    tools[1] = {}
-                    tools[1]["tooldia"] = mb_dia
-                    tools[1]['drills'] = holes
-                    tools[1]['solid_geometry'] = []
+                    tools = {
+                        1: {
+                            "tooldia": mb_dia,
+                            "drills": holes,
+                            "solid_geometry": []
+                        }
+                    }
 
 
                     exc_obj.tools = tools
                     exc_obj.tools = tools
                     exc_obj.create_geometry()
                     exc_obj.create_geometry()
                     exc_obj.source_file = app_o.f_handlers.export_excellon(obj_name=exc_obj.options['name'],
                     exc_obj.source_file = app_o.f_handlers.export_excellon(obj_name=exc_obj.options['name'],
-                                                                           local_use=exc_obj,filename=None,
+                                                                           local_use=exc_obj, filename=None,
                                                                            use_thread=False)
                                                                            use_thread=False)
                     # calculate the bounds
                     # calculate the bounds
                     xmin, ymin, xmax, ymax = CutOut.recursive_bounds(exc_obj.solid_geometry)
                     xmin, ymin, xmax, ymax = CutOut.recursive_bounds(exc_obj.solid_geometry)
@@ -897,7 +906,7 @@ class CutOut(AppTool):
                         return
                         return
 
 
                     # cutout_obj.plot(plot_tool=1)
                     # cutout_obj.plot(plot_tool=1)
-                    app_obj.inform.emit('[success] %s' % _("Any form CutOut operation finished."))
+                    app_obj.inform.emit('[success] %s' % _("Any-form Cutout operation finished."))
                     # self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
                     # self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
                     app_obj.should_we_save = True
                     app_obj.should_we_save = True
                 except Exception as ee:
                 except Exception as ee:
@@ -942,9 +951,9 @@ class CutOut(AppTool):
             return
             return
 
 
         if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
         if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Gaps value can be only one of: "
-                                                          "'None', 'lr', 'tb', '2lr', '2tb', 4 or 8. "
-                                                          "Fill in a correct value and retry. "))
+            msg = '[WARNING_NOTCL] %s' % _("Gaps value can be only one of: 'None', 'lr', 'tb', '2lr', '2tb', 4 or 8.\n"
+                                           "Fill in a correct value and retry.")
+            self.app.inform.emit(msg)
             return
             return
 
 
         # if cutout_obj.multigeo is True:
         # if cutout_obj.multigeo is True:
@@ -1237,11 +1246,13 @@ class CutOut(AppTool):
                     if not holes:
                     if not holes:
                         return 'fail'
                         return 'fail'
 
 
-                    tools = {}
-                    tools[1] = {}
-                    tools[1]["tooldia"] = mb_dia
-                    tools[1]['drills'] = holes
-                    tools[1]['solid_geometry'] = []
+                    tools = {
+                        1: {
+                            "tooldia": mb_dia,
+                            "drills": holes,
+                            "solid_geometry": []
+                        }
+                    }
 
 
                     exc_obj.tools = tools
                     exc_obj.tools = tools
                     exc_obj.create_geometry()
                     exc_obj.create_geometry()
@@ -1284,7 +1295,7 @@ class CutOut(AppTool):
             self.man_cutout_obj = self.app.collection.get_by_name(str(name))
             self.man_cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
         except Exception as e:
             log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
             log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Geometry object"), name))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name))
             return
             return
 
 
         if self.man_cutout_obj is None:
         if self.man_cutout_obj is None:
@@ -1316,7 +1327,7 @@ class CutOut(AppTool):
             self.man_cutout_obj = self.app.collection.get_by_name(str(name))
             self.man_cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
         except Exception as e:
             log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
             log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Geometry object"), name))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name))
             return
             return
 
 
         if self.app.is_legacy is False:
         if self.app.is_legacy is False:
@@ -1413,7 +1424,7 @@ class CutOut(AppTool):
             cutout_obj = self.app.collection.get_by_name(str(name))
             cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
         except Exception as e:
             log.debug("CutOut.on_manual_geo() --> %s" % str(e))
             log.debug("CutOut.on_manual_geo() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Gerber object"), name))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name))
             return "Could not retrieve object: %s" % name
             return "Could not retrieve object: %s" % name
 
 
         if cutout_obj is None:
         if cutout_obj is None:
@@ -1459,7 +1470,7 @@ class CutOut(AppTool):
                     geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
                     geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
                 else:
                 else:
                     app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (
                     app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (
-                        _("Geometry not supported for cutout"), type(geo_union)))
+                        _("Geometry not supported"), type(geo_union)))
                     return 'fail'
                     return 'fail'
             else:
             else:
                 geo = geo_union
                 geo = geo_union
@@ -1595,11 +1606,13 @@ class CutOut(AppTool):
                             if not holes:
                             if not holes:
                                 return 'fail'
                                 return 'fail'
 
 
-                            tools = {}
-                            tools[1] = {}
-                            tools[1]["tooldia"] = mb_dia
-                            tools[1]['drills'] = holes
-                            tools[1]['solid_geometry'] = []
+                            tools = {
+                                1: {
+                                    "tooldia": mb_dia,
+                                    "drills": holes,
+                                    "solid_geometry": []
+                                }
+                            }
 
 
                             exc_obj.tools = tools
                             exc_obj.tools = tools
                             exc_obj.create_geometry()
                             exc_obj.create_geometry()
@@ -1996,9 +2009,9 @@ class CutoutUI:
         # Object kind
         # Object kind
         self.kindlabel = QtWidgets.QLabel('%s:' % _('Kind'))
         self.kindlabel = QtWidgets.QLabel('%s:' % _('Kind'))
         self.kindlabel.setToolTip(
         self.kindlabel.setToolTip(
-            _("Choice of what kind the object we want to cutout is.<BR>"
-              "- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
-              "- <B>Panel</B>: a panel PCB Gerber object, which is made\n"
+            _("Choice of what kind the object we want to cutout is.\n"
+              "- Single: contain a single PCB Gerber outline object.\n"
+              "- Panel: a panel PCB Gerber object, which is made\n"
               "out of many individual PCB outlines.")
               "out of many individual PCB outlines.")
         )
         )
         self.obj_kind_combo = RadioSet([
         self.obj_kind_combo = RadioSet([
@@ -2059,7 +2072,7 @@ class CutoutUI:
         # Tool Diameter
         # Tool Diameter
         self.dia = FCDoubleSpinner(callback=self.confirmation_message)
         self.dia = FCDoubleSpinner(callback=self.confirmation_message)
         self.dia.set_precision(self.decimals)
         self.dia.set_precision(self.decimals)
-        self.dia.set_range(0.0000, 9999.9999)
+        self.dia.set_range(0.0000, 10000.0000)
 
 
         self.dia_label = QtWidgets.QLabel('%s:' % _("Tool Dia"))
         self.dia_label = QtWidgets.QLabel('%s:' % _("Tool Dia"))
         self.dia_label.setToolTip(
         self.dia_label.setToolTip(
@@ -2116,9 +2129,9 @@ class CutoutUI:
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.cutz_entry.setRange(-9999.9999, -0.00001)
+            self.cutz_entry.setRange(-10000.0000, -0.00001)
         else:
         else:
-            self.cutz_entry.setRange(-9999.9999, 9999.9999)
+            self.cutz_entry.setRange(-10000.0000, 10000.0000)
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
 
 
@@ -2138,7 +2151,7 @@ class CutoutUI:
 
 
         self.maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.maxdepth_entry.set_precision(self.decimals)
         self.maxdepth_entry.set_precision(self.decimals)
-        self.maxdepth_entry.setRange(0, 9999.9999)
+        self.maxdepth_entry.setRange(0, 10000.0000)
         self.maxdepth_entry.setSingleStep(0.1)
         self.maxdepth_entry.setSingleStep(0.1)
 
 
         self.maxdepth_entry.setToolTip(
         self.maxdepth_entry.setToolTip(
@@ -2154,7 +2167,7 @@ class CutoutUI:
 
 
         # Margin
         # Margin
         self.margin = FCDoubleSpinner(callback=self.confirmation_message)
         self.margin = FCDoubleSpinner(callback=self.confirmation_message)
-        self.margin.set_range(-9999.9999, 9999.9999)
+        self.margin.set_range(-10000.0000, 10000.0000)
         self.margin.setSingleStep(0.1)
         self.margin.setSingleStep(0.1)
         self.margin.set_precision(self.decimals)
         self.margin.set_precision(self.decimals)
 
 
@@ -2212,9 +2225,9 @@ class CutoutUI:
         self.thin_depth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.thin_depth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.thin_depth_entry.set_precision(self.decimals)
         self.thin_depth_entry.set_precision(self.decimals)
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.thin_depth_entry.setRange(-9999.9999, -0.00001)
+            self.thin_depth_entry.setRange(-10000.0000, -0.00001)
         else:
         else:
-            self.thin_depth_entry.setRange(-9999.9999, 9999.9999)
+            self.thin_depth_entry.setRange(-10000.0000, 10000.0000)
         self.thin_depth_entry.setSingleStep(0.1)
         self.thin_depth_entry.setSingleStep(0.1)
 
 
         grid0.addWidget(self.thin_depth_label, 32, 0)
         grid0.addWidget(self.thin_depth_label, 32, 0)

+ 40 - 34
appTools/ToolDblSided.py

@@ -2,7 +2,7 @@
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5 import QtWidgets, QtCore, QtGui
 
 
 from appTool import AppTool
 from appTool import AppTool
-from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCButton, FCComboBox, NumericalEvalTupleEntry
+from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCButton, FCComboBox, NumericalEvalTupleEntry, FCLabel
 
 
 from numpy import Inf
 from numpy import Inf
 
 
@@ -182,9 +182,9 @@ class DblSidedTool(AppTool):
             self.app.inform.emit(msg)
             self.app.inform.emit(msg)
             return
             return
 
 
-        tools = {}
-        tools[1] = {}
+        tools = {1: {}}
         tools[1]["tooldia"] = dia
         tools[1]["tooldia"] = dia
+        tools[1]['drills'] = []
         tools[1]['solid_geometry'] = []
         tools[1]['solid_geometry'] = []
 
 
         # holes = self.alignment_holes.get_value()
         # holes = self.alignment_holes.get_value()
@@ -198,9 +198,8 @@ class DblSidedTool(AppTool):
             point = Point(hole)
             point = Point(hole)
             point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
             point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
 
 
-            tools[1]['drills'] = [point, point_mirror]
-            tools[1]['solid_geometry'].append(point)
-            tools[1]['solid_geometry'].append(point_mirror)
+            tools[1]['drills'] += [point, point_mirror]
+            tools[1]['solid_geometry'] += [point, point_mirror]
 
 
         def obj_init(obj_inst, app_inst):
         def obj_init(obj_inst, app_inst):
             obj_inst.tools = tools
             obj_inst.tools = tools
@@ -210,9 +209,11 @@ class DblSidedTool(AppTool):
                                                                        filename=None,
                                                                        filename=None,
                                                                        use_thread=False)
                                                                        use_thread=False)
 
 
-        self.app.app_obj.new_object("excellon", "Alignment Drills", obj_init)
+        ret_val = self.app.app_obj.new_object("excellon", _("Alignment Drills"), obj_init)
         self.drill_values = ''
         self.drill_values = ''
-        self.app.inform.emit('[success] %s' % _("Excellon object with alignment drills created..."))
+
+        if not ret_val == 'fail':
+            self.app.inform.emit('[success] %s' % _("Excellon object with alignment drills created..."))
 
 
     def on_pick_hole(self):
     def on_pick_hole(self):
 
 
@@ -402,7 +403,7 @@ class DblSidedTool(AppTool):
         obj_list = self.app.collection.get_selected()
         obj_list = self.app.collection.get_selected()
 
 
         if not obj_list:
         if not obj_list:
-            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed. No object(s) selected..."))
+            self.app.inform.emit('[ERROR_NOTCL] %s %s' % (_("Failed."), _("No object is selected.")))
             return
             return
 
 
         for obj in obj_list:
         for obj in obj_list:
@@ -494,7 +495,7 @@ class DsidedUI:
         self.layout = layout
         self.layout = layout
 
 
         # ## Title
         # ## Title
-        title_label = QtWidgets.QLabel("%s" % self.toolName)
+        title_label = FCLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
         title_label.setStyleSheet("""
                                 QLabel
                                 QLabel
                                 {
                                 {
@@ -503,7 +504,7 @@ class DsidedUI:
                                 }
                                 }
                                 """)
                                 """)
         self.layout.addWidget(title_label)
         self.layout.addWidget(title_label)
-        self.layout.addWidget(QtWidgets.QLabel(""))
+        self.layout.addWidget(FCLabel(""))
 
 
         # ## Grid Layout
         # ## Grid Layout
         grid_lay = QtWidgets.QGridLayout()
         grid_lay = QtWidgets.QGridLayout()
@@ -512,13 +513,13 @@ class DsidedUI:
         self.layout.addLayout(grid_lay)
         self.layout.addLayout(grid_lay)
 
 
         # Objects to be mirrored
         # Objects to be mirrored
-        self.m_objects_label = QtWidgets.QLabel("<b>%s:</b>" % _("Source Object"))
+        self.m_objects_label = FCLabel("<b>%s:</b>" % _("Source Object"))
         self.m_objects_label.setToolTip('%s.' % _("Objects to be mirrored"))
         self.m_objects_label.setToolTip('%s.' % _("Objects to be mirrored"))
 
 
         grid_lay.addWidget(self.m_objects_label, 0, 0, 1, 2)
         grid_lay.addWidget(self.m_objects_label, 0, 0, 1, 2)
 
 
         # Type of object to be cutout
         # Type of object to be cutout
-        self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Type"))
+        self.type_obj_combo_label = FCLabel('%s:' % _("Type"))
         self.type_obj_combo_label.setToolTip(
         self.type_obj_combo_label.setToolTip(
             _("Select the type of application object to be processed in this tool.")
             _("Select the type of application object to be processed in this tool.")
         )
         )
@@ -554,7 +555,7 @@ class DsidedUI:
         self.layout.addLayout(grid0)
         self.layout.addLayout(grid0)
 
 
         # ## Title Bounds Values
         # ## Title Bounds Values
-        self.bv_label = QtWidgets.QLabel("<b>%s:</b>" % _('Bounds Values'))
+        self.bv_label = FCLabel("<b>%s:</b>" % _('Bounds Values'))
         self.bv_label.setToolTip(
         self.bv_label.setToolTip(
             _("Select on canvas the object(s)\n"
             _("Select on canvas the object(s)\n"
               "for which to calculate bounds values.")
               "for which to calculate bounds values.")
@@ -564,7 +565,7 @@ class DsidedUI:
         # Xmin value
         # Xmin value
         self.xmin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.xmin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.xmin_entry.set_precision(self.decimals)
         self.xmin_entry.set_precision(self.decimals)
-        self.xmin_entry.set_range(-9999.9999, 9999.9999)
+        self.xmin_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.xmin_btn = FCButton('%s:' % _("X min"))
         self.xmin_btn = FCButton('%s:' % _("X min"))
         self.xmin_btn.setToolTip(
         self.xmin_btn.setToolTip(
@@ -578,7 +579,7 @@ class DsidedUI:
         # Ymin value
         # Ymin value
         self.ymin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.ymin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.ymin_entry.set_precision(self.decimals)
         self.ymin_entry.set_precision(self.decimals)
-        self.ymin_entry.set_range(-9999.9999, 9999.9999)
+        self.ymin_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.ymin_btn = FCButton('%s:' % _("Y min"))
         self.ymin_btn = FCButton('%s:' % _("Y min"))
         self.ymin_btn.setToolTip(
         self.ymin_btn.setToolTip(
@@ -592,7 +593,7 @@ class DsidedUI:
         # Xmax value
         # Xmax value
         self.xmax_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.xmax_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.xmax_entry.set_precision(self.decimals)
         self.xmax_entry.set_precision(self.decimals)
-        self.xmax_entry.set_range(-9999.9999, 9999.9999)
+        self.xmax_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.xmax_btn = FCButton('%s:' % _("X max"))
         self.xmax_btn = FCButton('%s:' % _("X max"))
         self.xmax_btn.setToolTip(
         self.xmax_btn.setToolTip(
@@ -606,7 +607,7 @@ class DsidedUI:
         # Ymax value
         # Ymax value
         self.ymax_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.ymax_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.ymax_entry.set_precision(self.decimals)
         self.ymax_entry.set_precision(self.decimals)
-        self.ymax_entry.set_range(-9999.9999, 9999.9999)
+        self.ymax_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.ymax_btn = FCButton('%s:' % _("Y max"))
         self.ymax_btn = FCButton('%s:' % _("Y max"))
         self.ymax_btn.setToolTip(
         self.ymax_btn.setToolTip(
@@ -632,7 +633,7 @@ class DsidedUI:
         grid0.addWidget(self.center_entry, 12, 1)
         grid0.addWidget(self.center_entry, 12, 1)
 
 
         # Calculate Bounding box
         # Calculate Bounding box
-        self.calculate_bb_button = QtWidgets.QPushButton(_("Calculate Bounds Values"))
+        self.calculate_bb_button = FCButton(_("Calculate Bounds Values"))
         self.calculate_bb_button.setToolTip(
         self.calculate_bb_button.setToolTip(
             _("Calculate the enveloping rectangular shape coordinates,\n"
             _("Calculate the enveloping rectangular shape coordinates,\n"
               "for the selection of objects.\n"
               "for the selection of objects.\n"
@@ -659,13 +660,13 @@ class DsidedUI:
         grid1.setColumnStretch(1, 1)
         grid1.setColumnStretch(1, 1)
         self.layout.addLayout(grid1)
         self.layout.addLayout(grid1)
 
 
-        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Mirror Operation"))
+        self.param_label = FCLabel("<b>%s:</b>" % _("Mirror Operation"))
         self.param_label.setToolTip('%s.' % _("Parameters for the mirror operation"))
         self.param_label.setToolTip('%s.' % _("Parameters for the mirror operation"))
 
 
         grid1.addWidget(self.param_label, 0, 0, 1, 2)
         grid1.addWidget(self.param_label, 0, 0, 1, 2)
 
 
         # ## Axis
         # ## Axis
-        self.mirax_label = QtWidgets.QLabel('%s:' % _("Axis"))
+        self.mirax_label = FCLabel('%s:' % _("Axis"))
         self.mirax_label.setToolTip(_("Mirror vertically (X) or horizontally (Y)."))
         self.mirax_label.setToolTip(_("Mirror vertically (X) or horizontally (Y)."))
         self.mirror_axis = RadioSet(
         self.mirror_axis = RadioSet(
             [
             [
@@ -680,7 +681,7 @@ class DsidedUI:
         grid1.addWidget(self.mirror_axis, 2, 1, 1, 2)
         grid1.addWidget(self.mirror_axis, 2, 1, 1, 2)
 
 
         # ## Axis Location
         # ## Axis Location
-        self.axloc_label = QtWidgets.QLabel('%s:' % _("Reference"))
+        self.axloc_label = FCLabel('%s:' % _("Reference"))
         self.axloc_label.setToolTip(
         self.axloc_label.setToolTip(
             _("The coordinates used as reference for the mirror operation.\n"
             _("The coordinates used as reference for the mirror operation.\n"
               "Can be:\n"
               "Can be:\n"
@@ -705,7 +706,8 @@ class DsidedUI:
         self.point_entry.setPlaceholderText(_("Point coordinates"))
         self.point_entry.setPlaceholderText(_("Point coordinates"))
 
 
         # Add a reference
         # Add a reference
-        self.add_point_button = QtWidgets.QPushButton(_("Add"))
+        self.add_point_button = FCButton(_("Add"))
+        self.add_point_button.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
         self.add_point_button.setToolTip(
         self.add_point_button.setToolTip(
             _("Add the coordinates in format <b>(x, y)</b> through which the mirroring axis\n "
             _("Add the coordinates in format <b>(x, y)</b> through which the mirroring axis\n "
               "selected in 'MIRROR AXIS' pass.\n"
               "selected in 'MIRROR AXIS' pass.\n"
@@ -723,7 +725,7 @@ class DsidedUI:
         grid1.addWidget(self.point_entry, 7, 0, 1, 2)
         grid1.addWidget(self.point_entry, 7, 0, 1, 2)
         grid1.addWidget(self.add_point_button, 7, 2)
         grid1.addWidget(self.add_point_button, 7, 2)
 
 
-        self.exc_hole_lbl = QtWidgets.QLabel('%s:' % _("Excellon"))
+        self.exc_hole_lbl = FCLabel('%s:' % _("Excellon"))
         self.exc_hole_lbl.setToolTip(
         self.exc_hole_lbl.setToolTip(
             _("Object that holds holes that can be picked as reference for mirroring.")
             _("Object that holds holes that can be picked as reference for mirroring.")
         )
         )
@@ -756,7 +758,7 @@ class DsidedUI:
         grid_lay3.setColumnStretch(1, 1)
         grid_lay3.setColumnStretch(1, 1)
         grid1.addLayout(grid_lay3, 14, 0, 1, 3)
         grid1.addLayout(grid_lay3, 14, 0, 1, 3)
 
 
-        self.box_type_label = QtWidgets.QLabel('%s:' % _("Reference Object"))
+        self.box_type_label = FCLabel('%s:' % _("Reference Object"))
         self.box_type_label.setToolTip(
         self.box_type_label.setToolTip(
             _("It can be of type: Gerber or Excellon or Geometry.\n"
             _("It can be of type: Gerber or Excellon or Geometry.\n"
               "The coordinates of the center of the bounding box are used\n"
               "The coordinates of the center of the bounding box are used\n"
@@ -784,7 +786,8 @@ class DsidedUI:
 
 
         grid_lay3.addWidget(self.box_combo, 3, 0, 1, 2)
         grid_lay3.addWidget(self.box_combo, 3, 0, 1, 2)
 
 
-        self.mirror_button = QtWidgets.QPushButton(_("Mirror"))
+        self.mirror_button = FCButton(_("Mirror"))
+        self.mirror_button.setIcon(QtGui.QIcon(self.app.resource_location + '/doubleside16.png'))
         self.mirror_button.setToolTip(
         self.mirror_button.setToolTip(
             _("Mirrors (flips) the specified object around \n"
             _("Mirrors (flips) the specified object around \n"
               "the specified axis. Does not create a new \n"
               "the specified axis. Does not create a new \n"
@@ -812,7 +815,7 @@ class DsidedUI:
         self.layout.addLayout(grid4)
         self.layout.addLayout(grid4)
 
 
         # ## Alignment holes
         # ## Alignment holes
-        self.alignment_label = QtWidgets.QLabel("<b>%s:</b>" % _('PCB Alignment'))
+        self.alignment_label = FCLabel("<b>%s:</b>" % _('PCB Alignment'))
         self.alignment_label.setToolTip(
         self.alignment_label.setToolTip(
             _("Creates an Excellon Object containing the\n"
             _("Creates an Excellon Object containing the\n"
               "specified alignment holes and their mirror\n"
               "specified alignment holes and their mirror\n"
@@ -821,7 +824,7 @@ class DsidedUI:
         grid4.addWidget(self.alignment_label, 0, 0, 1, 2)
         grid4.addWidget(self.alignment_label, 0, 0, 1, 2)
 
 
         # ## Drill diameter for alignment holes
         # ## Drill diameter for alignment holes
-        self.dt_label = QtWidgets.QLabel("%s:" % _('Drill Diameter'))
+        self.dt_label = FCLabel("%s:" % _('Drill Dia'))
         self.dt_label.setToolTip(
         self.dt_label.setToolTip(
             _("Diameter of the drill for the alignment holes.")
             _("Diameter of the drill for the alignment holes.")
         )
         )
@@ -831,13 +834,13 @@ class DsidedUI:
             _("Diameter of the drill for the alignment holes.")
             _("Diameter of the drill for the alignment holes.")
         )
         )
         self.drill_dia.set_precision(self.decimals)
         self.drill_dia.set_precision(self.decimals)
-        self.drill_dia.set_range(0.0000, 9999.9999)
+        self.drill_dia.set_range(0.0000, 10000.0000)
 
 
         grid4.addWidget(self.dt_label, 2, 0)
         grid4.addWidget(self.dt_label, 2, 0)
         grid4.addWidget(self.drill_dia, 2, 1)
         grid4.addWidget(self.drill_dia, 2, 1)
 
 
         # ## Alignment Axis
         # ## Alignment Axis
-        self.align_ax_label = QtWidgets.QLabel('%s:' % _("Axis"))
+        self.align_ax_label = FCLabel('%s:' % _("Axis"))
         self.align_ax_label.setToolTip(
         self.align_ax_label.setToolTip(
             _("Mirror vertically (X) or horizontally (Y).")
             _("Mirror vertically (X) or horizontally (Y).")
         )
         )
@@ -854,7 +857,7 @@ class DsidedUI:
         grid4.addWidget(self.align_axis_radio, 4, 1)
         grid4.addWidget(self.align_axis_radio, 4, 1)
 
 
         # ## Alignment Reference Point
         # ## Alignment Reference Point
-        self.align_ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
+        self.align_ref_label = FCLabel('%s:' % _("Reference"))
         self.align_ref_label.setToolTip(
         self.align_ref_label.setToolTip(
             _("The reference point used to create the second alignment drill\n"
             _("The reference point used to create the second alignment drill\n"
               "from the first alignment drill, by doing mirror.\n"
               "from the first alignment drill, by doing mirror.\n"
@@ -876,7 +879,7 @@ class DsidedUI:
         self.layout.addLayout(grid5)
         self.layout.addLayout(grid5)
 
 
         # ## Alignment holes
         # ## Alignment holes
-        self.ah_label = QtWidgets.QLabel("%s:" % _('Alignment Drill Coordinates'))
+        self.ah_label = FCLabel("%s:" % _('Alignment Drill Coordinates'))
         self.ah_label.setToolTip(
         self.ah_label.setToolTip(
             _("Alignment holes (x1, y1), (x2, y2), ... "
             _("Alignment holes (x1, y1), (x2, y2), ... "
               "on one side of the mirror axis. For each set of (x, y) coordinates\n"
               "on one side of the mirror axis. For each set of (x, y) coordinates\n"
@@ -892,6 +895,7 @@ class DsidedUI:
         grid5.addWidget(self.alignment_holes, 1, 0, 1, 2)
         grid5.addWidget(self.alignment_holes, 1, 0, 1, 2)
 
 
         self.add_drill_point_button = FCButton(_("Add"))
         self.add_drill_point_button = FCButton(_("Add"))
+        self.add_drill_point_button.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
         self.add_drill_point_button.setToolTip(
         self.add_drill_point_button.setToolTip(
             _("Add alignment drill holes coordinates in the format: (x1, y1), (x2, y2), ... \n"
             _("Add alignment drill holes coordinates in the format: (x1, y1), (x2, y2), ... \n"
               "on one side of the alignment axis.\n\n"
               "on one side of the alignment axis.\n\n"
@@ -909,6 +913,7 @@ class DsidedUI:
         #                 """)
         #                 """)
 
 
         self.delete_drill_point_button = FCButton(_("Delete Last"))
         self.delete_drill_point_button = FCButton(_("Delete Last"))
+        self.delete_drill_point_button.setIcon(QtGui.QIcon(self.app.resource_location + '/trash32.png'))
         self.delete_drill_point_button.setToolTip(
         self.delete_drill_point_button.setToolTip(
             _("Delete the last coordinates tuple in the list.")
             _("Delete the last coordinates tuple in the list.")
         )
         )
@@ -920,7 +925,8 @@ class DsidedUI:
         grid5.addLayout(drill_hlay, 2, 0, 1, 2)
         grid5.addLayout(drill_hlay, 2, 0, 1, 2)
 
 
         # ## Buttons
         # ## Buttons
-        self.create_alignment_hole_button = QtWidgets.QPushButton(_("Create Excellon Object"))
+        self.create_alignment_hole_button = FCButton(_("Create Excellon Object"))
+        self.create_alignment_hole_button.setIcon(QtGui.QIcon(self.app.resource_location + '/drill32.png'))
         self.create_alignment_hole_button.setToolTip(
         self.create_alignment_hole_button.setToolTip(
             _("Creates an Excellon Object containing the\n"
             _("Creates an Excellon Object containing the\n"
               "specified alignment holes and their mirror\n"
               "specified alignment holes and their mirror\n"
@@ -937,7 +943,7 @@ class DsidedUI:
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         # ## Reset Tool
         # ## Reset Tool
-        self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
+        self.reset_button = FCButton(_("Reset Tool"))
         self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
         self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
         self.reset_button.setToolTip(
         self.reset_button.setToolTip(
             _("Will reset the tool parameters.")
             _("Will reset the tool parameters.")

+ 3 - 2
appTools/ToolDistance.py

@@ -364,7 +364,8 @@ class Distance(AppTool):
                         self.app.inform.emit('[WARNING_NOTCL] %s' % _("Pads overlapped. Aborting."))
                         self.app.inform.emit('[WARNING_NOTCL] %s' % _("Pads overlapped. Aborting."))
                         return
                         return
 
 
-                    pos = (clicked_pads[0].x, clicked_pads[0].y)
+                    if clicked_pads:
+                        pos = (clicked_pads[0].x, clicked_pads[0].y)
 
 
                 self.app.on_jump_to(custom_location=pos, fit_center=False)
                 self.app.on_jump_to(custom_location=pos, fit_center=False)
                 # Update cursor
                 # Update cursor
@@ -394,7 +395,7 @@ class Distance(AppTool):
     def calculate_distance(self, pos):
     def calculate_distance(self, pos):
         if len(self.points) == 1:
         if len(self.points) == 1:
             self.ui.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
             self.ui.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
-            self.app.inform.emit(_("MEASURING: Click on the Destination point ..."))
+            self.app.inform.emit(_("Click on the DESTINATION point ..."))
         elif len(self.points) == 2:
         elif len(self.points) == 2:
             # self.app.app_cursor.enabled = False
             # self.app.app_cursor.enabled = False
             dx = self.points[1][0] - self.points[0][0]
             dx = self.points[1][0] - self.points[0][0]

+ 22 - 22
appTools/ToolDrilling.py

@@ -884,7 +884,7 @@ class ToolDrilling(AppTool, Excellon):
 
 
     def on_tool_db_load(self):
     def on_tool_db_load(self):
 
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
 
         # load the database tools from the file
         # load the database tools from the file
         try:
         try:
@@ -1213,7 +1213,7 @@ class ToolDrilling(AppTool, Excellon):
         # if the sender is in the column with index 2 then we update the tool_type key
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
         if cw_col == 2:
             tt = cw.currentText()
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else 'Rough'
 
 
             self.excellon_tools[current_uid].update({
             self.excellon_tools[current_uid].update({
                 'type': typ,
                 'type': typ,
@@ -1670,7 +1670,7 @@ class ToolDrilling(AppTool, Excellon):
         # Object initialization function for app.app_obj.new_object()
         # Object initialization function for app.app_obj.new_object()
         def job_init(job_obj, app_obj):
         def job_init(job_obj, app_obj):
             assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
             assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
-            app_obj.inform.emit(_("Generating Excellon CNCJob..."))
+            app_obj.inform.emit(_("Generating CNCJob..."))
 
 
             # #########################################################################################################
             # #########################################################################################################
             # #########################################################################################################
             # #########################################################################################################
@@ -2069,7 +2069,7 @@ class DrillingUI:
               "will be showed as a T1, T2 ... Tn in the Machine Code.\n\n"
               "will be showed as a T1, T2 ... Tn in the Machine Code.\n\n"
               "Here the tools are selected for G-code generation."))
               "Here the tools are selected for G-code generation."))
         self.tools_table.horizontalHeaderItem(1).setToolTip(
         self.tools_table.horizontalHeaderItem(1).setToolTip(
-            _("Tool Diameter. It's value (in current FlatCAM units) \n"
+            _("Tool Diameter. Its value\n"
               "is the cut width into the material."))
               "is the cut width into the material."))
         self.tools_table.horizontalHeaderItem(2).setToolTip(
         self.tools_table.horizontalHeaderItem(2).setToolTip(
             _("The number of Drill holes. Holes that are drilled with\n"
             _("The number of Drill holes. Holes that are drilled with\n"
@@ -2150,9 +2150,9 @@ class DrillingUI:
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.cutz_entry.set_range(-9999.9999, 0.0000)
+            self.cutz_entry.set_range(-10000.0000, 0.0000)
         else:
         else:
-            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+            self.cutz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setObjectName("e_cutz")
         self.cutz_entry.setObjectName("e_cutz")
@@ -2174,7 +2174,7 @@ class DrillingUI:
 
 
         self.maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.maxdepth_entry.set_precision(self.decimals)
         self.maxdepth_entry.set_precision(self.decimals)
-        self.maxdepth_entry.set_range(0, 9999.9999)
+        self.maxdepth_entry.set_range(0, 10000.0000)
         self.maxdepth_entry.setSingleStep(0.1)
         self.maxdepth_entry.setSingleStep(0.1)
 
 
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
@@ -2196,9 +2196,9 @@ class DrillingUI:
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.travelz_entry.set_range(0.00001, 9999.9999)
+            self.travelz_entry.set_range(0.00001, 10000.0000)
         else:
         else:
-            self.travelz_entry.set_range(-9999.9999, 9999.9999)
+            self.travelz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setObjectName("e_travelz")
         self.travelz_entry.setObjectName("e_travelz")
@@ -2216,7 +2216,7 @@ class DrillingUI:
         )
         )
         self.feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_z_entry.set_precision(self.decimals)
         self.feedrate_z_entry.set_precision(self.decimals)
-        self.feedrate_z_entry.set_range(0.0, 99999.9999)
+        self.feedrate_z_entry.set_range(0.0, 910000.0000)
         self.feedrate_z_entry.setSingleStep(0.1)
         self.feedrate_z_entry.setSingleStep(0.1)
         self.feedrate_z_entry.setObjectName("e_feedratez")
         self.feedrate_z_entry.setObjectName("e_feedratez")
 
 
@@ -2234,7 +2234,7 @@ class DrillingUI:
         )
         )
         self.feedrate_rapid_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_rapid_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_rapid_entry.set_precision(self.decimals)
         self.feedrate_rapid_entry.set_precision(self.decimals)
-        self.feedrate_rapid_entry.set_range(0.0, 99999.9999)
+        self.feedrate_rapid_entry.set_range(0.0, 910000.0000)
         self.feedrate_rapid_entry.setSingleStep(0.1)
         self.feedrate_rapid_entry.setSingleStep(0.1)
         self.feedrate_rapid_entry.setObjectName("e_fr_rapid")
         self.feedrate_rapid_entry.setObjectName("e_fr_rapid")
 
 
@@ -2271,7 +2271,7 @@ class DrillingUI:
         # Dwelltime
         # Dwelltime
         self.dwelltime_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dwelltime_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dwelltime_entry.set_precision(self.decimals)
         self.dwelltime_entry.set_precision(self.decimals)
-        self.dwelltime_entry.set_range(0.0, 9999.9999)
+        self.dwelltime_entry.set_range(0.0, 10000.0000)
         self.dwelltime_entry.setSingleStep(0.1)
         self.dwelltime_entry.setSingleStep(0.1)
 
 
         self.dwelltime_entry.setToolTip(
         self.dwelltime_entry.setToolTip(
@@ -2294,7 +2294,7 @@ class DrillingUI:
 
 
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry.set_precision(self.decimals)
         self.offset_entry.set_precision(self.decimals)
-        self.offset_entry.set_range(-9999.9999, 9999.9999)
+        self.offset_entry.set_range(-10000.0000, 10000.0000)
         self.offset_entry.setObjectName("e_offset")
         self.offset_entry.setObjectName("e_offset")
 
 
         self.grid1.addWidget(self.tool_offset_label, 25, 0)
         self.grid1.addWidget(self.tool_offset_label, 25, 0)
@@ -2402,9 +2402,9 @@ class DrillingUI:
         self.toolchangez_entry.setObjectName("e_toolchangez")
         self.toolchangez_entry.setObjectName("e_toolchangez")
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.toolchangez_entry.set_range(0.0, 9999.9999)
+            self.toolchangez_entry.set_range(0.0, 10000.0000)
         else:
         else:
-            self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
+            self.toolchangez_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.toolchangez_entry.setSingleStep(0.1)
         self.toolchangez_entry.setSingleStep(0.1)
 
 
@@ -2414,7 +2414,7 @@ class DrillingUI:
         # Start move Z:
         # Start move Z:
         self.estartz_label = QtWidgets.QLabel('%s:' % _("Start Z"))
         self.estartz_label = QtWidgets.QLabel('%s:' % _("Start Z"))
         self.estartz_label.setToolTip(
         self.estartz_label.setToolTip(
-            _("Height of the tool just after start.\n"
+            _("Height of the tool just after starting the work.\n"
               "Delete the value if you don't need this feature.")
               "Delete the value if you don't need this feature.")
         )
         )
         self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
         self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
@@ -2434,9 +2434,9 @@ class DrillingUI:
         self.endz_entry.setObjectName("e_endz")
         self.endz_entry.setObjectName("e_endz")
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.endz_entry.set_range(0.0, 9999.9999)
+            self.endz_entry.set_range(0.0, 10000.0000)
         else:
         else:
-            self.endz_entry.set_range(-9999.9999, 9999.9999)
+            self.endz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.endz_entry.setSingleStep(0.1)
         self.endz_entry.setSingleStep(0.1)
 
 
@@ -2466,7 +2466,7 @@ class DrillingUI:
 
 
         self.pdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.pdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.pdepth_entry.set_precision(self.decimals)
         self.pdepth_entry.set_precision(self.decimals)
-        self.pdepth_entry.set_range(-9999.9999, 9999.9999)
+        self.pdepth_entry.set_range(-10000.0000, 10000.0000)
         self.pdepth_entry.setSingleStep(0.1)
         self.pdepth_entry.setSingleStep(0.1)
         self.pdepth_entry.setObjectName("e_depth_probe")
         self.pdepth_entry.setObjectName("e_depth_probe")
 
 
@@ -2484,7 +2484,7 @@ class DrillingUI:
 
 
         self.feedrate_probe_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_probe_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_probe_entry.set_precision(self.decimals)
         self.feedrate_probe_entry.set_precision(self.decimals)
-        self.feedrate_probe_entry.set_range(0.0, 9999.9999)
+        self.feedrate_probe_entry.set_range(0.0, 10000.0000)
         self.feedrate_probe_entry.setSingleStep(0.1)
         self.feedrate_probe_entry.setSingleStep(0.1)
         self.feedrate_probe_entry.setObjectName("e_fr_probe")
         self.feedrate_probe_entry.setObjectName("e_fr_probe")
 
 
@@ -2573,7 +2573,7 @@ class DrillingUI:
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
                                        "an interdiction area."))
                                        "an interdiction area."))
         self.over_z_entry = FCDoubleSpinner()
         self.over_z_entry = FCDoubleSpinner()
-        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_range(0.000, 10000.0000)
         self.over_z_entry.set_precision(self.decimals)
         self.over_z_entry.set_precision(self.decimals)
         self.over_z_entry.setObjectName("e_area_overz")
         self.over_z_entry.setObjectName("e_area_overz")
 
 
@@ -2581,7 +2581,7 @@ class DrillingUI:
         grid_a1.addWidget(self.over_z_entry, 2, 1)
         grid_a1.addWidget(self.over_z_entry, 2, 1)
 
 
         # Button Add Area
         # Button Add Area
-        self.add_area_button = QtWidgets.QPushButton(_('Add area:'))
+        self.add_area_button = QtWidgets.QPushButton(_('Add Area:'))
         self.add_area_button.setToolTip(_("Add an Exclusion Area."))
         self.add_area_button.setToolTip(_("Add an Exclusion Area."))
 
 
         # Area Selection shape
         # Area Selection shape

+ 10 - 13
appTools/ToolEtchCompensation.py

@@ -190,9 +190,9 @@ class ToolEtchCompensation(AppTool):
 
 
         # update the apertures attributes (keys in the apertures dict)
         # update the apertures attributes (keys in the apertures dict)
         for ap in new_apertures:
         for ap in new_apertures:
-            type = new_apertures[ap]['type']
+            ap_type = new_apertures[ap]['type']
             for k in new_apertures[ap]:
             for k in new_apertures[ap]:
-                if type == 'R' or type == 'O':
+                if ap_type == 'R' or ap_type == 'O':
                     if k == 'width' or k == 'height':
                     if k == 'width' or k == 'height':
                         new_apertures[ap][k] += offset
                         new_apertures[ap][k] += offset
                 else:
                 else:
@@ -207,9 +207,9 @@ class ToolEtchCompensation(AppTool):
         # in case of 'R' or 'O' aperture type we need to update the aperture 'size' after
         # in case of 'R' or 'O' aperture type we need to update the aperture 'size' after
         # the 'width' and 'height' keys were updated
         # the 'width' and 'height' keys were updated
         for ap in new_apertures:
         for ap in new_apertures:
-            type = new_apertures[ap]['type']
+            ap_type = new_apertures[ap]['type']
             for k in new_apertures[ap]:
             for k in new_apertures[ap]:
-                if type == 'R' or type == 'O':
+                if ap_type == 'R' or ap_type == 'O':
                     if k == 'size':
                     if k == 'size':
                         new_apertures[ap][k] = math.sqrt(
                         new_apertures[ap][k] = math.sqrt(
                             new_apertures[ap]['width'] ** 2 + new_apertures[ap]['height'] ** 2)
                             new_apertures[ap]['width'] ** 2 + new_apertures[ap]['height'] ** 2)
@@ -233,8 +233,8 @@ class ToolEtchCompensation(AppTool):
             new_obj.apertures = deepcopy(new_apertures)
             new_obj.apertures = deepcopy(new_apertures)
 
 
             new_obj.solid_geometry = deepcopy(new_solid_geometry)
             new_obj.solid_geometry = deepcopy(new_solid_geometry)
-            new_obj.source_file = self.app.f_handlers.export_gerber(obj_name=outname, filename=None, local_use=new_obj,
-                                                                    use_thread=False)
+            new_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None, local_use=new_obj,
+                                                                   use_thread=False)
 
 
         self.app.app_obj.new_object('gerber', outname, init_func)
         self.app.app_obj.new_object('gerber', outname, init_func)
 
 
@@ -366,8 +366,7 @@ class EtchUI:
         )
         )
         self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.thick_entry.set_precision(self.decimals)
         self.thick_entry.set_precision(self.decimals)
-        self.thick_entry.set_range(0.0000, 9999.9999)
-        self.thick_entry.setObjectName(_("Thickness"))
+        self.thick_entry.set_range(0.0000, 10000.0000)
 
 
         grid0.addWidget(self.thick_label, 12, 0)
         grid0.addWidget(self.thick_label, 12, 0)
         grid0.addWidget(self.thick_entry, 12, 1)
         grid0.addWidget(self.thick_entry, 12, 1)
@@ -394,21 +393,19 @@ class EtchUI:
             _("A list of etchants.")
             _("A list of etchants.")
         )
         )
         self.etchants_combo = FCComboBox(callback=self.confirmation_message)
         self.etchants_combo = FCComboBox(callback=self.confirmation_message)
-        self.etchants_combo.setObjectName(_("Etchants"))
         self.etchants_combo.addItems(["CuCl2", "Fe3Cl", _("Alkaline baths")])
         self.etchants_combo.addItems(["CuCl2", "Fe3Cl", _("Alkaline baths")])
 
 
         grid0.addWidget(self.etchants_label, 18, 0)
         grid0.addWidget(self.etchants_label, 18, 0)
         grid0.addWidget(self.etchants_combo, 18, 1)
         grid0.addWidget(self.etchants_combo, 18, 1)
 
 
         # Etch Factor
         # Etch Factor
-        self.factor_label = QtWidgets.QLabel('%s:' % _('Etch factor'))
+        self.factor_label = QtWidgets.QLabel('%s:' % _('Etch Factor'))
         self.factor_label.setToolTip(
         self.factor_label.setToolTip(
             _("The ratio between depth etch and lateral etch .\n"
             _("The ratio between depth etch and lateral etch .\n"
               "Accepts real numbers and formulas using the operators: /,*,+,-,%")
               "Accepts real numbers and formulas using the operators: /,*,+,-,%")
         )
         )
         self.factor_entry = NumericalEvalEntry(border_color='#0069A9')
         self.factor_entry = NumericalEvalEntry(border_color='#0069A9')
         self.factor_entry.setPlaceholderText(_("Real number or formula"))
         self.factor_entry.setPlaceholderText(_("Real number or formula"))
-        self.factor_entry.setObjectName(_("Etch_factor"))
 
 
         grid0.addWidget(self.factor_label, 19, 0)
         grid0.addWidget(self.factor_label, 19, 0)
         grid0.addWidget(self.factor_entry, 19, 1)
         grid0.addWidget(self.factor_entry, 19, 1)
@@ -421,8 +418,7 @@ class EtchUI:
         )
         )
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry.set_precision(self.decimals)
         self.offset_entry.set_precision(self.decimals)
-        self.offset_entry.set_range(-9999.9999, 9999.9999)
-        self.offset_entry.setObjectName(_("Offset"))
+        self.offset_entry.set_range(-10000.0000, 10000.0000)
 
 
         grid0.addWidget(self.offset_label, 20, 0)
         grid0.addWidget(self.offset_label, 20, 0)
         grid0.addWidget(self.offset_entry, 20, 1)
         grid0.addWidget(self.offset_entry, 20, 1)
@@ -441,6 +437,7 @@ class EtchUI:
         grid0.addWidget(separator_line, 22, 0, 1, 2)
         grid0.addWidget(separator_line, 22, 0, 1, 2)
 
 
         self.compensate_btn = FCButton(_('Compensate'))
         self.compensate_btn = FCButton(_('Compensate'))
+        self.compensate_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/etch_32.png'))
         self.compensate_btn.setToolTip(
         self.compensate_btn.setToolTip(
             _("Will increase the copper features thickness to compensate the lateral etch.")
             _("Will increase the copper features thickness to compensate the lateral etch.")
         )
         )

+ 40 - 44
appTools/ToolExtractDrills.py

@@ -381,35 +381,35 @@ class ToolExtractDrills(AppTool):
 
 
     def on_hole_size_toggle(self, val):
     def on_hole_size_toggle(self, val):
         if val == "fixed":
         if val == "fixed":
-            self.ui.fixed_label.setDisabled(False)
-            self.ui.dia_entry.setDisabled(False)
-            self.ui.dia_label.setDisabled(False)
+            self.ui.fixed_label.setVisible(True)
+            self.ui.dia_entry.setVisible(True)
+            self.ui.dia_label.setVisible(True)
 
 
-            self.ui.ring_frame.setDisabled(True)
+            self.ui.ring_frame.setVisible(False)
 
 
-            self.ui.prop_label.setDisabled(True)
-            self.ui.factor_label.setDisabled(True)
-            self.ui.factor_entry.setDisabled(True)
+            self.ui.prop_label.setVisible(False)
+            self.ui.factor_label.setVisible(False)
+            self.ui.factor_entry.setVisible(False)
         elif val == "ring":
         elif val == "ring":
-            self.ui.fixed_label.setDisabled(True)
-            self.ui.dia_entry.setDisabled(True)
-            self.ui.dia_label.setDisabled(True)
+            self.ui.fixed_label.setVisible(False)
+            self.ui.dia_entry.setVisible(False)
+            self.ui.dia_label.setVisible(False)
 
 
-            self.ui.ring_frame.setDisabled(False)
+            self.ui.ring_frame.setVisible(True)
 
 
-            self.ui.prop_label.setDisabled(True)
-            self.ui.factor_label.setDisabled(True)
-            self.ui.factor_entry.setDisabled(True)
+            self.ui.prop_label.setVisible(False)
+            self.ui.factor_label.setVisible(False)
+            self.ui.factor_entry.setVisible(False)
         elif val == "prop":
         elif val == "prop":
-            self.ui.fixed_label.setDisabled(True)
-            self.ui.dia_entry.setDisabled(True)
-            self.ui.dia_label.setDisabled(True)
+            self.ui.fixed_label.setVisible(False)
+            self.ui.dia_entry.setVisible(False)
+            self.ui.dia_label.setVisible(False)
 
 
-            self.ui.ring_frame.setDisabled(True)
+            self.ui.ring_frame.setVisible(False)
 
 
-            self.ui.prop_label.setDisabled(False)
-            self.ui.factor_label.setDisabled(False)
-            self.ui.factor_entry.setDisabled(False)
+            self.ui.prop_label.setVisible(True)
+            self.ui.factor_label.setVisible(True)
+            self.ui.factor_entry.setVisible(True)
 
 
     def reset_fields(self):
     def reset_fields(self):
         self.ui.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.ui.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@@ -530,8 +530,8 @@ class ExtractDrillsUI:
         self.hole_size_radio = RadioSet(
         self.hole_size_radio = RadioSet(
             [
             [
                 {'label': _("Fixed Diameter"), 'value': 'fixed'},
                 {'label': _("Fixed Diameter"), 'value': 'fixed'},
-                {'label': _("Fixed Annular Ring"), 'value': 'ring'},
-                {'label': _("Proportional"), 'value': 'prop'}
+                {'label': _("Proportional"), 'value': 'prop'},
+                {'label': _("Fixed Annular Ring"), 'value': 'ring'}
             ],
             ],
             orientation='vertical',
             orientation='vertical',
             stretch=False)
             stretch=False)
@@ -552,7 +552,7 @@ class ExtractDrillsUI:
         # Diameter value
         # Diameter value
         self.dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dia_entry.set_precision(self.decimals)
         self.dia_entry.set_precision(self.decimals)
-        self.dia_entry.set_range(0.0000, 9999.9999)
+        self.dia_entry.set_range(0.0000, 10000.0000)
 
 
         self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
         self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
         self.dia_label.setToolTip(
         self.dia_label.setToolTip(
@@ -562,11 +562,6 @@ class ExtractDrillsUI:
         grid1.addWidget(self.dia_label, 8, 0)
         grid1.addWidget(self.dia_label, 8, 0)
         grid1.addWidget(self.dia_entry, 8, 1)
         grid1.addWidget(self.dia_entry, 8, 1)
 
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid1.addWidget(separator_line, 9, 0, 1, 2)
-
         self.ring_frame = QtWidgets.QFrame()
         self.ring_frame = QtWidgets.QFrame()
         self.ring_frame.setContentsMargins(0, 0, 0, 0)
         self.ring_frame.setContentsMargins(0, 0, 0, 0)
         self.layout.addWidget(self.ring_frame)
         self.layout.addWidget(self.ring_frame)
@@ -598,7 +593,7 @@ class ExtractDrillsUI:
 
 
         self.circular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.circular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.circular_ring_entry.set_precision(self.decimals)
         self.circular_ring_entry.set_precision(self.decimals)
-        self.circular_ring_entry.set_range(0.0000, 9999.9999)
+        self.circular_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid2.addWidget(self.circular_ring_label, 1, 0)
         grid2.addWidget(self.circular_ring_label, 1, 0)
         grid2.addWidget(self.circular_ring_entry, 1, 1)
         grid2.addWidget(self.circular_ring_entry, 1, 1)
@@ -611,7 +606,7 @@ class ExtractDrillsUI:
 
 
         self.oblong_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.oblong_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.oblong_ring_entry.set_precision(self.decimals)
         self.oblong_ring_entry.set_precision(self.decimals)
-        self.oblong_ring_entry.set_range(0.0000, 9999.9999)
+        self.oblong_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid2.addWidget(self.oblong_ring_label, 2, 0)
         grid2.addWidget(self.oblong_ring_label, 2, 0)
         grid2.addWidget(self.oblong_ring_entry, 2, 1)
         grid2.addWidget(self.oblong_ring_entry, 2, 1)
@@ -624,7 +619,7 @@ class ExtractDrillsUI:
 
 
         self.square_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.square_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.square_ring_entry.set_precision(self.decimals)
         self.square_ring_entry.set_precision(self.decimals)
-        self.square_ring_entry.set_range(0.0000, 9999.9999)
+        self.square_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid2.addWidget(self.square_ring_label, 3, 0)
         grid2.addWidget(self.square_ring_label, 3, 0)
         grid2.addWidget(self.square_ring_entry, 3, 1)
         grid2.addWidget(self.square_ring_entry, 3, 1)
@@ -637,7 +632,7 @@ class ExtractDrillsUI:
 
 
         self.rectangular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.rectangular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.rectangular_ring_entry.set_precision(self.decimals)
         self.rectangular_ring_entry.set_precision(self.decimals)
-        self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
+        self.rectangular_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid2.addWidget(self.rectangular_ring_label, 4, 0)
         grid2.addWidget(self.rectangular_ring_label, 4, 0)
         grid2.addWidget(self.rectangular_ring_entry, 4, 1)
         grid2.addWidget(self.rectangular_ring_entry, 4, 1)
@@ -650,7 +645,7 @@ class ExtractDrillsUI:
 
 
         self.other_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.other_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.other_ring_entry.set_precision(self.decimals)
         self.other_ring_entry.set_precision(self.decimals)
-        self.other_ring_entry.set_range(0.0000, 9999.9999)
+        self.other_ring_entry.set_range(0.0000, 10000.0000)
 
 
         grid2.addWidget(self.other_ring_label, 5, 0)
         grid2.addWidget(self.other_ring_label, 5, 0)
         grid2.addWidget(self.other_ring_entry, 5, 1)
         grid2.addWidget(self.other_ring_entry, 5, 1)
@@ -660,11 +655,6 @@ class ExtractDrillsUI:
         grid3.setColumnStretch(0, 0)
         grid3.setColumnStretch(0, 0)
         grid3.setColumnStretch(1, 1)
         grid3.setColumnStretch(1, 1)
 
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid3.addWidget(separator_line, 1, 0, 1, 2)
-
         # Annular Ring value
         # Annular Ring value
         self.prop_label = QtWidgets.QLabel('<b>%s</b>' % _("Proportional Diameter"))
         self.prop_label = QtWidgets.QLabel('<b>%s</b>' % _("Proportional Diameter"))
         grid3.addWidget(self.prop_label, 2, 0, 1, 2)
         grid3.addWidget(self.prop_label, 2, 0, 1, 2)
@@ -684,8 +674,14 @@ class ExtractDrillsUI:
         grid3.addWidget(self.factor_label, 3, 0)
         grid3.addWidget(self.factor_label, 3, 0)
         grid3.addWidget(self.factor_entry, 3, 1)
         grid3.addWidget(self.factor_entry, 3, 1)
 
 
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid3.addWidget(separator_line, 5, 0, 1, 2)
+
         # Extract drills from Gerber apertures flashes (pads)
         # Extract drills from Gerber apertures flashes (pads)
         self.e_drills_button = QtWidgets.QPushButton(_("Extract Drills"))
         self.e_drills_button = QtWidgets.QPushButton(_("Extract Drills"))
+        self.e_drills_button.setIcon(QtGui.QIcon(self.app.resource_location + '/drill16.png'))
         self.e_drills_button.setToolTip(
         self.e_drills_button.setToolTip(
             _("Extract drills from a given Gerber file.")
             _("Extract drills from a given Gerber file.")
         )
         )
@@ -719,12 +715,12 @@ class ExtractDrillsUI:
         self.rectangular_ring_entry.setEnabled(False)
         self.rectangular_ring_entry.setEnabled(False)
         self.other_ring_entry.setEnabled(False)
         self.other_ring_entry.setEnabled(False)
 
 
-        self.dia_entry.setDisabled(True)
-        self.dia_label.setDisabled(True)
-        self.factor_label.setDisabled(True)
-        self.factor_entry.setDisabled(True)
+        self.dia_entry.setVisible(False)
+        self.dia_label.setVisible(False)
+        self.factor_label.setVisible(False)
+        self.factor_entry.setVisible(False)
 
 
-        self.ring_frame.setDisabled(True)
+        self.ring_frame.setVisible(False)
         # #################################### FINSIHED GUI ###########################
         # #################################### FINSIHED GUI ###########################
         # #############################################################################
         # #############################################################################
 
 

+ 129 - 101
appTools/ToolFiducials.py

@@ -12,6 +12,7 @@ from appGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable, FC
 
 
 from shapely.geometry import Point, Polygon, MultiPolygon, LineString
 from shapely.geometry import Point, Polygon, MultiPolygon, LineString
 from shapely.geometry import box as box
 from shapely.geometry import box as box
+from shapely.ops import unary_union
 
 
 import math
 import math
 import logging
 import logging
@@ -76,6 +77,8 @@ class ToolFiducials(AppTool):
 
 
         self.click_points = []
         self.click_points = []
 
 
+        self.handlers_connected = False
+
         # SIGNALS
         # SIGNALS
         self.ui.add_cfid_button.clicked.connect(self.add_fiducials)
         self.ui.add_cfid_button.clicked.connect(self.add_fiducials)
         self.ui.add_sm_opening_button.clicked.connect(self.add_soldermask_opening)
         self.ui.add_sm_opening_button.clicked.connect(self.add_soldermask_opening)
@@ -147,12 +150,13 @@ class ToolFiducials(AppTool):
     def on_method_change(self, val):
     def on_method_change(self, val):
         """
         """
         Make sure that on method change we disconnect the event handlers and reset the points storage
         Make sure that on method change we disconnect the event handlers and reset the points storage
-        :param val: value of the Radio button which trigger this method
-        :return: None
+
+        :param val:     value of the Radio button which trigger this method
+        :return:        None
         """
         """
-        if val == 'auto':
-            self.click_points = []
+        self.click_points = []
 
 
+        if val == 'auto':
             try:
             try:
                 self.disconnect_event_handlers()
                 self.disconnect_event_handlers()
             except TypeError:
             except TypeError:
@@ -228,10 +232,14 @@ class ToolFiducials(AppTool):
                 )
                 )
                 self.ui.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y0))
                 self.ui.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y0))
 
 
-            self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
-            self.grb_object.source_file = self.app.f_handlers.export_gerber(obj_name=self.grb_object.options['name'],
-                                                                            filename=None,
-                                                                            local_use=self.grb_object, use_thread=False)
+            ret_val = self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
+            self.app.call_source = "app"
+            if ret_val == 'fail':
+                self.app.call_source = "app"
+                self.disconnect_event_handlers()
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
+                return
+
             self.on_exit()
             self.on_exit()
         else:
         else:
             self.app.inform.emit(_("Click to add first Fiducial. Bottom Left..."))
             self.app.inform.emit(_("Click to add first Fiducial. Bottom Left..."))
@@ -259,6 +267,8 @@ class ToolFiducials(AppTool):
 
 
         radius = fid_size / 2.0
         radius = fid_size / 2.0
 
 
+        new_apertures = deepcopy(g_obj.apertures)
+
         if fid_type == 'circular':
         if fid_type == 'circular':
             geo_list = [Point(pt).buffer(radius, self.grb_steps_per_circle) for pt in points_list]
             geo_list = [Point(pt).buffer(radius, self.grb_steps_per_circle) for pt in points_list]
 
 
@@ -270,10 +280,8 @@ class ToolFiducials(AppTool):
 
 
             if aperture_found:
             if aperture_found:
                 for geo in geo_list:
                 for geo in geo_list:
-                    dict_el = {}
-                    dict_el['follow'] = geo.centroid
-                    dict_el['solid'] = geo
-                    g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo.centroid, 'solid': geo}
+                    new_apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
             else:
             else:
                 ap_keys = list(g_obj.apertures.keys())
                 ap_keys = list(g_obj.apertures.keys())
                 if ap_keys:
                 if ap_keys:
@@ -281,16 +289,15 @@ class ToolFiducials(AppTool):
                 else:
                 else:
                     new_apid = '10'
                     new_apid = '10'
 
 
-                g_obj.apertures[new_apid] = {}
-                g_obj.apertures[new_apid]['type'] = 'C'
-                g_obj.apertures[new_apid]['size'] = fid_size
-                g_obj.apertures[new_apid]['geometry'] = []
+                new_apertures[new_apid] = {
+                    'type': 'C',
+                    'size': fid_size,
+                    'geometry': []
+                }
 
 
                 for geo in geo_list:
                 for geo in geo_list:
-                    dict_el = {}
-                    dict_el['follow'] = geo.centroid
-                    dict_el['solid'] = geo
-                    g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo.centroid, 'solid': geo}
+                    new_apertures[new_apid]['geometry'].append(deepcopy(dict_el))
 
 
             s_list = []
             s_list = []
             if g_obj.solid_geometry:
             if g_obj.solid_geometry:
@@ -301,7 +308,6 @@ class ToolFiducials(AppTool):
                     s_list.append(g_obj.solid_geometry)
                     s_list.append(g_obj.solid_geometry)
 
 
             s_list += geo_list
             s_list += geo_list
-            g_obj.solid_geometry = MultiPolygon(s_list)
         elif fid_type == 'cross':
         elif fid_type == 'cross':
             geo_list = []
             geo_list = []
 
 
@@ -330,13 +336,10 @@ class ToolFiducials(AppTool):
                     geo_buff_list.append(geo_buff_h)
                     geo_buff_list.append(geo_buff_h)
                     geo_buff_list.append(geo_buff_v)
                     geo_buff_list.append(geo_buff_v)
 
 
-                    dict_el = {}
-                    dict_el['follow'] = geo_buff_h.centroid
-                    dict_el['solid'] = geo_buff_h
-                    g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
-                    dict_el['follow'] = geo_buff_v.centroid
-                    dict_el['solid'] = geo_buff_v
-                    g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo_buff_h.centroid, 'solid': geo_buff_h}
+                    new_apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo_buff_v.centroid, 'solid': geo_buff_v}
+                    new_apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
             else:
             else:
                 ap_keys = list(g_obj.apertures.keys())
                 ap_keys = list(g_obj.apertures.keys())
                 if ap_keys:
                 if ap_keys:
@@ -344,10 +347,11 @@ class ToolFiducials(AppTool):
                 else:
                 else:
                     new_apid = '10'
                     new_apid = '10'
 
 
-                g_obj.apertures[new_apid] = {}
-                g_obj.apertures[new_apid]['type'] = 'C'
-                g_obj.apertures[new_apid]['size'] = line_thickness
-                g_obj.apertures[new_apid]['geometry'] = []
+                new_apertures[new_apid] = {
+                    'type': 'C',
+                    'size': line_thickness,
+                    'geometry': []
+                }
 
 
                 for geo in geo_list:
                 for geo in geo_list:
                     geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
                     geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
@@ -355,13 +359,10 @@ class ToolFiducials(AppTool):
                     geo_buff_list.append(geo_buff_h)
                     geo_buff_list.append(geo_buff_h)
                     geo_buff_list.append(geo_buff_v)
                     geo_buff_list.append(geo_buff_v)
 
 
-                    dict_el = {}
-                    dict_el['follow'] = geo_buff_h.centroid
-                    dict_el['solid'] = geo_buff_h
-                    g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
-                    dict_el['follow'] = geo_buff_v.centroid
-                    dict_el['solid'] = geo_buff_v
-                    g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo_buff_h.centroid, 'solid': geo_buff_h}
+                    new_apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo_buff_v.centroid, 'solid': geo_buff_v}
+                    new_apertures[new_apid]['geometry'].append(deepcopy(dict_el))
 
 
             s_list = []
             s_list = []
             if g_obj.solid_geometry:
             if g_obj.solid_geometry:
@@ -375,7 +376,6 @@ class ToolFiducials(AppTool):
             geo_buff_list = geo_buff_list.buffer(0)
             geo_buff_list = geo_buff_list.buffer(0)
             for poly in geo_buff_list:
             for poly in geo_buff_list:
                 s_list.append(poly)
                 s_list.append(poly)
-            g_obj.solid_geometry = MultiPolygon(s_list)
         else:
         else:
             # chess pattern fiducial type
             # chess pattern fiducial type
             geo_list = []
             geo_list = []
@@ -412,10 +412,8 @@ class ToolFiducials(AppTool):
                 for geo in geo_list:
                 for geo in geo_list:
                     geo_buff_list.append(geo)
                     geo_buff_list.append(geo)
 
 
-                    dict_el = {}
-                    dict_el['follow'] = geo.centroid
-                    dict_el['solid'] = geo
-                    g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo.centroid, 'solid': geo}
+                    new_apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
             else:
             else:
                 ap_keys = list(g_obj.apertures.keys())
                 ap_keys = list(g_obj.apertures.keys())
                 if ap_keys:
                 if ap_keys:
@@ -423,20 +421,19 @@ class ToolFiducials(AppTool):
                 else:
                 else:
                     new_apid = '10'
                     new_apid = '10'
 
 
-                g_obj.apertures[new_apid] = {}
-                g_obj.apertures[new_apid]['type'] = 'R'
-                g_obj.apertures[new_apid]['size'] = new_ap_size
-                g_obj.apertures[new_apid]['width'] = fid_size
-                g_obj.apertures[new_apid]['height'] = fid_size
-                g_obj.apertures[new_apid]['geometry'] = []
+                new_apertures[new_apid] = {
+                    'type': 'R',
+                    'size': new_ap_size,
+                    'width': fid_size,
+                    'height': fid_size,
+                    'geometry': []
+                }
 
 
                 for geo in geo_list:
                 for geo in geo_list:
                     geo_buff_list.append(geo)
                     geo_buff_list.append(geo)
 
 
-                    dict_el = {}
-                    dict_el['follow'] = geo.centroid
-                    dict_el['solid'] = geo
-                    g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+                    dict_el = {'follow': geo.centroid, 'solid': geo}
+                    new_apertures[new_apid]['geometry'].append(deepcopy(dict_el))
 
 
             s_list = []
             s_list = []
             if g_obj.solid_geometry:
             if g_obj.solid_geometry:
@@ -448,7 +445,28 @@ class ToolFiducials(AppTool):
 
 
             for poly in geo_buff_list:
             for poly in geo_buff_list:
                 s_list.append(poly)
                 s_list.append(poly)
-            g_obj.solid_geometry = MultiPolygon(s_list)
+
+        outname = '%s_%s' % (str(g_obj.options['name']), 'fid')
+
+        def initialize(grb_obj, app_obj):
+            grb_obj.options = {}
+            for opt in g_obj.options:
+                if opt != 'name':
+                    grb_obj.options[opt] = deepcopy(g_obj.options[opt])
+            grb_obj.options['name'] = outname
+            grb_obj.multitool = False
+            grb_obj.multigeo = False
+            grb_obj.follow = deepcopy(g_obj.follow)
+            grb_obj.apertures = new_apertures
+            grb_obj.solid_geometry = unary_union(s_list)
+            grb_obj.follow_geometry = deepcopy(g_obj.follow_geometry) + geo_list
+
+            grb_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None, local_use=grb_obj,
+                                                                   use_thread=False)
+
+        ret = self.app.app_obj.new_object('gerber', outname, initialize, plot=True)
+
+        return ret
 
 
     def add_soldermask_opening(self):
     def add_soldermask_opening(self):
         sm_opening_dia = self.ui.fid_size_entry.get_value() * 2.0
         sm_opening_dia = self.ui.fid_size_entry.get_value() * 2.0
@@ -465,12 +483,15 @@ class ToolFiducials(AppTool):
             return
             return
 
 
         self.sm_obj_set.add(self.sm_object.options['name'])
         self.sm_obj_set.add(self.sm_object.options['name'])
-        self.add_fiducials_geo(self.click_points, g_obj=self.sm_object, fid_size=sm_opening_dia, fid_type='circular')
+        ret_val = self.add_fiducials_geo(
+            self.click_points, g_obj=self.sm_object, fid_size=sm_opening_dia, fid_type='circular')
+        self.app.call_source = "app"
+        if ret_val == 'fail':
+            self.app.call_source = "app"
+            self.disconnect_event_handlers()
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
+            return
 
 
-        self.sm_object.source_file = self.app.f_handlers.export_gerber(obj_name=self.sm_object.options['name'],
-                                                                       filename=None,
-                                                                       local_use=self.sm_object,
-                                                                       use_thread=False)
         self.on_exit()
         self.on_exit()
 
 
     def on_mouse_release(self, event):
     def on_mouse_release(self, event):
@@ -496,7 +517,7 @@ class ToolFiducials(AppTool):
             self.check_points()
             self.check_points()
 
 
     def check_points(self):
     def check_points(self):
-        fid_type = self.fid_type_radio.get_value()
+        fid_type = self.ui.fid_type_radio.get_value()
 
 
         if len(self.click_points) == 1:
         if len(self.click_points) == 1:
             self.ui.bottom_left_coords_entry.set_value(self.click_points[0])
             self.ui.bottom_left_coords_entry.set_value(self.click_points[0])
@@ -508,20 +529,30 @@ class ToolFiducials(AppTool):
                 self.app.inform.emit(_("Click to add the second fiducial. Top Left or Bottom Right..."))
                 self.app.inform.emit(_("Click to add the second fiducial. Top Left or Bottom Right..."))
             elif len(self.click_points) == 3:
             elif len(self.click_points) == 3:
                 self.ui.sec_points_coords_entry.set_value(self.click_points[2])
                 self.ui.sec_points_coords_entry.set_value(self.click_points[2])
-                self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
-                self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
-                self.grb_object.source_file = self.app.f_handlers.export_gerber(
-                    obj_name=self.grb_object.options['name'], filename=None, local_use=self.grb_object,
-                    use_thread=False)
+                self.app.inform.emit('[success] %s' % _("Done."))
+
+                ret_val = self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
+                self.app.call_source = "app"
+
+                if ret_val == 'fail':
+                    self.app.call_source = "app"
+                    self.disconnect_event_handlers()
+                    self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
+                    return
                 self.on_exit()
                 self.on_exit()
         else:
         else:
             if len(self.click_points) == 2:
             if len(self.click_points) == 2:
                 self.ui.top_right_coords_entry.set_value(self.click_points[1])
                 self.ui.top_right_coords_entry.set_value(self.click_points[1])
-                self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
-                self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
-                self.grb_object.source_file = self.app.f_handlers.export_gerber(
-                    obj_name=self.grb_object.options['name'], filename=None,
-                    local_use=self.grb_object, use_thread=False)
+                self.app.inform.emit('[success] %s' % _("Done."))
+
+                ret_val = self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
+                self.app.call_source = "app"
+
+                if ret_val == 'fail':
+                    self.app.call_source = "app"
+                    self.disconnect_event_handlers()
+                    self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
+                    return
                 self.on_exit()
                 self.on_exit()
 
 
     def on_mouse_move(self, event):
     def on_mouse_move(self, event):
@@ -529,8 +560,9 @@ class ToolFiducials(AppTool):
 
 
     def replot(self, obj, run_thread=True):
     def replot(self, obj, run_thread=True):
         def worker_task():
         def worker_task():
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 obj.plot()
                 obj.plot()
+                self.app.app_obj.object_plotted.emit(obj)
 
 
         if run_thread:
         if run_thread:
             self.app.worker_task.emit({'fcn': worker_task, 'params': []})
             self.app.worker_task.emit({'fcn': worker_task, 'params': []})
@@ -579,10 +611,6 @@ class ToolFiducials(AppTool):
             except Exception as e:
             except Exception as e:
                 log.debug("ToolFiducials.on_exit() sm_obj bounds error --> %s" % str(e))
                 log.debug("ToolFiducials.on_exit() sm_obj bounds error --> %s" % str(e))
 
 
-        # reset the variables
-        self.grb_object = None
-        self.sm_object = None
-
         # Events ID
         # Events ID
         self.mr = None
         self.mr = None
         # self.mm = None
         # self.mm = None
@@ -597,32 +625,31 @@ class ToolFiducials(AppTool):
         self.app.inform.emit('[success] %s' % _("Fiducials Tool exit."))
         self.app.inform.emit('[success] %s' % _("Fiducials Tool exit."))
 
 
     def connect_event_handlers(self):
     def connect_event_handlers(self):
-        if self.app.is_legacy is False:
-            self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-            # self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-        else:
-            self.app.plotcanvas.graph_event_disconnect(self.app.mp)
-            # self.app.plotcanvas.graph_event_disconnect(self.app.mm)
-            self.app.plotcanvas.graph_event_disconnect(self.app.mr)
+        if self.handlers_connected is False:
+            if self.app.is_legacy is False:
+                self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+                self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+            else:
+                self.app.plotcanvas.graph_event_disconnect(self.app.mp)
+                self.app.plotcanvas.graph_event_disconnect(self.app.mr)
 
 
-        self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
-        # self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
+            self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
+
+            self.handlers_connected = True
 
 
     def disconnect_event_handlers(self):
     def disconnect_event_handlers(self):
-        if self.app.is_legacy is False:
-            self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
-            # self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
-        else:
-            self.app.plotcanvas.graph_event_disconnect(self.mr)
-            # self.app.plotcanvas.graph_event_disconnect(self.mm)
+        if self.handlers_connected is True:
+            if self.app.is_legacy is False:
+                self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
+            else:
+                self.app.plotcanvas.graph_event_disconnect(self.mr)
+
+            self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
+                                                                  self.app.on_mouse_click_over_plot)
 
 
-        self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
-                                                              self.app.on_mouse_click_over_plot)
-        # self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
-        #                                                       self.app.on_mouse_move_over_plot)
-        self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
-                                                              self.app.on_mouse_click_release_over_plot)
+            self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
+                                                                  self.app.on_mouse_click_release_over_plot)
+            self.handlers_connected = False
 
 
     def flatten(self, geometry):
     def flatten(self, geometry):
         """
         """
@@ -790,7 +817,7 @@ class FidoUI:
             _("Bounding box margin.")
             _("Bounding box margin.")
         )
         )
         self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.margin_entry.set_range(-9999.9999, 9999.9999)
+        self.margin_entry.set_range(-10000.0000, 10000.0000)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.setSingleStep(0.1)
         self.margin_entry.setSingleStep(0.1)
 
 
@@ -804,7 +831,7 @@ class FidoUI:
         ], stretch=False)
         ], stretch=False)
         self.mode_label = QtWidgets.QLabel(_("Mode:"))
         self.mode_label = QtWidgets.QLabel(_("Mode:"))
         self.mode_label.setToolTip(
         self.mode_label.setToolTip(
-            _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n "
+            _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n"
               "- 'Manual' - manual placement of fiducials.")
               "- 'Manual' - manual placement of fiducials.")
         )
         )
         grid_lay.addWidget(self.mode_label, 3, 0)
         grid_lay.addWidget(self.mode_label, 3, 0)
@@ -853,7 +880,7 @@ class FidoUI:
             _("Thickness of the line that makes the fiducial.")
             _("Thickness of the line that makes the fiducial.")
         )
         )
         self.line_thickness_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.line_thickness_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.line_thickness_entry.set_range(0.00001, 9999.9999)
+        self.line_thickness_entry.set_range(0.00001, 10000.0000)
         self.line_thickness_entry.set_precision(self.decimals)
         self.line_thickness_entry.set_precision(self.decimals)
         self.line_thickness_entry.setSingleStep(0.1)
         self.line_thickness_entry.setSingleStep(0.1)
 
 
@@ -882,6 +909,7 @@ class FidoUI:
 
 
         # ## Insert Copper Fiducial
         # ## Insert Copper Fiducial
         self.add_cfid_button = QtWidgets.QPushButton(_("Add Fiducial"))
         self.add_cfid_button = QtWidgets.QPushButton(_("Add Fiducial"))
+        self.add_cfid_button.setIcon(QtGui.QIcon(self.app.resource_location + '/fiducials_32.png'))
         self.add_cfid_button.setToolTip(
         self.add_cfid_button.setToolTip(
             _("Will add a polygon on the copper layer to serve as fiducial.")
             _("Will add a polygon on the copper layer to serve as fiducial.")
         )
         )

+ 20 - 15
appTools/ToolFilm.py

@@ -156,19 +156,19 @@ class Film(AppTool):
         try:
         try:
             name = self.ui.tf_object_combo.currentText()
             name = self.ui.tf_object_combo.currentText()
         except Exception:
         except Exception:
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("No FlatCAM object selected. Load an object for Film and retry."))
+            self.app.inform.emit('[ERROR_NOTCL] %s %s' %
+                                 (_("No object is selected."), _("Load an object for Film and retry.")))
             return
             return
 
 
         try:
         try:
             boxname = self.ui.tf_box_combo.currentText()
             boxname = self.ui.tf_box_combo.currentText()
         except Exception:
         except Exception:
-            self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                 _("No FlatCAM object selected. Load an object for Box and retry."))
+            self.app.inform.emit('[ERROR_NOTCL] %s %s' %
+                                 (_("No object is selected."), _("Load an object for Box and retry.")))
             return
             return
 
 
         if name == '' or boxname == '':
         if name == '' or boxname == '':
-            self.app.inform.emit('[ERROR_NOTCL] %s' % _("No FlatCAM object selected."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object is selected."))
             return
             return
 
 
         scale_stroke_width = float(self.ui.film_scale_stroke_entry.get_value())
         scale_stroke_width = float(self.ui.film_scale_stroke_entry.get_value())
@@ -232,7 +232,9 @@ class Film(AppTool):
                 directory=self.app.get_last_save_folder() + '/' + name + '_film',
                 directory=self.app.get_last_save_folder() + '/' + name + '_film',
                 ext_filter=filter_ext)
                 ext_filter=filter_ext)
         except TypeError:
         except TypeError:
-            filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export positive film"))
+            filename, _f = FCFileSaveDialog.get_saved_filename(
+                caption=_("Export positive film"),
+                ext_filter=filter_ext)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -289,7 +291,7 @@ class Film(AppTool):
                 if film_obj.apertures[apid]['type'] == 'C':
                 if film_obj.apertures[apid]['type'] == 'C':
                     if punch_size >= float(film_obj.apertures[apid]['size']):
                     if punch_size >= float(film_obj.apertures[apid]['size']):
                         self.app.inform.emit('[ERROR_NOTCL] %s' %
                         self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                             _(" Could not generate punched hole film because the punch hole size"
+                                             _("Failed. Punch hole size "
                                                "is bigger than some of the apertures in the Gerber object."))
                                                "is bigger than some of the apertures in the Gerber object."))
                         return 'fail'
                         return 'fail'
                     else:
                     else:
@@ -301,7 +303,7 @@ class Film(AppTool):
                     if punch_size >= float(film_obj.apertures[apid]['width']) or \
                     if punch_size >= float(film_obj.apertures[apid]['width']) or \
                             punch_size >= float(film_obj.apertures[apid]['height']):
                             punch_size >= float(film_obj.apertures[apid]['height']):
                         self.app.inform.emit('[ERROR_NOTCL] %s' %
                         self.app.inform.emit('[ERROR_NOTCL] %s' %
-                                             _("Could not generate punched hole film because the punch hole size"
+                                             _("Failed. Punch hole size "
                                                "is bigger than some of the apertures in the Gerber object."))
                                                "is bigger than some of the apertures in the Gerber object."))
                         return 'fail'
                         return 'fail'
                     else:
                     else:
@@ -319,7 +321,7 @@ class Film(AppTool):
 
 
             if punched_solid_geometry == temp_solid_geometry:
             if punched_solid_geometry == temp_solid_geometry:
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
-                                     _("Could not generate punched hole film because the newly created object geometry "
+                                     _("Failed. The new object geometry "
                                        "is the same as the one in the source object geometry..."))
                                        "is the same as the one in the source object geometry..."))
                 return 'fail'
                 return 'fail'
 
 
@@ -378,7 +380,9 @@ class Film(AppTool):
                 directory=self.app.get_last_save_folder() + '/' + name + '_film',
                 directory=self.app.get_last_save_folder() + '/' + name + '_film',
                 ext_filter=filter_ext)
                 ext_filter=filter_ext)
         except TypeError:
         except TypeError:
-            filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export negative film"))
+            filename, _f = FCFileSaveDialog.get_saved_filename(
+                caption=_("Export negative film"),
+                ext_filter=filter_ext)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -609,7 +613,8 @@ class Film(AppTool):
                     drawing = svg2rlg(doc_final)
                     drawing = svg2rlg(doc_final)
 
 
                     p_size = self.ui.pagesize_combo.get_value()
                     p_size = self.ui.pagesize_combo.get_value()
-                    if p_size == 'Bounds':                        renderPDF.drawToFile(drawing, filename)
+                    if p_size == 'Bounds':
+                        renderPDF.drawToFile(drawing, filename)
                     else:
                     else:
                         if self.ui.orientation_radio.get_value() == 'p':
                         if self.ui.orientation_radio.get_value() == 'p':
                             page_size = portrait(self.ui.pagesize[p_size])
                             page_size = portrait(self.ui.pagesize[p_size])
@@ -950,7 +955,7 @@ class FilmUI:
         self.tf_type_box_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'},
         self.tf_type_box_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'},
                                            {'label': _('Geometry'), 'value': 'geo'}])
                                            {'label': _('Geometry'), 'value': 'geo'}])
 
 
-        self.tf_type_box_combo_label = FCLabel(_("Box Type:"))
+        self.tf_type_box_combo_label = FCLabel('%s:' % _("Box Type"))
         self.tf_type_box_combo_label.setToolTip(
         self.tf_type_box_combo_label.setToolTip(
             _("Specify the type of object to be used as an container for\n"
             _("Specify the type of object to be used as an container for\n"
               "film creation. It can be: Gerber or Geometry type."
               "film creation. It can be: Gerber or Geometry type."
@@ -1103,7 +1108,7 @@ class FilmUI:
                                           {'label': _('Y'), 'value': 'y'},
                                           {'label': _('Y'), 'value': 'y'},
                                           {'label': _('Both'), 'value': 'both'}],
                                           {'label': _('Both'), 'value': 'both'}],
                                          stretch=False)
                                          stretch=False)
-        self.film_mirror_axis_label = FCLabel('%s:' % _("Mirror axis"))
+        self.film_mirror_axis_label = FCLabel('%s:' % _("Mirror Axis"))
 
 
         grid0.addWidget(self.film_mirror_axis_label, 16, 0)
         grid0.addWidget(self.film_mirror_axis_label, 16, 0)
         grid0.addWidget(self.film_mirror_axis, 16, 1)
         grid0.addWidget(self.film_mirror_axis, 16, 1)
@@ -1142,7 +1147,7 @@ class FilmUI:
         self.film_type = RadioSet([{'label': _('Positive'), 'value': 'pos'},
         self.film_type = RadioSet([{'label': _('Positive'), 'value': 'pos'},
                                    {'label': _('Negative'), 'value': 'neg'}],
                                    {'label': _('Negative'), 'value': 'neg'}],
                                   stretch=False)
                                   stretch=False)
-        self.film_type_label = FCLabel(_("Film Type:"))
+        self.film_type_label = FCLabel('%s:' % _("Film Type"))
         self.film_type_label.setToolTip(
         self.film_type_label.setToolTip(
             _("Generate a Positive black film or a Negative film.\n"
             _("Generate a Positive black film or a Negative film.\n"
               "Positive means that it will print the features\n"
               "Positive means that it will print the features\n"
@@ -1254,7 +1259,7 @@ class FilmUI:
                                          {'label': _('PDF'), 'value': 'pdf'}
                                          {'label': _('PDF'), 'value': 'pdf'}
                                          ], stretch=False)
                                          ], stretch=False)
 
 
-        self.file_type_label = FCLabel(_("Film Type:"))
+        self.file_type_label = FCLabel('%s:' % _("Film Type"))
         self.file_type_label.setToolTip(
         self.file_type_label.setToolTip(
             _("The file type of the saved film. Can be:\n"
             _("The file type of the saved film. Can be:\n"
               "- 'SVG' -> open-source vectorial format\n"
               "- 'SVG' -> open-source vectorial format\n"

+ 3 - 4
appTools/ToolImage.py

@@ -79,11 +79,10 @@ class ToolImage(AppTool):
     def on_file_importimage(self):
     def on_file_importimage(self):
         """
         """
         Callback for menu item File->Import IMAGE.
         Callback for menu item File->Import IMAGE.
-        :param type_of_obj: to import the IMAGE as Geometry or as Gerber
-        :type type_of_obj: str
+
         :return: None
         :return: None
         """
         """
-        mask = []
+
         self.app.log.debug("on_file_importimage()")
         self.app.log.debug("on_file_importimage()")
 
 
         _filter = "Image Files(*.BMP *.PNG *.JPG *.JPEG);;" \
         _filter = "Image Files(*.BMP *.PNG *.JPG *.JPEG);;" \
@@ -147,7 +146,7 @@ class ToolImage(AppTool):
             geo_obj.import_image(filename, units=units, dpi=dpi, mode=mode, mask=mask)
             geo_obj.import_image(filename, units=units, dpi=dpi, mode=mode, mask=mask)
             geo_obj.multigeo = False
             geo_obj.multigeo = False
 
 
-        with self.app.proc_container.new(_("Importing Image")) as proc:
+        with self.app.proc_container.new('%s ...' % _("Importing")):
 
 
             # Object name
             # Object name
             name = outname or filename.split('/')[-1].split('\\')[-1]
             name = outname or filename.split('/')[-1].split('\\')[-1]

+ 13 - 15
appTools/ToolInvertGerber.py

@@ -44,7 +44,7 @@ class ToolInvertGerber(AppTool):
         self.ui.reset_button.clicked.connect(self.set_tool_ui)
         self.ui.reset_button.clicked.connect(self.set_tool_ui)
 
 
     def install(self, icon=None, separator=None, **kwargs):
     def install(self, icon=None, separator=None, **kwargs):
-        AppTool.install(self, icon, separator, shortcut='', **kwargs)
+        AppTool.install(self, icon, separator, shortcut='ALT+G', **kwargs)
 
 
     def run(self, toggle=True):
     def run(self, toggle=True):
         self.app.defaults.report_usage("ToolInvertGerber()")
         self.app.defaults.report_usage("ToolInvertGerber()")
@@ -131,21 +131,18 @@ class ToolInvertGerber(AppTool):
         new_apertures = {}
         new_apertures = {}
 
 
         if '0' not in new_apertures:
         if '0' not in new_apertures:
-            new_apertures['0'] = {}
-            new_apertures['0']['type'] = 'C'
-            new_apertures['0']['size'] = 0.0
-            new_apertures['0']['geometry'] = []
+            new_apertures['0'] = {
+                'type': 'C',
+                'size': 0.0,
+                'geometry': []
+            }
 
 
         try:
         try:
             for poly in new_solid_geometry:
             for poly in new_solid_geometry:
-                new_el = {}
-                new_el['solid'] = poly
-                new_el['follow'] = poly.exterior
+                new_el = {'solid': poly, 'follow': poly.exterior}
                 new_apertures['0']['geometry'].append(new_el)
                 new_apertures['0']['geometry'].append(new_el)
         except TypeError:
         except TypeError:
-            new_el = {}
-            new_el['solid'] = new_solid_geometry
-            new_el['follow'] = new_solid_geometry.exterior
+            new_el = {'solid': new_solid_geometry, 'follow': new_solid_geometry.exterior}
             new_apertures['0']['geometry'].append(new_el)
             new_apertures['0']['geometry'].append(new_el)
 
 
         def init_func(new_obj, app_obj):
         def init_func(new_obj, app_obj):
@@ -157,8 +154,8 @@ class ToolInvertGerber(AppTool):
             new_obj.apertures = deepcopy(new_apertures)
             new_obj.apertures = deepcopy(new_apertures)
 
 
             new_obj.solid_geometry = deepcopy(new_solid_geometry)
             new_obj.solid_geometry = deepcopy(new_solid_geometry)
-            new_obj.source_file = self.app.f_handlers.export_gerber(obj_name=outname, filename=None,
-                                                                    local_use=new_obj, use_thread=False)
+            new_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None,
+                                                                   local_use=new_obj, use_thread=False)
 
 
         self.app.app_obj.new_object('gerber', outname, init_func)
         self.app.app_obj.new_object('gerber', outname, init_func)
 
 
@@ -238,7 +235,7 @@ class InvertUI:
         )
         )
         self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.margin_entry.set_precision(self.decimals)
         self.margin_entry.set_precision(self.decimals)
-        self.margin_entry.set_range(0.0000, 9999.9999)
+        self.margin_entry.set_range(0.0000, 10000.0000)
         self.margin_entry.setObjectName(_("Margin"))
         self.margin_entry.setObjectName(_("Margin"))
 
 
         grid0.addWidget(self.margin_label, 5, 0, 1, 2)
         grid0.addWidget(self.margin_label, 5, 0, 1, 2)
@@ -267,6 +264,7 @@ class InvertUI:
         grid0.addWidget(separator_line, 9, 0, 1, 2)
         grid0.addWidget(separator_line, 9, 0, 1, 2)
 
 
         self.invert_btn = FCButton(_('Invert Gerber'))
         self.invert_btn = FCButton(_('Invert Gerber'))
+        self.invert_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/invert32.png'))
         self.invert_btn.setToolTip(
         self.invert_btn.setToolTip(
             _("Will invert the Gerber object: areas that have copper\n"
             _("Will invert the Gerber object: areas that have copper\n"
               "will be empty of copper and previous empty area will be\n"
               "will be empty of copper and previous empty area will be\n"
@@ -314,4 +312,4 @@ class InvertUI:
             self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
             self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
                                             (_("Edited value is out of range"), minval, maxval), False)
                                             (_("Edited value is out of range"), minval, maxval), False)
         else:
         else:
-            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)

+ 213 - 120
appTools/ToolIsolation.py

@@ -8,8 +8,8 @@
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5 import QtWidgets, QtCore, QtGui
 
 
 from appTool import AppTool
 from appTool import AppTool
-from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog, FCButton, \
-    FCComboBox, OptionalInputSection, FCSpinner, FCLabel
+from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton, \
+    FCComboBox, OptionalInputSection, FCSpinner, FCLabel, FCInputDialogSpinnerButton, FCComboBox2
 from appParsers.ParseGerber import Gerber
 from appParsers.ParseGerber import Gerber
 from camlib import grace
 from camlib import grace
 
 
@@ -58,7 +58,8 @@ class ToolIsolation(AppTool, Gerber):
         # #############################################################################
         # #############################################################################
         self.ui.tools_table.setupContextMenu()
         self.ui.tools_table.setupContextMenu()
         self.ui.tools_table.addContextMenu(
         self.ui.tools_table.addContextMenu(
-            _("Search and Add"), self.on_add_tool_by_key,
+            _("Search and Add"),
+            self.on_add_tool_by_key,
             icon=QtGui.QIcon(self.app.resource_location + "/plus16.png")
             icon=QtGui.QIcon(self.app.resource_location + "/plus16.png")
         )
         )
         self.ui.tools_table.addContextMenu(
         self.ui.tools_table.addContextMenu(
@@ -207,7 +208,7 @@ class ToolIsolation(AppTool, Gerber):
         self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
         self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked)
 
 
         # adding Tools
         # adding Tools
-        self.ui.add_newtool_button.clicked.connect(lambda: self.on_tool_add())
+        self.ui.search_and_add_btn.clicked.connect(lambda: self.on_tool_add())
         self.ui.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked)
         self.ui.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked)
 
 
         self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click)
         self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click)
@@ -779,35 +780,47 @@ class ToolIsolation(AppTool, Gerber):
         self.blockSignals(False)
         self.blockSignals(False)
 
 
     def on_add_tool_by_key(self):
     def on_add_tool_by_key(self):
-        tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"),
-                                       text='%s:' % _('Enter a Tool Diameter'),
-                                       min=0.0001, max=9999.9999, decimals=self.decimals)
+        # tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"),
+        #                                text='%s:' % _('Enter a Tool Diameter'),
+        #                                min=0.0001, max=10000.0000, decimals=self.decimals)
+        btn_icon = QtGui.QIcon(self.app.resource_location + '/open_excellon32.png')
+
+        tool_add_popup = FCInputDialogSpinnerButton(title='%s...' % _("New Tool"),
+                                                    text='%s:' % _('Enter a Tool Diameter'),
+                                                    min=0.0001, max=10000.0000, decimals=self.decimals,
+                                                    button_icon=btn_icon,
+                                                    callback=self.on_find_optimal_tooldia,
+                                                    parent=self.app.ui)
         tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
         tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
 
 
-        val, ok = tool_add_popup.get_value()
+        def find_optimal(valor):
+            tool_add_popup.set_value(float(valor))
+
+        self.optimal_found_sig.connect(find_optimal)
+
+        val, ok = tool_add_popup.get_results()
         if ok:
         if ok:
             if float(val) == 0:
             if float(val) == 0:
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                                      _("Please enter a tool diameter with non-zero value, in Float format."))
                                      _("Please enter a tool diameter with non-zero value, in Float format."))
+                self.optimal_found_sig.disconnect(find_optimal)
                 return
                 return
             self.on_tool_add(custom_dia=float(val))
             self.on_tool_add(custom_dia=float(val))
         else:
         else:
             self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Adding Tool cancelled"))
             self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Adding Tool cancelled"))
+        self.optimal_found_sig.disconnect(find_optimal)
 
 
     def on_reference_combo_changed(self):
     def on_reference_combo_changed(self):
         obj_type = self.ui.reference_combo_type.currentIndex()
         obj_type = self.ui.reference_combo_type.currentIndex()
         self.ui.reference_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
         self.ui.reference_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
         self.ui.reference_combo.setCurrentIndex(0)
         self.ui.reference_combo.setCurrentIndex(0)
-        self.ui.reference_combo.obj_type = {
-            _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
-        }[self.ui.reference_combo_type.get_value()]
+        self.ui.reference_combo.obj_type = {0: "Gerber", 1: "Excellon", 2: "Geometry"}[obj_type]
 
 
     def on_toggle_reference(self):
     def on_toggle_reference(self):
         val = self.ui.select_combo.get_value()
         val = self.ui.select_combo.get_value()
 
 
-        if val == _("All"):
+        if val == 0:    # ALl
             self.ui.reference_combo.hide()
             self.ui.reference_combo.hide()
-            self.ui.reference_combo_label.hide()
             self.ui.reference_combo_type.hide()
             self.ui.reference_combo_type.hide()
             self.ui.reference_combo_type_label.hide()
             self.ui.reference_combo_type_label.hide()
             self.ui.area_shape_label.hide()
             self.ui.area_shape_label.hide()
@@ -816,9 +829,8 @@ class ToolIsolation(AppTool, Gerber):
 
 
             # disable rest-machining for area painting
             # disable rest-machining for area painting
             self.ui.rest_cb.setDisabled(False)
             self.ui.rest_cb.setDisabled(False)
-        elif val == _("Area Selection"):
+        elif val == 1:  # Area Selection
             self.ui.reference_combo.hide()
             self.ui.reference_combo.hide()
-            self.ui.reference_combo_label.hide()
             self.ui.reference_combo_type.hide()
             self.ui.reference_combo_type.hide()
             self.ui.reference_combo_type_label.hide()
             self.ui.reference_combo_type_label.hide()
             self.ui.area_shape_label.show()
             self.ui.area_shape_label.show()
@@ -828,17 +840,15 @@ class ToolIsolation(AppTool, Gerber):
             # disable rest-machining for area isolation
             # disable rest-machining for area isolation
             self.ui.rest_cb.set_value(False)
             self.ui.rest_cb.set_value(False)
             self.ui.rest_cb.setDisabled(True)
             self.ui.rest_cb.setDisabled(True)
-        elif val == _("Polygon Selection"):
+        elif val == 2:  # Polygon Selection
             self.ui.reference_combo.hide()
             self.ui.reference_combo.hide()
-            self.ui.reference_combo_label.hide()
             self.ui.reference_combo_type.hide()
             self.ui.reference_combo_type.hide()
             self.ui.reference_combo_type_label.hide()
             self.ui.reference_combo_type_label.hide()
             self.ui.area_shape_label.hide()
             self.ui.area_shape_label.hide()
             self.ui.area_shape_radio.hide()
             self.ui.area_shape_radio.hide()
             self.ui.poly_int_cb.show()
             self.ui.poly_int_cb.show()
-        else:
+        else:   # Reference Object
             self.ui.reference_combo.show()
             self.ui.reference_combo.show()
-            self.ui.reference_combo_label.show()
             self.ui.reference_combo_type.show()
             self.ui.reference_combo_type.show()
             self.ui.reference_combo_type_label.show()
             self.ui.reference_combo_type_label.show()
             self.ui.area_shape_label.hide()
             self.ui.area_shape_label.hide()
@@ -886,7 +896,7 @@ class ToolIsolation(AppTool, Gerber):
         # if the sender is in the column with index 2 then we update the tool_type key
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
         if cw_col == 2:
             tt = cw.currentText()
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else 'Rough'
 
 
             self.iso_tools[currenuid].update({
             self.iso_tools[currenuid].update({
                 'type': typ,
                 'type': typ,
@@ -894,9 +904,124 @@ class ToolIsolation(AppTool, Gerber):
             })
             })
 
 
     def on_find_optimal_tooldia(self):
     def on_find_optimal_tooldia(self):
-        self.find_safe_tooldia_worker(is_displayed=True)
+        self.find_safe_tooldia_worker()
+
+    @staticmethod
+    def find_optim_mp(aperture_storage, decimals):
+        msg = 'ok'
+        total_geo = []
+
+        for ap in list(aperture_storage.keys()):
+            if 'geometry' in aperture_storage[ap]:
+                for geo_el in aperture_storage[ap]['geometry']:
+                    if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
+                        total_geo.append(geo_el['solid'])
+
+        total_geo = MultiPolygon(total_geo)
+        total_geo = total_geo.buffer(0)
+
+        try:
+            __ = iter(total_geo)
+            geo_len = len(total_geo)
+        except TypeError:
+            msg = ('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n"
+                                          "There are no distances between geometry elements to be found."))
+
+        min_dict = {}
+        idx = 1
+        for geo in total_geo:
+            for s_geo in total_geo[idx:]:
+                # minimize the number of distances by not taking into considerations
+                # those that are too small
+                dist = geo.distance(s_geo)
+                dist = float('%.*f' % (decimals, dist))
+                loc_1, loc_2 = nearest_points(geo, s_geo)
+
+                proc_loc = (
+                    (float('%.*f' % (decimals, loc_1.x)), float('%.*f' % (decimals, loc_1.y))),
+                    (float('%.*f' % (decimals, loc_2.x)), float('%.*f' % (decimals, loc_2.y)))
+                )
+
+                if dist in min_dict:
+                    min_dict[dist].append(proc_loc)
+                else:
+                    min_dict[dist] = [proc_loc]
+
+            idx += 1
+
+        min_list = list(min_dict.keys())
+        min_dist = min(min_list)
+
+        return msg, min_dist
+
+    # multiprocessing variant
+    def find_safe_tooldia_multiprocessing(self):
+        self.app.inform.emit(_("Checking tools for validity."))
+        self.units = self.app.defaults['units'].upper()
+
+        obj_name = self.ui.object_combo.currentText()
+
+        # Get source object.
+        try:
+            fcobj = self.app.collection.get_by_name(obj_name)
+        except Exception:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
+            return
+
+        if fcobj is None:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
+            return
+
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
+
+                ap_storage = fcobj.apertures
 
 
-    def find_safe_tooldia_worker(self, is_displayed):
+                p = app_obj.pool.apply_async(self.find_optim_mp, args=(ap_storage, self.decimals))
+                res = p.get()
+
+                if res[0] != 'ok':
+                    app_obj.inform.emit(res[0])
+                    return 'fail'
+                else:
+                    min_dist = res[1]
+
+                try:
+                    min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
+                    self.safe_tooldia = min_dist_truncated
+
+                    if self.safe_tooldia:
+                        # find the selected tool ID's
+                        sorted_tools = []
+                        table_items = self.ui.tools_table.selectedItems()
+                        sel_rows = {t.row() for t in table_items}
+                        for row in sel_rows:
+                            tid = int(self.ui.tools_table.item(row, 3).text())
+                            sorted_tools.append(tid)
+                        if not sorted_tools:
+                            msg = _("There are no tools selected in the Tool Table.")
+                            self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
+                            return 'fail'
+
+                        # check if the tools diameters are less then the safe tool diameter
+                        for tool in sorted_tools:
+                            tool_dia = float(self.iso_tools[tool]['tooldia'])
+                            if tool_dia > self.safe_tooldia:
+                                msg = _("Incomplete isolation. "
+                                        "At least one tool could not do a complete isolation.")
+                                self.app.inform.emit('[WARNING] %s' % msg)
+                                break
+
+                        # reset the value to prepare for another isolation
+                        self.safe_tooldia = None
+                except Exception as ee:
+                    log.debug(str(ee))
+                    return
+
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
+
+    def find_safe_tooldia_worker(self):
+        self.app.inform.emit(_("Checking tools for validity."))
         self.units = self.app.defaults['units'].upper()
         self.units = self.app.defaults['units'].upper()
 
 
         obj_name = self.ui.object_combo.currentText()
         obj_name = self.ui.object_combo.currentText()
@@ -912,8 +1037,8 @@ class ToolIsolation(AppTool, Gerber):
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
             return
             return
 
 
-        def job_thread(app_obj, is_display):
-            with self.app.proc_container.new(_("Working...")) as proc:
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
                 try:
                 try:
                     old_disp_number = 0
                     old_disp_number = 0
                     pol_nr = 0
                     pol_nr = 0
@@ -981,69 +1106,40 @@ class ToolIsolation(AppTool, Gerber):
                     min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
                     min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
                     self.safe_tooldia = min_dist_truncated
                     self.safe_tooldia = min_dist_truncated
 
 
-                    if is_display:
-                        self.optimal_found_sig.emit(min_dist_truncated)
+                    self.optimal_found_sig.emit(min_dist_truncated)
 
 
-                        app_obj.inform.emit('[success] %s: %s %s' %
-                                            (_("Optimal tool diameter found"), str(min_dist_truncated),
-                                             self.units.lower()))
-                    else:
-                        if self.safe_tooldia:
-                            # find the selected tool ID's
-                            sorted_tools = []
-                            table_items = self.ui.tools_table.selectedItems()
-                            sel_rows = {t.row() for t in table_items}
-                            for row in sel_rows:
-                                tid = int(self.ui.tools_table.item(row, 3).text())
-                                sorted_tools.append(tid)
-                            if not sorted_tools:
-                                msg = _("There are no tools selected in the Tool Table.")
-                                self.app.inform.emit('[ERROR_NOTCL] %s' % msg)
-                                return 'fail'
-
-                            # check if the tools diameters are less then the safe tool diameter
-                            for tool in sorted_tools:
-                                tool_dia = float(self.iso_tools[tool]['tooldia'])
-                                if tool_dia > self.safe_tooldia:
-                                    msg = _("Incomplete isolation. "
-                                            "At least one tool could not do a complete isolation.")
-                                    self.app.inform.emit('[WARNING] %s' % msg)
-                                    break
-
-                            # reset the value to prepare for another isolation
-                            self.safe_tooldia = None
+                    app_obj.inform.emit('[success] %s: %s %s' %
+                                        (_("Optimal tool diameter found"), str(min_dist_truncated),
+                                         self.units.lower()))
                 except Exception as ee:
                 except Exception as ee:
                     log.debug(str(ee))
                     log.debug(str(ee))
                     return
                     return
 
 
-        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app, is_displayed]})
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
 
     def on_tool_add(self, custom_dia=None):
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
         self.blockSignals(True)
 
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
-
-        new_tools_dict = deepcopy(self.default_data)
-        updated_tooldia = None
+        filename = self.app.tools_database_path()
 
 
+        tool_dia = custom_dia if custom_dia is not None else self.ui.new_tooldia_entry.get_value()
         # construct a list of all 'tooluid' in the self.iso_tools
         # construct a list of all 'tooluid' in the self.iso_tools
         tool_uid_list = [int(tooluid_key) for tooluid_key in self.iso_tools]
         tool_uid_list = [int(tooluid_key) for tooluid_key in self.iso_tools]
 
 
         # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
         # find maximum from the temp_uid, add 1 and this is the new 'tooluid'
         max_uid = 0 if not tool_uid_list else max(tool_uid_list)
         max_uid = 0 if not tool_uid_list else max(tool_uid_list)
-        tooluid = int(max_uid + 1)
+        tooluid = int(max_uid) + 1
+
+        new_tools_dict = deepcopy(self.default_data)
+        updated_tooldia = None
 
 
         tool_dias = []
         tool_dias = []
         for k, v in self.iso_tools.items():
         for k, v in self.iso_tools.items():
             for tool_v in v.keys():
             for tool_v in v.keys():
                 if tool_v == 'tooldia':
                 if tool_v == 'tooldia':
-                    tool_dias.append(self.app.dec_format(v[tool_v], self.decimals))
+                    tool_dias.append(self.app.dec_format(v['tooldia'], self.decimals))
 
 
         # determine the new tool diameter
         # determine the new tool diameter
-        if custom_dia is None:
-            tool_dia = self.ui.new_tooldia_entry.get_value()
-        else:
-            tool_dia = custom_dia
         if tool_dia is None or tool_dia == 0:
         if tool_dia is None or tool_dia == 0:
             self.build_ui()
             self.build_ui()
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, "
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, "
@@ -1084,7 +1180,7 @@ class ToolIsolation(AppTool, Gerber):
 
 
         offset = 'Path'
         offset = 'Path'
         offset_val = 0.0
         offset_val = 0.0
-        typ = "Rough"
+        typ = 'Rough'
         tool_type = 'V'
         tool_type = 'V'
         # look in database tools
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -1098,7 +1194,7 @@ class ToolIsolation(AppTool, Gerber):
             high_limit = float(db_tool_val['data']['tol_max'])
             high_limit = float(db_tool_val['data']['tol_max'])
 
 
             # we need only tool marked for Isolation Tool
             # we need only tool marked for Isolation Tool
-            if db_tool_val['data']['tool_target'] != _('Isolation'):
+            if db_tool_val['data']['tool_target'] != 3:     # _('Isolation')
                 continue
                 continue
 
 
             # if we find a tool with the same diameter in the Tools DB just update it's data
             # if we find a tool with the same diameter in the Tools DB just update it's data
@@ -1173,12 +1269,8 @@ class ToolIsolation(AppTool, Gerber):
 
 
     def on_tool_default_add(self, dia=None, muted=None):
     def on_tool_default_add(self, dia=None, muted=None):
         self.blockSignals(True)
         self.blockSignals(True)
-        self.units = self.app.defaults['units'].upper()
 
 
-        if dia:
-            tool_dia = dia
-        else:
-            tool_dia = self.ui.new_tooldia_entry.get_value()
+        tool_dia = dia if dia is not None else self.ui.new_tooldia_entry.get_value()
 
 
         if tool_dia is None or tool_dia == 0:
         if tool_dia is None or tool_dia == 0:
             self.build_ui()
             self.build_ui()
@@ -1356,7 +1448,7 @@ class ToolIsolation(AppTool, Gerber):
 
 
                 self.grb_obj.solid_geometry = self.grb_obj.solid_geometry.buffer(0.0000001)
                 self.grb_obj.solid_geometry = self.grb_obj.solid_geometry.buffer(0.0000001)
                 self.grb_obj.solid_geometry = self.grb_obj.solid_geometry.buffer(-0.0000001)
                 self.grb_obj.solid_geometry = self.grb_obj.solid_geometry.buffer(-0.0000001)
-                app_obj.inform.emit('[success] %s.' % _("Done"))
+                app_obj.inform.emit('[success] %s' % _("Done."))
                 self.grb_obj.plot_single_object.emit()
                 self.grb_obj.plot_single_object.emit()
 
 
         self.app.worker_task.emit({'fcn': buffer_task, 'params': [self.app]})
         self.app.worker_task.emit({'fcn': buffer_task, 'params': [self.app]})
@@ -1377,10 +1469,10 @@ class ToolIsolation(AppTool, Gerber):
             return
             return
 
 
         if self.ui.valid_cb.get_value() is True:
         if self.ui.valid_cb.get_value() is True:
-            self.find_safe_tooldia_worker(is_displayed=False)
+            self.find_safe_tooldia_multiprocessing()
 
 
         def worker_task(iso_obj):
         def worker_task(iso_obj):
-            with self.app.proc_container.new(_("Isolating...")):
+            with self.app.proc_container.new('%s ...' % _("Isolating")):
                 self.isolate_handler(iso_obj)
                 self.isolate_handler(iso_obj)
 
 
         self.app.worker_task.emit({'fcn': worker_task, 'params': [self.grb_obj]})
         self.app.worker_task.emit({'fcn': worker_task, 'params': [self.grb_obj]})
@@ -1430,9 +1522,9 @@ class ToolIsolation(AppTool, Gerber):
         """
         """
         selection = self.ui.select_combo.get_value()
         selection = self.ui.select_combo.get_value()
 
 
-        if selection == _("All"):
+        if selection == 0:  # ALL
             self.isolate(isolated_obj=isolated_obj)
             self.isolate(isolated_obj=isolated_obj)
-        elif selection == _("Area Selection"):
+        elif selection == 1:    # Area Selection
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
 
 
             if self.app.is_legacy is False:
             if self.app.is_legacy is False:
@@ -1451,7 +1543,7 @@ class ToolIsolation(AppTool, Gerber):
             # disconnect flags
             # disconnect flags
             self.area_sel_disconnect_flag = True
             self.area_sel_disconnect_flag = True
 
 
-        elif selection == _("Polygon Selection"):
+        elif selection == 2:    # Polygon Selection
             # disengage the grid snapping since it may be hard to click on polygons with grid snapping on
             # disengage the grid snapping since it may be hard to click on polygons with grid snapping on
             if self.app.ui.grid_snap_btn.isChecked():
             if self.app.ui.grid_snap_btn.isChecked():
                 self.grid_status_memory = True
                 self.grid_status_memory = True
@@ -1472,7 +1564,7 @@ class ToolIsolation(AppTool, Gerber):
             # disconnect flags
             # disconnect flags
             self.poly_sel_disconnect_flag = True
             self.poly_sel_disconnect_flag = True
 
 
-        elif selection == _("Reference Object"):
+        elif selection == 3:    # Reference Object
             ref_obj = self.app.collection.get_by_name(self.ui.reference_combo.get_value())
             ref_obj = self.app.collection.get_by_name(self.ui.reference_combo.get_value())
             ref_geo = unary_union(ref_obj.solid_geometry)
             ref_geo = unary_union(ref_obj.solid_geometry)
             use_geo = unary_union(isolated_obj.solid_geometry).difference(ref_geo)
             use_geo = unary_union(isolated_obj.solid_geometry).difference(ref_geo)
@@ -1613,14 +1705,13 @@ class ToolIsolation(AppTool, Gerber):
                             self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
                             self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo"))
                             geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry)
                             geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry)
 
 
-                        geo_obj.tools = {}
-                        geo_obj.tools['1'] = {}
+                        geo_obj.tools = {'1': {}}
                         geo_obj.tools.update({
                         geo_obj.tools.update({
                             '1': {
                             '1': {
                                 'tooldia':          float(tool_dia),
                                 'tooldia':          float(tool_dia),
                                 'offset':           'Path',
                                 'offset':           'Path',
                                 'offset_value':     0.0,
                                 'offset_value':     0.0,
-                                'type':             _('Rough'),
+                                'type':             'Rough',
                                 'tool_type':        tool_type,
                                 'tool_type':        tool_type,
                                 'data':             tool_data,
                                 'data':             tool_data,
                                 'solid_geometry':   geo_obj.solid_geometry
                                 'solid_geometry':   geo_obj.solid_geometry
@@ -1782,7 +1873,7 @@ class ToolIsolation(AppTool, Gerber):
                             'tooldia':          float(tool_dia),
                             'tooldia':          float(tool_dia),
                             'offset':           'Path',
                             'offset':           'Path',
                             'offset_value':     0.0,
                             'offset_value':     0.0,
-                            'type':             _('Rough'),
+                            'type':             'Rough',
                             'tool_type':        tool_type,
                             'tool_type':        tool_type,
                             'data':             tool_data,
                             'data':             tool_data,
                             'solid_geometry':   deepcopy(new_solid_geo)
                             'solid_geometry':   deepcopy(new_solid_geo)
@@ -1899,7 +1990,10 @@ class ToolIsolation(AppTool, Gerber):
 
 
         for tool in sorted_tools:
         for tool in sorted_tools:
             tool_dia = tools_storage[tool]['tooldia']
             tool_dia = tools_storage[tool]['tooldia']
+            tool_has_offset = tools_storage[tool]['offset']
+            tool_offset_value = tools_storage[tool]['offset_value']
             tool_type = tools_storage[tool]['tool_type']
             tool_type = tools_storage[tool]['tool_type']
+            tool_cut_type = tools_storage[tool]['type']
             tool_data = tools_storage[tool]['data']
             tool_data = tools_storage[tool]['data']
 
 
             to_follow = tool_data['tools_iso_follow']
             to_follow = tool_data['tools_iso_follow']
@@ -1973,9 +2067,9 @@ class ToolIsolation(AppTool, Gerber):
             tools_storage.update({
             tools_storage.update({
                 tool: {
                 tool: {
                     'tooldia':          float(tool_dia),
                     'tooldia':          float(tool_dia),
-                    'offset':           'Path',
-                    'offset_value':     0.0,
-                    'type':             _('Rough'),
+                    'offset':           tool_has_offset,
+                    'offset_value':     tool_offset_value,
+                    'type':             tool_cut_type,
                     'tool_type':        tool_type,
                     'tool_type':        tool_type,
                     'data':             tool_data,
                     'data':             tool_data,
                     'solid_geometry':   deepcopy(new_solid_geo)
                     'solid_geometry':   deepcopy(new_solid_geo)
@@ -2194,7 +2288,7 @@ class ToolIsolation(AppTool, Gerber):
                     self.poly_dict[shape_id] = clicked_poly
                     self.poly_dict[shape_id] = clicked_poly
                     self.app.inform.emit(
                     self.app.inform.emit(
                         '%s: %d. %s' % (_("Added polygon"), int(len(self.poly_dict)),
                         '%s: %d. %s' % (_("Added polygon"), int(len(self.poly_dict)),
-                                        _("Click to add next polygon or right click to start isolation."))
+                                        _("Click to add next polygon or right click to start."))
                     )
                     )
                 else:
                 else:
                     try:
                     try:
@@ -2207,7 +2301,7 @@ class ToolIsolation(AppTool, Gerber):
                         return
                         return
                     self.app.inform.emit(
                     self.app.inform.emit(
                         '%s. %s' % (_("Removed polygon"),
                         '%s. %s' % (_("Removed polygon"),
-                                    _("Click to add/remove next polygon or right click to start isolation."))
+                                    _("Click to add/remove next polygon or right click to start."))
                     )
                     )
 
 
                 self.app.tool_shapes.redraw()
                 self.app.tool_shapes.redraw()
@@ -2308,13 +2402,15 @@ class ToolIsolation(AppTool, Gerber):
             self.app.inform.emit(
             self.app.inform.emit(
                 '%s: %d. %s' % (_("Added polygon"),
                 '%s: %d. %s' % (_("Added polygon"),
                                 int(added_poly_count),
                                 int(added_poly_count),
-                                _("Click to add next polygon or right click to start isolation."))
+                                _("Click to add next polygon or right click to start."))
             )
             )
         else:
         else:
             self.app.inform.emit(_("No polygon in selection."))
             self.app.inform.emit(_("No polygon in selection."))
 
 
     # To be called after clicking on the plot.
     # To be called after clicking on the plot.
     def on_mouse_release(self, event):
     def on_mouse_release(self, event):
+        shape_type = self.ui.area_shape_radio.get_value()
+
         if self.app.is_legacy is False:
         if self.app.is_legacy is False:
             event_pos = event.pos
             event_pos = event.pos
             # event_is_dragging = event.is_dragging
             # event_is_dragging = event.is_dragging
@@ -2332,8 +2428,6 @@ class ToolIsolation(AppTool, Gerber):
 
 
         x1, y1 = curr_pos[0], curr_pos[1]
         x1, y1 = curr_pos[0], curr_pos[1]
 
 
-        shape_type = self.area_shape_radio.get_value()
-
         # do clear area only for left mouse clicks
         # do clear area only for left mouse clicks
         if event.button == 1:
         if event.button == 1:
             if shape_type == "square":
             if shape_type == "square":
@@ -2573,7 +2667,12 @@ class ToolIsolation(AppTool, Gerber):
         """
         """
         tool_from_db = deepcopy(tool)
         tool_from_db = deepcopy(tool)
 
 
-        if tool['data']['tool_target'] != _("Isolation"):
+        if tool['data']['tool_target'] not in [0, 3]:   # [General, Isolation]
+            for idx in range(self.app.ui.plot_tab_area.count()):
+                if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                    wdg = self.app.ui.plot_tab_area.widget(idx)
+                    wdg.deleteLater()
+                    self.app.ui.plot_tab_area.removeTab(idx)
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
             return
 
 
@@ -2667,7 +2766,9 @@ class ToolIsolation(AppTool, Gerber):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 break
                 break
-        self.app.on_tools_database(source='iso')
+        ret_val = self.app.on_tools_database(source='iso')
+        if ret_val == 'fail':
+            return
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
@@ -3033,7 +3134,7 @@ class IsoUI:
               "this function will not be able to create routing geometry.")
               "this function will not be able to create routing geometry.")
         )
         )
         self.tools_table.horizontalHeaderItem(1).setToolTip(
         self.tools_table.horizontalHeaderItem(1).setToolTip(
-            _("Tool Diameter. It's value (in current FlatCAM units)\n"
+            _("Tool Diameter. Its value\n"
               "is the cut width into the material."))
               "is the cut width into the material."))
 
 
         self.tools_table.horizontalHeaderItem(2).setToolTip(
         self.tools_table.horizontalHeaderItem(2).setToolTip(
@@ -3098,7 +3199,7 @@ class IsoUI:
 
 
         self.new_tooldia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.new_tooldia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.new_tooldia_entry.set_precision(self.decimals)
         self.new_tooldia_entry.set_precision(self.decimals)
-        self.new_tooldia_entry.set_range(0.000, 9999.9999)
+        self.new_tooldia_entry.set_range(0.000, 10000.0000)
         self.new_tooldia_entry.setObjectName("i_new_tooldia")
         self.new_tooldia_entry.setObjectName("i_new_tooldia")
 
 
         new_tool_lay.addWidget(self.new_tooldia_entry)
         new_tool_lay.addWidget(self.new_tooldia_entry)
@@ -3119,16 +3220,16 @@ class IsoUI:
 
 
         bhlay = QtWidgets.QHBoxLayout()
         bhlay = QtWidgets.QHBoxLayout()
 
 
-        self.add_newtool_button = FCButton(_('Search and Add'))
-        self.add_newtool_button.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
-        self.add_newtool_button.setToolTip(
+        self.search_and_add_btn = FCButton(_('Search and Add'))
+        self.search_and_add_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/plus16.png'))
+        self.search_and_add_btn.setToolTip(
             _("Add a new tool to the Tool Table\n"
             _("Add a new tool to the Tool Table\n"
               "with the diameter specified above.\n"
               "with the diameter specified above.\n"
               "This is done by a background search\n"
               "This is done by a background search\n"
               "in the Tools Database. If nothing is found\n"
               "in the Tools Database. If nothing is found\n"
               "in the Tools DB then a default tool is added.")
               "in the Tools DB then a default tool is added.")
         )
         )
-        bhlay.addWidget(self.add_newtool_button)
+        bhlay.addWidget(self.search_and_add_btn)
 
 
         self.addtool_from_db_btn = FCButton(_('Pick from DB'))
         self.addtool_from_db_btn = FCButton(_('Pick from DB'))
         self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
         self.addtool_from_db_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/search_db32.png'))
@@ -3151,7 +3252,7 @@ class IsoUI:
         self.deltool_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/trash16.png'))
         self.deltool_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/trash16.png'))
         self.deltool_btn.setToolTip(
         self.deltool_btn.setToolTip(
             _("Delete a selection of tools in the Tool Table\n"
             _("Delete a selection of tools in the Tool Table\n"
-              "by first selecting a row(s) in the Tool Table.")
+              "by first selecting a row in the Tool Table.")
         )
         )
         self.grid3.addWidget(self.deltool_btn, 9, 0, 1, 2)
         self.grid3.addWidget(self.deltool_btn, 9, 0, 1, 2)
 
 
@@ -3203,7 +3304,7 @@ class IsoUI:
         # Milling Type Radio Button
         # Milling Type Radio Button
         self.milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.milling_type_label.setToolTip(
         self.milling_type_label.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -3211,7 +3312,7 @@ class IsoUI:
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
                                             {'label': _('Conventional'), 'value': 'cv'}])
                                             {'label': _('Conventional'), 'value': 'cv'}])
         self.milling_type_radio.setToolTip(
         self.milling_type_radio.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -3287,11 +3388,11 @@ class IsoUI:
         self.rest_cb.setObjectName("i_rest")
         self.rest_cb.setObjectName("i_rest")
         self.rest_cb.setToolTip(
         self.rest_cb.setToolTip(
             _("If checked, use 'rest machining'.\n"
             _("If checked, use 'rest machining'.\n"
-              "Basically it will isolate outside PCB features,\n"
+              "Basically it will process copper outside PCB features,\n"
               "using the biggest tool and continue with the next tools,\n"
               "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to isolate the copper features that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper features to isolate or there are no more tools.\n"
+              "from bigger to smaller, to process the copper features that\n"
+              "could not be processed by previous tool, until there is\n"
+              "nothing left to process or there are no more tools.\n\n"
               "If not checked, use the standard algorithm.")
               "If not checked, use the standard algorithm.")
         )
         )
 
 
@@ -3373,7 +3474,7 @@ class IsoUI:
               "- 'Polygon Selection' -> Isolate a selection of polygons.\n"
               "- 'Polygon Selection' -> Isolate a selection of polygons.\n"
               "- 'Reference Object' - will process the area specified by another object.")
               "- 'Reference Object' - will process the area specified by another object.")
         )
         )
-        self.select_combo = FCComboBox()
+        self.select_combo = FCComboBox2()
         self.select_combo.addItems(
         self.select_combo.addItems(
             [_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")]
             [_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")]
         )
         )
@@ -3382,31 +3483,23 @@ class IsoUI:
         self.grid3.addWidget(self.select_label, 34, 0)
         self.grid3.addWidget(self.select_label, 34, 0)
         self.grid3.addWidget(self.select_combo, 34, 1)
         self.grid3.addWidget(self.select_combo, 34, 1)
 
 
-        self.reference_combo_type_label = FCLabel('%s:' % _("Ref. Type"))
-        self.reference_combo_type_label.setToolTip(
-            _("The type of FlatCAM object to be used as non copper clearing reference.\n"
-              "It can be Gerber, Excellon or Geometry.")
-        )
-        self.reference_combo_type = FCComboBox()
+        # Reference Type
+        self.reference_combo_type_label = FCLabel('%s:' % _("Type"))
+
+        self.reference_combo_type = FCComboBox2()
         self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
         self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
 
 
         self.grid3.addWidget(self.reference_combo_type_label, 36, 0)
         self.grid3.addWidget(self.reference_combo_type_label, 36, 0)
         self.grid3.addWidget(self.reference_combo_type, 36, 1)
         self.grid3.addWidget(self.reference_combo_type, 36, 1)
 
 
-        self.reference_combo_label = FCLabel('%s:' % _("Ref. Object"))
-        self.reference_combo_label.setToolTip(
-            _("The FlatCAM object to be used as non copper clearing reference.")
-        )
         self.reference_combo = FCComboBox()
         self.reference_combo = FCComboBox()
         self.reference_combo.setModel(self.app.collection)
         self.reference_combo.setModel(self.app.collection)
         self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.reference_combo.is_last = True
         self.reference_combo.is_last = True
 
 
-        self.grid3.addWidget(self.reference_combo_label, 38, 0)
-        self.grid3.addWidget(self.reference_combo, 38, 1)
+        self.grid3.addWidget(self.reference_combo, 38, 0, 1, 2)
 
 
         self.reference_combo.hide()
         self.reference_combo.hide()
-        self.reference_combo_label.hide()
         self.reference_combo_type.hide()
         self.reference_combo_type.hide()
         self.reference_combo_type_label.hide()
         self.reference_combo_type_label.hide()
 
 

+ 26 - 26
appTools/ToolMilling.py

@@ -635,7 +635,7 @@ class ToolMilling(AppTool, Excellon):
         # Get source object.
         # Get source object.
         try:
         try:
             self.excellon_obj = self.app.collection.get_by_name(self.obj_name)
             self.excellon_obj = self.app.collection.get_by_name(self.obj_name)
-        except Exception as e:
+        except Exception:
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name)))
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name)))
             return
             return
 
 
@@ -980,7 +980,7 @@ class ToolMilling(AppTool, Excellon):
         # if the sender is in the column with index 2 then we update the tool_type key
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
         if cw_col == 2:
             tt = cw.currentText()
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else 'Rough'
 
 
             self.iso_tools[current_uid].update({
             self.iso_tools[current_uid].update({
                 'type': typ,
                 'type': typ,
@@ -1352,7 +1352,7 @@ class ToolMilling(AppTool, Excellon):
         def job_init(job_obj, app_obj):
         def job_init(job_obj, app_obj):
             assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
             assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj)
 
 
-            app_obj.inform.emit(_("Generating Excellon CNCJob..."))
+            app_obj.inform.emit(_("Generating CNCJob..."))
 
 
             # get the tool_table items in a list of row items
             # get the tool_table items in a list of row items
             tool_table_items = self.get_selected_tools_table_items()
             tool_table_items = self.get_selected_tools_table_items()
@@ -1674,7 +1674,7 @@ class MillingUI:
               "will be showed as a T1, T2 ... Tn in the Machine Code.\n\n"
               "will be showed as a T1, T2 ... Tn in the Machine Code.\n\n"
               "Here the tools are selected for G-code generation."))
               "Here the tools are selected for G-code generation."))
         self.tools_table.horizontalHeaderItem(1).setToolTip(
         self.tools_table.horizontalHeaderItem(1).setToolTip(
-            _("Tool Diameter. It's value (in current FlatCAM units) \n"
+            _("Tool Diameter. Its value\n"
               "is the cut width into the material."))
               "is the cut width into the material."))
         self.tools_table.horizontalHeaderItem(2).setToolTip(
         self.tools_table.horizontalHeaderItem(2).setToolTip(
             _("The number of Drill holes. Holes that are drilled with\n"
             _("The number of Drill holes. Holes that are drilled with\n"
@@ -1783,7 +1783,7 @@ class MillingUI:
 
 
         self.mill_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.mill_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.mill_dia_entry.set_precision(self.decimals)
         self.mill_dia_entry.set_precision(self.decimals)
-        self.mill_dia_entry.set_range(0.0000, 9999.9999)
+        self.mill_dia_entry.set_range(0.0000, 10000.0000)
         self.mill_dia_entry.setObjectName("e_milling_dia")
         self.mill_dia_entry.setObjectName("e_milling_dia")
 
 
         self.grid1.addWidget(self.mill_dia_label, 3, 0)
         self.grid1.addWidget(self.mill_dia_label, 3, 0)
@@ -1800,9 +1800,9 @@ class MillingUI:
         self.cutz_entry.set_precision(self.decimals)
         self.cutz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.cutz_entry.set_range(-9999.9999, 0.0000)
+            self.cutz_entry.set_range(-10000.0000, 0.0000)
         else:
         else:
-            self.cutz_entry.set_range(-9999.9999, 9999.9999)
+            self.cutz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setObjectName("e_cutz")
         self.cutz_entry.setObjectName("e_cutz")
@@ -1824,7 +1824,7 @@ class MillingUI:
 
 
         self.maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.maxdepth_entry.set_precision(self.decimals)
         self.maxdepth_entry.set_precision(self.decimals)
-        self.maxdepth_entry.set_range(0, 9999.9999)
+        self.maxdepth_entry.set_range(0, 10000.0000)
         self.maxdepth_entry.setSingleStep(0.1)
         self.maxdepth_entry.setSingleStep(0.1)
 
 
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
         self.maxdepth_entry.setToolTip(_("Depth of each pass (positive)."))
@@ -1846,9 +1846,9 @@ class MillingUI:
         self.travelz_entry.set_precision(self.decimals)
         self.travelz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.travelz_entry.set_range(0.00001, 9999.9999)
+            self.travelz_entry.set_range(0.00001, 10000.0000)
         else:
         else:
-            self.travelz_entry.set_range(-9999.9999, 9999.9999)
+            self.travelz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setObjectName("e_travelz")
         self.travelz_entry.setObjectName("e_travelz")
@@ -1864,7 +1864,7 @@ class MillingUI:
         )
         )
         self.xyfeedrate_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.xyfeedrate_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.xyfeedrate_entry.set_precision(self.decimals)
         self.xyfeedrate_entry.set_precision(self.decimals)
-        self.xyfeedrate_entry.set_range(0, 9999.9999)
+        self.xyfeedrate_entry.set_range(0, 10000.0000)
         self.xyfeedrate_entry.setSingleStep(0.1)
         self.xyfeedrate_entry.setSingleStep(0.1)
         self.xyfeedrate_entry.setObjectName("e_feedratexy")
         self.xyfeedrate_entry.setObjectName("e_feedratexy")
 
 
@@ -1881,7 +1881,7 @@ class MillingUI:
         )
         )
         self.feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_z_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_z_entry.set_precision(self.decimals)
         self.feedrate_z_entry.set_precision(self.decimals)
-        self.feedrate_z_entry.set_range(0.0, 99999.9999)
+        self.feedrate_z_entry.set_range(0.0, 910000.0000)
         self.feedrate_z_entry.setSingleStep(0.1)
         self.feedrate_z_entry.setSingleStep(0.1)
         self.feedrate_z_entry.setObjectName("e_feedratez")
         self.feedrate_z_entry.setObjectName("e_feedratez")
 
 
@@ -1899,7 +1899,7 @@ class MillingUI:
         )
         )
         self.feedrate_rapid_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_rapid_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_rapid_entry.set_precision(self.decimals)
         self.feedrate_rapid_entry.set_precision(self.decimals)
-        self.feedrate_rapid_entry.set_range(0.0, 99999.9999)
+        self.feedrate_rapid_entry.set_range(0.0, 910000.0000)
         self.feedrate_rapid_entry.setSingleStep(0.1)
         self.feedrate_rapid_entry.setSingleStep(0.1)
         self.feedrate_rapid_entry.setObjectName("e_fr_rapid")
         self.feedrate_rapid_entry.setObjectName("e_fr_rapid")
 
 
@@ -1963,7 +1963,7 @@ class MillingUI:
 
 
         self.dwelltime_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dwelltime_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dwelltime_entry.set_precision(self.decimals)
         self.dwelltime_entry.set_precision(self.decimals)
-        self.dwelltime_entry.set_range(0.0, 9999.9999)
+        self.dwelltime_entry.set_range(0.0, 10000.0000)
         self.dwelltime_entry.setSingleStep(0.1)
         self.dwelltime_entry.setSingleStep(0.1)
 
 
         self.dwelltime_entry.setToolTip(
         self.dwelltime_entry.setToolTip(
@@ -1986,7 +1986,7 @@ class MillingUI:
 
 
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry.set_precision(self.decimals)
         self.offset_entry.set_precision(self.decimals)
-        self.offset_entry.set_range(-9999.9999, 9999.9999)
+        self.offset_entry.set_range(-10000.0000, 10000.0000)
         self.offset_entry.setObjectName("e_offset")
         self.offset_entry.setObjectName("e_offset")
 
 
         self.grid1.addWidget(self.tool_offset_label, 25, 0)
         self.grid1.addWidget(self.tool_offset_label, 25, 0)
@@ -2073,9 +2073,9 @@ class MillingUI:
               "tool change.")
               "tool change.")
         )
         )
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.toolchangez_entry.set_range(0.0, 9999.9999)
+            self.toolchangez_entry.set_range(0.0, 10000.0000)
         else:
         else:
-            self.toolchangez_entry.set_range(-9999.9999, 9999.9999)
+            self.toolchangez_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.toolchangez_entry.setSingleStep(0.1)
         self.toolchangez_entry.setSingleStep(0.1)
         self.ois_tcz_e = OptionalInputSection(self.toolchange_cb, [self.toolchangez_entry])
         self.ois_tcz_e = OptionalInputSection(self.toolchange_cb, [self.toolchangez_entry])
@@ -2086,7 +2086,7 @@ class MillingUI:
         # Start move Z:
         # Start move Z:
         self.estartz_label = QtWidgets.QLabel('%s:' % _("Start Z"))
         self.estartz_label = QtWidgets.QLabel('%s:' % _("Start Z"))
         self.estartz_label.setToolTip(
         self.estartz_label.setToolTip(
-            _("Height of the tool just after start.\n"
+            _("Height of the tool just after starting the work.\n"
               "Delete the value if you don't need this feature.")
               "Delete the value if you don't need this feature.")
         )
         )
         self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
         self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
@@ -2104,9 +2104,9 @@ class MillingUI:
         self.endz_entry.set_precision(self.decimals)
         self.endz_entry.set_precision(self.decimals)
 
 
         if machinist_setting == 0:
         if machinist_setting == 0:
-            self.endz_entry.set_range(0.0, 9999.9999)
+            self.endz_entry.set_range(0.0, 10000.0000)
         else:
         else:
-            self.endz_entry.set_range(-9999.9999, 9999.9999)
+            self.endz_entry.set_range(-10000.0000, 10000.0000)
 
 
         self.endz_entry.setSingleStep(0.1)
         self.endz_entry.setSingleStep(0.1)
 
 
@@ -2134,7 +2134,7 @@ class MillingUI:
 
 
         self.pdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.pdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.pdepth_entry.set_precision(self.decimals)
         self.pdepth_entry.set_precision(self.decimals)
-        self.pdepth_entry.set_range(-9999.9999, 9999.9999)
+        self.pdepth_entry.set_range(-10000.0000, 10000.0000)
         self.pdepth_entry.setSingleStep(0.1)
         self.pdepth_entry.setSingleStep(0.1)
         self.pdepth_entry.setObjectName("e_depth_probe")
         self.pdepth_entry.setObjectName("e_depth_probe")
 
 
@@ -2152,7 +2152,7 @@ class MillingUI:
 
 
         self.feedrate_probe_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_probe_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.feedrate_probe_entry.set_precision(self.decimals)
         self.feedrate_probe_entry.set_precision(self.decimals)
-        self.feedrate_probe_entry.set_range(0.0, 9999.9999)
+        self.feedrate_probe_entry.set_range(0.0, 10000.0000)
         self.feedrate_probe_entry.setSingleStep(0.1)
         self.feedrate_probe_entry.setSingleStep(0.1)
         self.feedrate_probe_entry.setObjectName("e_fr_probe")
         self.feedrate_probe_entry.setObjectName("e_fr_probe")
 
 
@@ -2163,7 +2163,7 @@ class MillingUI:
         self.feedrate_probe_entry.setVisible(False)
         self.feedrate_probe_entry.setVisible(False)
 
 
         # Preprocessor Excellon selection
         # Preprocessor Excellon selection
-        pp_excellon_label = QtWidgets.QLabel('%s:' % _("Preprocessor E"))
+        pp_excellon_label = QtWidgets.QLabel('%s:' % _("Preprocessor"))
         pp_excellon_label.setToolTip(
         pp_excellon_label.setToolTip(
             _("The preprocessor JSON file that dictates\n"
             _("The preprocessor JSON file that dictates\n"
               "Gcode output for Excellon Objects.")
               "Gcode output for Excellon Objects.")
@@ -2175,7 +2175,7 @@ class MillingUI:
         self.grid3.addWidget(self.pp_excellon_name_cb, 15, 1)
         self.grid3.addWidget(self.pp_excellon_name_cb, 15, 1)
 
 
         # Preprocessor Geometry selection
         # Preprocessor Geometry selection
-        pp_geo_label = QtWidgets.QLabel('%s:' % _("Preprocessor G"))
+        pp_geo_label = QtWidgets.QLabel('%s:' % _("Preprocessor"))
         pp_geo_label.setToolTip(
         pp_geo_label.setToolTip(
             _("The preprocessor JSON file that dictates\n"
             _("The preprocessor JSON file that dictates\n"
               "Gcode output for Geometry (Milling) Objects.")
               "Gcode output for Geometry (Milling) Objects.")
@@ -2250,14 +2250,14 @@ class MillingUI:
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
         self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n"
                                        "an interdiction area."))
                                        "an interdiction area."))
         self.over_z_entry = FCDoubleSpinner()
         self.over_z_entry = FCDoubleSpinner()
-        self.over_z_entry.set_range(0.000, 9999.9999)
+        self.over_z_entry.set_range(0.000, 10000.0000)
         self.over_z_entry.set_precision(self.decimals)
         self.over_z_entry.set_precision(self.decimals)
 
 
         grid_a1.addWidget(self.over_z_label, 2, 0)
         grid_a1.addWidget(self.over_z_label, 2, 0)
         grid_a1.addWidget(self.over_z_entry, 2, 1)
         grid_a1.addWidget(self.over_z_entry, 2, 1)
 
 
         # Button Add Area
         # Button Add Area
-        self.add_area_button = QtWidgets.QPushButton(_('Add area:'))
+        self.add_area_button = QtWidgets.QPushButton(_('Add Area:'))
         self.add_area_button.setToolTip(_("Add an Exclusion Area."))
         self.add_area_button.setToolTip(_("Add an Exclusion Area."))
 
 
         # Area Selection shape
         # Area Selection shape

+ 8 - 8
appTools/ToolMove.py

@@ -137,7 +137,7 @@ class ToolMove(AppTool):
                 else:
                 else:
                     self.point2 = copy(self.point1)
                     self.point2 = copy(self.point1)
                     self.point1 = pos
                     self.point1 = pos
-                self.app.inform.emit(_("MOVE: Click on the Destination point ..."))
+                self.app.inform.emit(_("Click on the DESTINATION point ..."))
 
 
             if self.clicked_move == 1:
             if self.clicked_move == 1:
                 try:
                 try:
@@ -160,10 +160,11 @@ class ToolMove(AppTool):
                                 if obj.options['plot'] and obj.visible is True]
                                 if obj.options['plot'] and obj.visible is True]
 
 
                     def job_move(app_obj):
                     def job_move(app_obj):
-                        with self.app.proc_container.new(_("Moving...")) as proc:
+                        with self.app.proc_container.new(_("Moving ...")):
 
 
                             if not obj_list:
                             if not obj_list:
-                                app_obj.app.inform.emit('[WARNING_NOTCL] %s' % _("No object(s) selected."))
+                                app_obj.app.inform.emit('[ERROR_NOTCL] %s %s' % (_("Failed."),
+                                                                                 _("No object is selected.")))
                                 return "fail"
                                 return "fail"
 
 
                             try:
                             try:
@@ -206,8 +207,8 @@ class ToolMove(AppTool):
 
 
                         # delete the selection bounding box
                         # delete the selection bounding box
                         self.delete_shape()
                         self.delete_shape()
-                        self.app.inform.emit('[success] %s %s' %
-                                             (str(sel_obj.kind).capitalize(), 'object was moved ...'))
+                        self.app.inform.emit('[success] %s %s ...' %
+                                             (str(sel_obj.kind).capitalize(), _('object was moved')))
 
 
                     self.app.worker_task.emit({'fcn': job_move, 'params': [self]})
                     self.app.worker_task.emit({'fcn': job_move, 'params': [self]})
 
 
@@ -217,8 +218,7 @@ class ToolMove(AppTool):
 
 
                 except TypeError as e:
                 except TypeError as e:
                     log.debug("ToolMove.on_left_click() --> %s" % str(e))
                     log.debug("ToolMove.on_left_click() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] ToolMove.on_left_click() --> %s' %
-                                         _('Error when mouse left click.'))
+                    self.app.inform.emit('[ERROR_NOTCL] ToolMove. %s' % _('Error when mouse left click.'))
                     return
                     return
 
 
             self.clicked_move = 1
             self.clicked_move = 1
@@ -226,7 +226,7 @@ class ToolMove(AppTool):
     def replot(self, obj_list):
     def replot(self, obj_list):
 
 
         def worker_task():
         def worker_task():
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 for sel_obj in obj_list:
                 for sel_obj in obj_list:
                     sel_obj.plot()
                     sel_obj.plot()
 
 

+ 282 - 185
appTools/ToolNCC.py

@@ -8,8 +8,8 @@
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5 import QtWidgets, QtCore, QtGui
 
 
 from appTool import AppTool
 from appTool import AppTool
-from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog, FCButton,\
-    FCComboBox, OptionalInputSection, FCLabel
+from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton,\
+    FCComboBox, OptionalInputSection, FCLabel, FCInputDialogSpinnerButton, FCComboBox2
 from appParsers.ParseGerber import Gerber
 from appParsers.ParseGerber import Gerber
 
 
 from camlib import grace
 from camlib import grace
@@ -451,20 +451,35 @@ class NonCopperClear(AppTool, Gerber):
         self.blockSignals(False)
         self.blockSignals(False)
 
 
     def on_add_tool_by_key(self):
     def on_add_tool_by_key(self):
-        tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"),
-                                       text='%s:' % _('Enter a Tool Diameter'),
-                                       min=0.0001, max=9999.9999, decimals=self.decimals)
+        # tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"),
+        #                                text='%s:' % _('Enter a Tool Diameter'),
+        #                                min=0.0001, max=10000.0000, decimals=self.decimals)
+        btn_icon = QtGui.QIcon(self.app.resource_location + '/open_excellon32.png')
+
+        tool_add_popup = FCInputDialogSpinnerButton(title='%s...' % _("New Tool"),
+                                                    text='%s:' % _('Enter a Tool Diameter'),
+                                                    min=0.0001, max=10000.0000, decimals=self.decimals,
+                                                    button_icon=btn_icon,
+                                                    callback=self.on_find_optimal_tooldia,
+                                                    parent=self.app.ui)
         tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
         tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
 
 
-        val, ok = tool_add_popup.get_value()
+        def find_optimal(valor):
+            tool_add_popup.set_value(float(valor))
+
+        self.optimal_found_sig.connect(find_optimal)
+
+        val, ok = tool_add_popup.get_results()
         if ok:
         if ok:
             if float(val) == 0:
             if float(val) == 0:
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                 self.app.inform.emit('[WARNING_NOTCL] %s' %
                                      _("Please enter a tool diameter with non-zero value, in Float format."))
                                      _("Please enter a tool diameter with non-zero value, in Float format."))
+                self.optimal_found_sig.disconnect(find_optimal)
                 return
                 return
             self.on_tool_add(custom_dia=float(val))
             self.on_tool_add(custom_dia=float(val))
         else:
         else:
             self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Adding Tool cancelled"))
             self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Adding Tool cancelled"))
+        self.optimal_found_sig.disconnect(find_optimal)
 
 
     def set_tool_ui(self):
     def set_tool_ui(self):
         self.units = self.app.defaults['units'].upper()
         self.units = self.app.defaults['units'].upper()
@@ -615,7 +630,7 @@ class NonCopperClear(AppTool, Gerber):
         # read the table tools uid
         # read the table tools uid
         current_uid_list = []
         current_uid_list = []
         for row in range(self.ui.tools_table.rowCount()):
         for row in range(self.ui.tools_table.rowCount()):
-            uid = int(self.ui.tools_table.item(row,3).text())
+            uid = int(self.ui.tools_table.item(row, 3).text())
             current_uid_list.append(uid)
             current_uid_list.append(uid)
 
 
         new_tools = {}
         new_tools = {}
@@ -661,7 +676,8 @@ class NonCopperClear(AppTool, Gerber):
                     tool_id += 1
                     tool_id += 1
 
 
                     id_ = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
                     id_ = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
-                    id_.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+                    flags = QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
+                    id_.setFlags(flags)
                     row_no = tool_id - 1
                     row_no = tool_id - 1
                     self.ui.tools_table.setItem(row_no, 0, id_)  # Tool name/id
                     self.ui.tools_table.setItem(row_no, 0, id_)  # Tool name/id
 
 
@@ -682,8 +698,8 @@ class NonCopperClear(AppTool, Gerber):
 
 
         # make the diameter column editable
         # make the diameter column editable
         for row in range(tool_id):
         for row in range(tool_id):
-            self.ui.tools_table.item(row, 1).setFlags(
-                QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            flags = QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled
+            self.ui.tools_table.item(row, 1).setFlags(flags)
 
 
         self.ui.tools_table.resizeColumnsToContents()
         self.ui.tools_table.resizeColumnsToContents()
         self.ui.tools_table.resizeRowsToContents()
         self.ui.tools_table.resizeRowsToContents()
@@ -801,9 +817,7 @@ class NonCopperClear(AppTool, Gerber):
         obj_type = self.ui.reference_combo_type.currentIndex()
         obj_type = self.ui.reference_combo_type.currentIndex()
         self.ui.reference_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
         self.ui.reference_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
         self.ui.reference_combo.setCurrentIndex(0)
         self.ui.reference_combo.setCurrentIndex(0)
-        self.ui.reference_combo.obj_type = {
-            _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
-        }[self.ui.reference_combo_type.get_value()]
+        self.ui.reference_combo.obj_type = {0: "Gerber", 1: "Excellon", 2: "Geometry"}[obj_type]
 
 
     def on_order_changed(self, order):
     def on_order_changed(self, order):
         if order != 'no':
         if order != 'no':
@@ -823,7 +837,7 @@ class NonCopperClear(AppTool, Gerber):
         # if the sender is in the column with index 2 then we update the tool_type key
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
         if cw_col == 2:
             tt = cw.currentText()
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else 'Rough'
 
 
             self.ncc_tools[current_uid].update({
             self.ncc_tools[current_uid].update({
                 'type': typ,
                 'type': typ,
@@ -831,10 +845,59 @@ class NonCopperClear(AppTool, Gerber):
             })
             })
 
 
     def on_find_optimal_tooldia(self):
     def on_find_optimal_tooldia(self):
-        self.find_safe_tooldia_worker(is_displayed=True)
+        self.find_safe_tooldia_worker()
+
+    @staticmethod
+    def find_optim_mp(aperture_storage, decimals):
+        msg = 'ok'
+        total_geo = []
+
+        for ap in list(aperture_storage.keys()):
+            if 'geometry' in aperture_storage[ap]:
+                for geo_el in aperture_storage[ap]['geometry']:
+                    if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
+                        total_geo.append(geo_el['solid'])
+
+        total_geo = MultiPolygon(total_geo)
+        total_geo = total_geo.buffer(0)
+
+        try:
+            __ = iter(total_geo)
+            geo_len = len(total_geo)
+        except TypeError:
+            msg = ('[ERROR_NOTCL] %s' % _("The Gerber object has one Polygon as geometry.\n"
+                                          "There are no distances between geometry elements to be found."))
+
+        min_dict = {}
+        idx = 1
+        for geo in total_geo:
+            for s_geo in total_geo[idx:]:
+                # minimize the number of distances by not taking into considerations
+                # those that are too small
+                dist = geo.distance(s_geo)
+                dist = float('%.*f' % (decimals, dist))
+                loc_1, loc_2 = nearest_points(geo, s_geo)
+
+                proc_loc = (
+                    (float('%.*f' % (decimals, loc_1.x)), float('%.*f' % (decimals, loc_1.y))),
+                    (float('%.*f' % (decimals, loc_2.x)), float('%.*f' % (decimals, loc_2.y)))
+                )
+
+                if dist in min_dict:
+                    min_dict[dist].append(proc_loc)
+                else:
+                    min_dict[dist] = [proc_loc]
+
+            idx += 1
+
+        min_list = list(min_dict.keys())
+        min_dist = min(min_list)
+
+        return msg, min_dist
 
 
-    def find_safe_tooldia_worker(self, is_displayed):
-        self.app.inform.emit(_("NCC Tool. Checking tools for validity."))
+    # multiprocessing variant
+    def find_safe_tooldia_multiprocessing(self):
+        self.app.inform.emit(_("Checking tools for validity."))
         self.units = self.app.defaults['units'].upper()
         self.units = self.app.defaults['units'].upper()
 
 
         obj_name = self.ui.object_combo.currentText()
         obj_name = self.ui.object_combo.currentText()
@@ -850,82 +913,24 @@ class NonCopperClear(AppTool, Gerber):
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
             return
             return
 
 
-        proc = self.app.proc_container.new(_("Working..."))
-
-        def job_thread(app_obj, is_display):
-            try:
-                old_disp_number = 0
-                pol_nr = 0
-                app_obj.proc_container.update_view_text(' %d%%' % 0)
-                total_geo = []
-
-                for ap in list(fcobj.apertures.keys()):
-                    if 'geometry' in fcobj.apertures[ap]:
-                        for geo_el in fcobj.apertures[ap]['geometry']:
-                            if self.app.abort_flag:
-                                # graceful abort requested by the user
-                                raise grace
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
 
 
-                            if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
-                                total_geo.append(geo_el['solid'])
+                ap_storage = fcobj.apertures
 
 
-                total_geo = MultiPolygon(total_geo)
-                total_geo = total_geo.buffer(0)
+                p = app_obj.pool.apply_async(self.find_optim_mp, args=(ap_storage, self.decimals))
+                res = p.get()
 
 
-                try:
-                    __ = iter(total_geo)
-                    geo_len = len(total_geo)
-                    geo_len = (geo_len * (geo_len - 1)) / 2
-                except TypeError:
-                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
-                                        _("The Gerber object has one Polygon as geometry.\n"
-                                          "There are no distances between geometry elements to be found."))
+                if res[0] != 'ok':
+                    app_obj.inform.emit(res[0])
                     return 'fail'
                     return 'fail'
+                else:
+                    min_dist = res[1]
 
 
-                min_dict = {}
-                idx = 1
-                for geo in total_geo:
-                    for s_geo in total_geo[idx:]:
-                        if self.app.abort_flag:
-                            # graceful abort requested by the user
-                            raise grace
-
-                        # minimize the number of distances by not taking into considerations those that are too small
-                        dist = geo.distance(s_geo)
-                        dist = float('%.*f' % (self.decimals, dist))
-                        loc_1, loc_2 = nearest_points(geo, s_geo)
-
-                        proc_loc = (
-                            (float('%.*f' % (self.decimals, loc_1.x)), float('%.*f' % (self.decimals, loc_1.y))),
-                            (float('%.*f' % (self.decimals, loc_2.x)), float('%.*f' % (self.decimals, loc_2.y)))
-                        )
-
-                        if dist in min_dict:
-                            min_dict[dist].append(proc_loc)
-                        else:
-                            min_dict[dist] = [proc_loc]
-
-                        pol_nr += 1
-                        disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
-
-                        if old_disp_number < disp_number <= 100:
-                            app_obj.proc_container.update_view_text(' %d%%' % disp_number)
-                            old_disp_number = disp_number
-                    idx += 1
-
-                min_list = list(min_dict.keys())
-                min_dist = min(min_list)
-
-                min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
-                self.safe_tooldia = min_dist_truncated
-
-                if is_display:
-                    self.optimal_found_sig.emit(min_dist_truncated)
+                try:
+                    min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
+                    self.safe_tooldia = min_dist_truncated
 
 
-                    app_obj.inform.emit('[success] %s: %s %s' %
-                                        (_("Optimal tool diameter found"), str(min_dist_truncated),
-                                         self.units.lower()))
-                else:
                     # find the selected tool ID's
                     # find the selected tool ID's
                     sorted_tools = []
                     sorted_tools = []
                     table_items = self.ui.tools_table.selectedItems()
                     table_items = self.ui.tools_table.selectedItems()
@@ -954,16 +959,113 @@ class NonCopperClear(AppTool, Gerber):
 
 
                     # reset the value to prepare for another isolation
                     # reset the value to prepare for another isolation
                     self.safe_tooldia = None
                     self.safe_tooldia = None
-            except Exception as ee:
-                log.debug(str(ee))
-                return
+                except Exception as ee:
+                    log.debug(str(ee))
+                    return
+
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
 
-        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app, is_displayed]})
+    def find_safe_tooldia_worker(self):
+        self.app.inform.emit(_("Checking tools for validity."))
+        self.units = self.app.defaults['units'].upper()
+
+        obj_name = self.ui.object_combo.currentText()
+
+        # Get source object.
+        try:
+            fcobj = self.app.collection.get_by_name(obj_name)
+        except Exception:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
+            return
+
+        if fcobj is None:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
+            return
+
+        def job_thread(app_obj):
+            with self.app.proc_container.new(_("Checking ...")):
+                try:
+                    old_disp_number = 0
+                    pol_nr = 0
+                    app_obj.proc_container.update_view_text(' %d%%' % 0)
+                    total_geo = []
+
+                    for ap in list(fcobj.apertures.keys()):
+                        if 'geometry' in fcobj.apertures[ap]:
+                            for geo_el in fcobj.apertures[ap]['geometry']:
+                                if self.app.abort_flag:
+                                    # graceful abort requested by the user
+                                    raise grace
+
+                                if 'solid' in geo_el and geo_el['solid'] is not None and geo_el['solid'].is_valid:
+                                    total_geo.append(geo_el['solid'])
+
+                    total_geo = MultiPolygon(total_geo)
+                    total_geo = total_geo.buffer(0)
+
+                    try:
+                        __ = iter(total_geo)
+                        geo_len = len(total_geo)
+                        geo_len = (geo_len * (geo_len - 1)) / 2
+                    except TypeError:
+                        app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                            _("The Gerber object has one Polygon as geometry.\n"
+                                              "There are no distances between geometry elements to be found."))
+                        return 'fail'
+
+                    min_dict = {}
+                    idx = 1
+                    for geo in total_geo:
+                        for s_geo in total_geo[idx:]:
+                            if self.app.abort_flag:
+                                # graceful abort requested by the user
+                                raise grace
+
+                            # minimize the number of distances by not taking into considerations
+                            # those that are too small
+                            dist = geo.distance(s_geo)
+                            dist = float('%.*f' % (self.decimals, dist))
+                            loc_1, loc_2 = nearest_points(geo, s_geo)
+
+                            proc_loc = (
+                                (float('%.*f' % (self.decimals, loc_1.x)), float('%.*f' % (self.decimals, loc_1.y))),
+                                (float('%.*f' % (self.decimals, loc_2.x)), float('%.*f' % (self.decimals, loc_2.y)))
+                            )
+
+                            if dist in min_dict:
+                                min_dict[dist].append(proc_loc)
+                            else:
+                                min_dict[dist] = [proc_loc]
+
+                            pol_nr += 1
+                            disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100]))
+
+                            if old_disp_number < disp_number <= 100:
+                                app_obj.proc_container.update_view_text(' %d%%' % disp_number)
+                                old_disp_number = disp_number
+                        idx += 1
+
+                    min_list = list(min_dict.keys())
+                    min_dist = min(min_list)
+
+                    min_dist_truncated = self.app.dec_format(float(min_dist), self.decimals)
+                    self.safe_tooldia = min_dist_truncated
+
+                    self.optimal_found_sig.emit(min_dist_truncated)
+
+                    app_obj.inform.emit('[success] %s: %s %s' %
+                                        (_("Optimal tool diameter found"), str(min_dist_truncated),
+                                         self.units.lower()))
+                except Exception as ee:
+                    log.debug(str(ee))
+                    return
+
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
 
     def on_tool_add(self, custom_dia=None):
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
         self.blockSignals(True)
 
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
 
         new_tools_dict = deepcopy(self.default_data)
         new_tools_dict = deepcopy(self.default_data)
         updated_tooldia = None
         updated_tooldia = None
@@ -1008,7 +1110,7 @@ class NonCopperClear(AppTool, Gerber):
                 tools = f.read()
                 tools = f.read()
         except IOError:
         except IOError:
             self.app.log.error("Could not load tools DB file.")
             self.app.log.error("Could not load tools DB file.")
-            self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
+            self.app.inform.emit('[ERROR] %s' % _("Could not load the file."))
             self.blockSignals(False)
             self.blockSignals(False)
             self.on_tool_default_add(dia=tool_dia)
             self.on_tool_default_add(dia=tool_dia)
             return
             return
@@ -1029,7 +1131,7 @@ class NonCopperClear(AppTool, Gerber):
 
 
         offset = 'Path'
         offset = 'Path'
         offset_val = 0.0
         offset_val = 0.0
-        typ = "Rough"
+        typ = 'Rough'
         tool_type = 'V'
         tool_type = 'V'
         # look in database tools
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -1313,7 +1415,8 @@ class NonCopperClear(AppTool, Gerber):
             return
             return
 
 
         if self.ui.valid_cb.get_value() is True:
         if self.ui.valid_cb.get_value() is True:
-            self.find_safe_tooldia_worker(is_displayed=False)
+            # this is done in another Process
+            self.find_safe_tooldia_multiprocessing()
 
 
         # use the selected tools in the tool table; get diameters for isolation
         # use the selected tools in the tool table; get diameters for isolation
         self.iso_dia_list = []
         self.iso_dia_list = []
@@ -1348,7 +1451,7 @@ class NonCopperClear(AppTool, Gerber):
         self.o_name = '%s_ncc' % self.obj_name
         self.o_name = '%s_ncc' % self.obj_name
 
 
         self.select_method = self.ui.select_combo.get_value()
         self.select_method = self.ui.select_combo.get_value()
-        if self.select_method == _('Itself'):
+        if self.select_method == 0:   # Itself
             self.bound_obj_name = self.ui.object_combo.currentText()
             self.bound_obj_name = self.ui.object_combo.currentText()
             # Get source object.
             # Get source object.
             try:
             try:
@@ -1362,7 +1465,7 @@ class NonCopperClear(AppTool, Gerber):
                               isotooldia=self.iso_dia_list,
                               isotooldia=self.iso_dia_list,
                               outname=self.o_name,
                               outname=self.o_name,
                               tools_storage=self.ncc_tools)
                               tools_storage=self.ncc_tools)
-        elif self.select_method == _("Area Selection"):
+        elif self.select_method == 1:   # Area Selection
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
 
 
             if self.app.is_legacy is False:
             if self.app.is_legacy is False:
@@ -1381,7 +1484,7 @@ class NonCopperClear(AppTool, Gerber):
             # disconnect flags
             # disconnect flags
             self.area_sel_disconnect_flag = True
             self.area_sel_disconnect_flag = True
 
 
-        elif self.select_method == _("Reference Object"):
+        elif self.select_method == 2:   # Reference Object
             self.bound_obj_name = self.ui.reference_combo.currentText()
             self.bound_obj_name = self.ui.reference_combo.currentText()
             # Get source object.
             # Get source object.
             try:
             try:
@@ -1664,7 +1767,7 @@ class NonCopperClear(AppTool, Gerber):
         box_kind = box_obj.kind if box_obj is not None else None
         box_kind = box_obj.kind if box_obj is not None else None
 
 
         env_obj = None
         env_obj = None
-        if ncc_select == _('Itself'):
+        if ncc_select == 0:     # _('Itself')
             geo_n = ncc_obj.solid_geometry
             geo_n = ncc_obj.solid_geometry
 
 
             try:
             try:
@@ -1680,13 +1783,13 @@ class NonCopperClear(AppTool, Gerber):
                 log.debug("NonCopperClear.calculate_bounding_box() 'itself'  --> %s" % str(e))
                 log.debug("NonCopperClear.calculate_bounding_box() 'itself'  --> %s" % str(e))
                 self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
                 self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
                 return None
                 return None
-        elif ncc_select == _("Area Selection"):
+        elif ncc_select == 1:   # _("Area Selection")
             env_obj = unary_union(self.sel_rect)
             env_obj = unary_union(self.sel_rect)
             try:
             try:
                 __ = iter(env_obj)
                 __ = iter(env_obj)
             except Exception:
             except Exception:
                 env_obj = [env_obj]
                 env_obj = [env_obj]
-        elif ncc_select == _("Reference Object"):
+        elif ncc_select == 2:   # _("Reference Object")
             if box_obj is None:
             if box_obj is None:
                 return None, None
                 return None, None
 
 
@@ -1728,14 +1831,14 @@ class NonCopperClear(AppTool, Gerber):
             return 'fail'
             return 'fail'
 
 
         new_bounding_box = None
         new_bounding_box = None
-        if ncc_select == _('Itself'):
+        if ncc_select == 0:     # _('Itself')
             try:
             try:
                 new_bounding_box = bbox.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
                 new_bounding_box = bbox.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)
             except Exception as e:
             except Exception as e:
                 log.debug("NonCopperClear.apply_margin_to_bounding_box() 'itself'  --> %s" % str(e))
                 log.debug("NonCopperClear.apply_margin_to_bounding_box() 'itself'  --> %s" % str(e))
                 self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
                 self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
                 return 'fail'
                 return 'fail'
-        elif ncc_select == _("Area Selection"):
+        elif ncc_select == 1:   # _("Area Selection")
             geo_buff_list = []
             geo_buff_list = []
             for poly in bbox:
             for poly in bbox:
                 if self.app.abort_flag:
                 if self.app.abort_flag:
@@ -1743,7 +1846,7 @@ class NonCopperClear(AppTool, Gerber):
                     raise grace
                     raise grace
                 geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
                 geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre))
             new_bounding_box = unary_union(geo_buff_list)
             new_bounding_box = unary_union(geo_buff_list)
-        elif ncc_select == _("Reference Object"):
+        elif ncc_select == 2:   # _("Reference Object")
             if box_kind == 'geometry':
             if box_kind == 'geometry':
                 geo_buff_list = []
                 geo_buff_list = []
                 for poly in bbox:
                 for poly in bbox:
@@ -1962,7 +2065,7 @@ class NonCopperClear(AppTool, Gerber):
 
 
         cp = None
         cp = None
 
 
-        if ncc_method == _("Standard"):
+        if ncc_method == 0:     # standard
             try:
             try:
                 cp = self.clear_polygon(pol, tooldia,
                 cp = self.clear_polygon(pol, tooldia,
                                         steps_per_circle=self.circle_steps,
                                         steps_per_circle=self.circle_steps,
@@ -1973,7 +2076,7 @@ class NonCopperClear(AppTool, Gerber):
                 return "fail"
                 return "fail"
             except Exception as ee:
             except Exception as ee:
                 log.debug("NonCopperClear.clear_polygon_worker() Standard --> %s" % str(ee))
                 log.debug("NonCopperClear.clear_polygon_worker() Standard --> %s" % str(ee))
-        elif ncc_method == _("Seed"):
+        elif ncc_method == 1:   # seed
             try:
             try:
                 cp = self.clear_polygon2(pol, tooldia,
                 cp = self.clear_polygon2(pol, tooldia,
                                          steps_per_circle=self.circle_steps,
                                          steps_per_circle=self.circle_steps,
@@ -1984,7 +2087,7 @@ class NonCopperClear(AppTool, Gerber):
                 return "fail"
                 return "fail"
             except Exception as ee:
             except Exception as ee:
                 log.debug("NonCopperClear.clear_polygon_worker() Seed --> %s" % str(ee))
                 log.debug("NonCopperClear.clear_polygon_worker() Seed --> %s" % str(ee))
-        elif ncc_method == _("Lines"):
+        elif ncc_method == 2:   # Lines
             try:
             try:
                 cp = self.clear_polygon3(pol, tooldia,
                 cp = self.clear_polygon3(pol, tooldia,
                                          steps_per_circle=self.circle_steps,
                                          steps_per_circle=self.circle_steps,
@@ -1995,7 +2098,7 @@ class NonCopperClear(AppTool, Gerber):
                 return "fail"
                 return "fail"
             except Exception as ee:
             except Exception as ee:
                 log.debug("NonCopperClear.clear_polygon_worker() Lines --> %s" % str(ee))
                 log.debug("NonCopperClear.clear_polygon_worker() Lines --> %s" % str(ee))
-        elif ncc_method == _("Combo"):
+        elif ncc_method == 3:   # Combo
             try:
             try:
                 self.app.inform.emit(_("Clearing the polygon with the method: lines."))
                 self.app.inform.emit(_("Clearing the polygon with the method: lines."))
                 cp = self.clear_polygon3(pol, tooldia,
                 cp = self.clear_polygon3(pol, tooldia,
@@ -2064,9 +2167,9 @@ class NonCopperClear(AppTool, Gerber):
         log.debug("Executing the handler ...")
         log.debug("Executing the handler ...")
 
 
         if run_threaded:
         if run_threaded:
-            proc = self.app.proc_container.new(_("Non-Copper clearing ..."))
+            proc = self.app.proc_container.new('%s...' % _("Non-Copper Clearing"))
         else:
         else:
-            self.app.proc_container.view.set_busy(_("Non-Copper clearing ..."))
+            self.app.proc_container.view.set_busy('%s...' % _("Non-Copper Clearing"))
             QtWidgets.QApplication.processEvents()
             QtWidgets.QApplication.processEvents()
 
 
         # ######################################################################################################
         # ######################################################################################################
@@ -2121,7 +2224,7 @@ class NonCopperClear(AppTool, Gerber):
 
 
             app_obj.poly_not_cleared = False    # flag for polygons not cleared
             app_obj.poly_not_cleared = False    # flag for polygons not cleared
 
 
-            if ncc_select == _("Reference Object"):
+            if ncc_select == 2:     # Reference Object
                 bbox_geo, bbox_kind = self.calculate_bounding_box(
                 bbox_geo, bbox_kind = self.calculate_bounding_box(
                     ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
                     ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
             else:
             else:
@@ -2249,11 +2352,11 @@ class NonCopperClear(AppTool, Gerber):
 
 
                         # check if there is a geometry at all in the cleared geometry
                         # check if there is a geometry at all in the cleared geometry
                         if cleared_geo:
                         if cleared_geo:
+                            formatted_tool = self.app.dec_format(tool, self.decimals)
                             # find the tooluid associated with the current tool_dia so we know where to add the tool
                             # find the tooluid associated with the current tool_dia so we know where to add the tool
                             # solid_geometry
                             # solid_geometry
                             for k, v in tools_storage.items():
                             for k, v in tools_storage.items():
-                                if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals,
-                                                                                                    tool)):
+                                if self.app.dec_format(v['tooldia'], self.decimals) == formatted_tool:
                                     current_uid = int(k)
                                     current_uid = int(k)
 
 
                                     # add the solid_geometry to the current too in self.paint_tools dictionary
                                     # add the solid_geometry to the current too in self.paint_tools dictionary
@@ -2348,14 +2451,10 @@ class NonCopperClear(AppTool, Gerber):
 
 
             sorted_clear_tools.sort(reverse=True)
             sorted_clear_tools.sort(reverse=True)
 
 
-            cleared_by_last_tool = []
-            rest_geo = []
-            current_uid = 1
-
-            # repurposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
+            # re purposed flag for final object, geo_obj. True if it has any solid_geometry, False if not.
             app_obj.poly_not_cleared = True
             app_obj.poly_not_cleared = True
 
 
-            if ncc_select == _("Reference Object"):
+            if ncc_select == 2:     # Reference Object
                 env_obj, box_obj_kind = self.calculate_bounding_box(
                 env_obj, box_obj_kind = self.calculate_bounding_box(
                     ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
                     ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select)
             else:
             else:
@@ -2652,9 +2751,9 @@ class NonCopperClear(AppTool, Gerber):
         :return:
         :return:
         """
         """
         if run_threaded:
         if run_threaded:
-            proc = self.app.proc_container.new(_("Non-Copper clearing ..."))
+            proc = self.app.proc_container.new('%s...' % _("Non-Copper Clearing"))
         else:
         else:
-            self.app.proc_container.view.set_busy(_("Non-Copper clearing ..."))
+            self.app.proc_container.view.set_busy('%s...' % _("Non-Copper Clearing"))
             QtWidgets.QApplication.processEvents()
             QtWidgets.QApplication.processEvents()
 
 
         # #####################################################################
         # #####################################################################
@@ -2706,7 +2805,7 @@ class NonCopperClear(AppTool, Gerber):
         self.app.inform.emit(_("NCC Tool. Preparing non-copper polygons."))
         self.app.inform.emit(_("NCC Tool. Preparing non-copper polygons."))
 
 
         try:
         try:
-            if sel_obj is None or sel_obj == _('Itself'):
+            if sel_obj is None or sel_obj == 0:     # sel_obj == 'itself'
                 ncc_sel_obj = ncc_obj
                 ncc_sel_obj = ncc_obj
             else:
             else:
                 ncc_sel_obj = sel_obj
                 ncc_sel_obj = sel_obj
@@ -2715,7 +2814,7 @@ class NonCopperClear(AppTool, Gerber):
             return 'fail'
             return 'fail'
 
 
         bounding_box = None
         bounding_box = None
-        if ncc_select == _('Itself'):
+        if ncc_select == 0:     # itself
             geo_n = ncc_sel_obj.solid_geometry
             geo_n = ncc_sel_obj.solid_geometry
 
 
             try:
             try:
@@ -2734,7 +2833,7 @@ class NonCopperClear(AppTool, Gerber):
                 self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
                 self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
                 return 'fail'
                 return 'fail'
 
 
-        elif ncc_select == 'area':
+        elif ncc_select == 1:   # area
             geo_n = unary_union(self.sel_rect)
             geo_n = unary_union(self.sel_rect)
             try:
             try:
                 __ = iter(geo_n)
                 __ = iter(geo_n)
@@ -2751,7 +2850,7 @@ class NonCopperClear(AppTool, Gerber):
 
 
             bounding_box = unary_union(geo_buff_list)
             bounding_box = unary_union(geo_buff_list)
 
 
-        elif ncc_select == _("Reference Object"):
+        elif ncc_select == 2:   # Reference Object
             geo_n = ncc_sel_obj.solid_geometry
             geo_n = ncc_sel_obj.solid_geometry
             if ncc_sel_obj.kind == 'geometry':
             if ncc_sel_obj.kind == 'geometry':
                 try:
                 try:
@@ -2822,10 +2921,10 @@ class NonCopperClear(AppTool, Gerber):
             # Generate area for each tool
             # Generate area for each tool
             offset_a = sum(sorted_tools)
             offset_a = sum(sorted_tools)
             current_uid = int(1)
             current_uid = int(1)
-            try:
-                tool = eval(self.app.defaults["tools_ncc_tools"])[0]
-            except TypeError:
-                tool = eval(self.app.defaults["tools_ncc_tools"])
+            # try:
+            #     tool = eval(self.app.defaults["tools_ncc_tools"])[0]
+            # except TypeError:
+            #     tool = eval(self.app.defaults["tools_ncc_tools"])
 
 
             # ###################################################################################################
             # ###################################################################################################
             # Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
             # Calculate the empty area by subtracting the solid_geometry from the object bounding box geometry ##
@@ -2840,6 +2939,8 @@ class NonCopperClear(AppTool, Gerber):
                     sol_geo = ncc_obj.solid_geometry.buffer(0)
                     sol_geo = ncc_obj.solid_geometry.buffer(0)
                 else:
                 else:
                     sol_geo = ncc_obj.solid_geometry
                     sol_geo = ncc_obj.solid_geometry
+                    if isinstance(sol_geo, list):
+                        sol_geo = unary_union(sol_geo)
 
 
                 if has_offset is True:
                 if has_offset is True:
                     app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
                     app_obj.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering"))
@@ -2984,6 +3085,7 @@ class NonCopperClear(AppTool, Gerber):
             log.debug("NCC Tool. Finished calculation of 'empty' area.")
             log.debug("NCC Tool. Finished calculation of 'empty' area.")
             self.app.inform.emit(_("NCC Tool. Finished calculation of 'empty' area."))
             self.app.inform.emit(_("NCC Tool. Finished calculation of 'empty' area."))
 
 
+            tool = 1
             # COPPER CLEARING #
             # COPPER CLEARING #
             for tool in sorted_tools:
             for tool in sorted_tools:
                 log.debug("Starting geometry processing for tool: %s" % str(tool))
                 log.debug("Starting geometry processing for tool: %s" % str(tool))
@@ -3040,13 +3142,13 @@ class NonCopperClear(AppTool, Gerber):
                                 try:
                                 try:
                                     for pol in p:
                                     for pol in p:
                                         if pol is not None and isinstance(pol, Polygon):
                                         if pol is not None and isinstance(pol, Polygon):
-                                            if ncc_method == 'standard':
+                                            if ncc_method == 0:    # standard
                                                 cp = self.clear_polygon(pol, tool,
                                                 cp = self.clear_polygon(pol, tool,
                                                                         self.circle_steps,
                                                                         self.circle_steps,
                                                                         overlap=overlap, contour=contour,
                                                                         overlap=overlap, contour=contour,
                                                                         connect=connect,
                                                                         connect=connect,
                                                                         prog_plot=False)
                                                                         prog_plot=False)
-                                            elif ncc_method == 'seed':
+                                            elif ncc_method == 1:  # seed
                                                 cp = self.clear_polygon2(pol, tool,
                                                 cp = self.clear_polygon2(pol, tool,
                                                                          self.circle_steps,
                                                                          self.circle_steps,
                                                                          overlap=overlap, contour=contour,
                                                                          overlap=overlap, contour=contour,
@@ -3069,11 +3171,11 @@ class NonCopperClear(AppTool, Gerber):
                                                         "It is: %s" % str(type(pol)))
                                                         "It is: %s" % str(type(pol)))
                                 except TypeError:
                                 except TypeError:
                                     if isinstance(p, Polygon):
                                     if isinstance(p, Polygon):
-                                        if ncc_method == 'standard':
+                                        if ncc_method == 0:    # standard
                                             cp = self.clear_polygon(p, tool, self.circle_steps,
                                             cp = self.clear_polygon(p, tool, self.circle_steps,
                                                                     overlap=overlap, contour=contour, connect=connect,
                                                                     overlap=overlap, contour=contour, connect=connect,
                                                                     prog_plot=False)
                                                                     prog_plot=False)
-                                        elif ncc_method == 'seed':
+                                        elif ncc_method == 1:   # seed
                                             cp = self.clear_polygon2(p, tool, self.circle_steps,
                                             cp = self.clear_polygon2(p, tool, self.circle_steps,
                                                                      overlap=overlap, contour=contour, connect=connect,
                                                                      overlap=overlap, contour=contour, connect=connect,
                                                                      prog_plot=False)
                                                                      prog_plot=False)
@@ -3436,12 +3538,12 @@ class NonCopperClear(AppTool, Gerber):
 
 
                                 if isinstance(p, Polygon):
                                 if isinstance(p, Polygon):
                                     try:
                                     try:
-                                        if ncc_method == 'standard':
+                                        if ncc_method == 0:     # standard
                                             cp = self.clear_polygon(p, tool_used,
                                             cp = self.clear_polygon(p, tool_used,
                                                                     self.circle_steps,
                                                                     self.circle_steps,
                                                                     overlap=overlap, contour=contour, connect=connect,
                                                                     overlap=overlap, contour=contour, connect=connect,
                                                                     prog_plot=False)
                                                                     prog_plot=False)
-                                        elif ncc_method == 'seed':
+                                        elif ncc_method == 1:   # seed
                                             cp = self.clear_polygon2(p, tool_used,
                                             cp = self.clear_polygon2(p, tool_used,
                                                                      self.circle_steps,
                                                                      self.circle_steps,
                                                                      overlap=overlap, contour=contour, connect=connect,
                                                                      overlap=overlap, contour=contour, connect=connect,
@@ -3465,13 +3567,13 @@ class NonCopperClear(AppTool, Gerber):
                                             QtWidgets.QApplication.processEvents()
                                             QtWidgets.QApplication.processEvents()
 
 
                                             try:
                                             try:
-                                                if ncc_method == 'standard':
+                                                if ncc_method == 0:     # 'standard'
                                                     cp = self.clear_polygon(poly_p, tool_used,
                                                     cp = self.clear_polygon(poly_p, tool_used,
                                                                             self.circle_steps,
                                                                             self.circle_steps,
                                                                             overlap=overlap, contour=contour,
                                                                             overlap=overlap, contour=contour,
                                                                             connect=connect,
                                                                             connect=connect,
                                                                             prog_plot=False)
                                                                             prog_plot=False)
-                                                elif ncc_method == 'seed':
+                                                elif ncc_method == 1:   # 'seed'
                                                     cp = self.clear_polygon2(poly_p, tool_used,
                                                     cp = self.clear_polygon2(poly_p, tool_used,
                                                                              self.circle_steps,
                                                                              self.circle_steps,
                                                                              overlap=overlap, contour=contour,
                                                                              overlap=overlap, contour=contour,
@@ -3622,6 +3724,10 @@ class NonCopperClear(AppTool, Gerber):
             geo_len = 1
             geo_len = 1
         else:
         else:
             geo_len = len(target)
             geo_len = len(target)
+
+        if isinstance(target, list):
+            target = MultiPolygon(target)
+
         pol_nr = 0
         pol_nr = 0
         old_disp_number = 0
         old_disp_number = 0
 
 
@@ -3705,7 +3811,12 @@ class NonCopperClear(AppTool, Gerber):
         """
         """
         tool_from_db = deepcopy(tool)
         tool_from_db = deepcopy(tool)
 
 
-        if tool['data']['tool_target'] != _("NCC"):
+        if tool['data']['tool_target'] not in [0, 5]:   # [General, NCC]
+            for idx in range(self.app.ui.plot_tab_area.count()):
+                if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                    wdg = self.app.ui.plot_tab_area.widget(idx)
+                    wdg.deleteLater()
+                    self.app.ui.plot_tab_area.removeTab(idx)
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
             return
 
 
@@ -3795,7 +3906,9 @@ class NonCopperClear(AppTool, Gerber):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 break
                 break
-        self.app.on_tools_database(source='ncc')
+        ret_val = self.app.on_tools_database(source='ncc')
+        if ret_val == 'fail':
+            return
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
@@ -3896,7 +4009,7 @@ class NccUI:
               "this function will not be able to create painting geometry.")
               "this function will not be able to create painting geometry.")
         )
         )
         self.tools_table.horizontalHeaderItem(1).setToolTip(
         self.tools_table.horizontalHeaderItem(1).setToolTip(
-            _("Tool Diameter. It's value (in current FlatCAM units)\n"
+            _("Tool Diameter. Its value\n"
               "is the cut width into the material."))
               "is the cut width into the material."))
 
 
         self.tools_table.horizontalHeaderItem(2).setToolTip(
         self.tools_table.horizontalHeaderItem(2).setToolTip(
@@ -3973,7 +4086,7 @@ class NccUI:
 
 
         self.new_tooldia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.new_tooldia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.new_tooldia_entry.set_precision(self.decimals)
         self.new_tooldia_entry.set_precision(self.decimals)
-        self.new_tooldia_entry.set_range(0.000, 9999.9999)
+        self.new_tooldia_entry.set_range(0.000, 10000.0000)
         self.new_tooldia_entry.setObjectName(_("Tool Dia"))
         self.new_tooldia_entry.setObjectName(_("Tool Dia"))
 
 
         new_tool_lay.addWidget(self.new_tooldia_entry)
         new_tool_lay.addWidget(self.new_tooldia_entry)
@@ -4025,7 +4138,7 @@ class NccUI:
         self.deltool_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/trash16.png'))
         self.deltool_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/trash16.png'))
         self.deltool_btn.setToolTip(
         self.deltool_btn.setToolTip(
             _("Delete a selection of tools in the Tool Table\n"
             _("Delete a selection of tools in the Tool Table\n"
-              "by first selecting a row(s) in the Tool Table.")
+              "by first selecting a row in the Tool Table.")
         )
         )
         self.grid3.addWidget(self.deltool_btn, 9, 0, 1, 2)
         self.grid3.addWidget(self.deltool_btn, 9, 0, 1, 2)
 
 
@@ -4067,7 +4180,7 @@ class NccUI:
         # Milling Type Radio Button
         # Milling Type Radio Button
         self.milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.milling_type_label = FCLabel('%s:' % _('Milling Type'))
         self.milling_type_label.setToolTip(
         self.milling_type_label.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -4075,7 +4188,7 @@ class NccUI:
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
                                             {'label': _('Conventional'), 'value': 'cv'}])
                                             {'label': _('Conventional'), 'value': 'cv'}])
         self.milling_type_radio.setToolTip(
         self.milling_type_radio.setToolTip(
-            _("Milling type when the selected tool is of type: 'iso_op':\n"
+            _("Milling type:\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
               "- conventional / useful when there is no backlash compensation")
         )
         )
@@ -4092,8 +4205,8 @@ class NccUI:
         self.nccoverlabel.setToolTip(
         self.nccoverlabel.setToolTip(
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
               "Adjust the value starting with lower values\n"
               "Adjust the value starting with lower values\n"
-              "and increasing it if areas that should be cleared are still \n"
-              "not cleared.\n"
+              "and increasing it if areas that should be processed are still \n"
+              "not processed.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "due of too many paths.")
               "due of too many paths.")
@@ -4122,7 +4235,7 @@ class NccUI:
         #     {"label": _("Straight lines"), "value": "lines"}
         #     {"label": _("Straight lines"), "value": "lines"}
         # ], orientation='vertical', stretch=False)
         # ], orientation='vertical', stretch=False)
 
 
-        self.ncc_method_combo = FCComboBox()
+        self.ncc_method_combo = FCComboBox2()
         self.ncc_method_combo.addItems(
         self.ncc_method_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
             [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
         )
         )
@@ -4138,7 +4251,7 @@ class NccUI:
         )
         )
         self.ncc_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.ncc_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.ncc_margin_entry.set_precision(self.decimals)
         self.ncc_margin_entry.set_precision(self.decimals)
-        self.ncc_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.ncc_margin_entry.set_range(-10000.0000, 10000.0000)
         self.ncc_margin_entry.setObjectName("n_margin")
         self.ncc_margin_entry.setObjectName("n_margin")
 
 
         self.grid3.addWidget(self.nccmarginlabel, 17, 0)
         self.grid3.addWidget(self.nccmarginlabel, 17, 0)
@@ -4171,8 +4284,7 @@ class NccUI:
         self.ncc_choice_offset_cb.setToolTip(
         self.ncc_choice_offset_cb.setToolTip(
             _("If used, it will add an offset to the copper features.\n"
             _("If used, it will add an offset to the copper features.\n"
               "The copper clearing will finish to a distance\n"
               "The copper clearing will finish to a distance\n"
-              "from the copper features.\n"
-              "The value can be between 0 and 10 FlatCAM units.")
+              "from the copper features.")
         )
         )
         self.grid3.addWidget(self.ncc_choice_offset_cb, 19, 0)
         self.grid3.addWidget(self.ncc_choice_offset_cb, 19, 0)
 
 
@@ -4224,11 +4336,11 @@ class NccUI:
 
 
         self.ncc_rest_cb.setToolTip(
         self.ncc_rest_cb.setToolTip(
             _("If checked, use 'rest machining'.\n"
             _("If checked, use 'rest machining'.\n"
-              "Basically it will clear copper outside PCB features,\n"
+              "Basically it will process copper outside PCB features,\n"
               "using the biggest tool and continue with the next tools,\n"
               "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to clear areas of copper that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper to clear or there are no more tools.\n"
+              "from bigger to smaller, to process the copper features that\n"
+              "could not be processed by previous tool, until there is\n"
+              "nothing left to process or there are no more tools.\n\n"
               "If not checked, use the standard algorithm.")
               "If not checked, use the standard algorithm.")
         )
         )
 
 
@@ -4241,7 +4353,7 @@ class NccUI:
         )
         )
         self.rest_ncc_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.rest_ncc_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.rest_ncc_margin_entry.set_precision(self.decimals)
         self.rest_ncc_margin_entry.set_precision(self.decimals)
-        self.rest_ncc_margin_entry.set_range(-9999.9999, 9999.9999)
+        self.rest_ncc_margin_entry.set_range(-10000.0000, 10000.0000)
         self.rest_ncc_margin_entry.setObjectName("n_margin")
         self.rest_ncc_margin_entry.setObjectName("n_margin")
 
 
         self.grid3.addWidget(self.rest_nccmarginlabel, 26, 0)
         self.grid3.addWidget(self.rest_nccmarginlabel, 26, 0)
@@ -4268,8 +4380,7 @@ class NccUI:
         self.rest_ncc_choice_offset_cb.setToolTip(
         self.rest_ncc_choice_offset_cb.setToolTip(
             _("If used, it will add an offset to the copper features.\n"
             _("If used, it will add an offset to the copper features.\n"
               "The copper clearing will finish to a distance\n"
               "The copper clearing will finish to a distance\n"
-              "from the copper features.\n"
-              "The value can be between 0 and 10 FlatCAM units.")
+              "from the copper features.")
         )
         )
         self.grid3.addWidget(self.rest_ncc_choice_offset_cb, 28, 0)
         self.grid3.addWidget(self.rest_ncc_choice_offset_cb, 28, 0)
 
 
@@ -4289,13 +4400,8 @@ class NccUI:
 
 
         self.rest_ois_ncc_offset = OptionalInputSection(self.rest_ncc_choice_offset_cb, [self.rest_ncc_offset_spinner])
         self.rest_ois_ncc_offset = OptionalInputSection(self.rest_ncc_choice_offset_cb, [self.rest_ncc_offset_spinner])
 
 
-        # ## Reference
-        # self.select_radio = RadioSet([
-        #     {'label': _('Itself'), 'value': 'itself'},
-        #     {"label": _("Area Selection"), "value": "area"},
-        #     {'label': _("Reference Object"), 'value': 'box'}
-        # ], orientation='vertical', stretch=False)
-        self.select_combo = FCComboBox()
+        # Reference Selection Combo
+        self.select_combo = FCComboBox2()
         self.select_combo.addItems(
         self.select_combo.addItems(
             [_("Itself"), _("Area Selection"), _("Reference Object")]
             [_("Itself"), _("Area Selection"), _("Reference Object")]
         )
         )
@@ -4311,31 +4417,25 @@ class NccUI:
         self.grid3.addWidget(self.select_label, 29, 0, )
         self.grid3.addWidget(self.select_label, 29, 0, )
         self.grid3.addWidget(self.select_combo, 29, 1)
         self.grid3.addWidget(self.select_combo, 29, 1)
 
 
-        form1 = QtWidgets.QFormLayout()
-        self.grid3.addLayout(form1, 30, 0, 1, 2)
-
-        self.reference_combo_type_label = FCLabel('%s:' % _("Ref. Type"))
+        self.reference_combo_type_label = FCLabel('%s:' % _("Type"))
         self.reference_combo_type_label.setToolTip(
         self.reference_combo_type_label.setToolTip(
             _("The type of FlatCAM object to be used as non copper clearing reference.\n"
             _("The type of FlatCAM object to be used as non copper clearing reference.\n"
               "It can be Gerber, Excellon or Geometry.")
               "It can be Gerber, Excellon or Geometry.")
         )
         )
-        self.reference_combo_type = FCComboBox()
+        self.reference_combo_type = FCComboBox2()
         self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
         self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
 
 
-        form1.addRow(self.reference_combo_type_label, self.reference_combo_type)
+        self.grid3.addWidget(self.reference_combo_type_label, 31, 0, )
+        self.grid3.addWidget(self.reference_combo_type, 31, 1)
 
 
-        self.reference_combo_label = FCLabel('%s:' % _("Ref. Object"))
-        self.reference_combo_label.setToolTip(
-            _("The FlatCAM object to be used as non copper clearing reference.")
-        )
         self.reference_combo = FCComboBox()
         self.reference_combo = FCComboBox()
         self.reference_combo.setModel(self.app.collection)
         self.reference_combo.setModel(self.app.collection)
         self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.reference_combo.is_last = True
         self.reference_combo.is_last = True
-        form1.addRow(self.reference_combo_label, self.reference_combo)
+
+        self.grid3.addWidget(self.reference_combo, 33, 0, 1, 2)
 
 
         self.reference_combo.hide()
         self.reference_combo.hide()
-        self.reference_combo_label.hide()
         self.reference_combo_type.hide()
         self.reference_combo_type.hide()
         self.reference_combo_type_label.hide()
         self.reference_combo_type_label.hide()
 
 
@@ -4348,8 +4448,8 @@ class NccUI:
         self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
         self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
                                           {'label': _("Polygon"), 'value': 'polygon'}])
                                           {'label': _("Polygon"), 'value': 'polygon'}])
 
 
-        self.grid3.addWidget(self.area_shape_label, 31, 0)
-        self.grid3.addWidget(self.area_shape_radio, 31, 1)
+        self.grid3.addWidget(self.area_shape_label, 35, 0)
+        self.grid3.addWidget(self.area_shape_radio, 35, 1)
 
 
         self.area_shape_label.hide()
         self.area_shape_label.hide()
         self.area_shape_radio.hide()
         self.area_shape_radio.hide()
@@ -4362,12 +4462,12 @@ class NccUI:
         )
         )
         self.valid_cb.setObjectName("n_check")
         self.valid_cb.setObjectName("n_check")
 
 
-        self.grid3.addWidget(self.valid_cb, 33, 0, 1, 2)
+        self.grid3.addWidget(self.valid_cb, 37, 0, 1, 2)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        self.grid3.addWidget(separator_line, 35, 0, 1, 2)
+        self.grid3.addWidget(separator_line, 39, 0, 1, 2)
 
 
         self.generate_ncc_button = FCButton(_('Generate Geometry'))
         self.generate_ncc_button = FCButton(_('Generate Geometry'))
         self.generate_ncc_button.setIcon(QtGui.QIcon(self.app.resource_location + '/geometry32.png'))
         self.generate_ncc_button.setIcon(QtGui.QIcon(self.app.resource_location + '/geometry32.png'))
@@ -4433,9 +4533,8 @@ class NccUI:
     def on_toggle_reference(self):
     def on_toggle_reference(self):
         sel_combo = self.select_combo.get_value()
         sel_combo = self.select_combo.get_value()
 
 
-        if sel_combo == _("Itself"):
+        if sel_combo == 0:  # itself
             self.reference_combo.hide()
             self.reference_combo.hide()
-            self.reference_combo_label.hide()
             self.reference_combo_type.hide()
             self.reference_combo_type.hide()
             self.reference_combo_type_label.hide()
             self.reference_combo_type_label.hide()
             self.area_shape_label.hide()
             self.area_shape_label.hide()
@@ -4443,9 +4542,8 @@ class NccUI:
 
 
             # disable rest-machining for area painting
             # disable rest-machining for area painting
             self.ncc_rest_cb.setDisabled(False)
             self.ncc_rest_cb.setDisabled(False)
-        elif sel_combo == _("Area Selection"):
+        elif sel_combo == 1:    # area selection
             self.reference_combo.hide()
             self.reference_combo.hide()
-            self.reference_combo_label.hide()
             self.reference_combo_type.hide()
             self.reference_combo_type.hide()
             self.reference_combo_type_label.hide()
             self.reference_combo_type_label.hide()
             self.area_shape_label.show()
             self.area_shape_label.show()
@@ -4456,7 +4554,6 @@ class NccUI:
             # self.ncc_rest_cb.setDisabled(True)
             # self.ncc_rest_cb.setDisabled(True)
         else:
         else:
             self.reference_combo.show()
             self.reference_combo.show()
-            self.reference_combo_label.show()
             self.reference_combo_type.show()
             self.reference_combo_type.show()
             self.reference_combo_type_label.show()
             self.reference_combo_type_label.show()
             self.area_shape_label.hide()
             self.area_shape_label.hide()

+ 2 - 1
appTools/ToolOptimal.py

@@ -142,7 +142,7 @@ class ToolOptimal(AppTool):
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Only Gerber objects can be evaluated."))
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Only Gerber objects can be evaluated."))
             return
             return
 
 
-        proc = self.app.proc_container.new(_("Working..."))
+        proc = self.app.proc_container.new(_("Working ..."))
 
 
         def job_thread(app_obj):
         def job_thread(app_obj):
             app_obj.inform.emit(_("Optimal Tool. Started to search for the minimum distance between copper features."))
             app_obj.inform.emit(_("Optimal Tool. Started to search for the minimum distance between copper features."))
@@ -573,6 +573,7 @@ class OptimalUI:
 
 
         # GO button
         # GO button
         self.calculate_button = FCButton(_("Find Minimum"))
         self.calculate_button = FCButton(_("Find Minimum"))
+        self.calculate_button.setIcon(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'))
         self.calculate_button.setToolTip(
         self.calculate_button.setToolTip(
             _("Calculate the minimum distance between copper features,\n"
             _("Calculate the minimum distance between copper features,\n"
               "this will allow the determination of the right tool to\n"
               "this will allow the determination of the right tool to\n"

+ 14 - 9
appTools/ToolPDF.py

@@ -109,9 +109,10 @@ class ToolPDF(AppTool):
         short_name = filename.split('/')[-1].split('\\')[-1]
         short_name = filename.split('/')[-1].split('\\')[-1]
         self.parsing_promises.append(short_name)
         self.parsing_promises.append(short_name)
 
 
-        self.pdf_parsed[short_name] = {}
-        self.pdf_parsed[short_name]['pdf'] = {}
-        self.pdf_parsed[short_name]['filename'] = filename
+        self.pdf_parsed[short_name] = {
+            'pdf': {},
+            'filename': filename
+        }
 
 
         self.pdf_decompressed[short_name] = ''
         self.pdf_decompressed[short_name] = ''
 
 
@@ -119,7 +120,7 @@ class ToolPDF(AppTool):
             # graceful abort requested by the user
             # graceful abort requested by the user
             raise grace
             raise grace
 
 
-        with self.app.proc_container.new(_("Parsing PDF file ...")):
+        with self.app.proc_container.new(_("Parsing ...")):
             with open(filename, "rb") as f:
             with open(filename, "rb") as f:
                 pdf = f.read()
                 pdf = f.read()
 
 
@@ -181,22 +182,26 @@ class ToolPDF(AppTool):
             name_tool = 0
             name_tool = 0
             for dia in sorted_dia:
             for dia in sorted_dia:
                 name_tool += 1
                 name_tool += 1
+                tool = str(name_tool)
 
 
-                # create tools dictionary
-                spec = {"C": dia, 'solid_geometry': []}
-                exc_obj.tools[str(name_tool)] = spec
+                exc_obj.tools[tool] = {
+                    'tooldia': dia,
+                    'drills': [],
+                    'solid_geometry': []
+                }
 
 
-                # create drill list of dictionaries
+                # update the drill list
                 for dia_points in points:
                 for dia_points in points:
                     if dia == dia_points:
                     if dia == dia_points:
                         for pt in points[dia_points]:
                         for pt in points[dia_points]:
-                            exc_obj.drills.append({'point': Point(pt), 'tool': str(name_tool)})
+                            exc_obj.tools[tool]['drills'].append(Point(pt))
                         break
                         break
 
 
             ret = exc_obj.create_geometry()
             ret = exc_obj.create_geometry()
             if ret == 'fail':
             if ret == 'fail':
                 log.debug("Could not create geometry for Excellon object.")
                 log.debug("Could not create geometry for Excellon object.")
                 return "fail"
                 return "fail"
+
             for tool in exc_obj.tools:
             for tool in exc_obj.tools:
                 if exc_obj.tools[tool]['solid_geometry']:
                 if exc_obj.tools[tool]['solid_geometry']:
                     return
                     return

+ 93 - 110
appTools/ToolPaint.py

@@ -13,8 +13,8 @@ from copy import deepcopy
 
 
 from appParsers.ParseGerber import Gerber
 from appParsers.ParseGerber import Gerber
 from camlib import Geometry, FlatCAMRTreeStorage, grace
 from camlib import Geometry, FlatCAMRTreeStorage, grace
-from appGUI.GUIElements import FCTable, FCDoubleSpinner, FCCheckBox, FCInputDialog, RadioSet, FCButton, FCComboBox, \
-    FCLabel
+from appGUI.GUIElements import FCTable, FCDoubleSpinner, FCCheckBox, FCInputDoubleSpinner, RadioSet, \
+    FCButton, FCComboBox, FCLabel, FCComboBox2
 
 
 from shapely.geometry import base, Polygon, MultiPolygon, LinearRing, Point
 from shapely.geometry import base, Polygon, MultiPolygon, LinearRing, Point
 from shapely.ops import unary_union, linemerge
 from shapely.ops import unary_union, linemerge
@@ -156,16 +156,14 @@ class ToolPaint(AppTool, Gerber):
             self.ui.paintmethod_combo.model().item(idx).setEnabled(True)
             self.ui.paintmethod_combo.model().item(idx).setEnabled(True)
         else:
         else:
             self.ui.paintmethod_combo.model().item(idx).setEnabled(False)
             self.ui.paintmethod_combo.model().item(idx).setEnabled(False)
-            if self.ui.paintmethod_combo.get_value() == _("Laser_lines"):
-                self.ui.paintmethod_combo.set_value(_("Lines"))
+            if self.ui.paintmethod_combo.get_value() == idx:    # if its Laser Lines
+                self.ui.paintmethod_combo.set_value(idx+1)
 
 
     def on_reference_combo_changed(self):
     def on_reference_combo_changed(self):
         obj_type = self.ui.reference_type_combo.currentIndex()
         obj_type = self.ui.reference_type_combo.currentIndex()
         self.ui.reference_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
         self.ui.reference_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
         self.ui.reference_combo.setCurrentIndex(0)
         self.ui.reference_combo.setCurrentIndex(0)
-        self.ui.reference_combo.obj_type = {
-            _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
-        }[self.ui.reference_type_combo.get_value()]
+        self.ui.reference_combo.obj_type = {0: "Gerber", 1: "Excellon", 2: "Geometry"}[obj_type]
 
 
     def connect_signals_at_init(self):
     def connect_signals_at_init(self):
         # #############################################################################
         # #############################################################################
@@ -383,10 +381,11 @@ class ToolPaint(AppTool, Gerber):
         self.blockSignals(False)
         self.blockSignals(False)
 
 
     def on_add_tool_by_key(self):
     def on_add_tool_by_key(self):
-        tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"),
-                                       text='%s:' % _('Enter a Tool Diameter'),
-                                       min=0.0000, max=99.9999, decimals=4)
-        tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
+        tool_add_popup = FCInputDoubleSpinner(title='%s...' % _("New Tool"),
+                                              text='%s:' % _('Enter a Tool Diameter'),
+                                              min=0.0000, max=99.9999, decimals=self.decimals,
+                                              parent=self.app.ui)
+        tool_add_popup.set_icon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
 
 
         val, ok = tool_add_popup.get_value()
         val, ok = tool_add_popup.get_value()
         if ok:
         if ok:
@@ -413,7 +412,7 @@ class ToolPaint(AppTool, Gerber):
         # if the sender is in the column with index 2 then we update the tool_type key
         # if the sender is in the column with index 2 then we update the tool_type key
         if cw_col == 2:
         if cw_col == 2:
             tt = cw.currentText()
             tt = cw.currentText()
-            typ = 'Iso' if tt == 'V' else "Rough"
+            typ = 'Iso' if tt == 'V' else 'Rough'
 
 
             self.paint_tools[current_uid].update({
             self.paint_tools[current_uid].update({
                 'type': typ,
                 'type': typ,
@@ -542,10 +541,20 @@ class ToolPaint(AppTool, Gerber):
 
 
         self.ui.on_rest_machining_check(state=self.app.defaults["tools_paint_rest"])
         self.ui.on_rest_machining_check(state=self.app.defaults["tools_paint_rest"])
 
 
-        # if the Paint Method is "Single" disable the tool table context menu
-        if self.default_data["tools_paint_selectmethod"] == "single":
+        # if the Paint Method is "Polygon Selection" disable the tool table context menu
+        if self.default_data["tools_paint_selectmethod"] == 1:
             self.ui.tools_table.setContextMenuPolicy(Qt.NoContextMenu)
             self.ui.tools_table.setContextMenuPolicy(Qt.NoContextMenu)
 
 
+        # make sure that we can't get selection of Laser Lines for Geometry even if it's set in the Preferences
+        # because we don't select the default object type in Preferences but here
+        idx = self.ui.paintmethod_combo.findText(_("Laser_lines"))
+        if self.ui.type_obj_radio.get_value().lower() == 'gerber':
+            self.ui.paintmethod_combo.model().item(idx).setEnabled(True)
+        else:
+            self.ui.paintmethod_combo.model().item(idx).setEnabled(False)
+            if self.ui.paintmethod_combo.get_value() == idx:  # if its Laser Lines
+                self.ui.paintmethod_combo.set_value(idx + 1)
+
         self.ui.tools_table.drag_drop_sig.connect(self.rebuild_ui)
         self.ui.tools_table.drag_drop_sig.connect(self.rebuild_ui)
 
 
     def rebuild_ui(self):
     def rebuild_ui(self):
@@ -658,7 +667,7 @@ class ToolPaint(AppTool, Gerber):
     def on_tool_add(self, custom_dia=None):
     def on_tool_add(self, custom_dia=None):
         self.blockSignals(True)
         self.blockSignals(True)
 
 
-        filename = self.app.data_path + '\\tools_db.FlatDB'
+        filename = self.app.tools_database_path()
 
 
         new_tools_dict = deepcopy(self.default_data)
         new_tools_dict = deepcopy(self.default_data)
         updated_tooldia = None
         updated_tooldia = None
@@ -701,7 +710,7 @@ class ToolPaint(AppTool, Gerber):
                 tools = f.read()
                 tools = f.read()
         except IOError:
         except IOError:
             self.app.log.error("Could not load tools DB file.")
             self.app.log.error("Could not load tools DB file.")
-            self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
+            self.app.inform.emit('[ERROR] %s' % _("Could not load the file."))
             self.blockSignals(False)
             self.blockSignals(False)
             self.on_tool_default_add(dia=tool_dia)
             self.on_tool_default_add(dia=tool_dia)
             return
             return
@@ -721,7 +730,7 @@ class ToolPaint(AppTool, Gerber):
 
 
         offset = 'Path'
         offset = 'Path'
         offset_val = 0.0
         offset_val = 0.0
-        typ = "Rough"
+        typ = 'Rough'
         tool_type = 'V'
         tool_type = 'V'
         # look in database tools
         # look in database tools
         for db_tool, db_tool_val in tools_db_dict.items():
         for db_tool, db_tool_val in tools_db_dict.items():
@@ -986,7 +995,7 @@ class ToolPaint(AppTool, Gerber):
 
 
         self.sel_rect = []
         self.sel_rect = []
 
 
-        obj_type = self.ui.type_obj_radio.get_value
+        obj_type = self.ui.type_obj_radio.get_value()
         self.circle_steps = int(self.app.defaults["gerber_circle_steps"]) if obj_type == 'gerber' else \
         self.circle_steps = int(self.app.defaults["gerber_circle_steps"]) if obj_type == 'gerber' else \
             int(self.app.defaults["geometry_circle_steps"])
             int(self.app.defaults["geometry_circle_steps"])
         self.obj_name = self.ui.obj_combo.currentText()
         self.obj_name = self.ui.obj_combo.currentText()
@@ -996,16 +1005,16 @@ class ToolPaint(AppTool, Gerber):
             self.paint_obj = self.app.collection.get_by_name(str(self.obj_name))
             self.paint_obj = self.app.collection.get_by_name(str(self.obj_name))
         except Exception as e:
         except Exception as e:
             log.debug("ToolPaint.on_paint_button_click() --> %s" % str(e))
             log.debug("ToolPaint.on_paint_button_click() --> %s" % str(e))
-            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object: %s"), self.obj_name))
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.obj_name))
             return
             return
 
 
         if self.paint_obj is None:
         if self.paint_obj is None:
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), self.paint_obj))
             self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), self.paint_obj))
             return
             return
 
 
-        # test if the Geometry Object is multigeo and return Fail if True because
-        # for now Paint don't work on MultiGeo
-        if self.paint_obj.kind == 'geometry' and self.paint_obj.multigeo is True:
+        # test if the Geometry Object is multigeo with more than one tool and return Fail if True because
+        # for now Paint don't work on MultiGeo with more than one tools
+        if self.paint_obj.kind == 'geometry' and self.paint_obj.multigeo is True and len(self.paint_obj.tools) > 1:
             self.app.inform.emit('[ERROR_NOTCL] %s...' % _("Can't do Paint on MultiGeo geometries"))
             self.app.inform.emit('[ERROR_NOTCL] %s...' % _("Can't do Paint on MultiGeo geometries"))
             return 'Fail'
             return 'Fail'
 
 
@@ -1032,10 +1041,10 @@ class ToolPaint(AppTool, Gerber):
             return
             return
 
 
         self.select_method = self.ui.selectmethod_combo.get_value()
         self.select_method = self.ui.selectmethod_combo.get_value()
-        if self.select_method == _("All"):
+        if self.select_method == 0:  # _("All")
             self.paint_poly_all(self.paint_obj, tooldia=self.tooldia_list, outname=self.o_name)
             self.paint_poly_all(self.paint_obj, tooldia=self.tooldia_list, outname=self.o_name)
 
 
-        elif self.select_method == _("Polygon Selection"):
+        elif self.select_method == 1:   # _("Polygon Selection")
             # disengage the grid snapping since it may be hard to click on polygons with grid snapping on
             # disengage the grid snapping since it may be hard to click on polygons with grid snapping on
             if self.app.ui.grid_snap_btn.isChecked():
             if self.app.ui.grid_snap_btn.isChecked():
                 self.grid_status_memory = True
                 self.grid_status_memory = True
@@ -1058,8 +1067,8 @@ class ToolPaint(AppTool, Gerber):
             # disconnect flags
             # disconnect flags
             self.poly_sel_disconnect_flag = True
             self.poly_sel_disconnect_flag = True
 
 
-        elif self.select_method == _("Area Selection"):
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the paint area."))
+        elif self.select_method == 2:   # _("Area Selection")
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
 
 
             if self.app.is_legacy is False:
             if self.app.is_legacy is False:
                 self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
                 self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
@@ -1077,7 +1086,7 @@ class ToolPaint(AppTool, Gerber):
             # disconnect flags
             # disconnect flags
             self.area_sel_disconnect_flag = True
             self.area_sel_disconnect_flag = True
 
 
-        elif self.select_method == _("Reference Object"):
+        elif self.select_method == 3:   # _("Reference Object")
             self.bound_obj_name = self.reference_combo.currentText()
             self.bound_obj_name = self.reference_combo.currentText()
             # Get source object.
             # Get source object.
             try:
             try:
@@ -1125,7 +1134,7 @@ class ToolPaint(AppTool, Gerber):
                     self.app.inform.emit(
                     self.app.inform.emit(
                         '%s: %d. %s' % (_("Added polygon"),
                         '%s: %d. %s' % (_("Added polygon"),
                                         int(len(self.poly_dict)),
                                         int(len(self.poly_dict)),
-                                        _("Click to add next polygon or right click to start painting."))
+                                        _("Click to add next polygon or right click to start."))
                     )
                     )
                 else:
                 else:
                     try:
                     try:
@@ -1138,7 +1147,7 @@ class ToolPaint(AppTool, Gerber):
                         return
                         return
                     self.app.inform.emit(
                     self.app.inform.emit(
                         '%s. %s' % (_("Removed polygon"),
                         '%s. %s' % (_("Removed polygon"),
-                                    _("Click to add/remove next polygon or right click to start painting."))
+                                    _("Click to add/remove next polygon or right click to start."))
                     )
                     )
 
 
                 self.app.tool_shapes.redraw()
                 self.app.tool_shapes.redraw()
@@ -1453,7 +1462,7 @@ class ToolPaint(AppTool, Gerber):
 
 
         cpoly = None
         cpoly = None
 
 
-        if paint_method == _("Standard"):
+        if paint_method == 0:   # _("Standard")
             try:
             try:
                 # Type(cp) == FlatCAMRTreeStorage | None
                 # Type(cp) == FlatCAMRTreeStorage | None
                 cpoly = self.clear_polygon(polyg,
                 cpoly = self.clear_polygon(polyg,
@@ -1467,7 +1476,7 @@ class ToolPaint(AppTool, Gerber):
                 return "fail"
                 return "fail"
             except Exception as ee:
             except Exception as ee:
                 log.debug("ToolPaint.paint_polygon_worker() Standard --> %s" % str(ee))
                 log.debug("ToolPaint.paint_polygon_worker() Standard --> %s" % str(ee))
-        elif paint_method == _("Seed"):
+        elif paint_method == 1:  # _("Seed")
             try:
             try:
                 # Type(cp) == FlatCAMRTreeStorage | None
                 # Type(cp) == FlatCAMRTreeStorage | None
                 cpoly = self.clear_polygon2(polyg,
                 cpoly = self.clear_polygon2(polyg,
@@ -1481,7 +1490,7 @@ class ToolPaint(AppTool, Gerber):
                 return "fail"
                 return "fail"
             except Exception as ee:
             except Exception as ee:
                 log.debug("ToolPaint.paint_polygon_worker() Seed --> %s" % str(ee))
                 log.debug("ToolPaint.paint_polygon_worker() Seed --> %s" % str(ee))
-        elif paint_method == _("Lines"):
+        elif paint_method == 2:  # _("Lines")
             try:
             try:
                 # Type(cp) == FlatCAMRTreeStorage | None
                 # Type(cp) == FlatCAMRTreeStorage | None
                 cpoly = self.clear_polygon3(polyg,
                 cpoly = self.clear_polygon3(polyg,
@@ -1495,7 +1504,7 @@ class ToolPaint(AppTool, Gerber):
                 return "fail"
                 return "fail"
             except Exception as ee:
             except Exception as ee:
                 log.debug("ToolPaint.paint_polygon_worker() Lines --> %s" % str(ee))
                 log.debug("ToolPaint.paint_polygon_worker() Lines --> %s" % str(ee))
-        elif paint_method == _("Laser_lines"):
+        elif paint_method == 3:  # _("Laser_lines")
             try:
             try:
                 # line = None
                 # line = None
                 # aperture_size = None
                 # aperture_size = None
@@ -1544,7 +1553,7 @@ class ToolPaint(AppTool, Gerber):
 
 
                 # process the flashes found in the selected polygon with the 'lines' method for rectangular
                 # process the flashes found in the selected polygon with the 'lines' method for rectangular
                 # flashes and with _("Seed") for oblong and circular flashes
                 # flashes and with _("Seed") for oblong and circular flashes
-                # and pads (flahes) need the contour therefore I override the GUI settings with always True
+                # and pads (flashes) need the contour therefore I override the GUI settings with always True
                 for ap_type in flash_el_dict:
                 for ap_type in flash_el_dict:
                     for elem in flash_el_dict[ap_type]:
                     for elem in flash_el_dict[ap_type]:
                         if 'solid' in elem:
                         if 'solid' in elem:
@@ -1558,7 +1567,7 @@ class ToolPaint(AppTool, Gerber):
                                                           connect=conn,
                                                           connect=conn,
                                                           prog_plot=prog_plot)
                                                           prog_plot=prog_plot)
                                 pads_lines_list += [p for p in f_o.get_objects() if p]
                                 pads_lines_list += [p for p in f_o.get_objects() if p]
-
+                            # this is the same as above but I keep it in case I will modify something in the future
                             elif ap_type == 'O':
                             elif ap_type == 'O':
                                 f_o = self.clear_polygon2(elem['solid'],
                                 f_o = self.clear_polygon2(elem['solid'],
                                                           tooldia=tooldiameter,
                                                           tooldia=tooldiameter,
@@ -1646,7 +1655,7 @@ class ToolPaint(AppTool, Gerber):
                 return "fail"
                 return "fail"
             except Exception as ee:
             except Exception as ee:
                 log.debug("ToolPaint.paint_polygon_worker() Laser Lines --> %s" % str(ee))
                 log.debug("ToolPaint.paint_polygon_worker() Laser Lines --> %s" % str(ee))
-        elif paint_method == _("Combo"):
+        elif paint_method == 4:  # _("Combo")
             try:
             try:
                 self.app.inform.emit(_("Painting polygon with method: lines."))
                 self.app.inform.emit(_("Painting polygon with method: lines."))
                 cpoly = self.clear_polygon3(polyg,
                 cpoly = self.clear_polygon3(polyg,
@@ -1851,7 +1860,7 @@ class ToolPaint(AppTool, Gerber):
                 except Exception as e:
                 except Exception as e:
                     log.debug("Could not Paint the polygons. %s" % str(e))
                     log.debug("Could not Paint the polygons. %s" % str(e))
                     mssg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
                     mssg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
-                                                 "Or a different strategy of paint"), str(e))
+                                                 "Or a different method of Paint"), str(e))
                     self.app.inform.emit(mssg)
                     self.app.inform.emit(mssg)
                     continue
                     continue
 
 
@@ -2060,7 +2069,7 @@ class ToolPaint(AppTool, Gerber):
                 except Exception as e:
                 except Exception as e:
                     log.debug("Could not Paint the polygons. %s" % str(e))
                     log.debug("Could not Paint the polygons. %s" % str(e))
                     msg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
                     msg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
-                                                "Or a different strategy of paint"), str(e))
+                                                "Or a different method of Paint"), str(e))
                     self.app.inform.emit(msg)
                     self.app.inform.emit(msg)
                     continue
                     continue
 
 
@@ -2173,18 +2182,18 @@ class ToolPaint(AppTool, Gerber):
             proc.done()
             proc.done()
 
 
             if ret == 'fail':
             if ret == 'fail':
-                self.app.inform.emit('[ERROR] %s' % _("Paint failed."))
+                self.app.inform.emit('[ERROR] %s' % _("Failed."))
                 return
                 return
 
 
             # focus on Properties Tab
             # focus on Properties Tab
             # self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
             # self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
 
 
-            self.app.inform.emit('[success] %s' % _("Paint Done."))
+            self.app.inform.emit('[success] %s' % _("Done."))
 
 
         # Promise object with the new name
         # Promise object with the new name
         self.app.collection.promise(name)
         self.app.collection.promise(name)
 
 
-        proc = self.app.proc_container.new(_("Painting..."))
+        proc = self.app.proc_container.new(_("Painting ..."))
 
 
         if run_threaded:
         if run_threaded:
             # Background
             # Background
@@ -2234,7 +2243,9 @@ class ToolPaint(AppTool, Gerber):
             self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Normal painting polygon task started.")))
             self.app.inform.emit('%s %s' % (_("Paint Tool."), _("Normal painting polygon task started.")))
 
 
         if inside_pt and poly_list is None:
         if inside_pt and poly_list is None:
-            polygon_list = [self.find_polygon(point=inside_pt, geoset=obj.solid_geometry)]
+            polygon_list = self.find_polygon(point=inside_pt, geoset=obj.solid_geometry)
+            if polygon_list:
+                polygon_list = [polygon_list]
         elif (inside_pt is None and poly_list) or (inside_pt and poly_list):
         elif (inside_pt is None and poly_list) or (inside_pt and poly_list):
             polygon_list = poly_list
             polygon_list = poly_list
         else:
         else:
@@ -2244,7 +2255,7 @@ class ToolPaint(AppTool, Gerber):
         if polygon_list is None:
         if polygon_list is None:
             self.app.log.warning('No polygon found.')
             self.app.log.warning('No polygon found.')
             self.app.inform.emit('[WARNING] %s' % _('No polygon found.'))
             self.app.inform.emit('[WARNING] %s' % _('No polygon found.'))
-            return
+            return "fail"
 
 
         self.paint_geo(obj, polygon_list, tooldia=tooldia, order=order, method=method, outname=outname,
         self.paint_geo(obj, polygon_list, tooldia=tooldia, order=order, method=method, outname=outname,
                        tools_storage=tools_storage, plot=plot, run_threaded=run_threaded)
                        tools_storage=tools_storage, plot=plot, run_threaded=run_threaded)
@@ -2616,7 +2627,12 @@ class ToolPaint(AppTool, Gerber):
         """
         """
         tool_from_db = deepcopy(tool)
         tool_from_db = deepcopy(tool)
 
 
-        if tool['data']['tool_target'] != _("Paint"):
+        if tool['data']['tool_target'] not in [0, 4]:   # [General, Paint]
+            for idx in range(self.app.ui.plot_tab_area.count()):
+                if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
+                    wdg = self.app.ui.plot_tab_area.widget(idx)
+                    wdg.deleteLater()
+                    self.app.ui.plot_tab_area.removeTab(idx)
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             self.app.inform.emit('[ERROR_NOTCL] %s' % _("Selected tool can't be used here. Pick another."))
             return
             return
 
 
@@ -2664,7 +2680,7 @@ class ToolPaint(AppTool, Gerber):
             max_uid = max(tool_uid_list)
             max_uid = max(tool_uid_list)
         tooluid = max_uid + 1
         tooluid = max_uid + 1
 
 
-        tooldia = float('%.*f' % (self.decimals, tooldia))
+        tooldia = self.app.dec_format(tooldia, self.decimals)
 
 
         tool_dias = []
         tool_dias = []
         for k, v in self.paint_tools.items():
         for k, v in self.paint_tools.items():
@@ -2715,7 +2731,9 @@ class ToolPaint(AppTool, Gerber):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
             if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab)
                 break
                 break
-        self.app.on_tools_database(source='paint')
+        ret_val = self.app.on_tools_database(source='paint')
+        if ret_val == 'fail':
+            return
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ok_to_add = True
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.buttons_frame.hide()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
         self.app.tools_db_tab.ui.add_tool_from_db.show()
@@ -2824,7 +2842,7 @@ class PaintUI:
               "this function will not be able to create painting geometry.")
               "this function will not be able to create painting geometry.")
         )
         )
         self.tools_table.horizontalHeaderItem(1).setToolTip(
         self.tools_table.horizontalHeaderItem(1).setToolTip(
-            _("Tool Diameter. It's value (in current FlatCAM units) \n"
+            _("Tool Diameter. Its value\n"
               "is the cut width into the material."))
               "is the cut width into the material."))
 
 
         self.tools_table.horizontalHeaderItem(2).setToolTip(
         self.tools_table.horizontalHeaderItem(2).setToolTip(
@@ -2887,7 +2905,7 @@ class PaintUI:
         )
         )
         self.new_tooldia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.new_tooldia_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.new_tooldia_entry.set_precision(self.decimals)
         self.new_tooldia_entry.set_precision(self.decimals)
-        self.new_tooldia_entry.set_range(0.000, 9999.9999)
+        self.new_tooldia_entry.set_range(0.000, 10000.0000)
         self.new_tooldia_entry.setObjectName('p_tool_dia')
         self.new_tooldia_entry.setObjectName('p_tool_dia')
 
 
         self.grid3.addWidget(self.new_tooldia_lbl, 2, 0)
         self.grid3.addWidget(self.new_tooldia_lbl, 2, 0)
@@ -2927,7 +2945,7 @@ class PaintUI:
         self.deltool_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/trash16.png'))
         self.deltool_btn.setIcon(QtGui.QIcon(self.app.resource_location + '/trash16.png'))
         self.deltool_btn.setToolTip(
         self.deltool_btn.setToolTip(
             _("Delete a selection of tools in the Tool Table\n"
             _("Delete a selection of tools in the Tool Table\n"
-              "by first selecting a row(s) in the Tool Table.")
+              "by first selecting a row in the Tool Table.")
         )
         )
         self.grid3.addWidget(self.deltool_btn, 9, 0, 1, 2)
         self.grid3.addWidget(self.deltool_btn, 9, 0, 1, 2)
 
 
@@ -2956,8 +2974,8 @@ class PaintUI:
         ovlabel.setToolTip(
         ovlabel.setToolTip(
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
             _("How much (percentage) of the tool width to overlap each tool pass.\n"
               "Adjust the value starting with lower values\n"
               "Adjust the value starting with lower values\n"
-              "and increasing it if areas that should be painted are still \n"
-              "not painted.\n"
+              "and increasing it if areas that should be processed are still \n"
+              "not processed.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Lower values = faster processing, faster execution on CNC.\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "Higher values = slow processing and slow execution on CNC\n"
               "due of too many paths.")
               "due of too many paths.")
@@ -2981,7 +2999,7 @@ class PaintUI:
         )
         )
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.offset_entry.set_precision(self.decimals)
         self.offset_entry.set_precision(self.decimals)
-        self.offset_entry.set_range(-9999.9999, 9999.9999)
+        self.offset_entry.set_range(-10000.0000, 10000.0000)
         self.offset_entry.setObjectName('p_offset')
         self.offset_entry.setObjectName('p_offset')
 
 
         grid4.addWidget(self.offset_label, 2, 0)
         grid4.addWidget(self.offset_label, 2, 0)
@@ -2999,19 +3017,8 @@ class PaintUI:
               "- Combo: In case of failure a new method will be picked from the above\n"
               "- Combo: In case of failure a new method will be picked from the above\n"
               "in the order specified.")
               "in the order specified.")
         )
         )
-        # self.paintmethod_combo = RadioSet([
-        #     {"label": _("Standard"), "value": "standard"},
-        #     {"label": _("Seed-based"), "value": _("Seed")},
-        #     {"label": _("Straight lines"), "value": _("Lines")},
-        #     {"label": _("Laser lines"), "value": _("Laser_lines")},
-        #     {"label": _("Combo"), "value": _("Combo")}
-        # ], orientation='vertical', stretch=False)
-
-        # for choice in self.paintmethod_combo.choices:
-        #     if choice['value'] == _("Laser_lines"):
-        #         choice["radio"].setEnabled(False)
-
-        self.paintmethod_combo = FCComboBox()
+
+        self.paintmethod_combo = FCComboBox2()
         self.paintmethod_combo.addItems(
         self.paintmethod_combo.addItems(
             [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
             [_("Standard"), _("Seed"), _("Lines"), _("Laser_lines"), _("Combo")]
         )
         )
@@ -3070,11 +3077,11 @@ class PaintUI:
         self.rest_cb.setObjectName('p_rest_machining')
         self.rest_cb.setObjectName('p_rest_machining')
         self.rest_cb.setToolTip(
         self.rest_cb.setToolTip(
             _("If checked, use 'rest machining'.\n"
             _("If checked, use 'rest machining'.\n"
-              "Basically it will clear copper outside PCB features,\n"
+              "Basically it will process copper outside PCB features,\n"
               "using the biggest tool and continue with the next tools,\n"
               "using the biggest tool and continue with the next tools,\n"
-              "from bigger to smaller, to clear areas of copper that\n"
-              "could not be cleared by previous tool, until there is\n"
-              "no more copper to clear or there are no more tools.\n\n"
+              "from bigger to smaller, to process the copper features that\n"
+              "could not be processed by previous tool, until there is\n"
+              "nothing left to process or there are no more tools.\n\n"
               "If not checked, use the standard algorithm.")
               "If not checked, use the standard algorithm.")
         )
         )
         grid4.addWidget(self.rest_cb, 16, 0, 1, 2)
         grid4.addWidget(self.rest_cb, 16, 0, 1, 2)
@@ -3088,7 +3095,7 @@ class PaintUI:
         )
         )
         self.rest_offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.rest_offset_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.rest_offset_entry.set_precision(self.decimals)
         self.rest_offset_entry.set_precision(self.decimals)
-        self.rest_offset_entry.set_range(-9999.9999, 9999.9999)
+        self.rest_offset_entry.set_range(-10000.0000, 10000.0000)
 
 
         grid4.addWidget(self.rest_offset_label, 17, 0)
         grid4.addWidget(self.rest_offset_label, 17, 0)
         grid4.addWidget(self.rest_offset_entry, 17, 1)
         grid4.addWidget(self.rest_offset_entry, 17, 1)
@@ -3104,58 +3111,36 @@ class PaintUI:
               "- 'Reference Object' - will process the area specified by another object.")
               "- 'Reference Object' - will process the area specified by another object.")
         )
         )
 
 
-        # grid3 = QtWidgets.QGridLayout()
-        # self.selectmethod_combo = RadioSet([
-        #     {"label": _("Polygon Selection"), "value": "single"},
-        #     {"label": _("Area Selection"), "value": "area"},
-        #     {"label": _("All Polygons"), "value": "all"},
-        #     {"label": _("Reference Object"), "value": "ref"}
-        # ], orientation='vertical', stretch=False)
-        # self.selectmethod_combo.setObjectName('p_selection')
-        # self.selectmethod_combo.setToolTip(
-        #     _("How to select Polygons to be painted.\n"
-        #       "- 'Polygon Selection' - left mouse click to add/remove polygons to be painted.\n"
-        #       "- 'Area Selection' - left mouse click to start selection of the area to be painted.\n"
-        #       "Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple areas.\n"
-        #       "- 'All Polygons' - the Paint will start after click.\n"
-        #       "- 'Reference Object' - will do non copper clearing within the area\n"
-        #       "specified by another object.")
-        # )
-
-        self.selectmethod_combo = FCComboBox()
+        self.selectmethod_combo = FCComboBox2()
         self.selectmethod_combo.addItems(
         self.selectmethod_combo.addItems(
-            [_("Polygon Selection"), _("Area Selection"), _("All"), _("Reference Object")]
+            [_("All"), _("Polygon Selection"), _("Area Selection"), _("Reference Object")]
         )
         )
         self.selectmethod_combo.setObjectName('p_selection')
         self.selectmethod_combo.setObjectName('p_selection')
 
 
         grid4.addWidget(selectlabel, 18, 0)
         grid4.addWidget(selectlabel, 18, 0)
         grid4.addWidget(self.selectmethod_combo, 18, 1)
         grid4.addWidget(self.selectmethod_combo, 18, 1)
 
 
-        form1 = QtWidgets.QFormLayout()
-        grid4.addLayout(form1, 20, 0, 1, 2)
-
-        self.reference_type_label = FCLabel('%s:' % _("Ref. Type"))
+        # Type of Reference Object
+        self.reference_type_label = FCLabel('%s:' % _("Type"))
         self.reference_type_label.setToolTip(
         self.reference_type_label.setToolTip(
             _("The type of FlatCAM object to be used as paint reference.\n"
             _("The type of FlatCAM object to be used as paint reference.\n"
               "It can be Gerber, Excellon or Geometry.")
               "It can be Gerber, Excellon or Geometry.")
         )
         )
-        self.reference_type_combo = FCComboBox()
+        self.reference_type_combo = FCComboBox2()
         self.reference_type_combo.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
         self.reference_type_combo.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
 
 
-        form1.addRow(self.reference_type_label, self.reference_type_combo)
+        grid4.addWidget(self.reference_type_label, 20, 0)
+        grid4.addWidget(self.reference_type_combo, 20, 1)
 
 
-        self.reference_combo_label = FCLabel('%s:' % _("Ref. Object"))
-        self.reference_combo_label.setToolTip(
-            _("The FlatCAM object to be used as non copper clearing reference.")
-        )
+        # Reference Object
         self.reference_combo = FCComboBox()
         self.reference_combo = FCComboBox()
         self.reference_combo.setModel(self.app.collection)
         self.reference_combo.setModel(self.app.collection)
         self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
         self.reference_combo.is_last = True
         self.reference_combo.is_last = True
-        form1.addRow(self.reference_combo_label, self.reference_combo)
+
+        grid4.addWidget(self.reference_combo, 22, 0, 1, 2)
 
 
         self.reference_combo.hide()
         self.reference_combo.hide()
-        self.reference_combo_label.hide()
         self.reference_type_combo.hide()
         self.reference_type_combo.hide()
         self.reference_type_label.hide()
         self.reference_type_label.hide()
 
 
@@ -3168,8 +3153,8 @@ class PaintUI:
         self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
         self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
                                           {'label': _("Polygon"), 'value': 'polygon'}])
                                           {'label': _("Polygon"), 'value': 'polygon'}])
 
 
-        grid4.addWidget(self.area_shape_label, 21, 0)
-        grid4.addWidget(self.area_shape_radio, 21, 1)
+        grid4.addWidget(self.area_shape_label, 24, 0)
+        grid4.addWidget(self.area_shape_radio, 24, 1)
 
 
         self.area_shape_label.hide()
         self.area_shape_label.hide()
         self.area_shape_radio.hide()
         self.area_shape_radio.hide()
@@ -3229,31 +3214,29 @@ class PaintUI:
     def on_selection(self):
     def on_selection(self):
         sel_combo = self.selectmethod_combo.get_value()
         sel_combo = self.selectmethod_combo.get_value()
 
 
-        if sel_combo == _("Reference Object"):
+        if sel_combo == 3:  # _("Reference Object")
             self.reference_combo.show()
             self.reference_combo.show()
-            self.reference_combo_label.show()
             self.reference_type_combo.show()
             self.reference_type_combo.show()
             self.reference_type_label.show()
             self.reference_type_label.show()
         else:
         else:
             self.reference_combo.hide()
             self.reference_combo.hide()
-            self.reference_combo_label.hide()
             self.reference_type_combo.hide()
             self.reference_type_combo.hide()
             self.reference_type_label.hide()
             self.reference_type_label.hide()
 
 
-        if sel_combo == _("Polygon Selection"):
+        if sel_combo == 1:  # _("Polygon Selection")
             # disable rest-machining for single polygon painting
             # disable rest-machining for single polygon painting
             # self.ui.rest_cb.set_value(False)
             # self.ui.rest_cb.set_value(False)
             # self.ui.rest_cb.setDisabled(True)
             # self.ui.rest_cb.setDisabled(True)
             pass
             pass
 
 
-        if sel_combo == _("Area Selection"):
+        if sel_combo == 2:  # _("Area Selection") index 2 in combobox (FCComboBox2() returns index instead of text)
             # disable rest-machining for area painting
             # disable rest-machining for area painting
             # self.ui.rest_cb.set_value(False)
             # self.ui.rest_cb.set_value(False)
             # self.ui.rest_cb.setDisabled(True)
             # self.ui.rest_cb.setDisabled(True)
 
 
             self.area_shape_label.show()
             self.area_shape_label.show()
             self.area_shape_radio.show()
             self.area_shape_radio.show()
-        else:
+        else:   # All = index 0 in combobox
             self.new_tooldia_entry.setDisabled(False)
             self.new_tooldia_entry.setDisabled(False)
             self.add_newtool_button.setDisabled(False)
             self.add_newtool_button.setDisabled(False)
             self.deltool_btn.setDisabled(False)
             self.deltool_btn.setDisabled(False)

+ 28 - 29
appTools/ToolPanelize.py

@@ -8,7 +8,8 @@
 from PyQt5 import QtWidgets, QtGui, QtCore
 from PyQt5 import QtWidgets, QtGui, QtCore
 from appTool import AppTool
 from appTool import AppTool
 
 
-from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection, FCComboBox
+from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection, FCComboBox, \
+    FCButton, FCLabel
 from camlib import grace
 from camlib import grace
 
 
 from copy import deepcopy
 from copy import deepcopy
@@ -602,24 +603,21 @@ class Panelize(AppTool):
                         panel_type, self.outname, job_init_geometry, plot=True, autoselected=True)
                         panel_type, self.outname, job_init_geometry, plot=True, autoselected=True)
 
 
         if self.constrain_flag is False:
         if self.constrain_flag is False:
-            self.app.inform.emit('[success] %s' % _("Panel done..."))
+            self.app.inform.emit('[success] %s' % _("Done."))
         else:
         else:
             self.constrain_flag = False
             self.constrain_flag = False
             self.app.inform.emit(_("{text} Too big for the constrain area. "
             self.app.inform.emit(_("{text} Too big for the constrain area. "
                                    "Final panel has {col} columns and {row} rows").format(
                                    "Final panel has {col} columns and {row} rows").format(
                 text='[WARNING] ', col=columns, row=rows))
                 text='[WARNING] ', col=columns, row=rows))
 
 
-        proc = self.app.proc_container.new(_("Working..."))
-
         def job_thread(app_obj):
         def job_thread(app_obj):
-            try:
-                panelize_worker()
-                app_obj.inform.emit('[success] %s' % _("Panel created successfully."))
-            except Exception as ee:
-                proc.done()
-                log.debug(str(ee))
-                return
-            proc.done()
+            with self.app.proc_container.new(_("Working ...")):
+                try:
+                    panelize_worker()
+                    app_obj.inform.emit('[success] %s' % _("Panel created successfully."))
+                except Exception as ee:
+                    log.debug(str(ee))
+                    return
 
 
         self.app.collection.promise(self.outname)
         self.app.collection.promise(self.outname)
         self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
         self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
@@ -639,7 +637,7 @@ class PanelizeUI:
         self.layout = layout
         self.layout = layout
 
 
         # ## Title
         # ## Title
-        title_label = QtWidgets.QLabel("%s" % self.toolName)
+        title_label = FCLabel("%s" % self.toolName)
         title_label.setStyleSheet("""
         title_label.setStyleSheet("""
                                 QLabel
                                 QLabel
                                 {
                                 {
@@ -649,7 +647,7 @@ class PanelizeUI:
                                 """)
                                 """)
         self.layout.addWidget(title_label)
         self.layout.addWidget(title_label)
 
 
-        self.object_label = QtWidgets.QLabel('<b>%s:</b>' % _("Source Object"))
+        self.object_label = FCLabel('<b>%s:</b>' % _("Source Object"))
         self.object_label.setToolTip(
         self.object_label.setToolTip(
             _("Specify the type of object to be panelized\n"
             _("Specify the type of object to be panelized\n"
               "It can be of type: Gerber, Excellon or Geometry.\n"
               "It can be of type: Gerber, Excellon or Geometry.\n"
@@ -673,7 +671,7 @@ class PanelizeUI:
         self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
         self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
         self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
         self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
 
 
-        self.type_object_label = QtWidgets.QLabel('%s:' % _("Object Type"))
+        self.type_object_label = FCLabel('%s:' % _("Object Type"))
 
 
         form_layout_0.addRow(self.type_object_label, self.type_obj_combo)
         form_layout_0.addRow(self.type_object_label, self.type_obj_combo)
 
 
@@ -696,7 +694,7 @@ class PanelizeUI:
         # Type of box Panel object
         # Type of box Panel object
         self.reference_radio = RadioSet([{'label': _('Object'), 'value': 'object'},
         self.reference_radio = RadioSet([{'label': _('Object'), 'value': 'object'},
                                          {'label': _('Bounding Box'), 'value': 'bbox'}])
                                          {'label': _('Bounding Box'), 'value': 'bbox'}])
-        self.box_label = QtWidgets.QLabel("<b>%s:</b>" % _("Penelization Reference"))
+        self.box_label = FCLabel("<b>%s:</b>" % _("Penelization Reference"))
         self.box_label.setToolTip(
         self.box_label.setToolTip(
             _("Choose the reference for panelization:\n"
             _("Choose the reference for panelization:\n"
               "- Object = the bounding box of a different object\n"
               "- Object = the bounding box of a different object\n"
@@ -719,7 +717,7 @@ class PanelizeUI:
         self.type_box_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
         self.type_box_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
         self.type_box_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
         self.type_box_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
 
 
-        self.type_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Type"))
+        self.type_box_combo_label = FCLabel('%s:' % _("Box Type"))
         self.type_box_combo_label.setToolTip(
         self.type_box_combo_label.setToolTip(
             _("Specify the type of object to be used as an container for\n"
             _("Specify the type of object to be used as an container for\n"
               "panelization. It can be: Gerber or Geometry type.\n"
               "panelization. It can be: Gerber or Geometry type.\n"
@@ -745,7 +743,7 @@ class PanelizeUI:
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         form_layout.addRow(separator_line)
         form_layout.addRow(separator_line)
 
 
-        panel_data_label = QtWidgets.QLabel("<b>%s:</b>" % _("Panel Data"))
+        panel_data_label = FCLabel("<b>%s:</b>" % _("Panel Data"))
         panel_data_label.setToolTip(
         panel_data_label.setToolTip(
             _("This informations will shape the resulting panel.\n"
             _("This informations will shape the resulting panel.\n"
               "The number of rows and columns will set how many\n"
               "The number of rows and columns will set how many\n"
@@ -761,7 +759,7 @@ class PanelizeUI:
         self.spacing_columns.set_range(0, 9999)
         self.spacing_columns.set_range(0, 9999)
         self.spacing_columns.set_precision(4)
         self.spacing_columns.set_precision(4)
 
 
-        self.spacing_columns_label = QtWidgets.QLabel('%s:' % _("Spacing cols"))
+        self.spacing_columns_label = FCLabel('%s:' % _("Spacing cols"))
         self.spacing_columns_label.setToolTip(
         self.spacing_columns_label.setToolTip(
             _("Spacing between columns of the desired panel.\n"
             _("Spacing between columns of the desired panel.\n"
               "In current units.")
               "In current units.")
@@ -773,7 +771,7 @@ class PanelizeUI:
         self.spacing_rows.set_range(0, 9999)
         self.spacing_rows.set_range(0, 9999)
         self.spacing_rows.set_precision(4)
         self.spacing_rows.set_precision(4)
 
 
-        self.spacing_rows_label = QtWidgets.QLabel('%s:' % _("Spacing rows"))
+        self.spacing_rows_label = FCLabel('%s:' % _("Spacing rows"))
         self.spacing_rows_label.setToolTip(
         self.spacing_rows_label.setToolTip(
             _("Spacing between rows of the desired panel.\n"
             _("Spacing between rows of the desired panel.\n"
               "In current units.")
               "In current units.")
@@ -784,7 +782,7 @@ class PanelizeUI:
         self.columns = FCSpinner(callback=self.confirmation_message_int)
         self.columns = FCSpinner(callback=self.confirmation_message_int)
         self.columns.set_range(0, 9999)
         self.columns.set_range(0, 9999)
 
 
-        self.columns_label = QtWidgets.QLabel('%s:' % _("Columns"))
+        self.columns_label = FCLabel('%s:' % _("Columns"))
         self.columns_label.setToolTip(
         self.columns_label.setToolTip(
             _("Number of columns of the desired panel")
             _("Number of columns of the desired panel")
         )
         )
@@ -794,7 +792,7 @@ class PanelizeUI:
         self.rows = FCSpinner(callback=self.confirmation_message_int)
         self.rows = FCSpinner(callback=self.confirmation_message_int)
         self.rows.set_range(0, 9999)
         self.rows.set_range(0, 9999)
 
 
-        self.rows_label = QtWidgets.QLabel('%s:' % _("Rows"))
+        self.rows_label = FCLabel('%s:' % _("Rows"))
         self.rows_label.setToolTip(
         self.rows_label.setToolTip(
             _("Number of rows of the desired panel")
             _("Number of rows of the desired panel")
         )
         )
@@ -808,11 +806,11 @@ class PanelizeUI:
         # Type of resulting Panel object
         # Type of resulting Panel object
         self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'},
         self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'},
                                           {'label': _('Geo'), 'value': 'geometry'}])
                                           {'label': _('Geo'), 'value': 'geometry'}])
-        self.panel_type_label = QtWidgets.QLabel("<b>%s:</b>" % _("Panel Type"))
+        self.panel_type_label = FCLabel("<b>%s:</b>" % _("Panel Type"))
         self.panel_type_label.setToolTip(
         self.panel_type_label.setToolTip(
             _("Choose the type of object for the panel object:\n"
             _("Choose the type of object for the panel object:\n"
-              "- Geometry\n"
-              "- Gerber")
+              "- Gerber\n"
+              "- Geometry")
         )
         )
         form_layout.addRow(self.panel_type_label)
         form_layout.addRow(self.panel_type_label)
         form_layout.addRow(self.panel_type_radio)
         form_layout.addRow(self.panel_type_radio)
@@ -842,7 +840,7 @@ class PanelizeUI:
         self.x_width_entry.set_precision(4)
         self.x_width_entry.set_precision(4)
         self.x_width_entry.set_range(0, 9999)
         self.x_width_entry.set_range(0, 9999)
 
 
-        self.x_width_lbl = QtWidgets.QLabel('%s:' % _("Width (DX)"))
+        self.x_width_lbl = FCLabel('%s:' % _("Width (DX)"))
         self.x_width_lbl.setToolTip(
         self.x_width_lbl.setToolTip(
             _("The width (DX) within which the panel must fit.\n"
             _("The width (DX) within which the panel must fit.\n"
               "In current units.")
               "In current units.")
@@ -853,7 +851,7 @@ class PanelizeUI:
         self.y_height_entry.set_range(0, 9999)
         self.y_height_entry.set_range(0, 9999)
         self.y_height_entry.set_precision(4)
         self.y_height_entry.set_precision(4)
 
 
-        self.y_height_lbl = QtWidgets.QLabel('%s:' % _("Height (DY)"))
+        self.y_height_lbl = FCLabel('%s:' % _("Height (DY)"))
         self.y_height_lbl.setToolTip(
         self.y_height_lbl.setToolTip(
             _("The height (DY)within which the panel must fit.\n"
             _("The height (DY)within which the panel must fit.\n"
               "In current units.")
               "In current units.")
@@ -869,7 +867,8 @@ class PanelizeUI:
         form_layout.addRow(separator_line)
         form_layout.addRow(separator_line)
 
 
         # Buttons
         # Buttons
-        self.panelize_object_button = QtWidgets.QPushButton(_("Panelize Object"))
+        self.panelize_object_button = FCButton(_("Panelize Object"))
+        self.panelize_object_button.setIcon(QtGui.QIcon(self.app.resource_location + '/panelize16.png'))
         self.panelize_object_button.setToolTip(
         self.panelize_object_button.setToolTip(
             _("Panelize the specified object around the specified box.\n"
             _("Panelize the specified object around the specified box.\n"
               "In other words it creates multiple copies of the source object,\n"
               "In other words it creates multiple copies of the source object,\n"
@@ -886,7 +885,7 @@ class PanelizeUI:
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         # ## Reset Tool
         # ## Reset Tool
-        self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
+        self.reset_button = FCButton(_("Reset Tool"))
         self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
         self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
         self.reset_button.setToolTip(
         self.reset_button.setToolTip(
             _("Will reset the tool parameters.")
             _("Will reset the tool parameters.")

+ 5 - 5
appTools/ToolPcbWizard.py

@@ -333,7 +333,7 @@ class PcbWizard(AppTool):
 
 
         if excellon_fileobj is not None and excellon_fileobj != '':
         if excellon_fileobj is not None and excellon_fileobj != '':
             if self.process_finished:
             if self.process_finished:
-                with self.app.proc_container.new(_("Importing Excellon.")):
+                with self.app.proc_container.new('%s ...' % _("Importing")):
 
 
                     # Object name
                     # Object name
                     name = self.outname
                     name = self.outname
@@ -412,7 +412,7 @@ class WizardUI:
         self.tools_table.setVisible(False)
         self.tools_table.setVisible(False)
 
 
         self.layout.addWidget(FCLabel(""))
         self.layout.addWidget(FCLabel(""))
-        self.layout.addWidget(FCLabel("<b>%s:</b>" % _("Excellon format")))
+        self.layout.addWidget(FCLabel("<b>%s:</b>" % _("Excellon Format")))
         # Form Layout
         # Form Layout
         form_layout1 = QtWidgets.QFormLayout()
         form_layout1 = QtWidgets.QFormLayout()
         self.layout.addLayout(form_layout1)
         self.layout.addLayout(form_layout1)
@@ -450,8 +450,8 @@ class WizardUI:
         form_layout1.addRow(self.zeros_label, self.zeros_radio)
         form_layout1.addRow(self.zeros_label, self.zeros_radio)
 
 
         # Units type
         # Units type
-        self.units_radio = RadioSet([{'label': _('INCH'), 'value': 'INCH'},
-                                     {'label': _('MM'), 'value': 'METRIC'}])
+        self.units_radio = RadioSet([{'label': _('Inch'), 'value': 'INCH'},
+                                     {'label': _('mm'), 'value': 'METRIC'}])
         self.units_label = FCLabel("<b>%s:</b>" % _('Units'))
         self.units_label = FCLabel("<b>%s:</b>" % _('Units'))
         self.units_label.setToolTip(
         self.units_label.setToolTip(
             _("The type of units that the coordinates and tool\n"
             _("The type of units that the coordinates and tool\n"
@@ -463,7 +463,7 @@ class WizardUI:
 
 
         self.import_button = QtWidgets.QPushButton(_("Import Excellon"))
         self.import_button = QtWidgets.QPushButton(_("Import Excellon"))
         self.import_button.setToolTip(
         self.import_button.setToolTip(
-            _("Import in FlatCAM an Excellon file\n"
+            _("Import an Excellon file\n"
               "that store it's information's in 2 files.\n"
               "that store it's information's in 2 files.\n"
               "One usually has .DRL extension while\n"
               "One usually has .DRL extension while\n"
               "the other has .INF extension.")
               "the other has .INF extension.")

+ 2 - 2
appTools/ToolProperties.py

@@ -109,7 +109,7 @@ class Properties(AppTool):
     def properties(self):
     def properties(self):
         obj_list = self.app.collection.get_selected()
         obj_list = self.app.collection.get_selected()
         if not obj_list:
         if not obj_list:
-            self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object selected."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object is selected."))
             self.app.ui.notebook.setTabText(2, _("Tools"))
             self.app.ui.notebook.setTabText(2, _("Tools"))
             self.properties_frame.hide()
             self.properties_frame.hide()
             self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
             self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
@@ -192,7 +192,7 @@ class Properties(AppTool):
         self.treeWidget.addChild(obj_name, [obj.options['name']])
         self.treeWidget.addChild(obj_name, [obj.options['name']])
 
 
         def job_thread(obj_prop):
         def job_thread(obj_prop):
-            self.app.proc_container.new(_("Calculating dimensions ... Please wait."))
+            self.app.proc_container.new(_("Working ..."))
 
 
             length = 0.0
             length = 0.0
             width = 0.0
             width = 0.0

File diff suppressed because it is too large
+ 545 - 256
appTools/ToolPunchGerber.py


+ 63 - 56
appTools/ToolQRCode.py

@@ -163,47 +163,47 @@ class QRCode(AppTool):
         self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
         self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
         self.kr = self.app.plotcanvas.graph_event_connect('key_release', self.on_key_release)
         self.kr = self.app.plotcanvas.graph_event_connect('key_release', self.on_key_release)
 
 
-        self.proc = self.app.proc_container.new('%s...' % _("Generating QRCode geometry"))
-
         def job_thread_qr(app_obj):
         def job_thread_qr(app_obj):
-            error_code = {
-                'L': qrcode.constants.ERROR_CORRECT_L,
-                'M': qrcode.constants.ERROR_CORRECT_M,
-                'Q': qrcode.constants.ERROR_CORRECT_Q,
-                'H': qrcode.constants.ERROR_CORRECT_H
-            }[self.ui.error_radio.get_value()]
-
-            qr = qrcode.QRCode(
-                version=self.ui.version_entry.get_value(),
-                error_correction=error_code,
-                box_size=self.ui.bsize_entry.get_value(),
-                border=self.ui.border_size_entry.get_value(),
-                image_factory=qrcode.image.svg.SvgFragmentImage
-            )
-            qr.add_data(text_data)
-            qr.make()
-
-            svg_file = BytesIO()
-            img = qr.make_image()
-            img.save(svg_file)
-
-            svg_text = StringIO(svg_file.getvalue().decode('UTF-8'))
-            svg_geometry = self.convert_svg_to_geo(svg_text, units=self.units)
-            self.qrcode_geometry = deepcopy(svg_geometry)
-
-            svg_geometry = unary_union(svg_geometry).buffer(0.0000001).buffer(-0.0000001)
-            self.qrcode_utility_geometry = svg_geometry
-
-            # make a bounding box of the QRCode geometry to help drawing the utility geometry in case it is too
-            # complicated
-            try:
-                a, b, c, d = self.qrcode_utility_geometry.bounds
-                self.box_poly = box(minx=a, miny=b, maxx=c, maxy=d)
-            except Exception as ee:
-                log.debug("QRCode.make() bounds error --> %s" % str(ee))
+            with self.app.proc_container.new('%s' % _("Working ...")) as self.proc:
+
+                error_code = {
+                    'L': qrcode.constants.ERROR_CORRECT_L,
+                    'M': qrcode.constants.ERROR_CORRECT_M,
+                    'Q': qrcode.constants.ERROR_CORRECT_Q,
+                    'H': qrcode.constants.ERROR_CORRECT_H
+                }[self.ui.error_radio.get_value()]
+
+                qr = qrcode.QRCode(
+                    version=self.ui.version_entry.get_value(),
+                    error_correction=error_code,
+                    box_size=self.ui.bsize_entry.get_value(),
+                    border=self.ui.border_size_entry.get_value(),
+                    image_factory=qrcode.image.svg.SvgFragmentImage
+                )
+                qr.add_data(text_data)
+                qr.make()
+
+                svg_file = BytesIO()
+                img = qr.make_image()
+                img.save(svg_file)
+
+                svg_text = StringIO(svg_file.getvalue().decode('UTF-8'))
+                svg_geometry = self.convert_svg_to_geo(svg_text, units=self.units)
+                self.qrcode_geometry = deepcopy(svg_geometry)
+
+                svg_geometry = unary_union(svg_geometry).buffer(0.0000001).buffer(-0.0000001)
+                self.qrcode_utility_geometry = svg_geometry
+
+                # make a bounding box of the QRCode geometry to help drawing the utility geometry in case it is too
+                # complicated
+                try:
+                    a, b, c, d = self.qrcode_utility_geometry.bounds
+                    self.box_poly = box(minx=a, miny=b, maxx=c, maxy=d)
+                except Exception as ee:
+                    log.debug("QRCode.make() bounds error --> %s" % str(ee))
 
 
-            app_obj.call_source = 'qrcode_tool'
-            app_obj.inform.emit(_("Click on the Destination point ..."))
+                app_obj.call_source = 'qrcode_tool'
+                app_obj.inform.emit(_("Click on the DESTINATION point ..."))
 
 
         self.app.worker_task.emit({'fcn': job_thread_qr, 'params': [self.app]})
         self.app.worker_task.emit({'fcn': job_thread_qr, 'params': [self.app]})
 
 
@@ -270,9 +270,11 @@ class QRCode(AppTool):
 
 
         # don't know if the condition is required since I already made sure above that the new_apid is a new one
         # don't know if the condition is required since I already made sure above that the new_apid is a new one
         if new_apid not in self.grb_object.apertures:
         if new_apid not in self.grb_object.apertures:
-            self.grb_object.apertures[new_apid] = {}
-            self.grb_object.apertures[new_apid]['geometry'] = []
-            self.grb_object.apertures[new_apid]['type'] = 'R'
+            self.grb_object.apertures[new_apid] = {
+                'type': 'R',
+                'geometry': []
+            }
+
             # TODO: HACK
             # TODO: HACK
             # I've artificially added 1% to the height and width because otherwise after loading the
             # I've artificially added 1% to the height and width because otherwise after loading the
             # exported file, it will not be correctly reconstructed (it will be made from multiple shapes instead of
             # exported file, it will not be correctly reconstructed (it will be made from multiple shapes instead of
@@ -282,15 +284,15 @@ class QRCode(AppTool):
             self.grb_object.apertures[new_apid]['size'] = deepcopy(math.sqrt(box_size ** 2 + box_size ** 2))
             self.grb_object.apertures[new_apid]['size'] = deepcopy(math.sqrt(box_size ** 2 + box_size ** 2))
 
 
         if '0' not in self.grb_object.apertures:
         if '0' not in self.grb_object.apertures:
-            self.grb_object.apertures['0'] = {}
-            self.grb_object.apertures['0']['geometry'] = []
-            self.grb_object.apertures['0']['type'] = 'REG'
-            self.grb_object.apertures['0']['size'] = 0.0
+            self.grb_object.apertures['0'] = {
+                'type': 'REG',
+                'size': 0.0,
+                'geometry': []
+            }
 
 
         # in case that the QRCode geometry is dropped onto a copper region (found in the '0' aperture)
         # in case that the QRCode geometry is dropped onto a copper region (found in the '0' aperture)
         # make sure that I place a cutout there
         # make sure that I place a cutout there
-        zero_elem = {}
-        zero_elem['clear'] = offset_mask_geo
+        zero_elem = {'clear': offset_mask_geo}
         self.grb_object.apertures['0']['geometry'].append(deepcopy(zero_elem))
         self.grb_object.apertures['0']['geometry'].append(deepcopy(zero_elem))
 
 
         try:
         try:
@@ -304,13 +306,13 @@ class QRCode(AppTool):
 
 
         try:
         try:
             for geo in self.qrcode_geometry:
             for geo in self.qrcode_geometry:
-                geo_elem = {}
-                geo_elem['solid'] = translate(geo, xoff=pos[0], yoff=pos[1])
-                geo_elem['follow'] = translate(geo.centroid, xoff=pos[0], yoff=pos[1])
+                geo_elem = {
+                    'solid': translate(geo, xoff=pos[0], yoff=pos[1]),
+                    'follow': translate(geo.centroid, xoff=pos[0], yoff=pos[1])
+                }
                 self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
                 self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
         except TypeError:
         except TypeError:
-            geo_elem = {}
-            geo_elem['solid'] = self.qrcode_geometry
+            geo_elem = {'solid': self.qrcode_geometry}
             self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
             self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
 
 
         # update the source file with the new geometry:
         # update the source file with the new geometry:
@@ -461,7 +463,7 @@ class QRCode(AppTool):
 
 
     def replot(self, obj):
     def replot(self, obj):
         def worker_task():
         def worker_task():
-            with self.app.proc_container.new('%s...' % _("Plotting")):
+            with self.app.proc_container.new('%s ...' % _("Plotting")):
                 obj.plot()
                 obj.plot()
 
 
         self.app.worker_task.emit({'fcn': worker_task, 'params': []})
         self.app.worker_task.emit({'fcn': worker_task, 'params': []})
@@ -519,7 +521,9 @@ class QRCode(AppTool):
                 directory=self.app.get_last_save_folder() + '/' + str(name) + '_png',
                 directory=self.app.get_last_save_folder() + '/' + str(name) + '_png',
                 ext_filter=_filter)
                 ext_filter=_filter)
         except TypeError:
         except TypeError:
-            filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export PNG"), ext_filter=_filter)
+            filename, _f = FCFileSaveDialog.get_saved_filename(
+                caption=_("Export PNG"),
+                ext_filter=_filter)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -566,7 +570,9 @@ class QRCode(AppTool):
                 directory=self.app.get_last_save_folder() + '/' + str(name) + '_svg',
                 directory=self.app.get_last_save_folder() + '/' + str(name) + '_svg',
                 ext_filter=_filter)
                 ext_filter=_filter)
         except TypeError:
         except TypeError:
-            filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export SVG"), ext_filter=_filter)
+            filename, _f = FCFileSaveDialog.get_saved_filename(
+                caption=_("Export SVG"),
+                ext_filter=_filter)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -888,6 +894,7 @@ class QRcodeUI:
 
 
         # ## Insert QRCode
         # ## Insert QRCode
         self.qrcode_button = QtWidgets.QPushButton(_("Insert QRCode"))
         self.qrcode_button = QtWidgets.QPushButton(_("Insert QRCode"))
+        self.qrcode_button.setIcon(QtGui.QIcon(self.app.resource_location + '/qrcode32.png'))
         self.qrcode_button.setToolTip(
         self.qrcode_button.setToolTip(
             _("Create the QRCode object.")
             _("Create the QRCode object.")
         )
         )

+ 63 - 56
appTools/ToolRulesCheck.py

@@ -631,16 +631,18 @@ class RulesCheck(AppTool):
                 copper_list = []
                 copper_list = []
                 copper_name_1 = self.ui.copper_t_object.currentText()
                 copper_name_1 = self.ui.copper_t_object.currentText()
                 if copper_name_1 != '' and self.ui.copper_t_cb.get_value():
                 if copper_name_1 != '' and self.ui.copper_t_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(copper_name_1)
-                    elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_1).apertures)
+                    elem_dict = {
+                        'name': deepcopy(copper_name_1),
+                        'apertures': deepcopy(app_obj.collection.get_by_name(copper_name_1).apertures)
+                    }
                     copper_list.append(elem_dict)
                     copper_list.append(elem_dict)
 
 
                 copper_name_2 = self.ui.copper_b_object.currentText()
                 copper_name_2 = self.ui.copper_b_object.currentText()
                 if copper_name_2 != '' and self.ui.copper_b_cb.get_value():
                 if copper_name_2 != '' and self.ui.copper_b_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(copper_name_2)
-                    elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_2).apertures)
+                    elem_dict = {
+                        'name': deepcopy(copper_name_2),
+                        'apertures': deepcopy(app_obj.collection.get_by_name(copper_name_2).apertures)
+                    }
                     copper_list.append(elem_dict)
                     copper_list.append(elem_dict)
 
 
                 trace_size = float(self.ui.trace_size_entry.get_value())
                 trace_size = float(self.ui.trace_size_entry.get_value())
@@ -664,7 +666,7 @@ class RulesCheck(AppTool):
 
 
                     if copper_t_obj != '':
                     if copper_t_obj != '':
                         copper_t_dict['name'] = deepcopy(copper_t_obj)
                         copper_t_dict['name'] = deepcopy(copper_t_obj)
-                        copper_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_t_obj).apertures)
+                        copper_t_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_t_obj).apertures)
 
 
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                                                                   args=(copper_t_dict,
                                                                   args=(copper_t_dict,
@@ -675,7 +677,7 @@ class RulesCheck(AppTool):
                     copper_b_dict = {}
                     copper_b_dict = {}
                     if copper_b_obj != '':
                     if copper_b_obj != '':
                         copper_b_dict['name'] = deepcopy(copper_b_obj)
                         copper_b_dict['name'] = deepcopy(copper_b_obj)
-                        copper_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_b_obj).apertures)
+                        copper_b_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_b_obj).apertures)
 
 
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                                                                   args=(copper_b_dict,
                                                                   args=(copper_b_dict,
@@ -683,7 +685,7 @@ class RulesCheck(AppTool):
                                                                         _("BOTTOM -> Copper to Copper clearance"))))
                                                                         _("BOTTOM -> Copper to Copper clearance"))))
 
 
                 if self.ui.copper_t_cb.get_value() is False and self.ui.copper_b_cb.get_value() is False:
                 if self.ui.copper_t_cb.get_value() is False and self.ui.copper_b_cb.get_value() is False:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Copper to Copper clearance"),
                         _("Copper to Copper clearance"),
                         _("At least one Gerber object has to be selected for this rule but none is selected.")))
                         _("At least one Gerber object has to be selected for this rule but none is selected.")))
                     return
                     return
@@ -697,29 +699,29 @@ class RulesCheck(AppTool):
                 copper_top = self.ui.copper_t_object.currentText()
                 copper_top = self.ui.copper_t_object.currentText()
                 if copper_top != '' and self.ui.copper_t_cb.get_value():
                 if copper_top != '' and self.ui.copper_t_cb.get_value():
                     top_dict['name'] = deepcopy(copper_top)
                     top_dict['name'] = deepcopy(copper_top)
-                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_top).apertures)
+                    top_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_top).apertures)
 
 
                 copper_bottom = self.ui.copper_b_object.currentText()
                 copper_bottom = self.ui.copper_b_object.currentText()
                 if copper_bottom != '' and self.ui.copper_b_cb.get_value():
                 if copper_bottom != '' and self.ui.copper_b_cb.get_value():
                     bottom_dict['name'] = deepcopy(copper_bottom)
                     bottom_dict['name'] = deepcopy(copper_bottom)
-                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_bottom).apertures)
+                    bottom_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_bottom).apertures)
 
 
                 copper_outline = self.ui.outline_object.currentText()
                 copper_outline = self.ui.outline_object.currentText()
                 if copper_outline != '' and self.ui.out_cb.get_value():
                 if copper_outline != '' and self.ui.out_cb.get_value():
                     outline_dict['name'] = deepcopy(copper_outline)
                     outline_dict['name'] = deepcopy(copper_outline)
-                    outline_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_outline).apertures)
+                    outline_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_outline).apertures)
 
 
                 try:
                 try:
                     copper_outline_clearance = float(self.ui.clearance_copper2ol_entry.get_value())
                     copper_outline_clearance = float(self.ui.clearance_copper2ol_entry.get_value())
                 except Exception as e:
                 except Exception as e:
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Copper to Outline clearance"),
                         _("Copper to Outline clearance"),
                         _("Value is not valid.")))
                         _("Value is not valid.")))
                     return
                     return
 
 
                 if not top_dict and not bottom_dict or not outline_dict:
                 if not top_dict and not bottom_dict or not outline_dict:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Copper to Outline clearance"),
                         _("Copper to Outline clearance"),
                         _("One of the copper Gerber objects or the Outline Gerber object is not valid.")))
                         _("One of the copper Gerber objects or the Outline Gerber object is not valid.")))
                     return
                     return
@@ -732,7 +734,7 @@ class RulesCheck(AppTool):
                 if outline_dict:
                 if outline_dict:
                     objs.append(outline_dict)
                     objs.append(outline_dict)
                 else:
                 else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Copper to Outline clearance"),
                         _("Copper to Outline clearance"),
                         _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
                         _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
                     return
                     return
@@ -750,7 +752,7 @@ class RulesCheck(AppTool):
                     silk_silk_clearance = float(self.ui.clearance_silk2silk_entry.get_value())
                     silk_silk_clearance = float(self.ui.clearance_silk2silk_entry.get_value())
                 except Exception as e:
                 except Exception as e:
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Silk clearance"),
                         _("Silk to Silk clearance"),
                         _("Value is not valid.")))
                         _("Value is not valid.")))
                     return
                     return
@@ -759,7 +761,7 @@ class RulesCheck(AppTool):
                     silk_obj = self.ui.ss_t_object.currentText()
                     silk_obj = self.ui.ss_t_object.currentText()
                     if silk_obj != '':
                     if silk_obj != '':
                         silk_dict['name'] = deepcopy(silk_obj)
                         silk_dict['name'] = deepcopy(silk_obj)
-                        silk_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_obj).apertures)
+                        silk_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(silk_obj).apertures)
 
 
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                                                                   args=(silk_dict,
                                                                   args=(silk_dict,
@@ -769,7 +771,7 @@ class RulesCheck(AppTool):
                     silk_obj = self.ui.ss_b_object.currentText()
                     silk_obj = self.ui.ss_b_object.currentText()
                     if silk_obj != '':
                     if silk_obj != '':
                         silk_dict['name'] = deepcopy(silk_obj)
                         silk_dict['name'] = deepcopy(silk_obj)
-                        silk_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_obj).apertures)
+                        silk_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(silk_obj).apertures)
 
 
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                                                                   args=(silk_dict,
                                                                   args=(silk_dict,
@@ -777,7 +779,7 @@ class RulesCheck(AppTool):
                                                                         _("BOTTOM -> Silk to Silk clearance"))))
                                                                         _("BOTTOM -> Silk to Silk clearance"))))
 
 
                 if self.ui.ss_t_cb.get_value() is False and self.ui.ss_b_cb.get_value() is False:
                 if self.ui.ss_t_cb.get_value() is False and self.ui.ss_b_cb.get_value() is False:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Silk clearance"),
                         _("Silk to Silk clearance"),
                         _("At least one Gerber object has to be selected for this rule but none is selected.")))
                         _("At least one Gerber object has to be selected for this rule but none is selected.")))
                     return
                     return
@@ -797,38 +799,38 @@ class RulesCheck(AppTool):
                 silk_top = self.ui.ss_t_object.currentText()
                 silk_top = self.ui.ss_t_object.currentText()
                 if silk_top != '' and self.ui.ss_t_cb.get_value():
                 if silk_top != '' and self.ui.ss_t_cb.get_value():
                     silk_t_dict['name'] = deepcopy(silk_top)
                     silk_t_dict['name'] = deepcopy(silk_top)
-                    silk_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_top).apertures)
+                    silk_t_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(silk_top).apertures)
                     top_ss = True
                     top_ss = True
 
 
                 silk_bottom = self.ui.ss_b_object.currentText()
                 silk_bottom = self.ui.ss_b_object.currentText()
                 if silk_bottom != '' and self.ui.ss_b_cb.get_value():
                 if silk_bottom != '' and self.ui.ss_b_cb.get_value():
                     silk_b_dict['name'] = deepcopy(silk_bottom)
                     silk_b_dict['name'] = deepcopy(silk_bottom)
-                    silk_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_bottom).apertures)
+                    silk_b_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(silk_bottom).apertures)
                     bottom_ss = True
                     bottom_ss = True
 
 
                 sm_top = self.ui.sm_t_object.currentText()
                 sm_top = self.ui.sm_t_object.currentText()
                 if sm_top != '' and self.ui.sm_t_cb.get_value():
                 if sm_top != '' and self.ui.sm_t_cb.get_value():
                     sm_t_dict['name'] = deepcopy(sm_top)
                     sm_t_dict['name'] = deepcopy(sm_top)
-                    sm_t_dict['apertures'] = deepcopy(self.app.collection.get_by_name(sm_top).apertures)
+                    sm_t_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(sm_top).apertures)
                     top_sm = True
                     top_sm = True
 
 
                 sm_bottom = self.ui.sm_b_object.currentText()
                 sm_bottom = self.ui.sm_b_object.currentText()
                 if sm_bottom != '' and self.ui.sm_b_cb.get_value():
                 if sm_bottom != '' and self.ui.sm_b_cb.get_value():
                     sm_b_dict['name'] = deepcopy(sm_bottom)
                     sm_b_dict['name'] = deepcopy(sm_bottom)
-                    sm_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(sm_bottom).apertures)
+                    sm_b_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(sm_bottom).apertures)
                     bottom_sm = True
                     bottom_sm = True
 
 
                 try:
                 try:
                     silk_sm_clearance = float(self.ui.clearance_silk2sm_entry.get_value())
                     silk_sm_clearance = float(self.ui.clearance_silk2sm_entry.get_value())
                 except Exception as e:
                 except Exception as e:
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Solder Mask Clearance"),
                         _("Silk to Solder Mask Clearance"),
                         _("Value is not valid.")))
                         _("Value is not valid.")))
                     return
                     return
 
 
                 if (not silk_t_dict and not silk_b_dict) or (not sm_t_dict and not sm_b_dict):
                 if (not silk_t_dict and not silk_b_dict) or (not sm_t_dict and not sm_b_dict):
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Solder Mask Clearance"),
                         _("Silk to Solder Mask Clearance"),
                         _("One or more of the Gerber objects is not valid.")))
                         _("One or more of the Gerber objects is not valid.")))
                     return
                     return
@@ -846,7 +848,7 @@ class RulesCheck(AppTool):
                                                                     silk_sm_clearance,
                                                                     silk_sm_clearance,
                                                                     _("BOTTOM -> Silk to Solder Mask Clearance"))))
                                                                     _("BOTTOM -> Silk to Solder Mask Clearance"))))
                 else:
                 else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Solder Mask Clearance"),
                         _("Silk to Solder Mask Clearance"),
                         _("Both Silk and Solder Mask Gerber objects has to be either both Top or both Bottom.")))
                         _("Both Silk and Solder Mask Gerber objects has to be either both Top or both Bottom.")))
                     return
                     return
@@ -860,29 +862,29 @@ class RulesCheck(AppTool):
                 silk_top = self.ui.ss_t_object.currentText()
                 silk_top = self.ui.ss_t_object.currentText()
                 if silk_top != '' and self.ui.ss_t_cb.get_value():
                 if silk_top != '' and self.ui.ss_t_cb.get_value():
                     top_dict['name'] = deepcopy(silk_top)
                     top_dict['name'] = deepcopy(silk_top)
-                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_top).apertures)
+                    top_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(silk_top).apertures)
 
 
                 silk_bottom = self.ui.ss_b_object.currentText()
                 silk_bottom = self.ui.ss_b_object.currentText()
                 if silk_bottom != '' and self.ui.ss_b_cb.get_value():
                 if silk_bottom != '' and self.ui.ss_b_cb.get_value():
                     bottom_dict['name'] = deepcopy(silk_bottom)
                     bottom_dict['name'] = deepcopy(silk_bottom)
-                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_bottom).apertures)
+                    bottom_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(silk_bottom).apertures)
 
 
                 copper_outline = self.ui.outline_object.currentText()
                 copper_outline = self.ui.outline_object.currentText()
                 if copper_outline != '' and self.ui.out_cb.get_value():
                 if copper_outline != '' and self.ui.out_cb.get_value():
                     outline_dict['name'] = deepcopy(copper_outline)
                     outline_dict['name'] = deepcopy(copper_outline)
-                    outline_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_outline).apertures)
+                    outline_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_outline).apertures)
 
 
                 try:
                 try:
                     copper_outline_clearance = float(self.ui.clearance_copper2ol_entry.get_value())
                     copper_outline_clearance = float(self.ui.clearance_copper2ol_entry.get_value())
                 except Exception as e:
                 except Exception as e:
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Outline Clearance"),
                         _("Silk to Outline Clearance"),
                         _("Value is not valid.")))
                         _("Value is not valid.")))
                     return
                     return
 
 
                 if not top_dict and not bottom_dict or not outline_dict:
                 if not top_dict and not bottom_dict or not outline_dict:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Outline Clearance"),
                         _("Silk to Outline Clearance"),
                         _("One of the Silk Gerber objects or the Outline Gerber object is not valid.")))
                         _("One of the Silk Gerber objects or the Outline Gerber object is not valid.")))
                     return
                     return
@@ -896,7 +898,7 @@ class RulesCheck(AppTool):
                 if outline_dict:
                 if outline_dict:
                     objs.append(outline_dict)
                     objs.append(outline_dict)
                 else:
                 else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Silk to Outline Clearance"),
                         _("Silk to Outline Clearance"),
                         _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
                         _("Outline Gerber object presence is mandatory for this rule but it is not selected.")))
                     return
                     return
@@ -914,7 +916,7 @@ class RulesCheck(AppTool):
                     sm_sm_clearance = float(self.ui.clearance_sm2sm_entry.get_value())
                     sm_sm_clearance = float(self.ui.clearance_sm2sm_entry.get_value())
                 except Exception as e:
                 except Exception as e:
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Minimum Solder Mask Sliver"),
                         _("Minimum Solder Mask Sliver"),
                         _("Value is not valid.")))
                         _("Value is not valid.")))
                     return
                     return
@@ -923,7 +925,7 @@ class RulesCheck(AppTool):
                     solder_obj = self.ui.sm_t_object.currentText()
                     solder_obj = self.ui.sm_t_object.currentText()
                     if solder_obj != '':
                     if solder_obj != '':
                         sm_dict['name'] = deepcopy(solder_obj)
                         sm_dict['name'] = deepcopy(solder_obj)
-                        sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures)
+                        sm_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(solder_obj).apertures)
 
 
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                                                                   args=(sm_dict,
                                                                   args=(sm_dict,
@@ -933,7 +935,7 @@ class RulesCheck(AppTool):
                     solder_obj = self.ui.sm_b_object.currentText()
                     solder_obj = self.ui.sm_b_object.currentText()
                     if solder_obj != '':
                     if solder_obj != '':
                         sm_dict['name'] = deepcopy(solder_obj)
                         sm_dict['name'] = deepcopy(solder_obj)
-                        sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures)
+                        sm_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(solder_obj).apertures)
 
 
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                         self.results.append(self.pool.apply_async(self.check_inside_gerber_clearance,
                                                                   args=(sm_dict,
                                                                   args=(sm_dict,
@@ -941,7 +943,7 @@ class RulesCheck(AppTool):
                                                                         _("BOTTOM -> Minimum Solder Mask Sliver"))))
                                                                         _("BOTTOM -> Minimum Solder Mask Sliver"))))
 
 
                 if self.ui.sm_t_cb.get_value() is False and self.ui.sm_b_cb.get_value() is False:
                 if self.ui.sm_t_cb.get_value() is False and self.ui.sm_b_cb.get_value() is False:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Minimum Solder Mask Sliver"),
                         _("Minimum Solder Mask Sliver"),
                         _("At least one Gerber object has to be selected for this rule but none is selected.")))
                         _("At least one Gerber object has to be selected for this rule but none is selected.")))
                     return
                     return
@@ -956,36 +958,36 @@ class RulesCheck(AppTool):
                 copper_top = self.ui.copper_t_object.currentText()
                 copper_top = self.ui.copper_t_object.currentText()
                 if copper_top != '' and self.ui.copper_t_cb.get_value():
                 if copper_top != '' and self.ui.copper_t_cb.get_value():
                     top_dict['name'] = deepcopy(copper_top)
                     top_dict['name'] = deepcopy(copper_top)
-                    top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_top).apertures)
+                    top_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_top).apertures)
 
 
                 copper_bottom = self.ui.copper_b_object.currentText()
                 copper_bottom = self.ui.copper_b_object.currentText()
                 if copper_bottom != '' and self.ui.copper_b_cb.get_value():
                 if copper_bottom != '' and self.ui.copper_b_cb.get_value():
                     bottom_dict['name'] = deepcopy(copper_bottom)
                     bottom_dict['name'] = deepcopy(copper_bottom)
-                    bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_bottom).apertures)
+                    bottom_dict['apertures'] = deepcopy(app_obj.collection.get_by_name(copper_bottom).apertures)
 
 
                 excellon_1 = self.ui.e1_object.currentText()
                 excellon_1 = self.ui.e1_object.currentText()
                 if excellon_1 != '' and self.ui.e1_cb.get_value():
                 if excellon_1 != '' and self.ui.e1_cb.get_value():
                     exc_1_dict['name'] = deepcopy(excellon_1)
                     exc_1_dict['name'] = deepcopy(excellon_1)
                     exc_1_dict['tools'] = deepcopy(
                     exc_1_dict['tools'] = deepcopy(
-                        self.app.collection.get_by_name(excellon_1).tools)
+                        app_obj.collection.get_by_name(excellon_1).tools)
 
 
                 excellon_2 = self.ui.e2_object.currentText()
                 excellon_2 = self.ui.e2_object.currentText()
                 if excellon_2 != '' and self.ui.e2_cb.get_value():
                 if excellon_2 != '' and self.ui.e2_cb.get_value():
                     exc_2_dict['name'] = deepcopy(excellon_2)
                     exc_2_dict['name'] = deepcopy(excellon_2)
                     exc_2_dict['tools'] = deepcopy(
                     exc_2_dict['tools'] = deepcopy(
-                        self.app.collection.get_by_name(excellon_2).tools)
+                        app_obj.collection.get_by_name(excellon_2).tools)
 
 
                 try:
                 try:
                     ring_val = float(self.ui.ring_integrity_entry.get_value())
                     ring_val = float(self.ui.ring_integrity_entry.get_value())
                 except Exception as e:
                 except Exception as e:
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
                     log.debug("RulesCheck.execute.worker_job() --> %s" % str(e))
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Minimum Annular Ring"),
                         _("Minimum Annular Ring"),
                         _("Value is not valid.")))
                         _("Value is not valid.")))
                     return
                     return
 
 
                 if (not top_dict and not bottom_dict) or (not exc_1_dict and not exc_2_dict):
                 if (not top_dict and not bottom_dict) or (not exc_1_dict and not exc_2_dict):
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Minimum Annular Ring"),
                         _("Minimum Annular Ring"),
                         _("One of the Copper Gerber objects or the Excellon objects is not valid.")))
                         _("One of the Copper Gerber objects or the Excellon objects is not valid.")))
                     return
                     return
@@ -1001,7 +1003,7 @@ class RulesCheck(AppTool):
                 elif exc_2_dict:
                 elif exc_2_dict:
                     objs.append(exc_2_dict)
                     objs.append(exc_2_dict)
                 else:
                 else:
-                    self.app.inform.emit('[ERROR_NOTCL] %s. %s' % (
+                    app_obj.inform.emit('[ERROR_NOTCL] %s. %s' % (
                         _("Minimum Annular Ring"),
                         _("Minimum Annular Ring"),
                         _("Excellon object presence is mandatory for this rule but none is selected.")))
                         _("Excellon object presence is mandatory for this rule but none is selected.")))
                     return
                     return
@@ -1016,16 +1018,18 @@ class RulesCheck(AppTool):
                 exc_list = []
                 exc_list = []
                 exc_name_1 = self.ui.e1_object.currentText()
                 exc_name_1 = self.ui.e1_object.currentText()
                 if exc_name_1 != '' and self.ui.e1_cb.get_value():
                 if exc_name_1 != '' and self.ui.e1_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_1)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
+                    elem_dict = {
+                        'name': deepcopy(exc_name_1),
+                        'tools': deepcopy(app_obj.collection.get_by_name(exc_name_1).tools)
+                    }
                     exc_list.append(elem_dict)
                     exc_list.append(elem_dict)
 
 
                 exc_name_2 = self.ui.e2_object.currentText()
                 exc_name_2 = self.ui.e2_object.currentText()
                 if exc_name_2 != '' and self.ui.e2_cb.get_value():
                 if exc_name_2 != '' and self.ui.e2_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_2)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
+                    elem_dict = {
+                        'name': deepcopy(exc_name_2),
+                        'tools': deepcopy(app_obj.collection.get_by_name(exc_name_2).tools)
+                    }
                     exc_list.append(elem_dict)
                     exc_list.append(elem_dict)
 
 
                 hole_clearance = float(self.ui.clearance_d2d_entry.get_value())
                 hole_clearance = float(self.ui.clearance_d2d_entry.get_value())
@@ -1036,16 +1040,18 @@ class RulesCheck(AppTool):
                 exc_list = []
                 exc_list = []
                 exc_name_1 = self.ui.e1_object.currentText()
                 exc_name_1 = self.ui.e1_object.currentText()
                 if exc_name_1 != '' and self.ui.e1_cb.get_value():
                 if exc_name_1 != '' and self.ui.e1_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_1)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
+                    elem_dict = {
+                        'name': deepcopy(exc_name_1),
+                        'tools': deepcopy(app_obj.collection.get_by_name(exc_name_1).tools)
+                    }
                     exc_list.append(elem_dict)
                     exc_list.append(elem_dict)
 
 
                 exc_name_2 = self.ui.e2_object.currentText()
                 exc_name_2 = self.ui.e2_object.currentText()
                 if exc_name_2 != '' and self.ui.e2_cb.get_value():
                 if exc_name_2 != '' and self.ui.e2_cb.get_value():
-                    elem_dict = {}
-                    elem_dict['name'] = deepcopy(exc_name_2)
-                    elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
+                    elem_dict = {
+                        'name': deepcopy(exc_name_2),
+                        'tools': deepcopy(app_obj.collection.get_by_name(exc_name_2).tools)
+                    }
                     exc_list.append(elem_dict)
                     exc_list.append(elem_dict)
 
 
                 drill_size = float(self.ui.drill_size_entry.get_value())
                 drill_size = float(self.ui.drill_size_entry.get_value())
@@ -1056,7 +1062,7 @@ class RulesCheck(AppTool):
                 output.append(p.get())
                 output.append(p.get())
 
 
             self.tool_finished.emit(output)
             self.tool_finished.emit(output)
-            self.app.proc_container.view.set_idle()
+            app_obj.proc_container.view.set_idle()
 
 
             log.debug("RuleCheck() finished")
             log.debug("RuleCheck() finished")
 
 
@@ -1601,6 +1607,7 @@ class RulesUI:
 
 
         # hlay_2.addStretch()
         # hlay_2.addStretch()
         self.run_button = FCButton(_("Run Rules Check"))
         self.run_button = FCButton(_("Run Rules Check"))
+        self.run_button.setIcon(QtGui.QIcon(self.app.resource_location + '/rules32.png'))
         self.run_button.setToolTip(
         self.run_button.setToolTip(
             _("Panelize the specified object around the specified box.\n"
             _("Panelize the specified object around the specified box.\n"
               "In other words it creates multiple copies of the source object,\n"
               "In other words it creates multiple copies of the source object,\n"

+ 3 - 0
appTools/ToolShell.py

@@ -74,6 +74,9 @@ class TermWidget(QWidget):
 
 
         self._delete_line.clicked.connect(self.on_delete_line_clicked)
         self._delete_line.clicked.connect(self.on_delete_line_clicked)
 
 
+    def command_line(self):
+        return self._edit
+
     def on_delete_line_clicked(self):
     def on_delete_line_clicked(self):
         self._edit.clear()
         self._edit.clear()
 
 

+ 30 - 28
appTools/ToolSolderPaste.py

@@ -7,8 +7,8 @@
 
 
 from appTool import AppTool
 from appTool import AppTool
 from appCommon.Common import LoudDict
 from appCommon.Common import LoudDict
-from appGUI.GUIElements import FCComboBox, FCEntry, FCTable, \
-    FCInputDialog, FCDoubleSpinner, FCSpinner, FCFileSaveDialog
+from appGUI.GUIElements import FCComboBox, FCEntry, FCTable, FCDoubleSpinner, FCSpinner, FCFileSaveDialog, \
+    FCInputSpinner
 from app_Main import log
 from app_Main import log
 from camlib import distance
 from camlib import distance
 from appEditors.AppTextEditor import AppTextEditor
 from appEditors.AppTextEditor import AppTextEditor
@@ -119,9 +119,9 @@ class SolderPaste(AppTool):
         AppTool.install(self, icon, separator, shortcut='Alt+K', **kwargs)
         AppTool.install(self, icon, separator, shortcut='Alt+K', **kwargs)
 
 
     def on_add_tool_by_key(self):
     def on_add_tool_by_key(self):
-        tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"),
-                                       text='%s:' % _('Enter a Tool Diameter'),
-                                       min=0.0000, max=99.9999, decimals=4)
+        tool_add_popup = FCInputSpinner(title='%s...' % _("New Tool"),
+                                        text='%s:' % _('Enter a Tool Diameter'),
+                                        min=0.0000, max=100.0000, decimals=self.decimals, step=0.1)
         tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
         tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png'))
 
 
         val, ok = tool_add_popup.get_value()
         val, ok = tool_add_popup.get_value()
@@ -300,7 +300,7 @@ class SolderPaste(AppTool):
                 if int(tooluid_key) == tooluid:
                 if int(tooluid_key) == tooluid:
                     self.set_form(deepcopy(tooluid_value['data']))
                     self.set_form(deepcopy(tooluid_value['data']))
         except Exception as e:
         except Exception as e:
-            log.debug("FlatCAMObj ---> update_ui() " + str(e))
+            log.debug("ToolSolderPaste ---> update_ui() " + str(e))
 
 
         self.ui_connect()
         self.ui_connect()
 
 
@@ -590,12 +590,12 @@ class SolderPaste(AppTool):
                     self.tooltable_tools.pop(t, None)
                     self.tooltable_tools.pop(t, None)
 
 
         except AttributeError:
         except AttributeError:
-            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Delete failed. Select a Nozzle tool to delete."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Delete failed. Select a tool to delete."))
             return
             return
         except Exception as e:
         except Exception as e:
             log.debug(str(e))
             log.debug(str(e))
 
 
-        self.app.inform.emit('[success] %s' % _("Nozzle tool(s) deleted from Tool Table."))
+        self.app.inform.emit('[success] %s' % _("Tools deleted from Tool Table."))
         self.build_ui()
         self.build_ui()
 
 
     def on_rmb_combo(self, pos, combo):
     def on_rmb_combo(self, pos, combo):
@@ -667,7 +667,7 @@ class SolderPaste(AppTool):
         :param use_thread: use thread, True or False
         :param use_thread: use thread, True or False
         :return: a Geometry type object
         :return: a Geometry type object
         """
         """
-        proc = self.app.proc_container.new(_("Creating Solder Paste dispensing geometry."))
+        proc = self.app.proc_container.new(_("Working ..."))
         obj = work_object
         obj = work_object
 
 
         # Sort tools in descending order
         # Sort tools in descending order
@@ -806,7 +806,8 @@ class SolderPaste(AppTool):
                         if not geo_obj.tools[tooluid_key]['solid_geometry']:
                         if not geo_obj.tools[tooluid_key]['solid_geometry']:
                             a += 1
                             a += 1
                     if a == len(geo_obj.tools):
                     if a == len(geo_obj.tools):
-                        self.app.inform.emit('[ERROR_NOTCL] %s' % _('Cancelled. Empty file, it has no geometry...'))
+                        msg = '[ERROR_NOTCL] %s' % '%s ...' % _('Cancelled. Empty file, it has no geometry')
+                        self.app.inform.emit(msg)
                         return 'fail'
                         return 'fail'
 
 
                     app_obj.inform.emit('[success] %s...' % _("Solder Paste geometry generated successfully"))
                     app_obj.inform.emit('[success] %s...' % _("Solder Paste geometry generated successfully"))
@@ -890,7 +891,7 @@ class SolderPaste(AppTool):
             ymax = obj.options['ymax']
             ymax = obj.options['ymax']
         except Exception as e:
         except Exception as e:
             log.debug("SolderPaste.on_create_gcode() --> %s\n" % str(e))
             log.debug("SolderPaste.on_create_gcode() --> %s\n" % str(e))
-            msg = '[ERROR] %s' % _("An internal error has ocurred. See shell.\n")
+            msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n")
             msg += 'SolderPaste.on_create_gcode() --> %s' % str(e)
             msg += 'SolderPaste.on_create_gcode() --> %s' % str(e)
             msg += traceback.format_exc()
             msg += traceback.format_exc()
             self.app.inform.emit(msg)
             self.app.inform.emit(msg)
@@ -956,7 +957,7 @@ class SolderPaste(AppTool):
         if use_thread:
         if use_thread:
             # To be run in separate thread
             # To be run in separate thread
             def job_thread(app_obj):
             def job_thread(app_obj):
-                with self.app.proc_container.new("Generating CNC Code"):
+                with self.app.proc_container.new('%s' % _("Working ...")):
                     if app_obj.app_obj.new_object("cncjob", name, job_init) != 'fail':
                     if app_obj.app_obj.new_object("cncjob", name, job_init) != 'fail':
                         app_obj.inform.emit('[success] [success] %s: %s' %
                         app_obj.inform.emit('[success] [success] %s: %s' %
                                             (_("ToolSolderPaste CNCjob created"), name))
                                             (_("ToolSolderPaste CNCjob created"), name))
@@ -1060,7 +1061,8 @@ class SolderPaste(AppTool):
             )
             )
         except TypeError:
         except TypeError:
             filename, _f = FCFileSaveDialog.get_saved_filename(
             filename, _f = FCFileSaveDialog.get_saved_filename(
-                caption=_("Export Code ..."), ext_filter=_filter_)
+                caption=_("Export Code ..."),
+                ext_filter=_filter_)
 
 
         if filename == '':
         if filename == '':
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export cancelled ..."))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export cancelled ..."))
@@ -1176,7 +1178,7 @@ class SolderUI:
               "with solder paste, the app will issue a warning message box.")
               "with solder paste, the app will issue a warning message box.")
         )
         )
         self.tools_table.horizontalHeaderItem(1).setToolTip(
         self.tools_table.horizontalHeaderItem(1).setToolTip(
-            _("Nozzle tool Diameter. It's value (in current FlatCAM units)\n"
+            _("Tool Diameter. Its value\n"
               "is the width of the solder paste dispensed."))
               "is the width of the solder paste dispensed."))
 
 
         # ### Add a new Tool ## ##
         # ### Add a new Tool ## ##
@@ -1185,10 +1187,10 @@ class SolderUI:
 
 
         self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('New Nozzle Tool'))
         self.addtool_entry_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('New Nozzle Tool'))
         self.addtool_entry_lbl.setToolTip(
         self.addtool_entry_lbl.setToolTip(
-            _("Diameter for the new Nozzle tool to add in the Tool Table")
+            _("Diameter for the new tool to add in the Tool Table")
         )
         )
         self.addtool_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.addtool_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.addtool_entry.set_range(0.0000001, 9999.9999)
+        self.addtool_entry.set_range(0.0000001, 10000.0000)
         self.addtool_entry.set_precision(self.decimals)
         self.addtool_entry.set_precision(self.decimals)
         self.addtool_entry.setSingleStep(0.1)
         self.addtool_entry.setSingleStep(0.1)
 
 
@@ -1209,7 +1211,7 @@ class SolderUI:
         self.deltool_btn = QtWidgets.QPushButton(_('Delete'))
         self.deltool_btn = QtWidgets.QPushButton(_('Delete'))
         self.deltool_btn.setToolTip(
         self.deltool_btn.setToolTip(
             _("Delete a selection of tools in the Tool Table\n"
             _("Delete a selection of tools in the Tool Table\n"
-              "by first selecting a row(s) in the Tool Table.")
+              "by first selecting a row in the Tool Table.")
         )
         )
 
 
         grid0.addWidget(self.addtool_btn, 0, 0)
         grid0.addWidget(self.addtool_btn, 0, 0)
@@ -1248,7 +1250,7 @@ class SolderUI:
 
 
         # Z dispense start
         # Z dispense start
         self.z_start_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.z_start_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.z_start_entry.set_range(0.0000001, 9999.9999)
+        self.z_start_entry.set_range(0.0000001, 10000.0000)
         self.z_start_entry.set_precision(self.decimals)
         self.z_start_entry.set_precision(self.decimals)
         self.z_start_entry.setSingleStep(0.1)
         self.z_start_entry.setSingleStep(0.1)
 
 
@@ -1260,7 +1262,7 @@ class SolderUI:
 
 
         # Z dispense
         # Z dispense
         self.z_dispense_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.z_dispense_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.z_dispense_entry.set_range(0.0000001, 9999.9999)
+        self.z_dispense_entry.set_range(0.0000001, 10000.0000)
         self.z_dispense_entry.set_precision(self.decimals)
         self.z_dispense_entry.set_precision(self.decimals)
         self.z_dispense_entry.setSingleStep(0.1)
         self.z_dispense_entry.setSingleStep(0.1)
 
 
@@ -1272,7 +1274,7 @@ class SolderUI:
 
 
         # Z dispense stop
         # Z dispense stop
         self.z_stop_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.z_stop_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.z_stop_entry.set_range(0.0000001, 9999.9999)
+        self.z_stop_entry.set_range(0.0000001, 10000.0000)
         self.z_stop_entry.set_precision(self.decimals)
         self.z_stop_entry.set_precision(self.decimals)
         self.z_stop_entry.setSingleStep(0.1)
         self.z_stop_entry.setSingleStep(0.1)
 
 
@@ -1284,7 +1286,7 @@ class SolderUI:
 
 
         # Z travel
         # Z travel
         self.z_travel_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.z_travel_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.z_travel_entry.set_range(0.0000001, 9999.9999)
+        self.z_travel_entry.set_range(0.0000001, 10000.0000)
         self.z_travel_entry.set_precision(self.decimals)
         self.z_travel_entry.set_precision(self.decimals)
         self.z_travel_entry.setSingleStep(0.1)
         self.z_travel_entry.setSingleStep(0.1)
 
 
@@ -1297,7 +1299,7 @@ class SolderUI:
 
 
         # Z toolchange location
         # Z toolchange location
         self.z_toolchange_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.z_toolchange_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.z_toolchange_entry.set_range(0.0000001, 9999.9999)
+        self.z_toolchange_entry.set_range(0.0000001, 10000.0000)
         self.z_toolchange_entry.set_precision(self.decimals)
         self.z_toolchange_entry.set_precision(self.decimals)
         self.z_toolchange_entry.setSingleStep(0.1)
         self.z_toolchange_entry.setSingleStep(0.1)
 
 
@@ -1318,7 +1320,7 @@ class SolderUI:
 
 
         # Feedrate X-Y
         # Feedrate X-Y
         self.frxy_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.frxy_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.frxy_entry.set_range(0.0000, 99999.9999)
+        self.frxy_entry.set_range(0.0000, 910000.0000)
         self.frxy_entry.set_precision(self.decimals)
         self.frxy_entry.set_precision(self.decimals)
         self.frxy_entry.setSingleStep(0.1)
         self.frxy_entry.setSingleStep(0.1)
 
 
@@ -1330,7 +1332,7 @@ class SolderUI:
 
 
         # Feedrate Z
         # Feedrate Z
         self.frz_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.frz_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.frz_entry.set_range(0.0000, 99999.9999)
+        self.frz_entry.set_range(0.0000, 910000.0000)
         self.frz_entry.set_precision(self.decimals)
         self.frz_entry.set_precision(self.decimals)
         self.frz_entry.setSingleStep(0.1)
         self.frz_entry.setSingleStep(0.1)
 
 
@@ -1343,14 +1345,14 @@ class SolderUI:
 
 
         # Feedrate Z Dispense
         # Feedrate Z Dispense
         self.frz_dispense_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.frz_dispense_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.frz_dispense_entry.set_range(0.0000, 99999.9999)
+        self.frz_dispense_entry.set_range(0.0000, 910000.0000)
         self.frz_dispense_entry.set_precision(self.decimals)
         self.frz_dispense_entry.set_precision(self.decimals)
         self.frz_dispense_entry.setSingleStep(0.1)
         self.frz_dispense_entry.setSingleStep(0.1)
 
 
         self.frz_dispense_label = QtWidgets.QLabel('%s:' % _("Feedrate Z Dispense"))
         self.frz_dispense_label = QtWidgets.QLabel('%s:' % _("Feedrate Z Dispense"))
         self.frz_dispense_label.setToolTip(
         self.frz_dispense_label.setToolTip(
             _("Feedrate (speed) while moving up vertically\n"
             _("Feedrate (speed) while moving up vertically\n"
-              " to Dispense position (on Z plane).")
+              "to Dispense position (on Z plane).")
         )
         )
         self.gcode_form_layout.addRow(self.frz_dispense_label, self.frz_dispense_entry)
         self.gcode_form_layout.addRow(self.frz_dispense_label, self.frz_dispense_entry)
 
 
@@ -1368,7 +1370,7 @@ class SolderUI:
 
 
         # Dwell Forward
         # Dwell Forward
         self.dwellfwd_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dwellfwd_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.dwellfwd_entry.set_range(0.0000001, 9999.9999)
+        self.dwellfwd_entry.set_range(0.0000001, 10000.0000)
         self.dwellfwd_entry.set_precision(self.decimals)
         self.dwellfwd_entry.set_precision(self.decimals)
         self.dwellfwd_entry.setSingleStep(0.1)
         self.dwellfwd_entry.setSingleStep(0.1)
 
 
@@ -1392,7 +1394,7 @@ class SolderUI:
 
 
         # Dwell Reverse
         # Dwell Reverse
         self.dwellrev_entry = FCDoubleSpinner(callback=self.confirmation_message)
         self.dwellrev_entry = FCDoubleSpinner(callback=self.confirmation_message)
-        self.dwellrev_entry.set_range(0.0000001, 9999.9999)
+        self.dwellrev_entry.set_range(0.0000001, 10000.0000)
         self.dwellrev_entry.set_precision(self.decimals)
         self.dwellrev_entry.set_precision(self.decimals)
         self.dwellrev_entry.setSingleStep(0.1)
         self.dwellrev_entry.setSingleStep(0.1)
 
 

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