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

Merged in marius_stanciu/flatcam_beta/Beta (pull request #190)

Beta
Marius Stanciu 6 лет назад
Родитель
Сommit
cc7e758cda
38 измененных файлов с 5352 добавлено и 3048 удалено
  1. 2 2
      FlatCAM.py
  2. 340 110
      FlatCAMApp.py
  3. 330 134
      FlatCAMObj.py
  4. 25 3
      FlatCAMProcess.py
  5. 1 1
      FlatCAMWorkerStack.py
  6. 2 1
      ObjectCollection.py
  7. 106 0
      README.md
  8. 433 73
      camlib.py
  9. 94 71
      flatcamEditors/FlatCAMExcEditor.py
  10. 201 134
      flatcamEditors/FlatCAMGeoEditor.py
  11. 182 123
      flatcamEditors/FlatCAMGrbEditor.py
  12. 356 71
      flatcamGUI/FlatCAMGUI.py
  13. 46 3
      flatcamGUI/GUIElements.py
  14. 114 52
      flatcamGUI/ObjectUI.py
  15. 1 1
      flatcamGUI/PlotCanvas.py
  16. 1 1
      flatcamGUI/VisPyVisuals.py
  17. 8 1
      flatcamParsers/ParseFont.py
  18. 18 18
      flatcamTools/ToolCalculators.py
  19. 64 52
      flatcamTools/ToolCutOut.py
  20. 27 25
      flatcamTools/ToolDblSided.py
  21. 8 8
      flatcamTools/ToolFilm.py
  22. 34 21
      flatcamTools/ToolMove.py
  23. 444 209
      flatcamTools/ToolNonCopperClear.py
  24. 35 8
      flatcamTools/ToolPDF.py
  25. 384 143
      flatcamTools/ToolPaint.py
  26. 43 47
      flatcamTools/ToolPanelize.py
  27. 20 11
      flatcamTools/ToolPcbWizard.py
  28. 105 38
      flatcamTools/ToolProperties.py
  29. 61 37
      flatcamTools/ToolSolderPaste.py
  30. 28 14
      flatcamTools/ToolSub.py
  31. 44 30
      flatcamTools/ToolTransform.py
  32. BIN
      locale/de/LC_MESSAGES/strings.mo
  33. 479 458
      locale/de/LC_MESSAGES/strings.po
  34. BIN
      locale/en/LC_MESSAGES/strings.mo
  35. 487 347
      locale/en/LC_MESSAGES/strings.po
  36. BIN
      locale/ro/LC_MESSAGES/strings.mo
  37. 474 476
      locale/ro/LC_MESSAGES/strings.po
  38. 355 325
      locale_template/strings.pot

+ 2 - 2
FlatCAM.py

@@ -4,9 +4,10 @@ import os
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings, Qt
 from FlatCAMApp import App
-from multiprocessing import freeze_support
 from flatcamGUI import VisPyPatches
 
+from multiprocessing import freeze_support
+
 if sys.platform == "win32":
     # cx_freeze 'module win32' workaround
     pass
@@ -58,5 +59,4 @@ if __name__ == '__main__':
         app.setAttribute(Qt.AA_EnableHighDpiScaling, False)
 
     fc = App()
-
     sys.exit(app.exec_())

Разница между файлами не показана из-за своего большого размера
+ 340 - 110
FlatCAMApp.py


Разница между файлами не показана из-за своего большого размера
+ 330 - 134
FlatCAMObj.py


+ 25 - 3
FlatCAMProcess.py

@@ -10,6 +10,13 @@ from flatcamGUI.FlatCAMGUI import FlatCAMActivityView
 from PyQt5 import QtCore
 import weakref
 
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
 
 # import logging
 
@@ -121,6 +128,8 @@ class FCProcessContainer(object):
 
 class FCVisibleProcessContainer(QtCore.QObject, FCProcessContainer):
     something_changed = QtCore.pyqtSignal()
+    # this will signal that the application is IDLE
+    idle_flag = QtCore.pyqtSignal()
 
     def __init__(self, view):
         assert isinstance(view, FlatCAMActivityView), \
@@ -131,6 +140,9 @@ class FCVisibleProcessContainer(QtCore.QObject, FCProcessContainer):
 
         self.view = view
 
+        self.text_to_display_in_activity = ''
+        self.new_text = ' '
+
         self.something_changed.connect(self.update_view)
 
     def on_done(self, proc):
@@ -143,14 +155,24 @@ class FCVisibleProcessContainer(QtCore.QObject, FCProcessContainer):
         # self.app.log.debug("FCVisibleProcessContainer.on_change()")
         super(FCVisibleProcessContainer, self).on_change(proc)
 
+        # whenever there is a change update the message on activity
+        self.text_to_display_in_activity = self.procs[0]().status_msg()
+
         self.something_changed.emit()
 
     def update_view(self):
         if len(self.procs) == 0:
+            self.new_text = ''
             self.view.set_idle()
+            self.idle_flag.emit()
 
         elif len(self.procs) == 1:
-            self.view.set_busy(self.procs[0]().status_msg())
-
+            self.view.set_busy(self.text_to_display_in_activity + self.new_text)
         else:
-            self.view.set_busy("%d processes running." % len(self.procs))
+            self.view.set_busy("%d %s" % (len(self.procs), _("processes running.")))
+
+    def update_view_text(self, new_text):
+        # this has to be called after the method 'new' inherited by this class is called with a string text as param
+        self.new_text = new_text
+        if len(self.procs) == 1:
+            self.view.set_busy(self.text_to_display_in_activity + self.new_text, no_movie=True)

+ 1 - 1
FlatCAMWorkerStack.py

@@ -25,7 +25,7 @@ class WorkerStack(QtCore.QObject):
             thread.started.connect(worker.run)
             worker.task_completed.connect(self.on_task_completed)
 
-            thread.start(QtCore.QThread.LowPriority)
+            thread.start(QtCore.QThread.HighPriority)
 
             self.workers.append(worker)
             self.threads.append(thread)

+ 2 - 1
ObjectCollection.py

@@ -762,7 +762,8 @@ class ObjectCollection(QtCore.QAbstractItemModel):
             try:
                 a_idx.build_ui()
             except Exception as e:
-                self.app.inform.emit(_("[ERROR] Cause of error: %s") % str(e))
+                self.app.inform.emit('[ERROR] %s: %s' %
+                                     (_("Cause of error"), str(e)))
                 raise
 
     def get_list(self):

+ 106 - 0
README.md

@@ -9,6 +9,112 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+9.09.2019
+
+- changed the triangulation type in VisPyVisuals for ShapeCollectionVisual class
+- added a setting in Preferences -> Gerber -> Gerber General named Buffering. If set to 'no' the Gerber objects load a lot more faster (perhaps 10 times faster than when set to 'full') but the visual look is not so great as all the aperture polygons can be seen
+- added for NCC Tool and Paint Tool a setting in the Preferences -> Tools --> (NCC Tool/ Paint Tool) that can set a progressive plotting (plot shapes as they are processed)
+- some fixes in Paint Tool when done over the Gerber objects in case that the progressive plotting is selected
+- some fixes in Gerber isolation in case that the progressive plotting is selected; added a 'Buffer solid geometry' button shown only when progressive plotting for Gerber object is selected. It will buffer the entire geometry of the object and plot it, in a threaded way.
+- modified FlatCAMObj.py file to the new string format that will allow easier translations
+- modified camlib.py, FlatCAMAPp.py and ObjectCollection.py files to the new string format that will allow easier translations
+- updated the POT file and the German language
+- fixed issue when loading unbuffered a Gerber file that has negative regions
+- fixed Panelize Tool to save the aperture geometries into the panel apertures. Also made the tool faster by removing the buffering at the end of the job
+- modified FlatCAMEditor's files to the new string format that will allow easier translations
+- updated POT file and the Romanian translation
+
+8.09.2019
+
+- added some documentation strings for methods in FlatCAMApp.App class
+- removed some @pyqtSlot() decorators as they interfere with the current way the program works
+
+7.09.2019
+
+- added a method to gracefully exit from threaded tasks and implemented it for the NCC Tool and for the Paint Tool
+- modified the on_about() function to reflect the reality in 2019 - FlatCAM it is an Open Source contributed software
+- remade the handlers for the Enable/Disable Project Tree context menu so they are threaded and activity is shown in the lower right corner of the main window
+- added to GUI new options for the Gerber object related to area subtraction
+- added new feature in the Gerber object isolation allowing for the isolation to avoid an area defined by another object (Gerber or Geometry)
+- all transformation functions show now the progress (rotate, mirror, scale, offset, skew)
+- made threaded the Offset and Scale operations found in the Selected tab of the object
+- corrected some issues and made Move Tool to show correctly when it is plotting and when it is offsetting the objects position
+- made Set Origin feature, threaded
+- updated German language translation files
+- separated the Plotting thread from the transformations threads
+
+6.09.2019
+
+- remade visibility threaded
+- reimplemented the thread listening for new FlatCAM process starting with args so it is no longer subclassed but using the moveToThread function
+- added percentage display for work done in NCC Tool
+- added percentage display for work done in Paint Tool
+- some fixes and prepared the activity monitor area to receive updated texts
+- added progress display in status bar for generating CNCJob from Excellon objects
+- added progress display in status bar for generating CNCJob from Geometry objects
+- modified all the FlatCAM tools strings to the new format in which the status is no longer included in the translated strings to make it easier for the future translations
+- more customization for the progress display in case of NCC Tool, Paint Tool and for the Gcode generation
+- updated POT file with the new strings
+- made the objects offset (therefore the Move Tool) show progress display
+
+5.09.2019
+
+- fixed issue with loading files at start-up
+- fixed issue with generating bounding box geometry for CNCJob objects
+- added some more infobar messages and log.debug
+- increased the priority for the worker tasks
+- hidden the configuration for G91 coordinates due of deciding to leave this development for another time; it require too much refactoring
+- added some messages for the G-code generation so the user know in which stage the process is
+
+4.09.2019
+
+- started to work on support for G91 in Gcode (relative coordinates)
+- added support for G91 coordinates
+- working in plotting the CNCjob generated with G91 coordinates
+
+3.09.2019
+
+- in NCC tool there is now a depth of cut parameter named 'Cut Z' which will dictate how deep the tool will enter into the PCB material
+- in NCC tool added possibility to choose between the type of tools to be used and when V-shape is used then the tool diameter is calculated from the desired depth of cut and from the V-tip parameters
+- small changes in NCC tool regarding the usage of the V-shape tool
+- fixed the isolation distance in NCC Tool for the tools with iso_op type
+- in NCC Tool now the Area adding is continuous until RMB is clicked (no key modifier is needed anymore)
+- fixed German language translation
+- in NCC Tool added a warning in case there are isolation tools and if those isolation's are interrupted by an area or a box
+- in Paint Tool made that the area selection is repeated until RMB click
+- in Paint Tool and NCC Tool fixed the RMB click detection when Area selection is used
+- finished the work on file extensions registration with FlatCAM. If the file extensions are deleted in the Preferences -> File Associations then those extensions are unregistered with FlatCAM
+- fixed bug in NCC Tools and in SolderPaste Tool if in Edit -> Preferences only one tool is entered
+- fixed bug in camblib.clear_polygon3() which caused that some copper clearing / paintings were not complete (some polygons were not processed) when the Straight Lines method was used
+- some changes in NCC Tools regarding of the clearing itself
+
+2.09.2019
+
+- fixed issue in NCC Tool when using area option
+- added formatting for some strings in the app strings, making the future translations easier
+- made changes in the Excellon Tools Table to make it more clear that the tools are selected in the # column and not in the Plot column
+- in Excellon and Gerber Selected tab made the Plot (mark) columns not selectable
+- some ToolTips were modified
+- in Properties Tool made threaded the calculation of convex_hull area and also made it to work for multi-geo objects
+- in NCC tool the type of tool that is used is transferred to the Geometry object
+- in NCC tool the type of isolation done with the tools selected as isolation tools can now be selected and it has also an Edit -> Preferences entry
+- in Properties Tool fixed the dimensions calculations (length, width, area) to work for multi-geo objects
+
+1.09.2019
+
+- fixed open handlers
+- fixed issue in NCC Tool where the tool table context menu could be installed multiple times
+- added new ability to create simple isolation's in the NCC Tool
+- fixed an issue when multi depth step is larger than the depth of cut
+
+27.08.2019
+
+- made FlatCAM so that whenever an associated file is double clicked, if there is an opened instance of FlatCAM, the file will be opened in the first instance without launching a new instance of FlatCAM. If FlatCAM is launched again it will spawn a new process (hopefully it will work when freezed).
+
+26.08.2019
+
+- added support for file associations with FlatCAM, for Windows
+
 25.08.2019
 
 - initial add of a new Tcl Command named CopperClear

Разница между файлами не показана из-за своего большого размера
+ 433 - 73
camlib.py


+ 94 - 71
flatcamEditors/FlatCAMExcEditor.py

@@ -50,7 +50,8 @@ class FCDrillAdd(FCShapeTool):
             item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
             self.draw_app.tools_table_exc.setCurrentItem(item)
         except KeyError:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] To add a drill first select a tool"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("To add a drill first select a tool"))
             self.draw_app.select_tool("drill_select")
             return
 
@@ -112,7 +113,8 @@ class FCDrillAdd(FCShapeTool):
         self.geometry = DrawToolShape(self.util_shape(self.points))
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Drill added."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Drill added."))
 
 
 class FCDrillArray(FCShapeTool):
@@ -154,7 +156,8 @@ class FCDrillArray(FCShapeTool):
             item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
             self.draw_app.tools_table_exc.setCurrentItem(item)
         except KeyError:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] To add an Drill Array first select a tool in Tool Table"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("To add an Drill Array first select a tool in Tool Table"))
             return
 
         try:
@@ -208,11 +211,12 @@ class FCDrillArray(FCShapeTool):
                 self.drill_linear_angle = float(self.draw_app.linear_angle_spinner.get_value())
                 self.drill_angle = float(self.draw_app.drill_angle_entry.get_value())
             except TypeError:
-                self.draw_app.app.inform.emit(
-                    _("[ERROR_NOTCL] The value is not Float. Check for comma instead of dot separator."))
+                self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                              _("The value is not Float. Check for comma instead of dot separator."))
                 return
         except Exception as e:
-            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] The value is mistyped. Check the value. %s") % str(e))
+            self.draw_app.app.inform.emit('[ERROR_NOTCL] %s. %s' %
+                                          (_("The value is mistyped. Check the value"), str(e)))
             return
 
         if self.drill_array == 'Linear':
@@ -310,7 +314,8 @@ class FCDrillArray(FCShapeTool):
                 self.geometry.append(DrawToolShape(geo))
         else:
             if (self.drill_angle * self.drill_array_size) > 360:
-                self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Too many drills for the selected spacing angle."))
+                self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                              _("Too many drills for the selected spacing angle."))
                 return
 
             radius = distance(self.destination, self.origin)
@@ -327,7 +332,8 @@ class FCDrillArray(FCShapeTool):
                 geo = self.util_shape((x, y))
                 self.geometry.append(DrawToolShape(geo))
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Drill Array added."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Drill Array added."))
         self.draw_app.in_action = False
         self.draw_app.array_frame.hide()
         return
@@ -355,7 +361,8 @@ class FCSlot(FCShapeTool):
             item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
             self.draw_app.tools_table_exc.setCurrentItem(item)
         except KeyError:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] To add a slot first select a tool"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("To add a slot first select a tool"))
             self.draw_app.select_tool("drill_select")
             return
 
@@ -412,15 +419,15 @@ class FCSlot(FCShapeTool):
                 slot_length = float(self.draw_app.slot_length_entry.get_value().replace(',', '.'))
                 self.draw_app.slot_length_entry.set_value(slot_length)
             except ValueError:
-                self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Value is missing or wrong format. "
-                                                "Add it and retry."))
+                self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                              _("Value is missing or wrong format. Add it and retry."))
                 return
 
         try:
             slot_angle = float(self.draw_app.slot_angle_spinner.get_value())
         except ValueError:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Value is missing or wrong format. "
-                                            "Add it and retry."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("Value is missing or wrong format. Add it and retry."))
             return
 
         if self.draw_app.slot_axis_radio.get_value() == 'X':
@@ -518,7 +525,8 @@ class FCSlot(FCShapeTool):
 
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Adding Slot completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Adding Slot completed."))
         self.draw_app.slot_frame.hide()
 
 
@@ -544,7 +552,8 @@ class FCSlotArray(FCShapeTool):
             item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
             self.draw_app.tools_table_exc.setCurrentItem(item)
         except KeyError:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] To add an Slot Array first select a tool in Tool Table"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("To add an Slot Array first select a tool in Tool Table"))
             return
 
 
@@ -622,11 +631,12 @@ class FCSlotArray(FCShapeTool):
                 self.slot_linear_angle = float(self.draw_app.slot_array_linear_angle_spinner.get_value())
                 self.slot_angle = float(self.draw_app.slot_array_angle_entry.get_value())
             except TypeError:
-                self.draw_app.app.inform.emit(
-                    _("[ERROR_NOTCL] The value is not Float. Check for comma instead of dot separator."))
+                self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                              _("The value is not Float. Check for comma instead of dot separator."))
                 return
         except Exception as e:
-            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] The value is mistyped. Check the value."))
+            self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                          _("The value is mistyped. Check the value."))
             return
 
         if self.slot_array == 'Linear':
@@ -687,15 +697,15 @@ class FCSlotArray(FCShapeTool):
                 slot_length = float(self.draw_app.slot_length_entry.get_value().replace(',', '.'))
                 self.draw_app.slot_length_entry.set_value(slot_length)
             except ValueError:
-                self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Value is missing or wrong format. "
-                                                "Add it and retry."))
+                self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                              _("Value is missing or wrong format. Add it and retry."))
                 return
 
         try:
             slot_angle = float(self.draw_app.slot_angle_spinner.get_value())
         except ValueError:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Value is missing or wrong format. "
-                                            "Add it and retry."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("Value is missing or wrong format. Add it and retry."))
             return
 
         if self.draw_app.slot_axis_radio.get_value() == 'X':
@@ -804,7 +814,8 @@ class FCSlotArray(FCShapeTool):
                 self.geometry.append(DrawToolShape(geo))
         else:
             if (self.slot_angle * self.slot_array_size) > 360:
-                self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Too many Slots for the selected spacing angle."))
+                self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                              _("Too many Slots for the selected spacing angle."))
                 return
 
             radius = distance(self.destination, self.origin)
@@ -826,7 +837,8 @@ class FCSlotArray(FCShapeTool):
 
                 self.geometry.append(DrawToolShape(geo))
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Slot Array added."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Slot Array added."))
         self.draw_app.in_action = False
         self.draw_app.slot_frame.hide()
         self.draw_app.slot_array_frame.hide()
@@ -872,9 +884,8 @@ class FCDrillResize(FCShapeTool):
         try:
             new_dia = self.draw_app.resdrill_entry.get_value()
         except:
-            self.draw_app.app.inform.emit(
-                _("[ERROR_NOTCL] Resize drill(s) failed. Please enter a diameter for resize.")
-            )
+            self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                          _("Resize drill(s) failed. Please enter a diameter for resize."))
             return
 
         if new_dia not in self.draw_app.olddia_newdia:
@@ -963,7 +974,8 @@ class FCDrillResize(FCShapeTool):
                             self.geometry.append(DrawToolShape(new_poly))
                         else:
                             # unexpected geometry so we cancel
-                            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] Cancelled."))
+                            self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                                          _("Cancelled."))
                             return
 
                         # remove the geometry with the old size
@@ -1032,7 +1044,8 @@ class FCDrillResize(FCShapeTool):
                     except KeyError:
                         # if the exception happen here then we are not dealing with slots neither
                         # therefore something else is not OK so we return
-                        self.draw_app.app.inform.emit(_("[ERROR_NOTCL] Cancelled."))
+                        self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                                      _("Cancelled."))
                         return
 
             # this simple hack is used so we can delete form self.draw_app.selected but
@@ -1052,9 +1065,11 @@ class FCDrillResize(FCShapeTool):
             # we reactivate the signals after the after the tool editing
             self.draw_app.tools_table_exc.itemChanged.connect(self.draw_app.on_tool_edit)
 
-            self.draw_app.app.inform.emit(_("[success] Done. Drill/Slot Resize completed."))
+            self.draw_app.app.inform.emit('[success] Done. %s' %
+                                          _("Drill/Slot Resize completed."))
         else:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Cancelled. No drills/slots selected for resize ..."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("Cancelled. No drills/slots selected for resize ..."))
 
         # init this set() for another use perhaps
         self.selected_dia_set = set()
@@ -1137,7 +1152,8 @@ class FCDrillMove(FCShapeTool):
             sel_shapes_to_be_deleted = []
 
         self.draw_app.build_ui()
-        self.draw_app.app.inform.emit(_("[success] Done. Drill(s) Move completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Drill(s) Move completed."))
 
     def selection_bbox(self):
         geo_list = []
@@ -1234,7 +1250,8 @@ class FCDrillCopy(FCDrillMove):
             sel_shapes_to_be_deleted = []
 
         self.draw_app.build_ui()
-        self.draw_app.app.inform.emit(_("[success] Done. Drill(s) copied."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Drill(s) copied."))
 
 
 class FCDrillSelect(DrawTool):
@@ -1498,7 +1515,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         grid1 = QtWidgets.QGridLayout()
         self.tools_box.addLayout(grid1)
 
-        addtool_entry_lbl = QtWidgets.QLabel(_('Tool Dia:'))
+        addtool_entry_lbl = QtWidgets.QLabel('%s:' % _('Tool Dia'))
         addtool_entry_lbl.setToolTip(
             _("Diameter for the new tool")
         )
@@ -1551,7 +1568,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         grid3 = QtWidgets.QGridLayout()
         self.resize_box.addLayout(grid3)
 
-        res_entry_lbl = QtWidgets.QLabel(_('Resize Dia:'))
+        res_entry_lbl = QtWidgets.QLabel('%s:' % _('Resize Dia'))
         res_entry_lbl.setToolTip(
            _("Diameter to resize to.")
         )
@@ -1608,7 +1625,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.array_box.addLayout(self.array_form)
 
         # Set the number of drill holes in the drill array
-        self.drill_array_size_label = QtWidgets.QLabel(_('Nr of drills:'))
+        self.drill_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of drills'))
         self.drill_array_size_label.setToolTip(_("Specify how many drills to be in the array."))
         self.drill_array_size_label.setMinimumWidth(100)
 
@@ -1626,7 +1643,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.linear_box.addLayout(self.linear_form)
 
         # Linear Drill Array direction
-        self.drill_axis_label = QtWidgets.QLabel(_('Direction:'))
+        self.drill_axis_label = QtWidgets.QLabel('%s:'% _('Direction'))
         self.drill_axis_label.setToolTip(
             _("Direction on which the linear array is oriented:\n"
               "- 'X' - horizontal axis \n"
@@ -1641,7 +1658,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.linear_form.addRow(self.drill_axis_label, self.drill_axis_radio)
 
         # Linear Drill Array pitch distance
-        self.drill_pitch_label = QtWidgets.QLabel(_('Pitch:'))
+        self.drill_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
         self.drill_pitch_label.setToolTip(
             _("Pitch = Distance between elements of the array.")
         )
@@ -1651,7 +1668,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.linear_form.addRow(self.drill_pitch_label, self.drill_pitch_entry)
 
         # Linear Drill Array angle
-        self.linear_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
         self.linear_angle_label.setToolTip(
            _("Angle at which the linear array is placed.\n"
              "The precision is of max 2 decimals.\n"
@@ -1673,7 +1690,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.circular_box.setContentsMargins(0, 0, 0, 0)
         self.array_circular_frame.setLayout(self.circular_box)
 
-        self.drill_direction_label = QtWidgets.QLabel(_('Direction:'))
+        self.drill_direction_label = QtWidgets.QLabel('%s:' % _('Direction'))
         self.drill_direction_label.setToolTip(_("Direction for circular array."
                                                 "Can be CW = clockwise or CCW = counter clockwise."))
         self.drill_direction_label.setMinimumWidth(100)
@@ -1685,7 +1702,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                                                {'label': _('CCW'), 'value': 'CCW'}])
         self.circular_form.addRow(self.drill_direction_label, self.drill_direction_radio)
 
-        self.drill_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.drill_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
         self.drill_angle_label.setToolTip(_("Angle at which each element in circular array is placed."))
         self.drill_angle_label.setMinimumWidth(100)
 
@@ -1727,7 +1744,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_box.addLayout(self.slot_form)
 
         # Slot length
-        self.slot_length_label = QtWidgets.QLabel(_('Length:'))
+        self.slot_length_label = QtWidgets.QLabel('%s:' % _('Length'))
         self.slot_length_label.setToolTip(
             _("Length = The length of the slot.")
         )
@@ -1737,7 +1754,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_form.addRow(self.slot_length_label, self.slot_length_entry)
 
         # Slot direction
-        self.slot_axis_label = QtWidgets.QLabel(_('Direction:'))
+        self.slot_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
         self.slot_axis_label.setToolTip(
             _("Direction on which the slot is oriented:\n"
               "- 'X' - horizontal axis \n"
@@ -1752,7 +1769,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_form.addRow(self.slot_axis_label, self.slot_axis_radio)
 
         # Slot custom angle
-        self.slot_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.slot_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
         self.slot_angle_label.setToolTip(
            _("Angle at which the slot is placed.\n"
              "The precision is of max 2 decimals.\n"
@@ -1810,7 +1827,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_array_box.addLayout(self.slot_array_form)
 
         # Set the number of slot holes in the slot array
-        self.slot_array_size_label = QtWidgets.QLabel(_('Nr of slots:'))
+        self.slot_array_size_label = QtWidgets.QLabel('%s:' % _('Nr of slots'))
         self.slot_array_size_label.setToolTip(_("Specify how many slots to be in the array."))
         self.slot_array_size_label.setMinimumWidth(100)
 
@@ -1828,7 +1845,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_array_linear_box.addLayout(self.slot_array_linear_form)
 
         # Linear Slot Array direction
-        self.slot_array_axis_label = QtWidgets.QLabel(_('Direction:'))
+        self.slot_array_axis_label = QtWidgets.QLabel('%s:' % _('Direction'))
         self.slot_array_axis_label.setToolTip(
             _("Direction on which the linear array is oriented:\n"
               "- 'X' - horizontal axis \n"
@@ -1843,7 +1860,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_array_linear_form.addRow(self.slot_array_axis_label, self.slot_array_axis_radio)
 
         # Linear Slot Array pitch distance
-        self.slot_array_pitch_label = QtWidgets.QLabel(_('Pitch:'))
+        self.slot_array_pitch_label = QtWidgets.QLabel('%s:' % _('Pitch'))
         self.slot_array_pitch_label.setToolTip(
             _("Pitch = Distance between elements of the array.")
         )
@@ -1853,7 +1870,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_array_linear_form.addRow(self.slot_array_pitch_label, self.slot_array_pitch_entry)
 
         # Linear Slot Array angle
-        self.slot_array_linear_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.slot_array_linear_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
         self.slot_array_linear_angle_label.setToolTip(
             _("Angle at which the linear array is placed.\n"
               "The precision is of max 2 decimals.\n"
@@ -1875,7 +1892,7 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.slot_array_circular_box.setContentsMargins(0, 0, 0, 0)
         self.slot_array_circular_frame.setLayout(self.slot_array_circular_box)
 
-        self.slot_array_direction_label = QtWidgets.QLabel(_('Direction:'))
+        self.slot_array_direction_label = QtWidgets.QLabel('%s:' % _('Direction'))
         self.slot_array_direction_label.setToolTip(_("Direction for circular array."
                                                      "Can be CW = clockwise or CCW = counter clockwise."))
         self.slot_array_direction_label.setMinimumWidth(100)
@@ -1887,7 +1904,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                                                     {'label': _('CCW'), 'value': 'CCW'}])
         self.slot_array_circular_form.addRow(self.slot_array_direction_label, self.slot_array_direction_radio)
 
-        self.slot_array_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.slot_array_angle_label = QtWidgets.QLabel('%s:' % _('Angle'))
         self.slot_array_angle_label.setToolTip(_("Angle at which each element in circular array is placed."))
         self.slot_array_angle_label.setMinimumWidth(100)
 
@@ -2395,9 +2412,8 @@ class FlatCAMExcEditor(QtCore.QObject):
                 try:
                     tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                         "use a number.")
-                                         )
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         if tool_dia not in self.olddia_newdia:
@@ -2408,17 +2424,17 @@ class FlatCAMExcEditor(QtCore.QObject):
             # each time a tool diameter is edited or added
             self.olddia_newdia[tool_dia] = tool_dia
         else:
-            self.app.inform.emit(_("[WARNING_NOTCL] Tool already in the original or actual tool list.\n"
-                                 "Save and reedit Excellon if you need to add this tool. ")
-                                 )
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Tool already in the original or actual tool list.\n"
+                                 "Save and reedit Excellon if you need to add this tool. "))
             return
 
         # since we add a new tool, we update also the initial state of the tool_table through it's dictionary
         # we add a new entry in the tool2tooldia dict
         self.tool2tooldia[len(self.olddia_newdia)] = tool_dia
 
-        self.app.inform.emit(_("[success] Added new tool with dia: {dia} {units}").format(dia=str(tool_dia),
-                                                                                          units=str(self.units)))
+        self.app.inform.emit('[success] %s: %s %s' %
+                             (_("Added new tool with dia"), str(tool_dia), str(self.units)))
 
         self.build_ui()
 
@@ -2449,7 +2465,8 @@ class FlatCAMExcEditor(QtCore.QObject):
                 else:
                     deleted_tool_dia_list.append(float('%.4f' % dia))
         except Exception as e:
-            self.app.inform.emit(_("[WARNING_NOTCL] Select a tool in Tool Table"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Select a tool in Tool Table"))
             return
 
         for deleted_tool_dia in deleted_tool_dia_list:
@@ -2481,9 +2498,8 @@ class FlatCAMExcEditor(QtCore.QObject):
 
             self.olddia_newdia.pop(deleted_tool_dia, None)
 
-            self.app.inform.emit(_("[success] Deleted tool with dia: {del_dia} {units}").format(
-                del_dia=str(deleted_tool_dia),
-                units=str(self.units)))
+            self.app.inform.emit('[success] %s: %s %s' %
+                                 (_("Deleted tool with diameter"), str(deleted_tool_dia), str(self.units)))
 
         self.replot()
         # self.app.inform.emit("Could not delete selected tool")
@@ -2632,7 +2648,8 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.tools_table_exc.itemChanged.connect(self.on_tool_edit)
         self.tools_table_exc.cellPressed.connect(self.on_row_selected)
 
-        self.app.inform.emit(_("[success] Done. Tool edit completed."))
+        self.app.inform.emit('[success] %s' %
+                             _("Done. Tool edit completed."))
 
         # self.tools_table_exc.selectionModel().currentChanged.connect(self.on_row_selected)
 
@@ -3166,11 +3183,12 @@ class FlatCAMExcEditor(QtCore.QObject):
             try:
                 excellon_obj.create_geometry()
             except KeyError:
-                self.app.inform.emit(
-                   _("[ERROR_NOTCL] There are no Tools definitions in the file. Aborting Excellon creation.")
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("There are no Tools definitions in the file. Aborting Excellon creation.")
                 )
             except:
-                msg = _("[ERROR] An internal error has ocurred. See shell.\n")
+                msg = '[ERROR] %s' % \
+                      _("An internal error has ocurred. See Shell.\n")
                 msg += traceback.format_exc()
                 app_obj.inform.emit(msg)
                 raise
@@ -3185,7 +3203,8 @@ class FlatCAMExcEditor(QtCore.QObject):
                 self.app.progress.emit(100)
                 return
 
-            self.app.inform.emit(_("[success] Excellon editing finished."))
+            self.app.inform.emit('[success] %s' %
+                                 _("Excellon editing finished."))
             # self.progress.emit(100)
 
     def on_tool_select(self, tool):
@@ -3202,7 +3221,8 @@ class FlatCAMExcEditor(QtCore.QObject):
             # self.draw_app.select_tool('drill_select')
             self.complete = True
             current_tool = 'drill_select'
-            self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. There is no Tool/Drill selected"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Cancelled. There is no Tool/Drill selected"))
 
         # This is to make the group behave as radio group
         if current_tool in self.tools_exc:
@@ -3421,14 +3441,16 @@ class FlatCAMExcEditor(QtCore.QObject):
                         self.active_tool.complete = True
                         self.in_action = False
                         self.delete_utility_geometry()
-                        self.app.inform.emit(_("[success] Done."))
+                        self.app.inform.emit('[success] %s' %
+                                             _("Done."))
                         self.select_tool('drill_select')
                     else:
                         if isinstance(self.active_tool, FCDrillAdd):
                             self.active_tool.complete = True
                             self.in_action = False
                             self.delete_utility_geometry()
-                            self.app.inform.emit(_("[success] Done."))
+                            self.app.inform.emit('[success] %s' %
+                                                 _("Done."))
                             self.select_tool('drill_select')
 
                         self.app.cursor = QtGui.QCursor()
@@ -3784,7 +3806,8 @@ class FlatCAMExcEditor(QtCore.QObject):
 
         self.selected = []
         self.build_ui()
-        self.app.inform.emit(_("[success] Done. Drill(s) deleted."))
+        self.app.inform.emit('[success] %s' %
+                             _("Done. Drill(s) deleted."))
 
     def delete_shape(self, del_shape):
         self.is_modified = True

+ 201 - 134
flatcamEditors/FlatCAMGeoEditor.py

@@ -137,8 +137,8 @@ class BufferSelectionTool(FlatCAMTool):
                 buffer_distance = float(self.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buffer_distance_entry.set_value(buffer_distance)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Buffer distance value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
@@ -154,8 +154,8 @@ class BufferSelectionTool(FlatCAMTool):
                 buffer_distance = float(self.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buffer_distance_entry.set_value(buffer_distance)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Buffer distance value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
@@ -171,8 +171,8 @@ class BufferSelectionTool(FlatCAMTool):
                 buffer_distance = float(self.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buffer_distance_entry.set_value(buffer_distance)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Buffer distance value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
@@ -431,7 +431,7 @@ class PaintOptionsTool(FlatCAMTool):
         self.layout.addLayout(grid)
 
         # Tool dia
-        ptdlabel = QtWidgets.QLabel(_('Tool dia:'))
+        ptdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
         ptdlabel.setToolTip(
            _("Diameter of the tool to\n"
              "be used in the operation.")
@@ -442,7 +442,7 @@ class PaintOptionsTool(FlatCAMTool):
         grid.addWidget(self.painttooldia_entry, 0, 1)
 
         # Overlap
-        ovlabel = QtWidgets.QLabel(_('Overlap Rate:'))
+        ovlabel = QtWidgets.QLabel('%s:' % _('Overlap Rate'))
         ovlabel.setToolTip(
             _("How much (fraction) of the tool width to overlap each tool pass.\n"
               "Example:\n"
@@ -460,7 +460,7 @@ class PaintOptionsTool(FlatCAMTool):
         grid.addWidget(self.paintoverlap_entry, 1, 1)
 
         # Margin
-        marginlabel = QtWidgets.QLabel(_('Margin:'))
+        marginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
         marginlabel.setToolTip(
            _("Distance by which to avoid\n"
              "the edges of the polygon to\n"
@@ -471,7 +471,7 @@ class PaintOptionsTool(FlatCAMTool):
         grid.addWidget(self.paintmargin_entry, 2, 1)
 
         # Method
-        methodlabel = QtWidgets.QLabel(_('Method:'))
+        methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
         methodlabel.setToolTip(
             _("Algorithm to paint the polygon:<BR>"
               "<B>Standard</B>: Fixed step inwards.<BR>"
@@ -562,7 +562,8 @@ class PaintOptionsTool(FlatCAMTool):
 
     def on_paint(self):
         if not self.fcdraw.selected:
-            self.app.inform.emit(_("[WARNING_NOTCL] Paint cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Paint cancelled. No shape selected."))
             return
 
         try:
@@ -573,8 +574,8 @@ class PaintOptionsTool(FlatCAMTool):
                 tooldia = float(self.painttooldia_entry.get_value().replace(',', '.'))
                 self.painttooldia_entry.set_value(tooldia)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Tool diameter value is missing or wrong format. Add it and retry."))
                 return
         try:
             overlap = float(self.paintoverlap_entry.get_value())
@@ -584,8 +585,8 @@ class PaintOptionsTool(FlatCAMTool):
                 overlap = float(self.paintoverlap_entry.get_value().replace(',', '.'))
                 self.paintoverlap_entry.set_value(overlap)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Overlap value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Overlap value is missing or wrong format. Add it and retry."))
                 return
 
         try:
@@ -596,8 +597,8 @@ class PaintOptionsTool(FlatCAMTool):
                 margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
                 self.paintmargin_entry.set_value(margin)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Margin distance value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Margin distance value is missing or wrong format. Add it and retry."))
                 return
         method = self.paintmethod_combo.get_value()
         contour = self.paintcontour_cb.get_value()
@@ -1053,7 +1054,8 @@ class TransformEditorTool(FlatCAMTool):
 
     def template(self):
         if not self.fcdraw.selected:
-            self.app.inform.emit(_("[WARNING_NOTCL] Transformation cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Transformation cancelled. No shape selected."))
             return
 
 
@@ -1074,8 +1076,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.rotate_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Rotate, "
-                                         "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
         self.app.worker_task.emit({'fcn': self.on_rotate_action,
                                        'params': [value]})
@@ -1111,8 +1113,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.skewx_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew X, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_skew("X", value)
@@ -1132,8 +1134,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.skewy_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew Y, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_skew("Y", value)
@@ -1153,8 +1155,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     xvalue = float(self.scalex_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale X, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
@@ -1190,8 +1192,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     yvalue = float(self.scaley_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale Y, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
@@ -1222,8 +1224,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.offx_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset X, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_offset("X", value)
@@ -1243,8 +1245,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.offy_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset Y, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_offset("Y", value)
@@ -1261,7 +1263,8 @@ class TransformEditorTool(FlatCAMTool):
         ymaxlist = []
 
         if not shape_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to rotate!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to rotate!"))
             return
         else:
             with self.app.proc_container.new(_("Appying Rotate")):
@@ -1292,12 +1295,14 @@ class TransformEditorTool(FlatCAMTool):
 
                     # self.draw_app.transform_complete.emit()
 
-                    self.app.inform.emit(_("[success] Done. Rotate completed."))
+                    self.app.inform.emit('[success] %s' %
+                                         _("Done. Rotate completed."))
 
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, rotation movement was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Rotation action was not executed"),str(e)))
                     return
 
     def on_flip(self, axis):
@@ -1308,7 +1313,8 @@ class TransformEditorTool(FlatCAMTool):
         ymaxlist = []
 
         if not shape_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to flip!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to flip!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Flip")):
@@ -1341,10 +1347,12 @@ class TransformEditorTool(FlatCAMTool):
                     for sha in shape_list:
                         if axis is 'X':
                             sha.mirror('X', (px, py))
-                            self.app.inform.emit(_('[success] Flip on the Y axis done ...'))
+                            self.app.inform.emit('[success] %s...' %
+                                                 _('Flip on the Y axis done'))
                         elif axis is 'Y':
                             sha.mirror('Y', (px, py))
-                            self.app.inform.emit(_('[success] Flip on the X axis done ...'))
+                            self.app.inform.emit('[success] %s' %
+                                                 _('Flip on the X axis done'))
                         self.draw_app.replot()
 
                     #     self.draw_app.add_shape(DrawToolShape(sha.geo))
@@ -1354,7 +1362,8 @@ class TransformEditorTool(FlatCAMTool):
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Flip action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Flip action was not executed"), str(e)))
                     return
 
     def on_skew(self, axis, num):
@@ -1363,7 +1372,8 @@ class TransformEditorTool(FlatCAMTool):
         yminlist = []
 
         if not shape_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to shear/skew!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to shear/skew!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Skew")):
@@ -1390,12 +1400,17 @@ class TransformEditorTool(FlatCAMTool):
                     #     self.draw_app.add_shape(DrawToolShape(sha.geo))
                     #
                     # self.draw_app.transform_complete.emit()
-
-                    self.app.inform.emit(_('[success] Skew on the %s axis done ...') % str(axis))
+                    if axis == 'X':
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Skew on the X axis done'))
+                    else:
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Skew on the Y axis done'))
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Skew action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Skew action was not executed"), str(e)))
                     return
 
     def on_scale(self, axis, xfactor, yfactor, point=None):
@@ -1406,7 +1421,8 @@ class TransformEditorTool(FlatCAMTool):
         ymaxlist = []
 
         if not shape_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to scale!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to scale!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Scale")):
@@ -1442,10 +1458,16 @@ class TransformEditorTool(FlatCAMTool):
                     #
                     # self.draw_app.transform_complete.emit()
 
-                    self.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis))
+                    if str(axis) == 'X':
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Scale on the X axis done'))
+                    else:
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Scale on the Y axis done'))
                     self.app.progress.emit(100)
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Scale action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Scale action was not executed"), str(e)))
                     return
 
     def on_offset(self, axis, num):
@@ -1454,7 +1476,8 @@ class TransformEditorTool(FlatCAMTool):
         yminlist = []
 
         if not shape_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to offset!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to offset!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Offset")):
@@ -1468,16 +1491,22 @@ class TransformEditorTool(FlatCAMTool):
                             sha.offset((0, num))
                         self.draw_app.replot()
 
-                    self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis))
+                    if axis == 'X':
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Offset on the X axis done'))
+                    else:
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Offset on the Y axis done'))
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Offset action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Offset action was not executed"), str(e)))
                     return
 
     def on_rotate_key(self):
         val_box = FCInputDialog(title=_("Rotate ..."),
-                                text=_('Enter an Angle Value (degrees):'),
+                                text='%s:' % _('Enter an Angle Value (degrees)'),
                                 min=-359.9999, max=360.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_rotate']))
         val_box.setWindowIcon(QtGui.QIcon('share/rotate.png'))
@@ -1485,20 +1514,18 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_rotate(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape rotate done...")
-                )
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape rotate done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape rotate cancelled...")
-                )
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Geometry shape rotate cancelled"))
 
     def on_offx_key(self):
         units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
         val_box = FCInputDialog(title=_("Offset on X axis ..."),
-                                text=(_('Enter a distance Value (%s):') % str(units)),
+                                text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
                                 min=-9999.9999, max=10000.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_offset_x']))
         val_box.setWindowIcon(QtGui.QIcon('share/offsetx32.png'))
@@ -1506,18 +1533,18 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_offx(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape offset on X axis done..."))
+            self.app.inform.emit('[success] %s' %
+                                 _("Geometry shape offset on X axis done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape offset X cancelled..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Geometry shape offset X cancelled"))
 
     def on_offy_key(self):
         units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
         val_box = FCInputDialog(title=_("Offset on Y axis ..."),
-                                text=(_('Enter a distance Value (%s):') % str(units)),
+                                text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
                                 min=-9999.9999, max=10000.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_offset_y']))
         val_box.setWindowIcon(QtGui.QIcon('share/offsety32.png'))
@@ -1525,16 +1552,16 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_offx(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape offset on Y axis done..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape offset on Y axis done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape offset Y cancelled..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape offset on Y axis canceled"))
 
     def on_skewx_key(self):
         val_box = FCInputDialog(title=_("Skew on X axis ..."),
-                                text=_('Enter an Angle Value (degrees):'),
+                                text='%s:' % _('Enter an Angle Value (degrees)'),
                                 min=-359.9999, max=360.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_skew_x']))
         val_box.setWindowIcon(QtGui.QIcon('share/skewX.png'))
@@ -1542,16 +1569,16 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_skewx(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape skew on X axis done..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape skew on X axis done"))
             return
         else:
-            self.app.inform.emit(
-               _("[WARNING_NOTCL] Geometry shape skew X cancelled..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape skew on X axis canceled"))
 
     def on_skewy_key(self):
         val_box = FCInputDialog(title=_("Skew on Y axis ..."),
-                                text=_('Enter an Angle Value (degrees):'),
+                                text='%s:' % _('Enter an Angle Value (degrees)'),
                                 min=-359.9999, max=360.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_skew_y']))
         val_box.setWindowIcon(QtGui.QIcon('share/skewY.png'))
@@ -1559,12 +1586,12 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_skewx(val=val)
-            self.app.inform.emit(
-               _("[success] Geometry shape skew on Y axis done..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape skew on Y axis done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape skew Y cancelled..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape skew on Y axis canceled"))
 
 
 class DrawToolShape(object):
@@ -1964,7 +1991,8 @@ class FCCircle(FCShapeTool):
         radius = distance(p1, p2)
         self.geometry = DrawToolShape(Point(p1).buffer(radius, int(self.steps_per_circ / 4)))
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Adding Circle completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Adding Circle completed."))
 
 
 class FCArc(FCShapeTool):
@@ -2178,7 +2206,8 @@ class FCArc(FCShapeTool):
             self.geometry = DrawToolShape(LineString(arc(center, radius, startangle, stopangle,
                                                          self.direction, self.steps_per_circ)))
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Arc completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Arc completed."))
 
 
 class FCRectangle(FCShapeTool):
@@ -2231,7 +2260,8 @@ class FCRectangle(FCShapeTool):
         # self.geometry = LinearRing([p1, (p2[0], p1[1]), p2, (p1[0], p2[1])])
         self.geometry = DrawToolShape(Polygon([p1, (p2[0], p1[1]), p2, (p1[0], p2[1])]))
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Rectangle completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Rectangle completed."))
 
 
 class FCPolygon(FCShapeTool):
@@ -2285,7 +2315,8 @@ class FCPolygon(FCShapeTool):
         self.geometry = DrawToolShape(Polygon(self.points))
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Polygon completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Polygon completed."))
 
     def on_key(self, key):
         if key == 'Backspace' or key == QtCore.Qt.Key_Backspace:
@@ -2440,7 +2471,8 @@ class FCMove(FCShapeTool):
         self.selection_shape = self.selection_bbox()
 
         if len(self.draw_app.get_selected()) == 0:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] MOVE: No shape selected. Select a shape to move ..."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s...' %
+                                          _("MOVE: No shape selected. Select a shape to move"))
         else:
             self.draw_app.app.inform.emit(_(" MOVE: Click on reference point ..."))
 
@@ -2479,7 +2511,8 @@ class FCMove(FCShapeTool):
             # Delete old
             self.draw_app.delete_selected()
             self.complete = True
-            self.draw_app.app.inform.emit(_("[success] Done. Geometry(s) Move completed."))
+            self.draw_app.app.inform.emit('[success] %s' %
+                                          _("Done. Geometry(s) Move completed."))
 
     def selection_bbox(self):
         geo_list = []
@@ -2599,7 +2632,8 @@ class FCCopy(FCMove):
         self.geometry = [DrawToolShape(affinity.translate(geom.geo, xoff=dx, yoff=dy))
                          for geom in self.draw_app.get_selected()]
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Geometry(s) Copy completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Geometry(s) Copy completed."))
 
 
 class FCText(FCShapeTool):
@@ -2635,20 +2669,23 @@ class FCText(FCShapeTool):
                 self.geometry = DrawToolShape(affinity.translate(self.text_gui.text_path, xoff=dx, yoff=dy))
             except Exception as e:
                 log.debug("Font geometry is empty or incorrect: %s" % str(e))
-                self.draw_app.app.inform.emit(_("[ERROR]Font not supported. Only Regular, Bold, Italic and BoldItalic are "
-                                              "supported. Error: %s") % str(e))
+                self.draw_app.app.inform.emit('[ERROR] %s: %s' %
+                                              (_("Font not supported. Only Regular, Bold, Italic and BoldItalic are "
+                                              "supported. Error"), str(e)))
                 self.text_gui.text_path = []
                 self.text_gui.hide_tool()
                 self.draw_app.select_tool('select')
                 return
         else:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] No text to add."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("No text to add."))
             return
 
         self.text_gui.text_path = []
         self.text_gui.hide_tool()
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Adding Text completed."))
+        self.draw_app.app.inform.emit('[success]%s' %
+                                      _(" Done. Adding Text completed."))
 
     def utility_geometry(self, data=None):
         """
@@ -2687,7 +2724,8 @@ class FCBuffer(FCShapeTool):
 
     def on_buffer(self):
         if not self.draw_app.selected:
-            self.app.inform.emit(_("[WARNING_NOTCL] Buffer cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Buffer cancelled. No shape selected."))
             return
 
         try:
@@ -2698,8 +2736,8 @@ class FCBuffer(FCShapeTool):
                 buffer_distance = float(self.buff_tool.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buff_tool.buffer_distance_entry.set_value(buffer_distance)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Buffer distance value is missing or wrong format. "
-                                     "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # I populated the combobox such that the index coincide with the join styles value (whcih is really an INT)
@@ -2711,11 +2749,13 @@ class FCBuffer(FCShapeTool):
         self.disactivate()
         if ret_val == 'fail':
             return
-        self.draw_app.app.inform.emit(_("[success] Done. Buffer Tool completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Buffer Tool completed."))
 
     def on_buffer_int(self):
         if not self.draw_app.selected:
-            self.app.inform.emit(_("[WARNING_NOTCL] Buffer cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Buffer cancelled. No shape selected."))
             return
 
         try:
@@ -2726,8 +2766,8 @@ class FCBuffer(FCShapeTool):
                 buffer_distance = float(self.buff_tool.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buff_tool.buffer_distance_entry.set_value(buffer_distance)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Buffer distance value is missing or wrong format. "
-                                     "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # I populated the combobox such that the index coincide with the join styles value (whcih is really an INT)
@@ -2739,11 +2779,13 @@ class FCBuffer(FCShapeTool):
         self.disactivate()
         if ret_val == 'fail':
             return
-        self.draw_app.app.inform.emit(_("[success] Done. Buffer Int Tool completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Buffer Int Tool completed."))
 
     def on_buffer_ext(self):
         if not self.draw_app.selected:
-            self.app.inform.emit(_("[WARNING_NOTCL] Buffer cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Buffer cancelled. No shape selected."))
             return
 
         try:
@@ -2754,8 +2796,8 @@ class FCBuffer(FCShapeTool):
                 buffer_distance = float(self.buff_tool.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buff_tool.buffer_distance_entry.set_value(buffer_distance)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Buffer distance value is missing or wrong format. "
-                                     "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # I populated the combobox such that the index coincide with the join styles value (whcih is really an INT)
@@ -2767,7 +2809,8 @@ class FCBuffer(FCShapeTool):
         self.disactivate()
         if ret_val == 'fail':
             return
-        self.draw_app.app.inform.emit(_("[success] Done. Buffer Ext Tool completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Buffer Ext Tool completed."))
 
     def activate(self):
         self.buff_tool.buffer_button.clicked.disconnect()
@@ -2862,7 +2905,8 @@ class FCEraser(FCShapeTool):
 
         self.draw_app.delete_utility_geometry()
         self.draw_app.plot_all()
-        self.draw_app.app.inform.emit(_("[success] Done. Eraser tool action completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Eraser tool action completed."))
 
     def utility_geometry(self, data=None):
         """
@@ -3497,8 +3541,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
         if multigeo_tool:
             self.multigeo_tool = multigeo_tool
             geo_to_edit = fcgeometry.flatten(geometry=fcgeometry.tools[self.multigeo_tool]['solid_geometry'])
-            self.app.inform.emit(_("[WARNING_NOTCL] Editing MultiGeo Geometry, tool: {tool} with diameter: {dia}").
-                                 format(tool=self.multigeo_tool, dia=fcgeometry.tools[self.multigeo_tool]['tooldia']))
+            self.app.inform.emit('[WARNING_NOTCL] %s: %s %s: %s' %
+                                 (_("Editing MultiGeo Geometry, tool"),
+                                 str(self.multigeo_tool),
+                                 _("with diameter"),
+                                 str(fcgeometry.tools[self.multigeo_tool]['tooldia'])
+                                 )
+                                 )
         else:
             geo_to_edit = fcgeometry.flatten()
 
@@ -3738,7 +3787,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
                             self.active_tool.complete = True
                             self.in_action = False
                             self.delete_utility_geometry()
-                            self.app.inform.emit(_("[success] Done."))
+                            self.app.inform.emit('[success] %s' %
+                                                 _("Done."))
                             self.select_tool('select')
                         else:
                             self.app.cursor = QtGui.QCursor()
@@ -3752,7 +3802,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
                             self.active_tool.make()
                             if self.active_tool.complete:
                                 self.on_shape_complete()
-                                self.app.inform.emit(_("[success] Done."))
+                                self.app.inform.emit('[success] %s' %
+                                                     _("Done."))
                                 self.select_tool(self.active_tool.name)
         except Exception as e:
             log.warning("Error: %s" % str(e))
@@ -3869,7 +3920,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
     def on_copy_click(self):
         if not self.selected:
-            self.app.inform.emit(_("[WARNING_NOTCL] Copy cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Copy cancelled. No shape selected."))
             return
 
         self.app.ui.geo_copy_btn.setChecked(True)
@@ -4119,8 +4171,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
             results = geo_shapes[0].geo
         except Exception as e:
             log.debug("FlatCAMGeoEditor.intersection() --> %s" % str(e))
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] A selection of at least 2 geo items is required to do Intersection."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("A selection of at least 2 geo items is required to do Intersection."))
             self.select_tool('select')
             return
 
@@ -4154,8 +4206,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
             intersector = geo_shapes[0].geo
         except Exception as e:
             log.debug("FlatCAMGeoEditor.intersection() --> %s" % str(e))
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] A selection of at least 2 geo items is required to do Intersection."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("A selection of at least 2 geo items is required to do Intersection."))
             self.select_tool('select')
             return
 
@@ -4240,9 +4292,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
         selected = self.get_selected()
 
         if buf_distance < 0:
-            self.app.inform.emit(
-               _("[ERROR_NOTCL] Negative buffer value is not accepted. "
-                 "Use Buffer interior to generate an 'inside' shape"))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Negative buffer value is not accepted. Use Buffer interior to generate an "
+                                   "'inside' shape"))
 
             # deselect everything
             self.selected = []
@@ -4250,11 +4302,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
             return 'fail'
 
         if len(selected) == 0:
-            self.app.inform.emit(_("[WARNING_NOTCL] Nothing selected for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Nothing selected for buffering."))
             return 'fail'
 
         if not isinstance(buf_distance, float):
-            self.app.inform.emit(_("[WARNING_NOTCL] Invalid distance for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Invalid distance for buffering."))
 
             # deselect everything
             self.selected = []
@@ -4277,7 +4331,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
                 )
 
         if not results:
-            self.app.inform.emit(_("[ERROR_NOTCL] Failed, the result is empty. Choose a different buffer value."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Failed, the result is empty. Choose a different buffer value."))
             # deselect everything
             self.selected = []
             self.replot()
@@ -4287,14 +4342,15 @@ class FlatCAMGeoEditor(QtCore.QObject):
             self.add_shape(DrawToolShape(sha))
 
         self.replot()
-        self.app.inform.emit(_("[success] Full buffer geometry created."))
+        self.app.inform.emit('[success] %s' %
+                             _("Full buffer geometry created."))
 
     def buffer_int(self, buf_distance, join_style):
         selected = self.get_selected()
 
         if buf_distance < 0:
-            self.app.inform.emit(
-                _("[ERROR_NOTCL] Negative buffer value is not accepted.")
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Negative buffer value is not accepted.")
             )
             # deselect everything
             self.selected = []
@@ -4302,11 +4358,13 @@ class FlatCAMGeoEditor(QtCore.QObject):
             return 'fail'
 
         if len(selected) == 0:
-            self.app.inform.emit(_("[WARNING_NOTCL] Nothing selected for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Nothing selected for buffering."))
             return 'fail'
 
         if not isinstance(buf_distance, float):
-            self.app.inform.emit(_("[WARNING_NOTCL] Invalid distance for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Invalid distance for buffering."))
             # deselect everything
             self.selected = []
             self.replot()
@@ -4325,7 +4383,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
                 )
 
         if not results:
-            self.app.inform.emit(_("[ERROR_NOTCL] Failed, the result is empty. Choose a smaller buffer value."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Failed, the result is empty. Choose a smaller buffer value."))
             # deselect everything
             self.selected = []
             self.replot()
@@ -4335,25 +4394,29 @@ class FlatCAMGeoEditor(QtCore.QObject):
             self.add_shape(DrawToolShape(sha))
 
         self.replot()
-        self.app.inform.emit(_("[success] Interior buffer geometry created."))
+        self.app.inform.emit('[success] %s' %
+                             _("Interior buffer geometry created."))
 
     def buffer_ext(self, buf_distance, join_style):
         selected = self.get_selected()
 
         if buf_distance < 0:
-            self.app.inform.emit(_("[ERROR_NOTCL] Negative buffer value is not accepted. "
-                                   "Use Buffer interior to generate an 'inside' shape"))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Negative buffer value is not accepted. Use Buffer interior to generate an "
+                                   "'inside' shape"))
             # deselect everything
             self.selected = []
             self.replot()
             return
 
         if len(selected) == 0:
-            self.app.inform.emit(_("[WARNING_NOTCL] Nothing selected for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Nothing selected for buffering."))
             return
 
         if not isinstance(buf_distance, float):
-            self.app.inform.emit(_("[WARNING_NOTCL] Invalid distance for buffering."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Invalid distance for buffering."))
             # deselect everything
             self.selected = []
             self.replot()
@@ -4372,7 +4435,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
                 )
 
         if not results:
-            self.app.inform.emit(_("[ERROR_NOTCL] Failed, the result is empty. Choose a different buffer value."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Failed, the result is empty. Choose a different buffer value."))
             # deselect everything
             self.selected = []
             self.replot()
@@ -4382,7 +4446,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
             self.add_shape(DrawToolShape(sha))
 
         self.replot()
-        self.app.inform.emit(_("[success] Exterior buffer geometry created."))
+        self.app.inform.emit('[success] %s' %
+                             _("Exterior buffer geometry created."))
 
     # def paint(self, tooldia, overlap, margin, method):
     #     selected = self.get_selected()
@@ -4446,19 +4511,21 @@ class FlatCAMGeoEditor(QtCore.QObject):
         selected = self.get_selected()
 
         if len(selected) == 0:
-            self.app.inform.emit(_("[WARNING_NOTCL] Nothing selected for painting."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Nothing selected for painting."))
             return
 
         for param in [tooldia, overlap, margin]:
             if not isinstance(param, float):
                 param_name = [k for k, v in locals().items() if v is param][0]
-                self.app.inform.emit(_("[WARNING] Invalid value for {}").format(param))
+                self.app.inform.emit('[WARNING] %s: %s' %
+                                     (_("Invalid value for"), str(param)))
 
         results = []
 
         if overlap >= 1:
-            self.app.inform.emit(
-                _("[ERROR_NOTCL] Could not do Paint. Overlap value has to be less than 1.00 (100%)."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Could not do Paint. Overlap value has to be less than 1.00 (100%%)."))
             return
 
         def recurse(geometry, reset=True):
@@ -4516,8 +4583,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
                         local_results += list(cp.get_objects())
                 except Exception as e:
                     log.debug("Could not Paint the polygons. %s" % str(e))
-                    self.app.inform.emit(
-                        _("[ERROR] Could not do Paint. Try a different combination of parameters. "
+                    self.app.inform.emit('[ERROR] %s' %
+                                         _("Could not do Paint. Try a different combination of parameters. "
                           "Or a different method of Paint\n%s") % str(e))
                     return
 

+ 182 - 123
flatcamEditors/FlatCAMGrbEditor.py

@@ -204,14 +204,15 @@ class FCPad(FCShapeTool):
         try:
             self.radius = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) / 2
         except KeyError:
-            self.draw_app.app.inform.emit(_(
-                "[WARNING_NOTCL] To add an Pad first select a aperture in Aperture Table"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("To add an Pad first select a aperture in Aperture Table"))
             self.draw_app.in_action = False
             self.complete = True
             return
 
         if self.radius == 0:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Aperture size is zero. It needs to be greater than zero."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("Aperture size is zero. It needs to be greater than zero."))
             self.dont_execute = True
             return
         else:
@@ -374,7 +375,8 @@ class FCPad(FCShapeTool):
 
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Adding Pad completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Adding Pad completed."))
 
     def clean_up(self):
         self.draw_app.selected = []
@@ -395,15 +397,16 @@ class FCPadArray(FCShapeTool):
         try:
             self.radius = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) / 2
         except KeyError:
-            self.draw_app.app.inform.emit(_(
-                "[WARNING_NOTCL] To add an Pad Array first select a aperture in Aperture Table"))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("To add an Pad Array first select a aperture in Aperture Table"))
             self.complete = True
             self.draw_app.in_action = False
             self.draw_app.array_frame.hide()
             return
 
         if self.radius == 0:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Aperture size is zero. It needs to be greater than zero."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("Aperture size is zero. It needs to be greater than zero."))
             self.dont_execute = True
             return
         else:
@@ -498,11 +501,12 @@ class FCPadArray(FCShapeTool):
                 self.pad_linear_angle = float(self.draw_app.linear_angle_spinner.get_value())
                 self.pad_angle = float(self.draw_app.pad_angle_entry.get_value())
             except TypeError:
-                self.draw_app.app.inform.emit(
-                    _("[ERROR_NOTCL] The value is not Float. Check for comma instead of dot separator."))
+                self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                              _("The value is not Float. Check for comma instead of dot separator."))
                 return
         except Exception as e:
-            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] The value is mistyped. Check the value."))
+            self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                          _("The value is mistyped. Check the value."))
             return
 
         if self.pad_array == 'Linear':
@@ -692,7 +696,8 @@ class FCPadArray(FCShapeTool):
                 self.geometry.append(DrawToolShape(geo))
         else:
             if (self.pad_angle * self.pad_array_size) > 360:
-                self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Too many Pads for the selected spacing angle."))
+                self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                              _("Too many Pads for the selected spacing angle."))
                 return
 
             radius = distance(self.destination, self.origin)
@@ -714,7 +719,8 @@ class FCPadArray(FCShapeTool):
 
                 self.geometry.append(DrawToolShape(geo))
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Pad Array added."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Pad Array added."))
         self.draw_app.in_action = False
         self.draw_app.array_frame.hide()
         return
@@ -746,7 +752,8 @@ class FCPoligonize(FCShapeTool):
         if not self.draw_app.selected:
             self.draw_app.in_action = False
             self.complete = True
-            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] Failed. Nothing selected."))
+            self.draw_app.app.inform.emit('[ERROR_NOTCL] %s' %
+                                          _("Failed. Nothing selected."))
             self.draw_app.select_tool("select")
             return
 
@@ -761,8 +768,9 @@ class FCPoligonize(FCShapeTool):
         if len(apid_set) > 1:
             self.draw_app.in_action = False
             self.complete = True
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Failed. Poligonize works only on "
-                                            "geometries belonging to the same aperture."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' %
+                                          _("Failed. Poligonize works only on geometries belonging "
+                                            "to the same aperture."))
             self.draw_app.select_tool("select")
             return
 
@@ -814,7 +822,8 @@ class FCPoligonize(FCShapeTool):
 
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done. Poligonize completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Poligonize completed."))
 
         # MS: always return to the Select Tool if modifier key is not pressed
         # else return to the current tool
@@ -1054,7 +1063,8 @@ class FCRegion(FCShapeTool):
             self.geometry = DrawToolShape(new_geo_el)
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done."))
 
     def clean_up(self):
         self.draw_app.selected = []
@@ -1157,7 +1167,8 @@ class FCTrack(FCRegion):
 
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done."))
 
     def clean_up(self):
         self.draw_app.selected = []
@@ -1430,7 +1441,8 @@ class FCDisc(FCShapeTool):
 
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done."))
 
     def clean_up(self):
         self.draw_app.selected = []
@@ -1687,7 +1699,8 @@ class FCSemiDisc(FCShapeTool):
 
         self.draw_app.in_action = False
         self.complete = True
-        self.draw_app.app.inform.emit(_("[success] Done."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done."))
 
     def clean_up(self):
         self.draw_app.selected = []
@@ -1835,7 +1848,8 @@ class FCApertureMove(FCShapeTool):
         self.selected_apertures = []
 
         if len(self.draw_app.get_selected()) == 0:
-            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Nothing selected to move ..."))
+            self.draw_app.app.inform.emit('[WARNING_NOTCL] %s...' %
+                                          _("Nothing selected to move"))
             self.complete = True
             self.draw_app.select_tool("select")
             return
@@ -1958,7 +1972,8 @@ class FCApertureMove(FCShapeTool):
 
         self.draw_app.plot_all()
         self.draw_app.build_ui()
-        self.draw_app.app.inform.emit(_("[success] Done. Apertures Move completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Apertures Move completed."))
 
     def clean_up(self):
         self.draw_app.selected = []
@@ -2034,7 +2049,8 @@ class FCApertureCopy(FCApertureMove):
             sel_shapes_to_be_deleted = []
 
         self.draw_app.build_ui()
-        self.draw_app.app.inform.emit(_("[success] Done. Apertures copied."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Apertures copied."))
 
 
 class FCEraser(FCShapeTool):
@@ -2141,7 +2157,8 @@ class FCEraser(FCShapeTool):
 
         self.draw_app.delete_utility_geometry()
         self.draw_app.plot_all()
-        self.draw_app.app.inform.emit(_("[success] Done. Eraser tool action completed."))
+        self.draw_app.app.inform.emit('[success] %s' %
+                                      _("Done. Eraser tool action completed."))
 
     def clean_up(self):
         self.draw_app.selected = []
@@ -2448,7 +2465,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.apdim_entry = EvalEntry2()
         grid1.addWidget(self.apdim_entry, 4, 1)
 
-        apadd_del_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Add/Delete Aperture:'))
+        apadd_del_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Add/Delete Aperture'))
         apadd_del_lbl.setToolTip(
             _("Add/Delete an aperture in the aperture table")
         )
@@ -2481,7 +2498,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.buffer_tool_frame.hide()
 
         # Title
-        buf_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Buffer Aperture:'))
+        buf_title_lbl = QtWidgets.QLabel('<b>%s:</b>' % _('Buffer Aperture'))
         buf_title_lbl.setToolTip(
             _("Buffer a aperture in the aperture list")
         )
@@ -3132,12 +3149,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
             try:
                 ap_id = str(self.apcode_entry.get_value())
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Aperture code value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Aperture code value is missing or wrong format. Add it and retry."))
                 return
             if ap_id == '':
-                self.app.inform.emit(_("[WARNING_NOTCL] Aperture code value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Aperture code value is missing or wrong format. Add it and retry."))
                 return
 
         if ap_id == '0':
@@ -3172,7 +3189,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                     except Exception as e:
                         log.error("FlatCAMGrbEditor.on_aperture_add() --> the R or O aperture dims has to be in a "
                                   "tuple format (x,y)\nError: %s" % str(e))
-                        self.app.inform.emit(_("[WARNING_NOTCL] Aperture dimensions value is missing or wrong format. "
+                        self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                             _("Aperture dimensions value is missing or wrong format. "
                                                "Add it in format (width, height) and retry."))
                         return
                 else:
@@ -3184,8 +3202,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                             size_val = float(self.apsize_entry.get_value().replace(',', '.'))
                             self.apsize_entry.set_value(size_val)
                         except ValueError:
-                            self.app.inform.emit(_("[WARNING_NOTCL] Aperture size value is missing or wrong format. "
-                                                   "Add it and retry."))
+                            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                                 _("Aperture size value is missing or wrong format. Add it and retry."))
                             return
                 self.storage_dict[ap_id]['size'] = size_val
 
@@ -3195,14 +3213,16 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 # values  each time a aperture code is edited or added
                 self.olddia_newdia[ap_id] = ap_id
             else:
-                self.app.inform.emit(_("[WARNING_NOTCL] Aperture already in the aperture table."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Aperture already in the aperture table."))
                 return
 
         # since we add a new tool, we update also the initial state of the tool_table through it's dictionary
         # we add a new entry in the tool2tooldia dict
         self.tool2tooldia[len(self.olddia_newdia)] = int(ap_id)
 
-        self.app.inform.emit(_("[success] Added new aperture with code: {apid}").format(apid=str(ap_id)))
+        self.app.inform.emit('[success] %s: %s' %
+                             (_("Added new aperture with code"), str(ap_id)))
 
         self.build_ui()
 
@@ -3230,13 +3250,15 @@ class FlatCAMGrbEditor(QtCore.QObject):
             else:
                 # deleted_tool_dia = float(self.apertures_table.item(self.apertures_table.currentRow(), 1).text())
                 if len(self.apertures_table.selectionModel().selectedRows()) == 0:
-                    self.app.inform.emit(_("[WARNING_NOTCL] Select an aperture in Aperture Table"))
+                    self.app.inform.emit('[WARNING_NOTCL]%s' %
+                                         _(" Select an aperture in Aperture Table"))
                     return
                 for index in self.apertures_table.selectionModel().selectedRows():
                     row = index.row()
                     deleted_apcode_list.append(self.apertures_table.item(row, 1).text())
         except Exception as exc:
-            self.app.inform.emit(_("[WARNING_NOTCL] Select an aperture in Aperture Table --> %s" % str(exc)))
+            self.app.inform.emit('[WARNING_NOTCL] %s %s' %
+                                 (_("Select an aperture in Aperture Table -->", str(exc))))
             return
 
         if deleted_apcode_list:
@@ -3259,8 +3281,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
                     self.olddia_newdia.pop(deleted_aperture, None)
 
-                    self.app.inform.emit(_("[success] Deleted aperture with code: {del_dia}").format(
-                        del_dia=str(deleted_aperture)))
+                    self.app.inform.emit('[success] %s: %s' %
+                                         (_("Deleted aperture with code"), str(deleted_aperture)))
 
         self.plot_all()
         self.build_ui()
@@ -3910,11 +3932,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
             try:
                 grb_obj.create_geometry()
             except KeyError:
-                self.app.inform.emit(
-                   _("[ERROR_NOTCL] There are no Aperture definitions in the file. Aborting Gerber creation.")
-                )
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("There are no Aperture definitions in the file. Aborting Gerber creation."))
             except Exception as e:
-                msg = _("[ERROR] An internal error has occurred. See shell.\n")
+                msg = '[ERROR] %s' % \
+                      _("An internal error has occurred. See shell.\n")
                 msg += traceback.format_exc()
                 app_obj.inform.emit(msg)
                 raise
@@ -3927,7 +3949,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 self.app.progress.emit(100)
                 return
 
-            self.app.inform.emit(_("[success] Gerber editing finished."))
+            self.app.inform.emit('[success] %s' %
+                                 _("Done. Gerber editing finished."))
 
     def on_tool_select(self, tool):
         """
@@ -3943,7 +3966,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
             # self.draw_app.select_tool('select')
             self.complete = True
             current_tool = 'select'
-            self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. No aperture is selected"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Cancelled. No aperture is selected"))
 
         # This is to make the group behave as radio group
         if current_tool in self.tools_gerber:
@@ -4094,7 +4118,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                     self.app.clipboard.setText(
                         self.app.defaults["global_point_clipboard_format"] % (self.pos[0], self.pos[1])
                     )
-                    self.app.inform.emit(_("[success] Coordinates copied to clipboard."))
+                    self.app.inform.emit('[success] %s' %
+                                         _("Coordinates copied to clipboard."))
                     return
 
                 # Dispatch event to active_tool
@@ -4153,7 +4178,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                             self.active_tool.complete = True
                             self.in_action = False
                             self.delete_utility_geometry()
-                            self.app.inform.emit(_("[success] Done."))
+                            self.app.inform.emit('[success] %s' %
+                                                 _("Done."))
                             self.select_tool('select')
                         else:
                             self.app.cursor = QtGui.QCursor()
@@ -4167,7 +4193,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                             self.active_tool.make()
                             if self.active_tool.complete:
                                 self.on_grb_shape_complete()
-                                self.app.inform.emit(_("[success] Done."))
+                                self.app.inform.emit('[success] %s' %
+                                                     _("Done."))
 
                                 # MS: always return to the Select Tool if modifier key is not pressed
                                 # else return to the current tool but not for FCTrack
@@ -4474,7 +4501,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
         temp_ref = [s for s in self.selected]
 
         if len(temp_ref) == 0:
-            self.app.inform.emit(_("[ERROR_NOTCL] Failed. No aperture geometry is selected."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Failed. No aperture geometry is selected."))
             return
 
         for shape_sel in temp_ref:
@@ -4482,7 +4510,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
         self.selected = []
         self.build_ui()
-        self.app.inform.emit(_("[success] Done. Apertures geometry deleted."))
+        self.app.inform.emit('[success] %s' %
+                             _("Done. Apertures geometry deleted."))
 
     def delete_shape(self, geo_el):
         self.is_modified = True
@@ -4596,8 +4625,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 buff_value = float(self.buffer_distance_entry.get_value().replace(',', '.'))
                 self.buffer_distance_entry.set_value(buff_value)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Buffer distance value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Buffer distance value is missing or wrong format. Add it and retry."))
                 return
         # the cb index start from 0 but the join styles for the buffer start from 1 therefore the adjustment
         # I populated the combobox such that the index coincide with the join styles value (which is really an INT)
@@ -4624,9 +4653,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                     return geom_el
 
         if not self.apertures_table.selectedItems():
-            self.app.inform.emit(_(
-                "[WARNING_NOTCL] No aperture to buffer. Select at least one aperture and try again."
-            ))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No aperture to buffer. Select at least one aperture and try again."))
             return
 
         for x in self.apertures_table.selectedItems():
@@ -4638,10 +4666,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 self.storage_dict[apid]['geometry'] = temp_storage
             except Exception as e:
                 log.debug("FlatCAMGrbEditor.buffer() --> %s\n%s" % str(e))
-                self.app.inform.emit(_("[ERROR_NOTCL] Failed.\n%s") % str(traceback.print_exc()))
+                self.app.inform.emit('[ERROR_NOTCL] %s\n%s' %
+                                     (_("Failed."), str(traceback.print_exc())))
                 return
         self.plot_all()
-        self.app.inform.emit(_("[success] Done. Buffer Tool completed."))
+        self.app.inform.emit('[success] %s' %
+                             _("Done. Buffer Tool completed."))
 
     def on_scale(self):
         scale_factor = 1.0
@@ -4655,8 +4685,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 scale_factor = float(self.scale_factor_entry.get_value().replace(',', '.'))
                 self.scale_factor_entry.set_value(scale_factor)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Scale factor value is missing or wrong format. "
-                                     "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Scale factor value is missing or wrong format. Add it and retry."))
                 return
 
         def scale_recursion(geom_el, selection):
@@ -4687,9 +4717,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                     return geom_el
 
         if not self.apertures_table.selectedItems():
-            self.app.inform.emit(_(
-                "[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again."
-            ))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No aperture to scale. Select at least one aperture and try again."))
             return
 
         for x in self.apertures_table.selectedItems():
@@ -4704,7 +4733,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 log.debug("FlatCAMGrbEditor.on_scale() --> %s" % str(e))
 
         self.plot_all()
-        self.app.inform.emit(_("[success] Done. Scale Tool completed."))
+        self.app.inform.emit('[success] %s' %
+                             _("Done. Scale Tool completed."))
 
     def on_markarea(self):
         # clear previous marking
@@ -4741,9 +4771,11 @@ class FlatCAMGrbEditor(QtCore.QObject):
             self.ma_annotation.set(text=text, pos=position, visible=True,
                                    font_size=self.app.defaults["cncjob_annotation_fontsize"],
                                    color=self.app.defaults["global_sel_draw_color"])
-            self.app.inform.emit(_("[success] Polygon areas marked."))
+            self.app.inform.emit('[success] %s' %
+                                 _("Polygon areas marked."))
         else:
-            self.app.inform.emit(_("[WARNING_NOTCL] There are no polygons to mark area."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("There are no polygons to mark area."))
 
     def on_eraser(self):
         self.select_tool('eraser')
@@ -5226,7 +5258,8 @@ class TransformEditorTool(FlatCAMTool):
 
     def template(self):
         if not self.fcdraw.selected:
-            self.app.inform.emit(_("[WARNING_NOTCL] Transformation cancelled. No shape selected."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Transformation cancelled. No shape selected."))
             return
 
         self.draw_app.select_tool("select")
@@ -5246,8 +5279,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.rotate_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Rotate, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
         self.app.worker_task.emit({'fcn': self.on_rotate_action,
                                    'params': [value]})
@@ -5289,8 +5322,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.skewx_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew X, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_skew("X", value)
@@ -5316,8 +5349,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.skewy_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew Y, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_skew("Y", value)
@@ -5343,8 +5376,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     x_value = float(self.scalex_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale X, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
@@ -5384,8 +5417,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     y_value = float(self.scaley_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale Y, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
@@ -5422,8 +5455,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.offx_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset X, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_offset("X", value)
@@ -5448,8 +5481,8 @@ class TransformEditorTool(FlatCAMTool):
                 try:
                     value = float(self.offy_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset Y, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
         # self.on_offset("Y", value)
@@ -5471,7 +5504,8 @@ class TransformEditorTool(FlatCAMTool):
         ymaxlist = []
 
         if not elem_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to rotate!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to rotate!"))
             return
 
         with self.app.proc_container.new(_("Appying Rotate")):
@@ -5507,10 +5541,12 @@ class TransformEditorTool(FlatCAMTool):
                         sel_el['clear'] = affinity.rotate(sel_el['clear'], angle=-num, origin=(px, py))
                 self.draw_app.plot_all()
 
-                self.app.inform.emit(_("[success] Done. Rotate completed."))
+                self.app.inform.emit('[success] %s' %
+                                     _("Done. Rotate completed."))
                 self.app.progress.emit(100)
             except Exception as e:
-                self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, rotation movement was not executed.") % str(e))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                     (_("Rotation action was not executed."), str(e)))
                 return
 
     def on_flip(self, axis):
@@ -5526,7 +5562,8 @@ class TransformEditorTool(FlatCAMTool):
         ymaxlist = []
 
         if not elem_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to flip!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to flip!"))
             return
 
         with self.app.proc_container.new(_("Applying Flip")):
@@ -5568,7 +5605,8 @@ class TransformEditorTool(FlatCAMTool):
                             sel_el['follow'] = affinity.scale(sel_el['follow'], xfact=1, yfact=-1, origin=(px, py))
                         if 'clear' in sel_el:
                             sel_el['clear'] = affinity.scale(sel_el['clear'], xfact=1, yfact=-1, origin=(px, py))
-                        self.app.inform.emit(_('[success] Flip on the Y axis done ...'))
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Flip on the Y axis done'))
                     elif axis is 'Y':
                         if 'solid' in sel_el:
                             sel_el['solid'] = affinity.scale(sel_el['solid'], xfact=-1, yfact=1, origin=(px, py))
@@ -5576,12 +5614,14 @@ class TransformEditorTool(FlatCAMTool):
                             sel_el['follow'] = affinity.scale(sel_el['follow'], xfact=-1, yfact=1, origin=(px, py))
                         if 'clear' in sel_el:
                             sel_el['clear'] = affinity.scale(sel_el['clear'], xfact=-1, yfact=1, origin=(px, py))
-                        self.app.inform.emit(_('[success] Flip on the X axis done ...'))
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Flip on the X axis done'))
                 self.draw_app.plot_all()
                 self.app.progress.emit(100)
 
             except Exception as e:
-                self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Flip action was not executed.") % str(e))
+                self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                     (_("Flip action was not executed."), str(e)))
                 return
 
     def on_skew(self, axis, num):
@@ -5596,7 +5636,8 @@ class TransformEditorTool(FlatCAMTool):
         yminlist = []
 
         if not elem_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to shear/skew!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to shear/skew!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Skew")):
@@ -5634,11 +5675,17 @@ class TransformEditorTool(FlatCAMTool):
                                 sel_el['clear'] = affinity.skew(sel_el['clear'], 0, num, origin=(xminimal, yminimal))
                     self.draw_app.plot_all()
 
-                    self.app.inform.emit(_('[success] Skew on the %s axis done ...') % str(axis))
+                    if str(axis) == 'X':
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Skew on the X axis done'))
+                    else:
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Skew on the Y axis done'))
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Skew action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Skew action was not executed."), str(e)))
                     return
 
     def on_scale(self, axis, xfactor, yfactor, point=None):
@@ -5657,7 +5704,8 @@ class TransformEditorTool(FlatCAMTool):
         ymaxlist = []
 
         if not elem_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to scale!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to scale!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Scale")):
@@ -5698,10 +5746,16 @@ class TransformEditorTool(FlatCAMTool):
                             sel_el['clear'] = affinity.scale(sel_el['clear'], xfactor, yfactor, origin=(px, py))
                     self.draw_app.plot_all()
 
-                    self.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis))
+                    if str(axis) == 'X':
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Scale on the X axis done'))
+                    else:
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Scale on the Y axis done'))
                     self.app.progress.emit(100)
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Scale action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Scale action was not executed."), str(e)))
                     return
 
     def on_offset(self, axis, num):
@@ -5714,7 +5768,8 @@ class TransformEditorTool(FlatCAMTool):
         elem_list = self.draw_app.selected
 
         if not elem_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to offset!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No shape selected. Please Select a shape to offset!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Offset")):
@@ -5739,16 +5794,22 @@ class TransformEditorTool(FlatCAMTool):
                                 sel_el['clear'] = affinity.translate(sel_el['clear'], 0, num)
                         self.draw_app.plot_all()
 
-                    self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis))
+                    if str(axis) == 'X':
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Offset on the X axis done'))
+                    else:
+                        self.app.inform.emit('[success] %s...' %
+                                             _('Offset on the Y axis done'))
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Offset action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Offset action was not executed."), str(e)))
                     return
 
     def on_rotate_key(self):
         val_box = FCInputDialog(title=_("Rotate ..."),
-                                text=_('Enter an Angle Value (degrees):'),
+                                text='%s:' % _('Enter an Angle Value (degrees)'),
                                 min=-359.9999, max=360.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_rotate']))
         val_box.setWindowIcon(QtGui.QIcon('share/rotate.png'))
@@ -5756,20 +5817,18 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_rotate(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape rotate done...")
-            )
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape rotate done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape rotate cancelled...")
-            )
+            self.app.inform.emit('[WARNING_NOTCL] %s...' %
+                                 _("Geometry shape rotate cancelled"))
 
     def on_offx_key(self):
         units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
         val_box = FCInputDialog(title=_("Offset on X axis ..."),
-                                text=(_('Enter a distance Value (%s):') % str(units)),
+                                text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
                                 min=-9999.9999, max=10000.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_offset_x']))
         val_box.setWindowIcon(QtGui.QIcon('share/offsetx32.png'))
@@ -5777,18 +5836,18 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_offx(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape offset on X axis done..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape offset on X axis done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape offset X cancelled..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s...' %
+                                 _("Geometry shape offset X cancelled"))
 
     def on_offy_key(self):
         units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
         val_box = FCInputDialog(title=_("Offset on Y axis ..."),
-                                text=(_('Enter a distance Value (%s):') % str(units)),
+                                text='%s: (%s)' % (_('Enter a distance Value'), str(units)),
                                 min=-9999.9999, max=10000.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_offset_y']))
         val_box.setWindowIcon(QtGui.QIcon('share/offsety32.png'))
@@ -5796,16 +5855,16 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_offx(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape offset on Y axis done..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape offset on Y axis done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape offset Y cancelled..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s...' %
+                                 _("Geometry shape offset Y cancelled"))
 
     def on_skewx_key(self):
         val_box = FCInputDialog(title=_("Skew on X axis ..."),
-                                text=_('Enter an Angle Value (degrees):'),
+                                text='%s:' % _('Enter an Angle Value (degrees)'),
                                 min=-359.9999, max=360.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_skew_x']))
         val_box.setWindowIcon(QtGui.QIcon('share/skewX.png'))
@@ -5813,16 +5872,16 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_skewx(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape skew on X axis done..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape skew on X axis done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape skew X cancelled..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s...' %
+                                 _("Geometry shape skew X cancelled"))
 
     def on_skewy_key(self):
         val_box = FCInputDialog(title=_("Skew on Y axis ..."),
-                                text=_('Enter an Angle Value (degrees):'),
+                                text='%s:' % _('Enter an Angle Value (degrees)'),
                                 min=-359.9999, max=360.0000, decimals=4,
                                 init_val=float(self.app.defaults['tools_transform_skew_y']))
         val_box.setWindowIcon(QtGui.QIcon('share/skewY.png'))
@@ -5830,12 +5889,12 @@ class TransformEditorTool(FlatCAMTool):
         val, ok = val_box.get_value()
         if ok:
             self.on_skewx(val=val)
-            self.app.inform.emit(
-                _("[success] Geometry shape skew on Y axis done..."))
+            self.app.inform.emit('[success] %s...' %
+                                 _("Geometry shape skew on Y axis done"))
             return
         else:
-            self.app.inform.emit(
-                _("[WARNING_NOTCL] Geometry shape skew Y cancelled..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s...' %
+                                 _("Geometry shape skew Y cancelled"))
 
 
 def get_shapely_list_bounds(geometry_list):

+ 356 - 71
flatcamGUI/FlatCAMGUI.py

@@ -938,6 +938,16 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.tools_scroll_area = QtWidgets.QScrollArea()
         self.tools_tab_lay.addWidget(self.tools_scroll_area)
 
+        self.fa_tab = QtWidgets.QWidget()
+        self.fa_tab.setObjectName("fa_tab")
+        self.pref_tab_area.addTab(self.fa_tab, _("FILE ASSOCIATIONS"))
+        self.fa_tab_lay = QtWidgets.QVBoxLayout()
+        self.fa_tab_lay.setContentsMargins(2, 2, 2, 2)
+        self.fa_tab.setLayout(self.fa_tab_lay)
+
+        self.fa_scroll_area = QtWidgets.QScrollArea()
+        self.fa_tab_lay.addWidget(self.fa_scroll_area)
+
         self.pref_tab_bottom_layout = QtWidgets.QHBoxLayout()
         self.pref_tab_bottom_layout.setAlignment(QtCore.Qt.AlignVCenter)
         self.pref_tab_layout.addLayout(self.pref_tab_bottom_layout)
@@ -1243,7 +1253,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                     <tr height="20">
                         <td height="20"><strong>ALT+F10</strong></td>
                         <td>&nbsp;Toggle Full Screen</td>
+                    </tr>                 
+                    <tr height="20">
+                        <td height="20">&nbsp;</td>
+                        <td>&nbsp;</td>
                     </tr>
+                    <tr height="20">
+                        <td height="20"><strong>CTRL+ALT+X</strong></td>
+                        <td>&nbsp;Abort current task (gracefully)</td>
+                    </tr>                    
                     <tr height="20">
                         <td height="20">&nbsp;</td>
                         <td>&nbsp;</td>
@@ -1798,6 +1816,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.activity_view = FlatCAMActivityView()
         self.infobar.addWidget(self.activity_view)
 
+        # ###########################################################################
+        # ####### Set the APP ICON and the WINDOW TITLE and GEOMETRY ################
+        # ###########################################################################
+
         self.app_icon = QtGui.QIcon()
         self.app_icon.addFile('share/flatcam_icon16.png', QtCore.QSize(16, 16))
         self.app_icon.addFile('share/flatcam_icon24.png', QtCore.QSize(24, 24))
@@ -1848,6 +1870,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geometry_defaults_form = GeometryPreferencesUI()
         self.cncjob_defaults_form = CNCJobPreferencesUI()
         self.tools_defaults_form = ToolsPreferencesUI()
+        self.fa_defaults_form = FAPreferencesUI()
 
         self.general_options_form = GeneralPreferencesUI()
         self.gerber_options_form = GerberPreferencesUI()
@@ -1855,6 +1878,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geometry_options_form = GeometryPreferencesUI()
         self.cncjob_options_form = CNCJobPreferencesUI()
         self.tools_options_form = ToolsPreferencesUI()
+        self.fa_options_form = FAPreferencesUI()
 
         QtWidgets.qApp.installEventFilter(self)
 
@@ -1930,6 +1954,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.lock_action.triggered[bool].connect(self.lock_toolbar)
 
     def eventFilter(self, obj, event):
+        # filter the ToolTips display based on a Preferences setting
         if self.general_defaults_form.general_app_group.toggle_tooltips_cb.get_value() is False:
             if event.type() == QtCore.QEvent.ToolTip:
                 return True
@@ -2151,7 +2176,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             key = event.key
 
         if self.app.call_source == 'app':
-            if modifiers == QtCore.Qt.ControlModifier:
+            if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier:
+                if key == QtCore.Qt.Key_X:
+                    self.app.abort_all_tasks()
+                    return
+
+            elif modifiers == QtCore.Qt.ControlModifier:
                 if key == QtCore.Qt.Key_A:
                     self.app.on_selectall()
 
@@ -3123,7 +3153,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                     # ## Current application units in Upper Case
                     self.units = self.general_defaults_form.general_app_group.units_radio.get_value().upper()
                     tool_add_popup = FCInputDialog(title=_("New Tool ..."),
-                                                   text=_('Enter a Tool Diameter:'),
+                                                   text='%s:' % _('Enter a Tool Diameter'),
                                                    min=0.0000, max=99.9999, decimals=4)
                     tool_add_popup.setWindowIcon(QtGui.QIcon('share/letter_t_32.png'))
 
@@ -3436,7 +3466,6 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
         self.vlay = QtWidgets.QVBoxLayout()
         self.vlay.addWidget(self.tools_ncc_group)
         self.vlay.addWidget(self.tools_paint_group)
-        self.vlay.addWidget(self.tools_film_group)
 
         self.vlay1 = QtWidgets.QVBoxLayout()
         self.vlay1.addWidget(self.tools_cutout_group)
@@ -3450,6 +3479,7 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
         self.vlay3 = QtWidgets.QVBoxLayout()
         self.vlay3.addWidget(self.tools_solderpaste_group)
         self.vlay3.addWidget(self.tools_sub_group)
+        self.vlay3.addWidget(self.tools_film_group)
 
         self.layout.addLayout(self.vlay)
         self.layout.addLayout(self.vlay1)
@@ -3480,6 +3510,27 @@ class CNCJobPreferencesUI(QtWidgets.QWidget):
         self.layout.addStretch()
 
 
+class FAPreferencesUI(QtWidgets.QWidget):
+
+    def __init__(self, parent=None):
+        QtWidgets.QWidget.__init__(self, parent=parent)
+        self.layout = QtWidgets.QHBoxLayout()
+        self.setLayout(self.layout)
+
+        self.fa_excellon_group = FAExcPrefGroupUI()
+        self.fa_excellon_group.setMinimumWidth(260)
+        self.fa_gcode_group = FAGcoPrefGroupUI()
+        self.fa_gcode_group.setMinimumWidth(260)
+        self.fa_gerber_group = FAGrbPrefGroupUI()
+        self.fa_gerber_group.setMinimumWidth(260)
+
+        self.layout.addWidget(self.fa_excellon_group)
+        self.layout.addWidget(self.fa_gcode_group)
+        self.layout.addWidget(self.fa_gerber_group)
+
+        self.layout.addStretch()
+
+
 class OptionsGroupUI(QtWidgets.QGroupBox):
     def __init__(self, title, parent=None):
         # QtGui.QGroupBox.__init__(self, title, parent=parent)
@@ -3506,31 +3557,31 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box = QtWidgets.QFormLayout()
 
         # Grid X Entry
-        self.gridx_label = QtWidgets.QLabel(_('Grid X value:'))
+        self.gridx_label = QtWidgets.QLabel('%s:' % _('Grid X value'))
         self.gridx_label.setToolTip(
            _("This is the Grid snap value on X axis.")
         )
         self.gridx_entry = FCEntry3()
 
         # Grid Y Entry
-        self.gridy_label = QtWidgets.QLabel(_('Grid Y value:'))
+        self.gridy_label = QtWidgets.QLabel('%s:' % _('Grid Y value'))
         self.gridy_label.setToolTip(
             _("This is the Grid snap value on Y axis.")
         )
         self.gridy_entry = FCEntry3()
 
         # Snap Max Entry
-        self.snap_max_label = QtWidgets.QLabel(_('Snap Max:'))
+        self.snap_max_label = QtWidgets.QLabel('%s:' % _('Snap Max'))
         self.snap_max_label.setToolTip(_("Max. magnet distance"))
         self.snap_max_dist_entry = FCEntry()
 
         # Workspace
-        self.workspace_lbl = QtWidgets.QLabel(_('Workspace:'))
+        self.workspace_lbl = QtWidgets.QLabel('%s:' % _('Workspace'))
         self.workspace_lbl.setToolTip(
            _("Draw a delimiting rectangle on canvas.\n"
              "The purpose is to illustrate the limits for our work.")
         )
-        self.workspace_type_lbl = QtWidgets.QLabel(_('Wk. format:'))
+        self.workspace_type_lbl = QtWidgets.QLabel('%s:' % _('Wk. format'))
         self.workspace_type_lbl.setToolTip(
            _("Select the type of rectangle to be used on canvas,\n"
              "as valid workspace.")
@@ -3545,7 +3596,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.wks = OptionalInputSection(self.workspace_cb, [self.workspace_type_lbl, self.wk_cb])
 
         # Plot Fill Color
-        self.pf_color_label = QtWidgets.QLabel(_('Plot Fill:'))
+        self.pf_color_label = QtWidgets.QLabel('%s:' % _('Plot Fill'))
         self.pf_color_label.setToolTip(
            _("Set the fill color for plotted objects.\n"
              "First 6 digits are the color and the last 2\n"
@@ -3561,7 +3612,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Plot Fill Transparency Level
-        self.pf_alpha_label = QtWidgets.QLabel(_('Alpha Level:'))
+        self.pf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha Level'))
         self.pf_alpha_label.setToolTip(
            _("Set the fill transparency for plotted objects.")
         )
@@ -3580,7 +3631,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_2.addWidget(self.pf_color_alpha_spinner)
 
         # Plot Line Color
-        self.pl_color_label = QtWidgets.QLabel(_('Plot Line:'))
+        self.pl_color_label = QtWidgets.QLabel('%s:' % _('Plot Line'))
         self.pl_color_label.setToolTip(
            _("Set the line color for plotted objects.")
         )
@@ -3594,7 +3645,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_3.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Plot Selection (left - right) Fill Color
-        self.sf_color_label = QtWidgets.QLabel(_('Sel. Fill:'))
+        self.sf_color_label = QtWidgets.QLabel('%s:' % _('Sel. Fill'))
         self.sf_color_label.setToolTip(
             _("Set the fill color for the selection box\n"
               "in case that the selection is done from left to right.\n"
@@ -3611,7 +3662,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_4.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Plot Selection (left - right) Fill Transparency Level
-        self.sf_alpha_label = QtWidgets.QLabel(_('Alpha Level:'))
+        self.sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha Level'))
         self.sf_alpha_label.setToolTip(
             _("Set the fill transparency for the 'left to right' selection box.")
         )
@@ -3630,7 +3681,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_5.addWidget(self.sf_color_alpha_spinner)
 
         # Plot Selection (left - right) Line Color
-        self.sl_color_label = QtWidgets.QLabel(_('Sel. Line:'))
+        self.sl_color_label = QtWidgets.QLabel('%s:' % _('Sel. Line'))
         self.sl_color_label.setToolTip(
             _("Set the line color for the 'left to right' selection box.")
         )
@@ -3644,7 +3695,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_6.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Plot Selection (right - left) Fill Color
-        self.alt_sf_color_label = QtWidgets.QLabel(_('Sel2. Fill:'))
+        self.alt_sf_color_label = QtWidgets.QLabel('%s:' % _('Sel2. Fill'))
         self.alt_sf_color_label.setToolTip(
             _("Set the fill color for the selection box\n"
               "in case that the selection is done from right to left.\n"
@@ -3661,7 +3712,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_7.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Plot Selection (right - left) Fill Transparency Level
-        self.alt_sf_alpha_label = QtWidgets.QLabel(_('Alpha Level:'))
+        self.alt_sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha Level'))
         self.alt_sf_alpha_label.setToolTip(
             _("Set the fill transparency for selection 'right to left' box.")
         )
@@ -3680,7 +3731,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_8.addWidget(self.alt_sf_color_alpha_spinner)
 
         # Plot Selection (right - left) Line Color
-        self.alt_sl_color_label = QtWidgets.QLabel(_('Sel2. Line:'))
+        self.alt_sl_color_label = QtWidgets.QLabel('%s:' % _('Sel2. Line'))
         self.alt_sl_color_label.setToolTip(
             _("Set the line color for the 'right to left' selection box.")
         )
@@ -3694,7 +3745,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_9.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Editor Draw Color
-        self.draw_color_label = QtWidgets.QLabel(_('Editor Draw:'))
+        self.draw_color_label = QtWidgets.QLabel('%s:' % _('Editor Draw'))
         self.alt_sf_color_label.setToolTip(
             _("Set the color for the shape.")
         )
@@ -3708,7 +3759,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_10.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Editor Draw Selection Color
-        self.sel_draw_color_label = QtWidgets.QLabel(_('Editor Draw Sel.:'))
+        self.sel_draw_color_label = QtWidgets.QLabel('%s:' % _('Editor Draw Sel.'))
         self.sel_draw_color_label.setToolTip(
             _("Set the color of the shape when selected.")
         )
@@ -3722,7 +3773,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_11.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
         # Project Tab items color
-        self.proj_color_label = QtWidgets.QLabel(_('Project Items:'))
+        self.proj_color_label = QtWidgets.QLabel('%s:' % _('Project Items'))
         self.proj_color_label.setToolTip(
             _("Set the color of the items in Project Tab Tree.")
         )
@@ -3735,7 +3786,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.form_box_child_12.addWidget(self.proj_color_button)
         self.form_box_child_12.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
-        self.proj_color_dis_label = QtWidgets.QLabel(_('Proj. Dis. Items:'))
+        self.proj_color_dis_label = QtWidgets.QLabel('%s:' % _('Proj. Dis. Items'))
         self.proj_color_dis_label.setToolTip(
             _("Set the color of the items in Project Tab Tree,\n"
               "for the case when the items are disabled.")
@@ -3794,7 +3845,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         self.form_box = QtWidgets.QFormLayout()
 
         # Layout selection
-        self.layout_label = QtWidgets.QLabel(_('Layout:'))
+        self.layout_label = QtWidgets.QLabel('%s:' % _('Layout'))
         self.layout_label.setToolTip(
             _("Select an layout for FlatCAM.\n"
               "It is applied immediately.")
@@ -3812,7 +3863,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
             self.layout_combo.setCurrentIndex(idx)
 
         # Style selection
-        self.style_label = QtWidgets.QLabel(_('Style:'))
+        self.style_label = QtWidgets.QLabel('%s:' % _('Style'))
         self.style_label.setToolTip(
             _("Select an style for FlatCAM.\n"
               "It will be applied at the next app start.")
@@ -3825,7 +3876,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         self.style_combo.activated[str].connect(self.handle_style)
 
         # Enable High DPI Support
-        self.hdpi_label = QtWidgets.QLabel(_('HDPI Support:'))
+        self.hdpi_label = QtWidgets.QLabel('%s:' % _('HDPI Support'))
         self.hdpi_label.setToolTip(
             _("Enable High DPI support for FlatCAM.\n"
               "It will be applied at the next app start.")
@@ -3840,7 +3891,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         self.hdpi_cb.stateChanged.connect(self.handle_hdpi)
 
         # Clear Settings
-        self.clear_label = QtWidgets.QLabel(_('Clear GUI Settings:'))
+        self.clear_label = QtWidgets.QLabel('%s:' % _('Clear GUI Settings'))
         self.clear_label.setToolTip(
             _("Clear the GUI settings for FlatCAM,\n"
               "such as: layout, gui state, style, hdpi support etc.")
@@ -3849,7 +3900,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         self.clear_btn.clicked.connect(self.handle_clear)
 
         # Enable Hover box
-        self.hover_label = QtWidgets.QLabel(_('Hover Shape:'))
+        self.hover_label = QtWidgets.QLabel('%s:' % _('Hover Shape'))
         self.hover_label.setToolTip(
             _("Enable display of a hover shape for FlatCAM objects.\n"
               "It is displayed whenever the mouse cursor is hovering\n"
@@ -3858,7 +3909,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         self.hover_cb = FCCheckBox()
 
         # Enable Selection box
-        self.selection_label = QtWidgets.QLabel(_('Sel. Shape:'))
+        self.selection_label = QtWidgets.QLabel('%s:' % _('Sel. Shape'))
         self.selection_label.setToolTip(
             _("Enable the display of a selection shape for FlatCAM objects.\n"
               "It is displayed whenever the mouse selects an object\n"
@@ -3867,7 +3918,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         )
         self.selection_cb = FCCheckBox()
 
-        self.notebook_font_size_label = QtWidgets.QLabel(_('NB Font Size:'))
+        self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('NB Font Size'))
         self.notebook_font_size_label.setToolTip(
             _("This sets the font size for the elements found in the Notebook.\n"
               "The notebook is the collapsible area in the left side of the GUI,\n"
@@ -3884,7 +3935,7 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         else:
             self.notebook_font_size_spinner.set_value(12)
 
-        self.axis_font_size_label = QtWidgets.QLabel(_('Axis Font Size:'))
+        self.axis_font_size_label = QtWidgets.QLabel('%s:' % _('Axis Font Size'))
         self.axis_font_size_label.setToolTip(
             _("This sets the font size for canvas axis.")
         )
@@ -4053,7 +4104,7 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
                                           {'label': _('RMB'), 'value': '2'}])
 
         # Multiple Selection Modifier Key
-        self.mselectlabel = QtWidgets.QLabel('<b>%s:</b>' % _('Multiple Sel:'))
+        self.mselectlabel = QtWidgets.QLabel('<b>%s:</b>' % _('Multiple Sel'))
         self.mselectlabel.setToolTip(_("Select the key used for multiple selection."))
         self.mselect_radio = RadioSet([{'label': _('CTRL'), 'value': 'Control'},
                                        {'label': _('SHIFT'), 'value': 'Shift'}])
@@ -4263,10 +4314,23 @@ class GerberGenPrefGroupUI(OptionsGroupUI):
             _("The number of circle steps for Gerber \n"
               "circular aperture linear approximation.")
         )
-        grid0.addWidget(self.circle_steps_label, 1, 0)
         self.circle_steps_entry = IntEntry()
+        grid0.addWidget(self.circle_steps_label, 1, 0)
         grid0.addWidget(self.circle_steps_entry, 1, 1)
 
+        # Milling Type
+        buffering_label = QtWidgets.QLabel('%s:' % _('Buffering'))
+        buffering_label.setToolTip(
+            _("Buffering type:\n"
+              "- None --> best performance, fast file loading but no so good display\n"
+              "- Full --> slow file loading but good visuals. This is the default.\n"
+              "<<WARNING>>: Don't change this unless you know what you are doing !!!")
+        )
+        self.buffering_radio = RadioSet([{'label': _('None'), 'value': 'no'},
+                                          {'label': _('Full'), 'value': 'full'}])
+        grid0.addWidget(buffering_label, 2, 0)
+        grid0.addWidget(self.buffering_radio, 2, 1)
+
         self.layout.addStretch()
 
 
@@ -4319,6 +4383,7 @@ class GerberOptPrefGroupUI(OptionsGroupUI):
         self.iso_overlap_entry = FloatEntry()
         grid0.addWidget(self.iso_overlap_entry, 2, 1)
 
+        # Milling Type
         milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
         milling_type_label.setToolTip(
             _("Milling type:\n"
@@ -5055,7 +5120,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
         self.toolchangez_entry = LengthEntry()
         grid2.addWidget(self.toolchangez_entry, 3, 1)
 
-        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate (Plunge):'))
+        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate (Plunge)'))
         frlabel.setToolTip(
             _("Tool speed while drilling\n"
               "(in units per minute).\n"
@@ -5095,7 +5160,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
             _("Pause to allow the spindle to reach its\n"
               "speed before cutting.")
         )
-        dwelltime = QtWidgets.QLabel(_('Duration:'))
+        dwelltime = QtWidgets.QLabel('%s:' % _('Duration'))
         dwelltime.setToolTip(
             _("Number of time units for spindle to dwell.")
         )
@@ -5260,7 +5325,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
         self.feedrate_probe_entry = FCEntry()
         grid1.addWidget(self.feedrate_probe_entry, 6, 1)
 
-        fplungelabel = QtWidgets.QLabel(_('Fast Plunge:'))
+        fplungelabel = QtWidgets.QLabel('%s:' % _('Fast Plunge'))
         fplungelabel.setToolTip(
             _("By checking this, the vertical move from\n"
               "Z_Toolchange to Z_move is done with G0,\n"
@@ -5496,7 +5561,7 @@ class ExcellonEditorPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.drill_array_linear_label, 3, 0, 1, 2)
 
         # Linear Drill Array direction
-        self.drill_axis_label = QtWidgets.QLabel(_('Linear Dir.:'))
+        self.drill_axis_label = QtWidgets.QLabel('%s:' % _('Linear Dir.'))
         self.drill_axis_label.setToolTip(
             _("Direction on which the linear array is oriented:\n"
               "- 'X' - horizontal axis \n"
@@ -6086,8 +6151,8 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
 
         grid0 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid0)
-        grid0.setColumnStretch(1, 1)
-        grid0.setColumnStretch(2, 1)
+        # grid0.setColumnStretch(1, 1)
+        # grid0.setColumnStretch(2, 1)
 
         # Plot CB
         # self.plot_cb = QtWidgets.QCheckBox('Plot')
@@ -6096,7 +6161,7 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.plot_cb, 0, 0)
 
         # Plot Kind
-        self.cncplot_method_label = QtWidgets.QLabel('%s:' % _("Plot kind:"))
+        self.cncplot_method_label = QtWidgets.QLabel('%s:' % _("Plot kind"))
         self.cncplot_method_label.setToolTip(
             _("This selects the kind of geometries on the canvas to plot.\n"
               "Those can be either of type 'Travel' which means the moves\n"
@@ -6172,35 +6237,57 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.steps_per_circle_entry, 5, 1)
 
         # Tool dia for plot
-        tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
+        tdlabel = QtWidgets.QLabel('%s:' % _('Travel dia'))
         tdlabel.setToolTip(
-            _("Diameter of the tool to be\n"
+            _("The width of the travel lines to be\n"
               "rendered in the plot.")
         )
-        grid0.addWidget(tdlabel, 6, 0)
         self.tooldia_entry = LengthEntry()
+        grid0.addWidget(tdlabel, 6, 0)
         grid0.addWidget(self.tooldia_entry, 6, 1)
 
+        # add a space
+        grid0.addWidget(QtWidgets.QLabel(''), 7, 0)
+
         # Number of decimals to use in GCODE coordinates
-        cdeclabel = QtWidgets.QLabel('%s:' % _('Coords dec.'))
+        cdeclabel = QtWidgets.QLabel('%s:' % _('Coordinates decimals'))
         cdeclabel.setToolTip(
             _("The number of decimals to be used for \n"
               "the X, Y, Z coordinates in CNC code (GCODE, etc.)")
         )
-        grid0.addWidget(cdeclabel, 7, 0)
         self.coords_dec_entry = IntEntry()
-        grid0.addWidget(self.coords_dec_entry, 7, 1)
+        grid0.addWidget(cdeclabel, 8, 0)
+        grid0.addWidget(self.coords_dec_entry, 8, 1)
 
         # Number of decimals to use in GCODE feedrate
-        frdeclabel = QtWidgets.QLabel('%s:' % _('Feedrate dec.'))
+        frdeclabel = QtWidgets.QLabel('%s:' % _('Feedrate decimals'))
         frdeclabel.setToolTip(
             _("The number of decimals to be used for \n"
               "the Feedrate parameter in CNC code (GCODE, etc.)")
         )
-        grid0.addWidget(frdeclabel, 8, 0)
         self.fr_dec_entry = IntEntry()
-        grid0.addWidget(self.fr_dec_entry, 8, 1)
+        grid0.addWidget(frdeclabel, 9, 0)
+        grid0.addWidget(self.fr_dec_entry, 9, 1)
+
+        # The type of coordinates used in the Gcode: Absolute or Incremental
+        coords_type_label = QtWidgets.QLabel('%s:' % _('Coordinates type'))
+        coords_type_label.setToolTip(
+            _("The type of coordinates to be used in Gcode.\n"
+              "Can be:\n"
+              "- Absolute G90 -> the reference is the origin x=0, y=0\n"
+              "- Incremental G91 -> the reference is the previous position")
+        )
+        self.coords_type_radio = RadioSet([
+            {"label": _("Absolute G90"), "value": "G90"},
+            {"label": _("Incremental G91"), "value": "G91"}
+        ], orientation='vertical', stretch=False)
+        grid0.addWidget(coords_type_label, 10, 0)
+        grid0.addWidget(self.coords_type_radio, 10, 1)
 
+        # hidden for the time being, until implemented
+        coords_type_label.hide()
+        self.coords_type_radio.hide()
+        
         self.layout.addStretch()
 
 
@@ -6361,6 +6448,64 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         self.ncc_tool_dia_entry = FCEntry()
         grid0.addWidget(self.ncc_tool_dia_entry, 0, 1)
 
+        # Tool Type Radio Button
+        self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type'))
+        self.tool_type_label.setToolTip(
+            _("Default tool type:\n"
+              "- 'V-shape'\n"
+              "- Circular")
+        )
+
+        self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'},
+                                         {'label': _('Circular'), 'value': 'C1'}])
+        self.tool_type_radio.setToolTip(
+            _("Default tool type:\n"
+              "- 'V-shape'\n"
+              "- Circular")
+        )
+
+        grid0.addWidget(self.tool_type_label, 1, 0)
+        grid0.addWidget(self.tool_type_radio, 1, 1)
+
+        # Tip Dia
+        self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
+        self.tipdialabel.setToolTip(
+            _("The tip diameter for V-Shape Tool"))
+        self.tipdia_entry = LengthEntry()
+
+        grid0.addWidget(self.tipdialabel, 2, 0)
+        grid0.addWidget(self.tipdia_entry, 2, 1)
+
+        # Tip Angle
+        self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
+        self.tipanglelabel.setToolTip(
+            _("The tip angle for V-Shape Tool.\n"
+              "In degree."))
+        self.tipangle_entry = LengthEntry()
+
+        grid0.addWidget(self.tipanglelabel, 3, 0)
+        grid0.addWidget(self.tipangle_entry, 3, 1)
+
+        # Milling Type Radio Button
+        self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
+        self.milling_type_label.setToolTip(
+            _("Milling type when the selected tool is of type: 'iso_op':\n"
+              "- climb / best for precision milling and to reduce tool usage\n"
+              "- conventional / useful when there is no backlash compensation")
+        )
+
+        self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
+                                            {'label': _('Conv.'), 'value': 'cv'}])
+        self.milling_type_radio.setToolTip(
+            _("Milling type when the selected tool is of type: 'iso_op':\n"
+              "- climb / best for precision milling and to reduce tool usage\n"
+              "- conventional / useful when there is no backlash compensation")
+        )
+
+        grid0.addWidget(self.milling_type_label, 4, 0)
+        grid0.addWidget(self.milling_type_radio, 4, 1)
+
+        # Tool order Radio Button
         self.ncc_order_label = QtWidgets.QLabel('%s:' % _('Tool order'))
         self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n"
                                           "'No' --> means that the used order is the one in the tool table\n"
@@ -6378,9 +6523,25 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
                                           "'Reverse' --> menas that the tools will ordered from big to small\n\n"
                                           "WARNING: using rest machining will automatically set the order\n"
                                           "in reverse and disable this control."))
-        grid0.addWidget(self.ncc_order_label, 1, 0)
-        grid0.addWidget(self.ncc_order_radio, 1, 1)
+        grid0.addWidget(self.ncc_order_label, 5, 0)
+        grid0.addWidget(self.ncc_order_radio, 5, 1)
+
+        # Cut Z entry
+        cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
+        cutzlabel.setToolTip(
+           _("Depth of cut into material. Negative value.\n"
+             "In FlatCAM units.")
+        )
+        self.cutz_entry = FloatEntry()
+        self.cutz_entry.setToolTip(
+           _("Depth of cut into material. Negative value.\n"
+             "In FlatCAM units.")
+        )
+
+        grid0.addWidget(cutzlabel, 6, 0)
+        grid0.addWidget(self.cutz_entry, 6, 1)
 
+        # Overlap Entry
         nccoverlabel = QtWidgets.QLabel('%s:' % _('Overlap Rate'))
         nccoverlabel.setToolTip(
            _("How much (fraction) of the tool width to overlap each tool pass.\n"
@@ -6393,17 +6554,18 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
              "Higher values = slow processing and slow execution on CNC\n"
              "due of too many paths.")
         )
-        grid0.addWidget(nccoverlabel, 2, 0)
+        grid0.addWidget(nccoverlabel, 7, 0)
         self.ncc_overlap_entry = FloatEntry()
-        grid0.addWidget(self.ncc_overlap_entry, 2, 1)
+        grid0.addWidget(self.ncc_overlap_entry, 7, 1)
 
+        # Margin entry
         nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin'))
         nccmarginlabel.setToolTip(
             _("Bounding box margin.")
         )
-        grid0.addWidget(nccmarginlabel, 3, 0)
+        grid0.addWidget(nccmarginlabel, 8, 0)
         self.ncc_margin_entry = FloatEntry()
-        grid0.addWidget(self.ncc_margin_entry, 3, 1)
+        grid0.addWidget(self.ncc_margin_entry, 8, 1)
 
         # Method
         methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
@@ -6413,13 +6575,13 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
               "<B>Seed-based</B>: Outwards from seed.<BR>"
               "<B>Line-based</B>: Parallel lines.")
         )
-        grid0.addWidget(methodlabel, 4, 0)
+        grid0.addWidget(methodlabel, 9, 0)
         self.ncc_method_radio = RadioSet([
             {"label": _("Standard"), "value": "standard"},
             {"label": _("Seed-based"), "value": "seed"},
             {"label": _("Straight lines"), "value": "lines"}
         ], orientation='vertical', stretch=False)
-        grid0.addWidget(self.ncc_method_radio, 4, 1)
+        grid0.addWidget(self.ncc_method_radio, 9, 1)
 
         # Connect lines
         pathconnectlabel = QtWidgets.QLabel('%s:' % _("Connect"))
@@ -6427,19 +6589,21 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
             _("Draw lines between resulting\n"
               "segments to minimize tool lifts.")
         )
-        grid0.addWidget(pathconnectlabel, 5, 0)
+        grid0.addWidget(pathconnectlabel, 10, 0)
         self.ncc_connect_cb = FCCheckBox()
-        grid0.addWidget(self.ncc_connect_cb, 5, 1)
+        grid0.addWidget(self.ncc_connect_cb, 10, 1)
 
+        # Contour Checkbox
         contourlabel = QtWidgets.QLabel('%s:' % _("Contour"))
         contourlabel.setToolTip(
            _("Cut around the perimeter of the polygon\n"
              "to trim rough edges.")
         )
-        grid0.addWidget(contourlabel, 6, 0)
+        grid0.addWidget(contourlabel, 11, 0)
         self.ncc_contour_cb = FCCheckBox()
-        grid0.addWidget(self.ncc_contour_cb, 6, 1)
+        grid0.addWidget(self.ncc_contour_cb, 11, 1)
 
+        # Rest machining CheckBox
         restlabel = QtWidgets.QLabel('%s:' % _("Rest M."))
         restlabel.setToolTip(
             _("If checked, use 'rest machining'.\n"
@@ -6450,9 +6614,9 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
               "no more copper to clear or there are no more tools.\n"
               "If not checked, use the standard algorithm.")
         )
-        grid0.addWidget(restlabel, 7, 0)
+        grid0.addWidget(restlabel, 12, 0)
         self.ncc_rest_cb = FCCheckBox()
-        grid0.addWidget(self.ncc_rest_cb, 7, 1)
+        grid0.addWidget(self.ncc_rest_cb, 12, 1)
 
         # ## NCC Offset choice
         self.ncc_offset_choice_label = QtWidgets.QLabel('%s:' % _("Offset"))
@@ -6462,9 +6626,9 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
               "from the copper features.\n"
               "The value can be between 0 and 10 FlatCAM units.")
         )
-        grid0.addWidget(self.ncc_offset_choice_label, 8, 0)
+        grid0.addWidget(self.ncc_offset_choice_label, 13, 0)
         self.ncc_choice_offset_cb = FCCheckBox()
-        grid0.addWidget(self.ncc_choice_offset_cb, 8, 1)
+        grid0.addWidget(self.ncc_choice_offset_cb, 13, 1)
 
         # ## NCC Offset value
         self.ncc_offset_label = QtWidgets.QLabel('%s:' % _("Offset value"))
@@ -6474,14 +6638,14 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
               "from the copper features.\n"
               "The value can be between 0 and 10 FlatCAM units.")
         )
-        grid0.addWidget(self.ncc_offset_label, 9, 0)
+        grid0.addWidget(self.ncc_offset_label, 14, 0)
         self.ncc_offset_spinner = FCDoubleSpinner()
         self.ncc_offset_spinner.set_range(0.00, 10.00)
         self.ncc_offset_spinner.set_precision(4)
         self.ncc_offset_spinner.setWrapping(True)
         self.ncc_offset_spinner.setSingleStep(0.1)
 
-        grid0.addWidget(self.ncc_offset_spinner, 9, 1)
+        grid0.addWidget(self.ncc_offset_spinner, 14, 1)
 
         # ## Reference
         self.reference_radio = RadioSet([{'label': _('Itself'), 'value': 'itself'},
@@ -6496,8 +6660,19 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
               "- 'Reference Object' -  will do non copper clearing within the area\n"
               "specified by another object.")
         )
-        grid0.addWidget(reference_label, 10, 0)
-        grid0.addWidget(self.reference_radio, 10, 1)
+        grid0.addWidget(reference_label, 15, 0)
+        grid0.addWidget(self.reference_radio, 15, 1)
+
+        # ## Plotting type
+        self.ncc_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
+                                            {"label": _("Progressive"), "value": "progressive"}])
+        plotting_label = QtWidgets.QLabel('%s:' % _("NCC Plotting"))
+        plotting_label.setToolTip(
+            _("- 'Normal' -  normal plotting, done at the end of the NCC job\n"
+              "- 'Progressive' - after each shape is generated it will be plotted.")
+        )
+        grid0.addWidget(plotting_label, 16, 0)
+        grid0.addWidget(self.ncc_plotting_radio, 16, 1)
 
         self.layout.addStretch()
 
@@ -6785,15 +6960,26 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
               "- 'Reference Object' -  will do non copper clearing within the area\n"
               "specified by another object.")
         )
-        grid0.addWidget(selectlabel, 7, 0)
         self.selectmethod_combo = RadioSet([
             {"label": _("Single"), "value": "single"},
             {"label": _("Area"), "value": "area"},
             {"label": _("All"), "value": "all"},
             {"label": _("Ref."), "value": "ref"}
         ])
+        grid0.addWidget(selectlabel, 7, 0)
         grid0.addWidget(self.selectmethod_combo, 7, 1)
 
+        # ## Plotting type
+        self.paint_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'},
+                                              {"label": _("Progressive"), "value": "progressive"}])
+        plotting_label = QtWidgets.QLabel('%s:' % _("Paint Plotting"))
+        plotting_label.setToolTip(
+            _("- 'Normal' -  normal plotting, done at the end of the Paint job\n"
+              "- 'Progressive' - after each shape is generated it will be plotted.")
+        )
+        grid0.addWidget(plotting_label, 8, 0)
+        grid0.addWidget(self.paint_plotting_radio, 8, 1)
+
         self.layout.addStretch()
 
 
@@ -6818,7 +7004,7 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
 
         self.film_type_radio = RadioSet([{'label': 'Pos', 'value': 'pos'},
                                          {'label': 'Neg', 'value': 'neg'}])
-        ftypelbl = QtWidgets.QLabel(_('Film Type:'))
+        ftypelbl = QtWidgets.QLabel('%s:' % _('Film Type'))
         ftypelbl.setToolTip(
             _("Generate a Positive black film or a Negative film.\n"
               "Positive means that it will print the features\n"
@@ -7384,6 +7570,104 @@ class ToolsSubPrefGroupUI(OptionsGroupUI):
         self.layout.addStretch()
 
 
+class FAExcPrefGroupUI(OptionsGroupUI):
+    def __init__(self, parent=None):
+        # OptionsGroupUI.__init__(self, "Excellon File associations Preferences", parent=None)
+        super(FAExcPrefGroupUI, self).__init__(self)
+
+        self.setTitle(str(_("Excellon File associations")))
+
+        # ## Export G-Code
+        self.exc_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.exc_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.exc_list_label)
+
+        self.exc_list_text = FCTextArea()
+        self.exc_list_text.sizeHint(custom_sizehint=150)
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        self.exc_list_text.setFont(font)
+
+        self.layout.addWidget(self.exc_list_text)
+
+        self.exc_list_btn = FCButton("Apply")
+        self.exc_list_btn.setToolTip(_("Apply the file associations between\n"
+                                       "FlatCAM and the files with above extensions.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.layout.addWidget(self.exc_list_btn)
+
+        # self.layout.addStretch()
+
+
+class FAGcoPrefGroupUI(OptionsGroupUI):
+    def __init__(self, parent=None):
+        # OptionsGroupUI.__init__(self, "Gcode File associations Preferences", parent=None)
+        super(FAGcoPrefGroupUI, self).__init__(self)
+
+        self.setTitle(str(_("GCode File associations")))
+
+        # ## Export G-Code
+        self.gco_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.gco_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.gco_list_label)
+
+        self.gco_list_text = FCTextArea()
+        self.gco_list_text.sizeHint(custom_sizehint=150)
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        self.gco_list_text.setFont(font)
+
+        self.layout.addWidget(self.gco_list_text)
+
+        self.gco_list_btn = FCButton("Apply")
+        self.gco_list_btn.setToolTip(_("Apply the file associations between\n"
+                                       "FlatCAM and the files with above extensions.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.layout.addWidget(self.gco_list_btn)
+
+        # self.layout.addStretch()
+
+
+class FAGrbPrefGroupUI(OptionsGroupUI):
+    def __init__(self, parent=None):
+        # OptionsGroupUI.__init__(self, "Gerber File associations Preferences", parent=None)
+        super(FAGrbPrefGroupUI, self).__init__(self)
+
+        self.setTitle(str(_("Gerber File associations")))
+
+        # ## Export G-Code
+        self.grb_list_label = QtWidgets.QLabel("<b>%s:</b>" % _("Extensions list"))
+        self.grb_list_label.setToolTip(
+            _("List of file extensions to be\n"
+              "associated with FlatCAM.")
+        )
+        self.layout.addWidget(self.grb_list_label)
+
+        self.grb_list_text = FCTextArea()
+        self.grb_list_text.sizeHint(custom_sizehint=150)
+        self.layout.addWidget(self.grb_list_text)
+        font = QtGui.QFont()
+        font.setPointSize(12)
+        self.grb_list_text.setFont(font)
+
+        self.grb_list_btn = FCButton("Apply")
+        self.grb_list_btn.setToolTip(_("Apply the file associations between\n"
+                                       "FlatCAM and the files with above extensions.\n"
+                                       "They will be active after next logon.\n"
+                                       "This work only in Windows."))
+        self.layout.addWidget(self.grb_list_btn)
+
+        # self.layout.addStretch()
+
+
 class FlatCAMActivityView(QtWidgets.QWidget):
 
     def __init__(self, parent=None):
@@ -7412,8 +7696,9 @@ class FlatCAMActivityView(QtWidgets.QWidget):
         self.movie.stop()
         self.text.setText(_("Idle."))
 
-    def set_busy(self, msg):
-        self.movie.start()
+    def set_busy(self, msg, no_movie=None):
+        if no_movie is not True:
+            self.movie.start()
         self.text.setText(msg)
 
 

+ 46 - 3
flatcamGUI/GUIElements.py

@@ -531,9 +531,13 @@ class FCTextArea(QtWidgets.QPlainTextEdit):
     def get_value(self):
         return str(self.toPlainText())
 
-    def sizeHint(self):
+    def sizeHint(self, custom_sizehint=None):
         default_hint_size = super(FCTextArea, self).sizeHint()
-        return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
+
+        if custom_sizehint is None:
+            return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
+        else:
+            return QtCore.QSize(custom_sizehint, default_hint_size.height())
 
 
 class FCTextAreaRich(QtWidgets.QTextEdit):
@@ -1520,6 +1524,45 @@ class OptionalInputSection:
                     widget.setEnabled(True)
 
 
+class OptionalHideInputSection:
+
+    def __init__(self, cb, optinputs, logic=True):
+        """
+        Associates the a checkbox with a set of inputs.
+
+        :param cb: Checkbox that enables the optional inputs.
+        :param optinputs: List of widgets that are optional.
+        :param logic: When True the logic is normal, when False the logic is in reverse
+        It means that for logic=True, when the checkbox is checked the widgets are Enabled, and
+        for logic=False, when the checkbox is checked the widgets are Disabled
+        :return:
+        """
+        assert isinstance(cb, FCCheckBox), \
+            "Expected an FCCheckBox, got %s" % type(cb)
+
+        self.cb = cb
+        self.optinputs = optinputs
+        self.logic = logic
+
+        self.on_cb_change()
+        self.cb.stateChanged.connect(self.on_cb_change)
+
+    def on_cb_change(self):
+
+        if self.cb.checkState():
+            for widget in self.optinputs:
+                if self.logic is True:
+                    widget.show()
+                else:
+                    widget.hide()
+        else:
+            for widget in self.optinputs:
+                if self.logic is True:
+                    widget.hide()
+                else:
+                    widget.show()
+
+
 class FCTable(QtWidgets.QTableWidget):
     def __init__(self, parent=None):
         super(FCTable, self).__init__(parent)
@@ -1739,7 +1782,7 @@ class _BrowserTextEdit(QTextEdit):
 
     def clear(self):
         QTextEdit.clear(self)
-        text = "FlatCAM %s (c)2014-2019 Juan Pablo Caram (Type help to get started)\n\n" % self.version
+        text = "FlatCAM %s - Open Source Software - Type help to get started\n\n" % self.version
         text = html.escape(text)
         text = text.replace('\n', '<br/>')
         self.moveCursor(QTextCursor.End)

+ 114 - 52
flatcamGUI/ObjectUI.py

@@ -254,8 +254,13 @@ class GerberObjectUI(ObjectUI):
         )
         self.custom_box.addWidget(self.isolation_routing_label)
 
+        # ###########################################
+        # ########## NEW GRID #######################
+        # ###########################################
+
         grid1 = QtWidgets.QGridLayout()
         self.custom_box.addLayout(grid1)
+
         tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia'))
         tdlabel.setToolTip(
             _("Diameter of the cutting tool.\n"
@@ -265,9 +270,9 @@ class GerberObjectUI(ObjectUI):
               "this parameter.")
         )
         tdlabel.setMinimumWidth(90)
-        grid1.addWidget(tdlabel, 0, 0)
         self.iso_tool_dia_entry = LengthEntry()
-        grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
+        grid1.addWidget(tdlabel, 0, 0)
+        grid1.addWidget(self.iso_tool_dia_entry, 0, 1, 1, 2)
 
         passlabel = QtWidgets.QLabel('%s:' % _('# Passes'))
         passlabel.setToolTip(
@@ -275,10 +280,10 @@ class GerberObjectUI(ObjectUI):
               "number (integer) of tool widths.")
         )
         passlabel.setMinimumWidth(90)
-        grid1.addWidget(passlabel, 1, 0)
         self.iso_width_entry = FCSpinner()
         self.iso_width_entry.setRange(1, 999)
-        grid1.addWidget(self.iso_width_entry, 1, 1)
+        grid1.addWidget(passlabel, 1, 0)
+        grid1.addWidget(self.iso_width_entry, 1, 1, 1, 2)
 
         overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap'))
         overlabel.setToolTip(
@@ -287,9 +292,9 @@ class GerberObjectUI(ObjectUI):
               "A value here of 0.25 means an overlap of 25% from the tool diameter found above.")
         )
         overlabel.setMinimumWidth(90)
-        grid1.addWidget(overlabel, 2, 0)
         self.iso_overlap_entry = FloatEntry()
-        grid1.addWidget(self.iso_overlap_entry, 2, 1)
+        grid1.addWidget(overlabel, 2, 0)
+        grid1.addWidget(self.iso_overlap_entry, 2, 1, 1, 2)
 
         # Milling Type Radio Button
         self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type'))
@@ -298,27 +303,69 @@ class GerberObjectUI(ObjectUI):
               "- climb / best for precision milling and to reduce tool usage\n"
               "- conventional / useful when there is no backlash compensation")
         )
-        grid1.addWidget(self.milling_type_label, 3, 0)
         self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'},
                                             {'label': _('Conv.'), 'value': 'cv'}])
-        grid1.addWidget(self.milling_type_radio, 3, 1)
+        grid1.addWidget(self.milling_type_label, 3, 0)
+        grid1.addWidget(self.milling_type_radio, 3, 1, 1, 2)
 
         # combine all passes CB
         self.combine_passes_cb = FCCheckBox(label=_('Combine Passes'))
         self.combine_passes_cb.setToolTip(
             _("Combine all passes into one object")
         )
-        grid1.addWidget(self.combine_passes_cb, 4, 0)
 
         # generate follow
         self.follow_cb = FCCheckBox(label=_('"Follow"'))
-        self.follow_cb.setToolTip(
-           _("Generate a 'Follow' geometry.\n"
-             "This means that it will cut through\n"
-             "the middle of the trace.")
+        self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n"
+                                    "This means that it will cut through\n"
+                                    "the middle of the trace."))
 
-        )
+        # avoid an area from isolation
+        self.except_cb = FCCheckBox(label=_('Except'))
+        self.except_cb.setToolTip(_("When the isolation geometry is generated,\n"
+                                    "by checking this, the area of the object bellow\n"
+                                    "will be subtracted from the isolation geometry."))
+
+        grid1.addWidget(self.combine_passes_cb, 4, 0)
         grid1.addWidget(self.follow_cb, 4, 1)
+        grid1.addWidget(self.except_cb, 4, 2)
+
+        # ## Form Layout
+        form_layout = QtWidgets.QFormLayout()
+        grid1.addLayout(form_layout, 5, 0, 1, 3)
+
+        # ################################################
+        # ##### Type of object to be excepted ############
+        # ################################################
+        self.type_obj_combo = QtWidgets.QComboBox()
+        self.type_obj_combo.addItem("Gerber")
+        self.type_obj_combo.addItem("Excellon")
+        self.type_obj_combo.addItem("Geometry")
+
+        # we get rid of item1 ("Excellon") as it is not suitable
+        self.type_obj_combo.view().setRowHidden(1, True)
+        self.type_obj_combo.setItemIcon(0, QtGui.QIcon("share/flatcam_icon16.png"))
+        self.type_obj_combo.setItemIcon(2, QtGui.QIcon("share/geometry16.png"))
+
+        self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Obj Type"))
+        self.type_obj_combo_label.setToolTip(
+            _("Specify the type of object to be excepted from isolation.\n"
+              "It can be of type: Gerber or Geometry.\n"
+              "What is selected here will dictate the kind\n"
+              "of objects that will populate the 'Object' combobox.")
+        )
+        # self.type_obj_combo_label.setMinimumWidth(60)
+        form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo)
+
+        # ################################################
+        # ##### The object to be excepted ################
+        # ################################################
+        self.obj_combo = QtWidgets.QComboBox()
+
+        self.obj_label = QtWidgets.QLabel('%s:' % _("Object"))
+        self.obj_label.setToolTip(_("Object whose area will be removed from isolation geometry."))
+
+        form_layout.addRow(self.obj_label, self.obj_combo)
 
         self.gen_iso_label = QtWidgets.QLabel("<b>%s:</b>" % _("Generate Isolation Geometry"))
         self.gen_iso_label.setToolTip(
@@ -332,14 +379,16 @@ class GerberObjectUI(ObjectUI):
               "inside the actual Gerber feature, use a negative tool\n"
               "diameter above.")
         )
-        self.custom_box.addWidget(self.gen_iso_label)
-
-        hlay_1 = QtWidgets.QHBoxLayout()
-        self.custom_box.addLayout(hlay_1)
+        grid1.addWidget(self.gen_iso_label, 6, 0, 1, 3)
 
-        self.padding_area_label = QtWidgets.QLabel('')
-        self.padding_area_label.setMinimumWidth(90)
-        hlay_1.addWidget(self.padding_area_label)
+        self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry'))
+        self.create_buffer_button.setToolTip(
+            _("This button is shown only when the Gerber file\n"
+              "is loaded without buffering.\n"
+              "Clicking this will create the buffered geometry\n"
+              "required for isolation.")
+        )
+        grid1.addWidget(self.create_buffer_button, 7, 0, 1, 3)
 
         self.generate_iso_button = QtWidgets.QPushButton(_('FULL Geo'))
         self.generate_iso_button.setToolTip(
@@ -347,10 +396,10 @@ class GerberObjectUI(ObjectUI):
               "for isolation routing. It contains both\n"
               "the interiors and exteriors geometry.")
         )
-        self.generate_iso_button.setMinimumWidth(90)
-        hlay_1.addWidget(self.generate_iso_button, alignment=Qt.AlignLeft)
+        grid1.addWidget(self.generate_iso_button, 8, 0)
 
-        # hlay_1.addStretch()
+        hlay_1 = QtWidgets.QHBoxLayout()
+        grid1.addLayout(hlay_1, 8, 1, 1, 2)
 
         self.generate_ext_iso_button = QtWidgets.QPushButton(_('Ext Geo'))
         self.generate_ext_iso_button.setToolTip(
@@ -370,14 +419,28 @@ class GerberObjectUI(ObjectUI):
         # self.generate_ext_iso_button.setMinimumWidth(90)
         hlay_1.addWidget(self.generate_int_iso_button)
 
+        self.ohis_iso = OptionalHideInputSection(
+            self.except_cb,
+            [self.type_obj_combo, self.type_obj_combo_label, self.obj_combo, self.obj_label],
+            logic=True
+        )
         # when the follow checkbox is checked then the exteriors and interiors isolation generation buttons
         # are disabled as is doesn't make sense to have them enabled due of the nature of "follow"
         self.ois_iso = OptionalInputSection(self.follow_cb,
                                             [self.generate_int_iso_button, self.generate_ext_iso_button], logic=False)
 
+        grid1.addWidget(QtWidgets.QLabel(''), 9, 0)
+
+        # ###########################################
+        # ########## NEW GRID #######################
+        # ###########################################
+
         grid2 = QtWidgets.QGridLayout()
         self.custom_box.addLayout(grid2)
 
+        self.tool_lbl = QtWidgets.QLabel('<b>%s</b>:' % _("TOOLS"))
+        grid2.addWidget(self.tool_lbl, 0, 0, 1, 2)
+
         # ## Clear non-copper regions
         self.clearcopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Clear N-copper"))
         self.clearcopper_label.setToolTip(
@@ -385,14 +448,14 @@ class GerberObjectUI(ObjectUI):
               "toolpaths to cut all non-copper regions.")
         )
         self.clearcopper_label.setMinimumWidth(90)
-        grid2.addWidget(self.clearcopper_label, 0, 0)
 
         self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
         self.generate_ncc_button.setToolTip(
             _("Create the Geometry Object\n"
               "for non-copper routing.")
         )
-        grid2.addWidget(self.generate_ncc_button, 0, 1)
+        grid2.addWidget(self.clearcopper_label, 1, 0)
+        grid2.addWidget(self.generate_ncc_button, 1, 1)
 
         # ## Board cutout
         self.board_cutout_label = QtWidgets.QLabel("<b>%s:</b>" % _("Board cutout"))
@@ -401,14 +464,14 @@ class GerberObjectUI(ObjectUI):
               "the PCB and separate it from\n"
               "the original board.")
         )
-        grid2.addWidget(self.board_cutout_label, 1, 0)
 
         self.generate_cutout_button = QtWidgets.QPushButton(_('Cutout Tool'))
         self.generate_cutout_button.setToolTip(
             _("Generate the geometry for\n"
               "the board cutout.")
         )
-        grid2.addWidget(self.generate_cutout_button, 1, 1)
+        grid2.addWidget(self.board_cutout_label, 2, 0)
+        grid2.addWidget(self.generate_cutout_button, 2, 1)
 
         # ## Non-copper regions
         self.noncopper_label = QtWidgets.QLabel("<b>%s:</b>" % _("Non-copper regions"))
@@ -419,10 +482,8 @@ class GerberObjectUI(ObjectUI):
               "object. Can be used to remove all\n"
               "copper from a specified region.")
         )
-        self.custom_box.addWidget(self.noncopper_label)
 
-        grid4 = QtWidgets.QGridLayout()
-        self.custom_box.addLayout(grid4)
+        grid2.addWidget(self.noncopper_label, 3, 0, 1, 2)
 
         # Margin
         bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
@@ -433,9 +494,9 @@ class GerberObjectUI(ObjectUI):
               "distance.")
         )
         bmlabel.setMinimumWidth(90)
-        grid4.addWidget(bmlabel, 0, 0)
         self.noncopper_margin_entry = LengthEntry()
-        grid4.addWidget(self.noncopper_margin_entry, 0, 1)
+        grid2.addWidget(bmlabel, 4, 0)
+        grid2.addWidget(self.noncopper_margin_entry, 4, 1)
 
         # Rounded corners
         self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
@@ -443,10 +504,10 @@ class GerberObjectUI(ObjectUI):
             _("Resulting geometry will have rounded corners.")
         )
         self.noncopper_rounded_cb.setMinimumWidth(90)
-        grid4.addWidget(self.noncopper_rounded_cb, 1, 0)
 
         self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
-        grid4.addWidget(self.generate_noncopper_button, 1, 1)
+        grid2.addWidget(self.noncopper_rounded_cb, 5, 0)
+        grid2.addWidget(self.generate_noncopper_button, 5, 1)
 
         # ## Bounding box
         self.boundingbox_label = QtWidgets.QLabel('<b>%s:</b>' % _('Bounding Box'))
@@ -454,10 +515,8 @@ class GerberObjectUI(ObjectUI):
             _("Create a geometry surrounding the Gerber object.\n"
               "Square shape.")
         )
-        self.custom_box.addWidget(self.boundingbox_label)
 
-        grid5 = QtWidgets.QGridLayout()
-        self.custom_box.addLayout(grid5)
+        grid2.addWidget(self.boundingbox_label, 6, 0, 1, 2)
 
         bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
         bbmargin.setToolTip(
@@ -465,9 +524,9 @@ class GerberObjectUI(ObjectUI):
               "to the nearest polygon.")
         )
         bbmargin.setMinimumWidth(90)
-        grid5.addWidget(bbmargin, 0, 0)
         self.bbmargin_entry = LengthEntry()
-        grid5.addWidget(self.bbmargin_entry, 0, 1)
+        grid2.addWidget(bbmargin, 7, 0)
+        grid2.addWidget(self.bbmargin_entry, 7, 1)
 
         self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo"))
         self.bbrounded_cb.setToolTip(
@@ -477,13 +536,13 @@ class GerberObjectUI(ObjectUI):
               "the margin.")
         )
         self.bbrounded_cb.setMinimumWidth(90)
-        grid5.addWidget(self.bbrounded_cb, 1, 0)
 
         self.generate_bb_button = QtWidgets.QPushButton(_('Generate Geo'))
         self.generate_bb_button.setToolTip(
             _("Generate the Geometry object.")
         )
-        grid5.addWidget(self.generate_bb_button, 1, 1)
+        grid2.addWidget(self.bbrounded_cb, 8, 0)
+        grid2.addWidget(self.generate_bb_button, 8, 1)
 
 
 class ExcellonObjectUI(ObjectUI):
@@ -558,7 +617,8 @@ class ExcellonObjectUI(ObjectUI):
         self.tools_table.horizontalHeaderItem(0).setToolTip(
             _("This is the Tool Number.\n"
               "When ToolChange is checked, on toolchange event this value\n"
-              "will be showed as a T1, T2 ... Tn in the Machine Code."))
+              "will be showed as a T1, T2 ... Tn in the Machine Code.\n\n"
+              "Here the tools are selected for G-code generation."))
         self.tools_table.horizontalHeaderItem(1).setToolTip(
             _("Tool Diameter. It's value (in current FlatCAM units) \n"
               "is the cut width into the material."))
@@ -573,7 +633,8 @@ class ExcellonObjectUI(ObjectUI):
               "to create the desired exit hole diameter due of the tip shape.\n"
               "The value here can compensate the Cut Z parameter."))
         self.tools_table.horizontalHeaderItem(5).setToolTip(
-            _("Toggle display of the drills for the current tool."))
+            _("Toggle display of the drills for the current tool.\n"
+              "This does not select the tools for G-code generation."))
 
         self.empty_label = QtWidgets.QLabel('')
         self.tools_box.addWidget(self.empty_label)
@@ -649,7 +710,7 @@ class ExcellonObjectUI(ObjectUI):
         grid1.addWidget(self.eendz_entry, 5, 1)
 
         # Excellon Feedrate
-        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate (Plunge):'))
+        frlabel = QtWidgets.QLabel('%s:' % _('Feedrate (Plunge)'))
         frlabel.setToolTip(
             _("Tool speed while drilling\n"
               "(in units per minute).\n"
@@ -736,7 +797,8 @@ class ExcellonObjectUI(ObjectUI):
 
         choose_tools_label = QtWidgets.QLabel(
             _("Select from the Tools Table above\n"
-              "the tools you want to include.")
+              "the hole dias that are to be drilled.\n"
+              "Use the # column to make the selection.")
         )
         self.tools_box.addWidget(choose_tools_label)
 
@@ -759,7 +821,7 @@ class ExcellonObjectUI(ObjectUI):
         self.excellon_gcode_type_radio.setVisible(False)
         gcode_type_label.hide()
 
-        self.generate_cnc_button = QtWidgets.QPushButton(_('Create GCode'))
+        self.generate_cnc_button = QtWidgets.QPushButton(_('Create Drills GCode'))
         self.generate_cnc_button.setToolTip(
             _("Generate the CNC Job.")
         )
@@ -774,7 +836,8 @@ class ExcellonObjectUI(ObjectUI):
 
         self.choose_tools_label2 = QtWidgets.QLabel(
             _("Select from the Tools Table above\n"
-              " the hole dias that are to be milled.")
+              "the hole dias that are to be milled.\n"
+              "Use the # column to make the selection.")
         )
         self.tools_box.addWidget(self.choose_tools_label2)
 
@@ -912,7 +975,7 @@ class GeometryObjectUI(ObjectUI):
         self.geo_tools_table.horizontalHeaderItem(3).setToolTip(
             _(
                 "The (Operation) Type has only informative value. Usually the UI form values \n"
-                "are choosed based on the operation type and this will serve as a reminder.\n"
+                "are choose based on the operation type and this will serve as a reminder.\n"
                 "Can be 'Roughing', 'Finishing' or 'Isolation'.\n"
                 "For Roughing we may choose a lower Feedrate and multiDepth cut.\n"
                 "For Finishing we may choose a higher Feedrate, without multiDepth.\n"
@@ -1111,7 +1174,7 @@ class GeometryObjectUI(ObjectUI):
 
         # Tool change:
 
-        self.toolchzlabel = QtWidgets.QLabel('%s:' %_("Tool change Z"))
+        self.toolchzlabel = QtWidgets.QLabel('%s:' % _("Tool change Z"))
         self.toolchzlabel.setToolTip(
             _(
                 "Z-axis position (height) for\n"
@@ -1350,8 +1413,7 @@ class CNCObjectUI(ObjectUI):
         self.annotation_label.setToolTip(
             _("This selects if to display text annotation on the plot.\n"
               "When checked it will display numbers in order for each end\n"
-              "of a travel line."
-            )
+              "of a travel line.")
         )
         self.annotation_cb = FCCheckBox()
 

+ 1 - 1
flatcamGUI/PlotCanvas.py

@@ -34,7 +34,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
         """
 
         super(PlotCanvas, self).__init__()
-        VisPyCanvas.__init__(self)
+        # VisPyCanvas.__init__(self)
 
         # VisPyCanvas does not allow new attributes. Override.
         self.unfreeze()

+ 1 - 1
flatcamGUI/VisPyVisuals.py

@@ -187,7 +187,7 @@ class ShapeGroup(object):
 
 class ShapeCollectionVisual(CompoundVisual):
 
-    def __init__(self, line_width=1, triangulation='gpc', layers=3, pool=None, **kwargs):
+    def __init__(self, line_width=1, triangulation='vispy', layers=3, pool=None, **kwargs):
         """
         Represents collection of shapes to draw on VisPy scene
         :param line_width: float

+ 8 - 1
flatcamParsers/ParseFont.py

@@ -23,6 +23,13 @@ import freetype as ft
 from fontTools import ttLib
 
 import logging
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
 
 log = logging.getLogger('base2')
 
@@ -295,7 +302,7 @@ class ParseFont():
             elif font_type == 'regular':
                 path_filename = regular_dict[font_name]
         except Exception as e:
-            self.app.inform.emit("[ERROR_NOTCL] Font not supported, try another one.")
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Font not supported, try another one."))
             log.debug("[ERROR_NOTCL] Font Loading: %s" % str(e))
             return "flatcam font parse failed"
 

+ 18 - 18
flatcamTools/ToolCalculators.py

@@ -307,8 +307,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 tip_diameter = float(self.tipDia_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         try:
@@ -318,8 +318,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 half_tip_angle = float(self.tipAngle_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         half_tip_angle /= 2
 
@@ -330,8 +330,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 cut_depth = float(self.cutDepth_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle)))
@@ -345,8 +345,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 mm_val = float(self.mm_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         self.inch_entry.set_value('%.6f' % (mm_val / 25.4))
 
@@ -358,8 +358,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 inch_val = float(self.inch_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         self.mm_entry.set_value('%.6f' % (inch_val * 25.4))
 
@@ -372,8 +372,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 length = float(self.pcblength_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         try:
@@ -383,8 +383,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 width = float(self.pcbwidth_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         try:
@@ -394,8 +394,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 density = float(self.cdensity_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         try:
@@ -405,8 +405,8 @@ class ToolCalculator(FlatCAMTool):
             try:
                 copper = float(self.growth_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         calculated_current = (length * width * density) * 0.0021527820833419

+ 64 - 52
flatcamTools/ToolCutOut.py

@@ -368,11 +368,12 @@ class CutOut(FlatCAMTool):
             cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
             log.debug("CutOut.on_freeform_cutout() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name))
             return "Could not retrieve object: %s" % name
 
         if cutout_obj is None:
-            self.app.inform.emit(_("[ERROR_NOTCL] There is no object selected for Cutout.\nSelect one and try again."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("There is no object selected for Cutout.\nSelect one and try again."))
             return
 
         try:
@@ -382,12 +383,13 @@ class CutOut(FlatCAMTool):
             try:
                 dia = float(self.dia.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Tool diameter value is missing or wrong format. Add it and retry."))
                 return
 
         if 0 in {dia}:
-            self.app.inform.emit(_("[WARNING_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Tool Diameter is zero value. Change it to a positive real number."))
             return "Tool Diameter is zero value. Change it to a positive real number."
 
         try:
@@ -402,8 +404,8 @@ class CutOut(FlatCAMTool):
             try:
                 margin = float(self.margin.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Margin value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Margin value is missing or wrong format. Add it and retry."))
                 return
 
         try:
@@ -413,26 +415,27 @@ class CutOut(FlatCAMTool):
             try:
                 gapsize = float(self.gapsize.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Gap size value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Gap size value is missing or wrong format. Add it and retry."))
                 return
 
         try:
             gaps = self.gaps.get_value()
         except TypeError:
-            self.app.inform.emit(_("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Number of gaps value is missing. Add it and retry."))
             return
 
         if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
-            self.app.inform.emit(_("[WARNING_NOTCL] Gaps value can be only one of: "
-                                   "'None', 'lr', 'tb', '2lr', '2tb', 4 or 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. "))
             return
 
         if cutout_obj.multigeo is True:
-            self.app.inform.emit(_("[ERROR]Cutout operation cannot be done on a multi-geo Geometry.\n"
-                                   "Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n"
-                                   "and after that perform Cutout."))
+            self.app.inform.emit('[ERROR] %s' % _("Cutout operation cannot be done on a multi-geo Geometry.\n"
+                                                  "Optionally, this Multi-geo Geometry can be converted to "
+                                                  "Single-geo Geometry,\n"
+                                                  "and after that perform Cutout."))
             return
 
         convex_box = self.convex_box.get_value()
@@ -548,7 +551,7 @@ class CutOut(FlatCAMTool):
         self.app.new_object('geometry', outname, geo_init)
 
         cutout_obj.plot()
-        self.app.inform.emit(_("[success] Any form CutOut operation finished."))
+        self.app.inform.emit('[success] %s' % _("Any form CutOut operation finished."))
         self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
         self.app.should_we_save = True
 
@@ -565,11 +568,11 @@ class CutOut(FlatCAMTool):
             cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
             log.debug("CutOut.on_rectangular_cutout() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), name))
             return "Could not retrieve object: %s" % name
 
         if cutout_obj is None:
-            self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % cutout_obj)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found: %s"), cutout_obj))
 
         try:
             dia = float(self.dia.get_value())
@@ -578,12 +581,13 @@ class CutOut(FlatCAMTool):
             try:
                 dia = float(self.dia.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Tool diameter value is missing or wrong format. Add it and retry."))
                 return
 
         if 0 in {dia}:
-            self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Tool Diameter is zero value. Change it to a positive real number."))
             return "Tool Diameter is zero value. Change it to a positive real number."
 
         try:
@@ -598,8 +602,8 @@ class CutOut(FlatCAMTool):
             try:
                 margin = float(self.margin.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Margin value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Margin value is missing or wrong format. Add it and retry."))
                 return
 
         try:
@@ -609,26 +613,28 @@ class CutOut(FlatCAMTool):
             try:
                 gapsize = float(self.gapsize.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Gap size value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Gap size value is missing or wrong format. Add it and retry."))
                 return
 
         try:
             gaps = self.gaps.get_value()
         except TypeError:
-            self.app.inform.emit(_("[WARNING_NOTCL] Number of gaps value is missing. Add it and retry."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Number of gaps value is missing. Add it and retry."))
             return
 
         if gaps not in ['None', 'LR', 'TB', '2LR', '2TB', '4', '8']:
-            self.app.inform.emit(_("[WARNING_NOTCL] Gaps value can be only one of: "
-                                   "'None', 'lr', 'tb', '2lr', '2tb', 4 or 8. "
-                                   "Fill in a correct value and retry. "))
+            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. "))
             return
 
         if cutout_obj.multigeo is True:
-            self.app.inform.emit(_("[ERROR]Cutout operation cannot be done on a multi-geo Geometry.\n"
-                                   "Optionally, this Multi-geo Geometry can be converted to Single-geo Geometry,\n"
-                                   "and after that perform Cutout."))
+            self.app.inform.emit('[ERROR] %s' % _("Cutout operation cannot be done on a multi-geo Geometry.\n"
+                                                  "Optionally, this Multi-geo Geometry can be converted to "
+                                                  "Single-geo Geometry,\n"
+                                                  "and after that perform Cutout."))
             return
 
         # Get min and max data for each object as we just cut rectangles across X or Y
@@ -729,7 +735,8 @@ class CutOut(FlatCAMTool):
         self.app.new_object('geometry', outname, geo_init)
 
         # cutout_obj.plot()
-        self.app.inform.emit(_("[success] Any form CutOut operation finished."))
+        self.app.inform.emit('[success] %s' %
+                             _("Any form CutOut operation finished."))
         self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
         self.app.should_we_save = True
 
@@ -744,12 +751,13 @@ class CutOut(FlatCAMTool):
             try:
                 self.cutting_dia = float(self.dia.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
-                                     "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Tool diameter value is missing or wrong format. Add it and retry."))
                 return
 
         if 0 in {self.cutting_dia}:
-            self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Tool Diameter is zero value. Change it to a positive real number."))
             return "Tool Diameter is zero value. Change it to a positive real number."
 
         try:
@@ -759,8 +767,8 @@ class CutOut(FlatCAMTool):
             try:
                 self.cutting_gapsize = float(self.gapsize.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Gap size value is missing or wrong format. "
-                                     "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Gap size value is missing or wrong format. Add it and retry."))
                 return
 
         name = self.man_object_combo.currentText()
@@ -769,7 +777,7 @@ class CutOut(FlatCAMTool):
             self.man_cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
             log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Geometry object: %s") % name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Geometry object"), name))
             return "Could not retrieve object: %s" % name
 
         self.app.plotcanvas.vis_disconnect('key_press', self.app.ui.keyPressEvent)
@@ -788,12 +796,12 @@ class CutOut(FlatCAMTool):
             self.man_cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
             log.debug("CutOut.on_manual_cutout() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Geometry object: %s") % name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Geometry object"), name))
             return "Could not retrieve object: %s" % name
 
         if self.man_cutout_obj is None:
-            self.app.inform.emit(
-                _("[ERROR_NOTCL] Geometry object for manual cutout not found: %s") % self.man_cutout_obj)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Geometry object for manual cutout not found"), self.man_cutout_obj))
             return
 
         # use the snapped position as reference
@@ -803,7 +811,7 @@ class CutOut(FlatCAMTool):
         self.man_cutout_obj.subtract_polygon(cut_poly)
 
         self.man_cutout_obj.plot()
-        self.app.inform.emit(_("[success] Added manual Bridge Gap."))
+        self.app.inform.emit('[success] %s' % _("Added manual Bridge Gap."))
 
         self.app.should_we_save = True
 
@@ -815,16 +823,18 @@ class CutOut(FlatCAMTool):
             cutout_obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
             log.debug("CutOut.on_manual_geo() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Gerber object: %s") % name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Gerber object"), name))
             return "Could not retrieve object: %s" % name
 
         if cutout_obj is None:
-            self.app.inform.emit(_("[ERROR_NOTCL] There is no Gerber object selected for Cutout.\n"
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("There is no Gerber object selected for Cutout.\n"
                                    "Select one and try again."))
             return
 
         if not isinstance(cutout_obj, FlatCAMGerber):
-            self.app.inform.emit(_("[ERROR_NOTCL] The selected object has to be of Gerber type.\n"
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("The selected object has to be of Gerber type.\n"
                                    "Select a Gerber file and try again."))
             return
 
@@ -835,12 +845,13 @@ class CutOut(FlatCAMTool):
             try:
                 dia = float(self.dia.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Tool diameter value is missing or wrong format. Add it and retry."))
                 return
 
         if 0 in {dia}:
-            self.app.inform.emit(_("[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real number."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Tool Diameter is zero value. Change it to a positive real number."))
             return "Tool Diameter is zero value. Change it to a positive real number."
 
         try:
@@ -855,8 +866,8 @@ class CutOut(FlatCAMTool):
             try:
                 margin = float(self.margin.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Margin value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Margin value is missing or wrong format. Add it and retry."))
                 return
 
         convex_box = self.convex_box.get_value()
@@ -877,7 +888,8 @@ class CutOut(FlatCAMTool):
                     geo = box(x0, y0, x1, y1)
                     geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
                 else:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Geometry not supported for cutout: %s") % type(geo_union))
+                    self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                         (_("Geometry not supported for cutout"), type(geo_union)))
                     return 'fail'
             else:
                 geo = geo_union

+ 27 - 25
flatcamTools/ToolDblSided.py

@@ -328,8 +328,8 @@ class DblSidedTool(FlatCAMTool):
             try:
                 px, py = self.point_entry.get_value()
             except TypeError:
-                self.app.inform.emit(_("[WARNING_NOTCL] 'Point' reference is selected and 'Point' coordinates "
-                                       "are missing. Add them and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("'Point' reference is selected and 'Point' coordinates "
+                                                              "are missing. Add them and retry."))
                 return
         else:
             selection_index = self.box_combo.currentIndex()
@@ -347,7 +347,7 @@ class DblSidedTool(FlatCAMTool):
                         bb_obj = model_index.internalPointer().obj
                     except AttributeError:
                         self.app.inform.emit(
-                            _("[WARNING_NOTCL] There is no Box reference object loaded. Load one and retry."))
+                            '[WARNING_NOTCL] %s' % _("There is no Box reference object loaded. Load one and retry."))
                         return
 
             xmin, ymin, xmax, ymax = bb_obj.bounds()
@@ -364,20 +364,21 @@ class DblSidedTool(FlatCAMTool):
                 dia = float(self.drill_dia.get_value().replace(',', '.'))
                 self.drill_dia.set_value(dia)
             except ValueError:
-                self.app.inform.emit(_("[WARNING_NOTCL] Tool diameter value is missing or wrong format. "
-                                       "Add it and retry."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("Tool diameter value is missing or wrong format. "
+                                                              "Add it and retry."))
                 return
 
         if dia is '':
-            self.app.inform.emit(_("[WARNING_NOTCL] No value or wrong format in Drill Dia entry. Add it and retry."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No value or wrong format in Drill Dia entry. Add it and retry."))
             return
         tools = {"1": {"C": dia}}
 
         # holes = self.alignment_holes.get_value()
         holes = eval('[{}]'.format(self.alignment_holes.text()))
         if not holes:
-            self.app.inform.emit(_("[WARNING_NOTCL] There are no Alignment Drill Coordinates to use. "
-                                   "Add them and retry."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("There are no Alignment Drill Coordinates to use. "
+                                                          "Add them and retry."))
             return
 
         drills = []
@@ -399,7 +400,7 @@ class DblSidedTool(FlatCAMTool):
 
         self.app.new_object("excellon", "Alignment Drills", obj_init)
         self.drill_values = ''
-        self.app.inform.emit(_("[success] Excellon object with alignment drills created..."))
+        self.app.inform.emit('[success] %s' % _("Excellon object with alignment drills created..."))
 
     def on_mirror_gerber(self):
         selection_index = self.gerber_object_combo.currentIndex()
@@ -408,11 +409,11 @@ class DblSidedTool(FlatCAMTool):
         try:
             fcobj = model_index.internalPointer().obj
         except Exception as e:
-            self.app.inform.emit(_("[WARNING_NOTCL] There is no Gerber object loaded ..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
             return
 
         if not isinstance(fcobj, FlatCAMGerber):
-            self.app.inform.emit(_("[ERROR_NOTCL] Only Gerber, Excellon and Geometry objects can be mirrored."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Only Gerber, Excellon and Geometry objects can be mirrored."))
             return
 
         axis = self.mirror_axis.get_value()
@@ -422,8 +423,8 @@ class DblSidedTool(FlatCAMTool):
             try:
                 px, py = self.point_entry.get_value()
             except TypeError:
-                self.app.inform.emit(_("[WARNING_NOTCL] 'Point' coordinates missing. "
-                                       "Using Origin (0, 0) as mirroring reference."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("'Point' coordinates missing. "
+                                                              "Using Origin (0, 0) as mirroring reference."))
                 px, py = (0, 0)
 
         else:
@@ -432,7 +433,7 @@ class DblSidedTool(FlatCAMTool):
             try:
                 bb_obj = model_index_box.internalPointer().obj
             except Exception as e:
-                self.app.inform.emit(_("[WARNING_NOTCL] There is no Box object loaded ..."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Box object loaded ..."))
                 return
 
             xmin, ymin, xmax, ymax = bb_obj.bounds()
@@ -442,7 +443,7 @@ class DblSidedTool(FlatCAMTool):
         fcobj.mirror(axis, [px, py])
         self.app.object_changed.emit(fcobj)
         fcobj.plot()
-        self.app.inform.emit(_("[success] Gerber %s was mirrored...") % str(fcobj.options['name']))
+        self.app.inform.emit('[success] Gerber %s %s...' % (str(fcobj.options['name']), _("was mirrored")))
 
     def on_mirror_exc(self):
         selection_index = self.exc_object_combo.currentIndex()
@@ -451,11 +452,11 @@ class DblSidedTool(FlatCAMTool):
         try:
             fcobj = model_index.internalPointer().obj
         except Exception as e:
-            self.app.inform.emit(_("[WARNING_NOTCL] There is no Excellon object loaded ..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Excellon object loaded ..."))
             return
 
         if not isinstance(fcobj, FlatCAMExcellon):
-            self.app.inform.emit(_("[ERROR_NOTCL] Only Gerber, Excellon and Geometry objects can be mirrored."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Only Gerber, Excellon and Geometry objects can be mirrored."))
             return
 
         axis = self.mirror_axis.get_value()
@@ -466,8 +467,8 @@ class DblSidedTool(FlatCAMTool):
                 px, py = self.point_entry.get_value()
             except Exception as e:
                 log.debug("DblSidedTool.on_mirror_geo() --> %s" % str(e))
-                self.app.inform.emit(_("[WARNING_NOTCL] There are no Point coordinates in the Point field. "
-                                       "Add coords and try again ..."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("There are no Point coordinates in the Point field. "
+                                                              "Add coords and try again ..."))
                 return
         else:
             selection_index_box = self.box_combo.currentIndex()
@@ -476,7 +477,7 @@ class DblSidedTool(FlatCAMTool):
                 bb_obj = model_index_box.internalPointer().obj
             except Exception as e:
                 log.debug("DblSidedTool.on_mirror_geo() --> %s" % str(e))
-                self.app.inform.emit(_("[WARNING_NOTCL] There is no Box object loaded ..."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Box object loaded ..."))
                 return
 
             xmin, ymin, xmax, ymax = bb_obj.bounds()
@@ -486,7 +487,7 @@ class DblSidedTool(FlatCAMTool):
         fcobj.mirror(axis, [px, py])
         self.app.object_changed.emit(fcobj)
         fcobj.plot()
-        self.app.inform.emit(_("[success] Excellon %s was mirrored...") % str(fcobj.options['name']))
+        self.app.inform.emit('[success] Excellon %s %s...' % (str(fcobj.options['name']), _("was mirrored")))
 
     def on_mirror_geo(self):
         selection_index = self.geo_object_combo.currentIndex()
@@ -495,11 +496,11 @@ class DblSidedTool(FlatCAMTool):
         try:
             fcobj = model_index.internalPointer().obj
         except Exception as e:
-            self.app.inform.emit(_("[WARNING_NOTCL] There is no Geometry object loaded ..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Geometry object loaded ..."))
             return
 
         if not isinstance(fcobj, FlatCAMGeometry):
-            self.app.inform.emit(_("[ERROR_NOTCL] Only Gerber, Excellon and Geometry objects can be mirrored."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Only Gerber, Excellon and Geometry objects can be mirrored."))
             return
 
         axis = self.mirror_axis.get_value()
@@ -513,7 +514,7 @@ class DblSidedTool(FlatCAMTool):
             try:
                 bb_obj = model_index_box.internalPointer().obj
             except Exception as e:
-                self.app.inform.emit(_("[WARNING_NOTCL] There is no Box object loaded ..."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Box object loaded ..."))
                 return
 
             xmin, ymin, xmax, ymax = bb_obj.bounds()
@@ -523,7 +524,8 @@ class DblSidedTool(FlatCAMTool):
         fcobj.mirror(axis, [px, py])
         self.app.object_changed.emit(fcobj)
         fcobj.plot()
-        self.app.inform.emit(_("[success] Geometry %s was mirrored...") % str(fcobj.options['name']))
+        self.app.inform.emit('[success] Geometry %s %s...' % (str(fcobj.options['name']), _("was mirrored")))
+
 
     def on_point_add(self):
         val = self.app.defaults["global_point_clipboard_format"] % (self.app.pos[0], self.app.pos[1])

+ 8 - 8
flatcamTools/ToolFilm.py

@@ -227,13 +227,15 @@ class Film(FlatCAMTool):
         try:
             name = self.tf_object_combo.currentText()
         except Exception as e:
-            self.app.inform.emit(_("[ERROR_NOTCL] No FlatCAM object selected. Load an object for Film and retry."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("No FlatCAM object selected. Load an object for Film and retry."))
             return
 
         try:
             boxname = self.tf_box_combo.currentText()
         except Exception as e:
-            self.app.inform.emit(_("[ERROR_NOTCL] No FlatCAM object selected. Load an object for Box and retry."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("No FlatCAM object selected. Load an object for Box and retry."))
             return
 
         try:
@@ -243,15 +245,13 @@ class Film(FlatCAMTool):
             try:
                 border = float(self.boundary_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                     "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number."))
                 return
 
         try:
             scale_stroke_width = int(self.film_scale_entry.get_value())
         except ValueError:
-            self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                 "use a number."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number."))
             return
 
         if border is None:
@@ -271,7 +271,7 @@ class Film(FlatCAMTool):
             filename = str(filename)
 
             if str(filename) == "":
-                self.app.inform.emit(_("[WARNING_NOTCL] Export SVG positive cancelled."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export SVG positive cancelled."))
                 return
             else:
                 self.app.export_svg_black(name, boxname, filename, scale_factor=scale_stroke_width)
@@ -287,7 +287,7 @@ class Film(FlatCAMTool):
             filename = str(filename)
 
             if str(filename) == "":
-                self.app.inform.emit(_("[WARNING_NOTCL] Export SVG negative cancelled."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export SVG negative cancelled."))
                 return
             else:
                 self.app.export_svg_negative(name, boxname, filename, border, scale_factor=scale_stroke_width)

+ 34 - 21
flatcamTools/ToolMove.py

@@ -24,6 +24,7 @@ if '_' not in builtins.__dict__:
 class ToolMove(FlatCAMTool):
 
     toolName = _("Move")
+    replot_signal = pyqtSignal(list)
 
     def __init__(self, app):
         FlatCAMTool.__init__(self, app)
@@ -45,6 +46,8 @@ class ToolMove(FlatCAMTool):
         # VisPy visuals
         self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1)
 
+        self.replot_signal[list].connect(self.replot)
+
     def install(self, icon=None, separator=None, **kwargs):
         FlatCAMTool.install(self, icon, separator, shortcut='M', **kwargs)
 
@@ -85,7 +88,7 @@ class ToolMove(FlatCAMTool):
                 self.setVisible(False)
                 # signal that there is no command active
                 self.app.command_active = None
-                self.app.inform.emit(_("[WARNING_NOTCL] MOVE action cancelled. No object(s) to move."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' % _("MOVE action cancelled. No object(s) to move."))
 
     def on_left_click(self, event):
         # mouse click will be accepted only if the left button is clicked
@@ -125,21 +128,20 @@ class ToolMove(FlatCAMTool):
                     dx = pos[0] - self.point1[0]
                     dy = pos[1] - self.point1[1]
 
-                    proc = self.app.proc_container.new(_("Moving ..."))
+                    obj_list = self.app.collection.get_selected()
 
                     def job_move(app_obj):
-                        obj_list = self.app.collection.get_selected()
+                        with self.app.proc_container.new(_("Moving...")) as proc:
+                            try:
+                                if not obj_list:
+                                    self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object(s) selected."))
+                                    return "fail"
 
-                        try:
-                            if not obj_list:
-                                self.app.inform.emit(_("[WARNING_NOTCL] No object(s) selected."))
-                                return "fail"
-                            else:
                                 for sel_obj in obj_list:
 
                                     # offset solid_geometry
                                     sel_obj.offset((dx, dy))
-                                    sel_obj.plot()
+                                    # sel_obj.plot()
 
                                     try:
                                         sel_obj.replotApertures.emit()
@@ -152,17 +154,19 @@ class ToolMove(FlatCAMTool):
                                     sel_obj.options['ymin'] = b
                                     sel_obj.options['xmax'] = c
                                     sel_obj.options['ymax'] = d
-                                    # self.app.collection.set_active(sel_obj.options['name'])
-                        except Exception as e:
-                            proc.done()
-                            self.app.inform.emit(_('[ERROR_NOTCL] '
-                                                 'ToolMove.on_left_click() --> %s') % str(e))
-                            return "fail"
+
+                                # time to plot the moved objects
+                                self.replot_signal.emit(obj_list)
+                            except Exception as e:
+                                proc.done()
+                                self.app.inform.emit('[ERROR_NOTCL] %s --> %s' % (_('ToolMove.on_left_click()'), str(e)))
+                                return "fail"
+
                         proc.done()
                         # delete the selection bounding box
                         self.delete_shape()
-                        self.app.inform.emit(_('[success] %s object was moved ...') %
-                                             str(sel_obj.kind).capitalize())
+                        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]})
 
@@ -171,12 +175,21 @@ class ToolMove(FlatCAMTool):
                     return
 
                 except TypeError:
-                    self.app.inform.emit(_('[ERROR_NOTCL] '
-                                         'ToolMove.on_left_click() --> Error when mouse left click.'))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _('ToolMove.on_left_click() --> Error when mouse left click.'))
                     return
 
             self.clicked_move = 1
 
+    def replot(self, obj_list):
+
+        def worker_task():
+            with self.app.proc_container.new('%s...' % _("Plotting")):
+                for sel_obj in obj_list:
+                    sel_obj.plot()
+
+        self.app.worker_task.emit({'fcn': worker_task, 'params': []})
+
     def on_move(self, event):
         pos_canvas = self.app.plotcanvas.translate_coords(event.pos)
 
@@ -199,7 +212,7 @@ class ToolMove(FlatCAMTool):
     def on_key_press(self, event):
         if event.key == 'escape':
             # abort the move action
-            self.app.inform.emit(_("[WARNING_NOTCL] Move action cancelled."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Move action cancelled."))
             self.toggle()
         return
 
@@ -211,7 +224,7 @@ class ToolMove(FlatCAMTool):
 
         obj_list = self.app.collection.get_selected()
         if not obj_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] Object(s) not selected"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' % _("Object(s) not selected"))
             self.toggle()
         else:
             # if we have an object selected then we can safely activate the mouse events

Разница между файлами не показана из-за своего большого размера
+ 444 - 209
flatcamTools/ToolNonCopperClear.py


+ 35 - 8
flatcamTools/ToolPDF.py

@@ -156,7 +156,7 @@ class ToolPDF(FlatCAMTool):
             filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), filter=_filter_)
 
         if len(filenames) == 0:
-            self.app.inform.emit(_("[WARNING_NOTCL] Open PDF cancelled."))
+            self.app.inform.emit('[WARNING_NOTCL] %s.' % _("Open PDF cancelled"))
         else:
             # start the parsing timer with a period of 1 second
             self.periodic_check(1000)
@@ -183,12 +183,20 @@ class ToolPDF(FlatCAMTool):
             # 1 inch = 72 points => 1 point = 1 / 72 = 0.01388888888 inch
             self.point_to_unit_factor = 1 / 72
 
+        if self.app.abort_flag:
+            # graceful abort requested by the user
+            raise FlatCAMApp.GracefulException
+
         with self.app.proc_container.new(_("Parsing PDF file ...")):
             with open(filename, "rb") as f:
                 pdf = f.read()
 
             stream_nr = 0
             for s in re.findall(self.stream_re, pdf):
+                if self.app.abort_flag:
+                    # graceful abort requested by the user
+                    raise FlatCAMApp.GracefulException
+
                 stream_nr += 1
                 log.debug(" PDF STREAM: %d\n" % stream_nr)
                 s = s.strip(b'\r\n')
@@ -241,8 +249,7 @@ class ToolPDF(FlatCAMTool):
                 name_tool += 1
 
                 # create tools dictionary
-                spec = {"C": dia}
-                spec['solid_geometry'] = []
+                spec = {"C": dia, 'solid_geometry': []}
                 exc_obj.tools[str(name_tool)] = spec
 
                 # create drill list of dictionaries
@@ -259,19 +266,22 @@ class ToolPDF(FlatCAMTool):
             for tool in exc_obj.tools:
                 if exc_obj.tools[tool]['solid_geometry']:
                     return
-            app_obj.inform.emit(_("[ERROR_NOTCL] No geometry found in file: %s") % outname)
+            app_obj.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                (_("No geometry found in file"), outname))
             return "fail"
 
         with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)):
 
             ret_val = self.app.new_object("excellon", outname, obj_init, autoselected=False)
             if ret_val == 'fail':
-                self.app.inform.emit(_('[ERROR_NOTCL] Open PDF file failed.'))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _('Open PDF file failed.'))
                 return
             # Register recent file
             self.app.file_opened.emit("excellon", filename)
             # GUI feedback
-            self.app.inform.emit(_("[success] Rendered: %s") % outname)
+            self.app.inform.emit('[success] %s: %s' %
+                                 (_("Rendered"),  outname))
 
     def layer_rendering_as_gerber(self, filename, ap_dict, layer_nr):
         outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr)
@@ -339,12 +349,13 @@ class ToolPDF(FlatCAMTool):
 
             ret = self.app.new_object('gerber', outname, obj_init, autoselected=False)
             if ret == 'fail':
-                self.app.inform.emit(_('[ERROR_NOTCL] Open PDF file failed.'))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _('Open PDF file failed.'))
                 return
             # Register recent file
             self.app.file_opened.emit('gerber', filename)
             # GUI feedback
-            self.app.inform.emit(_("[success] Rendered: %s") % outname)
+            self.app.inform.emit('[success] %s: %s' % (_("Rendered"), outname))
 
     def periodic_check(self, check_period):
         """
@@ -386,10 +397,18 @@ class ToolPDF(FlatCAMTool):
                 if self.pdf_parsed:
                     obj_to_delete = []
                     for object_name in self.pdf_parsed:
+                        if self.app.abort_flag:
+                            # graceful abort requested by the user
+                            raise FlatCAMApp.GracefulException
+
                         filename = deepcopy(self.pdf_parsed[object_name]['filename'])
                         pdf_content = deepcopy(self.pdf_parsed[object_name]['pdf'])
                         obj_to_delete.append(object_name)
                         for k in pdf_content:
+                            if self.app.abort_flag:
+                                # graceful abort requested by the user
+                                raise FlatCAMApp.GracefulException
+
                             ap_dict = pdf_content[k]
                             if ap_dict:
                                 layer_nr = k
@@ -467,6 +486,10 @@ class ToolPDF(FlatCAMTool):
         lines = pdf_content.splitlines()
 
         for pline in lines:
+            if self.app.abort_flag:
+                # graceful abort requested by the user
+                raise FlatCAMApp.GracefulException
+
             line_nr += 1
             log.debug("line %d: %s" % (line_nr, pline))
 
@@ -1325,6 +1348,10 @@ class ToolPDF(FlatCAMTool):
             if x in object_dict:
                 object_dict.pop(x)
 
+        if self.app.abort_flag:
+            # graceful abort requested by the user
+            raise FlatCAMApp.GracefulException
+
         return object_dict
 
     def bezier_to_points(self, start, c1, c2, stop):

Разница между файлами не показана из-за своего большого размера
+ 384 - 143
flatcamTools/ToolPaint.py


+ 43 - 47
flatcamTools/ToolPanelize.py

@@ -355,13 +355,15 @@ class Panelize(FlatCAMTool):
             obj = self.app.collection.get_by_name(str(name))
         except Exception as e:
             log.debug("Panelize.on_panelize() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Could not retrieve object"), name))
             return "Could not retrieve object: %s" % name
 
         panel_obj = obj
 
         if panel_obj is None:
-            self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % panel_obj)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Object not found"), panel_obj))
             return "Object not found: %s" % panel_obj
 
         boxname = self.box_combo.currentText()
@@ -370,11 +372,13 @@ class Panelize(FlatCAMTool):
             box = self.app.collection.get_by_name(boxname)
         except Exception as e:
             log.debug("Panelize.on_panelize() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % boxname)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Could not retrieve object"), boxname))
             return "Could not retrieve object: %s" % boxname
 
         if box is None:
-            self.app.inform.emit(_("[WARNING_NOTCL]No object Box. Using instead %s") % panel_obj)
+            self.app.inform.emit('[WARNING_NOTCL]%s: %s' %
+                                 (_("No object Box. Using instead"), panel_obj))
             self.reference_radio.set_value('bbox')
 
         if self.reference_radio.get_value() == 'bbox':
@@ -389,8 +393,8 @@ class Panelize(FlatCAMTool):
             try:
                 spacing_columns = float(self.spacing_columns.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                     "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         spacing_columns = spacing_columns if spacing_columns is not None else 0
 
@@ -401,8 +405,8 @@ class Panelize(FlatCAMTool):
             try:
                 spacing_rows = float(self.spacing_rows.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                     "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         spacing_rows = spacing_rows if spacing_rows is not None else 0
 
@@ -414,8 +418,8 @@ class Panelize(FlatCAMTool):
                 rows = float(self.rows.get_value().replace(',', '.'))
                 rows = int(rows)
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                     "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         rows = rows if rows is not None else 1
 
@@ -427,8 +431,8 @@ class Panelize(FlatCAMTool):
                 columns = float(self.columns.get_value().replace(',', '.'))
                 columns = int(columns)
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                     "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         columns = columns if columns is not None else 1
 
@@ -439,8 +443,8 @@ class Panelize(FlatCAMTool):
             try:
                 constrain_dx = float(self.x_width_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                     "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         try:
@@ -450,14 +454,15 @@ class Panelize(FlatCAMTool):
             try:
                 constrain_dy = float(self.y_height_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                     "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         panel_type = str(self.panel_type_radio.get_value())
 
         if 0 in {columns, rows}:
-            self.app.inform.emit(_("[ERROR_NOTCL] Columns or Rows are zero value. Change them to a positive integer."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Columns or Rows are zero value. Change them to a positive integer."))
             return "Columns or Rows are zero value. Change them to a positive integer."
 
         xmin, ymin, xmax, ymax = box.bounds()
@@ -560,12 +565,7 @@ class Panelize(FlatCAMTool):
                     if isinstance(panel_obj, FlatCAMGerber):
                         obj_fin.apertures = deepcopy(panel_obj.apertures)
                         for ap in obj_fin.apertures:
-                            if 'solid_geometry' in obj_fin.apertures[ap]:
-                                obj_fin.apertures[ap]['solid_geometry'] = []
-                            if 'clear_geometry' in obj_fin.apertures[ap]:
-                                obj_fin.apertures[ap]['clear_geometry'] = []
-                            if 'follow_geometry' in obj_fin.apertures[ap]:
-                                obj_fin.apertures[ap]['follow_geometry'] = []
+                            obj_fin.apertures[ap]['geometry'] = list()
 
                     self.app.progress.emit(0)
                     for row in range(rows):
@@ -594,33 +594,29 @@ class Panelize(FlatCAMTool):
                                     obj_fin.solid_geometry.append(geo)
 
                                 for apid in panel_obj.apertures:
-                                    if 'solid_geometry' in panel_obj.apertures[apid]:
-                                        geo_aper = translate_recursion(panel_obj.apertures[apid]['solid_geometry'])
-                                        if isinstance(geo_aper, list):
-                                            obj_fin.apertures[apid]['solid_geometry'] += geo_aper
-                                        else:
-                                            obj_fin.apertures[apid]['solid_geometry'].append(geo_aper)
+                                    for el in panel_obj.apertures[apid]['geometry']:
+                                        new_el = dict()
+                                        if 'solid' in el:
+                                            geo_aper = translate_recursion(el['solid'])
+                                            new_el['solid'] = deepcopy(geo_aper)
 
-                                    if 'clear_geometry' in panel_obj.apertures[apid]:
-                                        geo_aper = translate_recursion(panel_obj.apertures[apid]['clear_geometry'])
-                                        if isinstance(geo_aper, list):
-                                            obj_fin.apertures[apid]['clear_geometry'] += geo_aper
-                                        else:
-                                            obj_fin.apertures[apid]['clear_geometry'].append(geo_aper)
+                                        if 'clear' in el:
+                                            geo_aper = translate_recursion(el['clear'])
+                                            new_el['clear'] = deepcopy(geo_aper)
 
-                                    if 'follow_geometry' in panel_obj.apertures[apid]:
-                                        geo_aper = translate_recursion(panel_obj.apertures[apid]['follow_geometry'])
-                                        if isinstance(geo_aper, list):
-                                            obj_fin.apertures[apid]['follow_geometry'] += geo_aper
-                                        else:
-                                            obj_fin.apertures[apid]['follow_geometry'].append(geo_aper)
+                                        if 'follow' in el:
+                                            geo_aper = translate_recursion(el['follow'])
+                                            new_el['follow'] = deepcopy(geo_aper)
+
+                                        obj_fin.apertures[apid]['geometry'].append(deepcopy(new_el))
 
                             currentx += lenghtx
                         currenty += lenghty
 
                     app_obj.log.debug("Found %s geometries. Creating a panel geometry cascaded union ..." %
                                       len(obj_fin.solid_geometry))
-                    obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry)
+
+                    # obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry)
                     app_obj.log.debug("Finished creating a cascaded union for the panel.")
 
                 if isinstance(panel_obj, FlatCAMExcellon):
@@ -632,19 +628,19 @@ class Panelize(FlatCAMTool):
                                         plot=True, autoselected=True)
 
         if self.constrain_flag is False:
-            self.app.inform.emit(_("[success] Panel done..."))
+            self.app.inform.emit('[success] %s' % _("Panel done..."))
         else:
             self.constrain_flag = False
-            self.app.inform.emit(_("[WARNING] 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(
-                col=columns, row=rows))
+                text='[WARNING] ', col=columns, row=rows))
 
-        proc = self.app.proc_container.new(_("Generating panel ... Please wait."))
+        proc = self.app.proc_container.new(_("Generating panel..."))
 
         def job_thread(app_obj):
             try:
                 panelize_2()
-                self.app.inform.emit(_("[success] Panel created successfully."))
+                self.app.inform.emit('[success] %s' % _("Panel created successfully."))
             except Exception as ee:
                 proc.done()
                 log.debug(str(ee))

+ 20 - 11
flatcamTools/ToolPcbWizard.py

@@ -362,7 +362,8 @@ class PcbWizard(FlatCAMTool):
                 self.frac_entry.set_value(self.fractional)
 
         if not self.tools_from_inf:
-            self.app.inform.emit(_("[ERROR] The INF file does not contain the tool table.\n"
+            self.app.inform.emit('[ERROR] %s' %
+                                 _("The INF file does not contain the tool table.\n"
                                    "Try to open the Excellon file from File -> Open -> Excellon\n"
                                    "and edit the drill diameters manually."))
             return "fail"
@@ -382,11 +383,13 @@ class PcbWizard(FlatCAMTool):
         if signal == 'inf':
             self.inf_loaded = True
             self.tools_table.setVisible(True)
-            self.app.inform.emit(_("[success] PcbWizard .INF file loaded."))
+            self.app.inform.emit('[success] %s' %
+                                 _("PcbWizard .INF file loaded."))
         elif signal == 'excellon':
             self.excellon_loaded = True
             self.outname = os.path.split(str(filename))[1]
-            self.app.inform.emit(_("[success] Main PcbWizard Excellon file loaded."))
+            self.app.inform.emit('[success] %s' %
+                                 _("Main PcbWizard Excellon file loaded."))
 
         if self.excellon_loaded and self.inf_loaded:
             self.update_params()
@@ -420,16 +423,18 @@ class PcbWizard(FlatCAMTool):
                 ret = excellon_obj.parse_file(file_obj=excellon_fileobj)
                 if ret == "fail":
                     app_obj.log.debug("Excellon parsing failed.")
-                    app_obj.inform.emit(_("[ERROR_NOTCL] This is not Excellon file."))
+                    app_obj.inform.emit('[ERROR_NOTCL] %s' %
+                                        _("This is not Excellon file."))
                     return "fail"
             except IOError:
-                app_obj.inform.emit(_("[ERROR_NOTCL] Cannot parse file: %s") % self.outname)
+                app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (
+                        _("Cannot parse file"), self.outname))
                 app_obj.log.debug("Could not import Excellon object.")
                 app_obj.progress.emit(0)
                 return "fail"
             except Exception as e:
                 app_obj.log.debug("PcbWizard.on_import_excellon().obj_init() %s" % str(e))
-                msg = _("[ERROR_NOTCL] An internal error has occurred. See shell.\n")
+                msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n")
                 msg += app_obj.traceback.format_exc()
                 app_obj.inform.emit(msg)
                 return "fail"
@@ -442,7 +447,8 @@ class PcbWizard(FlatCAMTool):
             for tool in excellon_obj.tools:
                 if excellon_obj.tools[tool]['solid_geometry']:
                     return
-            app_obj.inform.emit(_("[ERROR_NOTCL] No geometry found in file: %s") % name)
+            app_obj.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                (_("No geometry found in file"), name))
             return "fail"
 
         if excellon_fileobj is not None and excellon_fileobj != '':
@@ -454,16 +460,19 @@ class PcbWizard(FlatCAMTool):
 
                     ret_val = self.app.new_object("excellon", name, obj_init, autoselected=False)
                     if ret_val == 'fail':
-                        self.app.inform.emit(_('[ERROR_NOTCL] Import Excellon file failed.'))
+                        self.app.inform.emit('[ERROR_NOTCL] %s' % _('Import Excellon file failed.'))
                         return
 
                         # Register recent file
                     self.app.file_opened.emit("excellon", name)
 
                     # GUI feedback
-                    self.app.inform.emit(_("[success] Imported: %s") % name)
+                    self.app.inform.emit('[success] %s: %s' %
+                                         (_("Imported"), name))
                     self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
             else:
-                self.app.inform.emit(_('[WARNING_NOTCL] Excellon merging is in progress. Please wait...'))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _('Excellon merging is in progress. Please wait...'))
         else:
-            self.app.inform.emit(_('[ERROR_NOTCL] The imported Excellon file is None.'))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _('The imported Excellon file is None.'))

+ 105 - 38
flatcamTools/ToolProperties.py

@@ -21,9 +21,10 @@ if '_' not in builtins.__dict__:
 
 
 class Properties(FlatCAMTool):
-
     toolName = _("Properties")
 
+    calculations_finished = pyqtSignal(float, float, float, float, object)
+
     def __init__(self, app):
         FlatCAMTool.__init__(self, app)
 
@@ -64,6 +65,8 @@ class Properties(FlatCAMTool):
         self.vlay.addWidget(self.treeWidget)
         self.vlay.setStretch(0, 0)
 
+        self.calculations_finished.connect(self.show_area_chull)
+
     def run(self, toggle=True):
         self.app.report_usage("ToolProperties()")
 
@@ -105,14 +108,16 @@ class Properties(FlatCAMTool):
     def properties(self):
         obj_list = self.app.collection.get_selected()
         if not obj_list:
-            self.app.inform.emit(_("[ERROR_NOTCL] Properties Tool was not displayed. No object selected."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Properties Tool was not displayed. No object selected."))
             self.app.ui.notebook.setTabText(2, _("Tools"))
             self.properties_frame.hide()
             self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
             return
         for obj in obj_list:
             self.addItems(obj)
-            self.app.inform.emit(_("[success] Object Properties are displayed."))
+            self.app.inform.emit('[success] %s' %
+                                 _("Object Properties are displayed."))
         self.app.ui.notebook.setTabText(2, _("Properties Tool"))
 
     def addItems(self, obj):
@@ -146,49 +151,94 @@ class Properties(FlatCAMTool):
 
         self.addChild(obj_name, [obj.options['name']])
 
-        # calculate physical dimensions
-        try:
-            xmin, ymin, xmax, ymax = obj.bounds()
-        except Exception as e:
-            log.debug("PropertiesTool.addItems() --> %s" % str(e))
-            return
+        def job_thread(obj):
+            proc = self.app.proc_container.new(_("Calculating dimensions ... Please wait."))
 
-        length = abs(xmax - xmin)
-        width = abs(ymax - ymin)
+            length = 0.0
+            width = 0.0
+            area = 0.0
 
-        self.addChild(dims, ['%s:' % _('Length'), '%.4f %s' % (
-            length, self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower())], True)
-        self.addChild(dims, ['%s:' % _('Width'), '%.4f %s' % (
-            width, self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower())], True)
+            geo = obj.solid_geometry
+            if geo:
+                # calculate physical dimensions
+                try:
+                    xmin, ymin, xmax, ymax = obj.bounds()
 
-        # calculate and add box area
-        if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm':
-            area = (length * width) / 100
-            self.addChild(dims, ['%s:' % _('Box Area'), '%.4f %s' % (area, 'cm2')], True)
-        else:
-            area = length * width
-            self.addChild(dims, ['%s:' % _('Box Area'), '%.4f %s' % (area, 'in2')], True)
+                    length = abs(xmax - xmin)
+                    width = abs(ymax - ymin)
+                except Exception as e:
+                    log.debug("PropertiesTool.addItems() --> %s" % str(e))
 
-        if not isinstance(obj, FlatCAMCNCjob):
-            # calculate and add convex hull area
-            geo = obj.solid_geometry
-            if isinstance(geo, MultiPolygon):
-                env_obj = geo.convex_hull
-            elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
-                    (isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
-                env_obj = cascaded_union(obj.solid_geometry)
-                env_obj = env_obj.convex_hull
+                # calculate box area
+                if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm':
+                    area = (length * width) / 100
+                else:
+                    area = length * width
             else:
-                env_obj = cascaded_union(obj.solid_geometry)
-                env_obj = env_obj.convex_hull
+                xmin = []
+                ymin = []
+                xmax = []
+                ymax = []
+
+                for tool in obj.tools:
+                    try:
+                        x0, y0, x1, y1 = cascaded_union(obj.tools[tool]['solid_geometry']).bounds
+                        xmin.append(x0)
+                        ymin.append(y0)
+                        xmax.append(x1)
+                        ymax.append(y1)
+                    except Exception as ee:
+                        log.debug("PropertiesTool.addItems() --> %s" % str(ee))
+
+                try:
+                    xmin = min(xmin)
+                    ymin = min(ymin)
+                    xmax = max(xmax)
+                    ymax = max(ymax)
+
+                    length = abs(xmax - xmin)
+                    width = abs(ymax - ymin)
+
+                    # calculate box area
+                    if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm':
+                        area = (length * width) / 100
+                    else:
+                        area = length * width
+                except Exception as e:
+                    log.debug("Properties.addItems() --> %s" % str(e))
+
+            area_chull = 0.0
+            if not isinstance(obj, FlatCAMCNCjob):
+                # calculate and add convex hull area
+                if geo:
+                    if isinstance(geo, MultiPolygon):
+                        env_obj = geo.convex_hull
+                    elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
+                            (isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
+                        env_obj = cascaded_union(obj.solid_geometry)
+                        env_obj = env_obj.convex_hull
+                    else:
+                        env_obj = cascaded_union(obj.solid_geometry)
+                        env_obj = env_obj.convex_hull
+
+                    area_chull = env_obj.area
+                else:
+                    try:
+                        area_chull = []
+                        for tool in obj.tools:
+                            area_el = cascaded_union(obj.tools[tool]['solid_geometry']).convex_hull
+                            area_chull.append(area_el.area)
+                        area_chull = max(area_chull)
+                    except Exception as e:
+                        area_chull = None
+                        log.debug("Properties.addItems() --> %s" % str(e))
 
-            area_chull = env_obj.area
             if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm':
                 area_chull = area_chull / 100
-                self.addChild(dims, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'cm2')], True)
-            else:
-                area_chull = area_chull
-                self.addChild(dims, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'in2')], True)
+
+            self.calculations_finished.emit(area, length, width, area_chull, dims)
+
+        self.app.worker_task.emit({'fcn': job_thread, 'params': [obj]})
 
         self.addChild(units,
                       ['FlatCAM units:',
@@ -294,4 +344,21 @@ class Properties(FlatCAMTool):
         if column1 is not None:
             item.setText(1, str(title[1]))
 
+    def show_area_chull(self, area, length, width, chull_area, location):
+
+        # add dimensions
+        self.addChild(location, ['%s:' % _('Length'), '%.4f %s' % (
+            length, self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower())], True)
+        self.addChild(location, ['%s:' % _('Width'), '%.4f %s' % (
+            width, self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower())], True)
+
+        # add box area
+        if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm':
+            self.addChild(location, ['%s:' % _('Box Area'), '%.4f %s' % (area, 'cm2')], True)
+            self.addChild(location, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (chull_area, 'cm2')], True)
+
+        else:
+            self.addChild(location, ['%s:' % _('Box Area'), '%.4f %s' % (area, 'in2')], True)
+            self.addChild(location, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (chull_area, 'in2')], True)
+
 # end of file

+ 61 - 37
flatcamTools/ToolSolderPaste.py

@@ -760,17 +760,18 @@ class SolderPaste(FlatCAMTool):
                 try:
                     tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
             if tool_dia is None:
                 self.build_ui()
-                self.app.inform.emit(_("[WARNING_NOTCL] Please enter a tool diameter to add, in Float format."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Please enter a tool diameter to add, in Float format."))
                 return
 
         if tool_dia == 0:
-            self.app.inform.emit(_("[WARNING_NOTCL] Please enter a tool diameter with non-zero value, "
-                                   "in Float format."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Please enter a tool diameter with non-zero value, in Float format."))
             return
 
         # construct a list of all 'tooluid' in the self.tooltable_tools
@@ -794,12 +795,14 @@ class SolderPaste(FlatCAMTool):
 
         if float('%.4f' % tool_dia) in tool_dias:
             if muted is None:
-                self.app.inform.emit(_("[WARNING_NOTCL] Adding Nozzle tool cancelled. Tool already in Tool Table."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Adding Nozzle tool cancelled. Tool already in Tool Table."))
             self.tools_table.itemChanged.connect(self.on_tool_edit)
             return
         else:
             if muted is None:
-                self.app.inform.emit(_("[success] New Nozzle tool added to Tool Table."))
+                self.app.inform.emit('[success] %s' %
+                                     _("New Nozzle tool added to Tool Table."))
             self.tooltable_tools.update({
                 int(self.tooluid): {
                     'tooldia': float('%.4f' % tool_dia),
@@ -832,8 +835,8 @@ class SolderPaste(FlatCAMTool):
                 try:
                     new_tool_dia = float(self.tools_table.item(row, 1).text().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
-                                           "use a number."))
+                    self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                         _("Wrong value format entered, use a number."))
                     return
 
             tooluid = int(self.tools_table.item(row, 2).text())
@@ -841,7 +844,8 @@ class SolderPaste(FlatCAMTool):
             # identify the tool that was edited and get it's tooluid
             if new_tool_dia not in tool_dias:
                 self.tooltable_tools[tooluid]['tooldia'] = new_tool_dia
-                self.app.inform.emit(_("[success] Nozzle tool from Tool Table was edited."))
+                self.app.inform.emit('[success] %s' %
+                                     _("Nozzle tool from Tool Table was edited."))
                 self.build_ui()
                 return
             else:
@@ -852,8 +856,8 @@ class SolderPaste(FlatCAMTool):
                         break
                 restore_dia_item = self.tools_table.item(row, 1)
                 restore_dia_item.setText(str(old_tool_dia))
-                self.app.inform.emit(_("[WARNING_NOTCL] Edit cancelled. "
-                                       "New diameter value is already in the Tool Table."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("Edit cancelled. New diameter value is already in the Tool Table."))
         self.build_ui()
 
     def on_tool_delete(self, rows_to_delete=None, all=None):
@@ -898,12 +902,14 @@ class SolderPaste(FlatCAMTool):
                     self.tooltable_tools.pop(t, None)
 
         except AttributeError:
-            self.app.inform.emit(_("[WARNING_NOTCL] Delete failed. Select a Nozzle tool to delete."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Delete failed. Select a Nozzle tool to delete."))
             return
         except Exception as e:
             log.debug(str(e))
 
-        self.app.inform.emit(_("[success] Nozzle tool(s) deleted from Tool Table."))
+        self.app.inform.emit('[success] %s' %
+                             _("Nozzle tool(s) deleted from Tool Table."))
         self.build_ui()
 
     def on_rmb_combo(self, pos, combo):
@@ -958,7 +964,8 @@ class SolderPaste(FlatCAMTool):
         """
         name = self.obj_combo.currentText()
         if name == '':
-            self.app.inform.emit(_("[WARNING_NOTCL] No SolderPaste mask Gerber object loaded."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No SolderPaste mask Gerber object loaded."))
             return
 
         obj = self.app.collection.get_by_name(name)
@@ -988,7 +995,8 @@ class SolderPaste(FlatCAMTool):
         sorted_tools.sort(reverse=True)
 
         if not sorted_tools:
-            self.app.inform.emit(_("[WARNING_NOTCL] No Nozzle tools in the tool table."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No Nozzle tools in the tool table."))
             return 'fail'
 
         def flatten(geometry=None, reset=True, pathonly=False):
@@ -1114,17 +1122,20 @@ class SolderPaste(FlatCAMTool):
                         if not geo_obj.tools[tooluid_key]['solid_geometry']:
                             a += 1
                     if a == len(geo_obj.tools):
-                        self.app.inform.emit(_('[ERROR_NOTCL] Cancelled. Empty file, it has no geometry...'))
+                        self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                             _('Cancelled. Empty file, it has no geometry...'))
                         return 'fail'
 
-                    app_obj.inform.emit(_("[success] Solder Paste geometry generated successfully..."))
+                    app_obj.inform.emit('[success] %s...' %
+                                        _("Solder Paste geometry generated successfully"))
                     return
 
             # if we still have geometry not processed at the end of the tools then we failed
             # some or all the pads are not covered with solder paste
             if work_geo:
-                app_obj.inform.emit(_("[WARNING_NOTCL] Some or all pads have no solder "
-                                    "due of inadequate nozzle diameters..."))
+                app_obj.inform.emit('[WARNING_NOTCL] %s' %
+                                    _("Some or all pads have no solder "
+                                      "due of inadequate nozzle diameters..."))
                 return 'fail'
 
         if use_thread:
@@ -1157,11 +1168,13 @@ class SolderPaste(FlatCAMTool):
         obj = self.app.collection.get_by_name(name)
 
         if name == '':
-            self.app.inform.emit(_("[WARNING_NOTCL] There is no Geometry object available."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("There is no Geometry object available."))
             return 'fail'
 
         if obj.special_group != 'solder_paste_tool':
-            self.app.inform.emit(_("[WARNING_NOTCL] This Geometry can't be processed. "
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("This Geometry can't be processed. "
                                    "NOT a solder_paste_tool geometry."))
             return 'fail'
 
@@ -1170,7 +1183,8 @@ class SolderPaste(FlatCAMTool):
             if obj.tools[tooluid_key]['solid_geometry'] is None:
                 a += 1
         if a == len(obj.tools):
-            self.app.inform.emit(_('[ERROR_NOTCL] Cancelled. Empty file, it has no geometry...'))
+            self.app.inform.emit('[ERROR_NOTCL] %s...' %
+                                 _('Cancelled. Empty file, it has no geometry'))
             return 'fail'
 
         # use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia
@@ -1197,7 +1211,7 @@ class SolderPaste(FlatCAMTool):
             ymax = obj.options['ymax']
         except Exception as e:
             log.debug("FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s\n" % str(e))
-            msg = "[ERROR] An internal error has ocurred. See shell.\n"
+            msg = '[ERROR] %s' % _("An internal error has ocurred. See shell.\n")
             msg += 'FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s' % str(e)
             msg += traceback.format_exc()
             self.app.inform.emit(msg)
@@ -1267,7 +1281,8 @@ class SolderPaste(FlatCAMTool):
             def job_thread(app_obj):
                 with self.app.proc_container.new("Generating CNC Code"):
                     if app_obj.new_object("cncjob", name, job_init) != 'fail':
-                        app_obj.inform.emit(_("[success] ToolSolderPaste CNCjob created: %s") % name)
+                        app_obj.inform.emit('[success] [success] %s: %s' %
+                                            (_("ToolSolderPaste CNCjob created"), name))
                         app_obj.progress.emit(100)
 
             # Create a promise with the name
@@ -1299,12 +1314,14 @@ class SolderPaste(FlatCAMTool):
 
         try:
             if obj.special_group != 'solder_paste_tool':
-                self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed. "
-                                     "NOT a solder_paste_tool CNCJob object."))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("This CNCJob object can't be processed. "
+                                       "NOT a solder_paste_tool CNCJob object."))
                 return
         except AttributeError:
-            self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed. "
-                                 "NOT a solder_paste_tool CNCJob object."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("This CNCJob object can't be processed. "
+                                   "NOT a solder_paste_tool CNCJob object."))
             return
 
         gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \
@@ -1327,7 +1344,8 @@ class SolderPaste(FlatCAMTool):
             lines = StringIO(gcode)
         except Exception as e:
             log.debug("ToolSolderpaste.on_view_gcode() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] No Gcode in the object..."))
+            self.app.inform.emit('[ERROR_NOTCL] %s...' %
+                                 _("No Gcode in the object"))
             return
 
         try:
@@ -1336,7 +1354,8 @@ class SolderPaste(FlatCAMTool):
                 self.app.ui.code_editor.append(proc_line)
         except Exception as e:
             log.debug('ToolSolderPaste.on_view_gcode() -->%s' % str(e))
-            self.app.inform.emit(_('[ERROR] ToolSolderPaste.on_view_gcode() -->%s') % str(e))
+            self.app.inform.emit('[ERROR] %s --> %s' %
+                                 (_('ToolSolderPaste.on_view_gcode()'), str(e)))
             return
 
         self.app.ui.code_editor.moveCursor(QtGui.QTextCursor.Start)
@@ -1355,8 +1374,9 @@ class SolderPaste(FlatCAMTool):
         obj = self.app.collection.get_by_name(name)
 
         if obj.special_group != 'solder_paste_tool':
-            self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed. "
-                                 "NOT a solder_paste_tool CNCJob object."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("This CNCJob object can't be processed. "
+                                   "NOT a solder_paste_tool CNCJob object."))
             return
 
         _filter_ = "G-Code Files (*.nc);;G-Code Files (*.txt);;G-Code Files (*.tap);;G-Code Files (*.cnc);;" \
@@ -1373,7 +1393,8 @@ class SolderPaste(FlatCAMTool):
             filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export Machine Code ..."), filter=_filter_)
 
         if filename == '':
-            self.app.inform.emit(_("[WARNING_NOTCL] Export Machine Code cancelled ..."))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("Export Machine Code cancelled ..."))
             return
 
         gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \
@@ -1399,17 +1420,20 @@ class SolderPaste(FlatCAMTool):
                     for line in lines:
                         f.write(line)
             except FileNotFoundError:
-                self.app.inform.emit(_("[WARNING_NOTCL] No such file or directory"))
+                self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                     _("No such file or directory"))
                 return
             except PermissionError:
-                self.app.inform.emit(_("[WARNING] Permission denied, saving not possible.\n"
+                self.app.inform.emit('[WARNING] %s' %
+                                     _("Permission denied, saving not possible.\n"
                                        "Most likely another app is holding the file open and not accessible."))
                 return 'fail'
 
         if self.app.defaults["global_open_style"] is False:
             self.app.file_opened.emit("gcode", filename)
         self.app.file_saved.emit("gcode", filename)
-        self.app.inform.emit(_("[success] Solder paste dispenser GCode file saved to: %s") % filename)
+        self.app.inform.emit('[success] %s: %s' %
+                             (_("Solder paste dispenser GCode file saved to"), filename))
 
     def reset_fields(self):
         self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))

+ 28 - 14
flatcamTools/ToolSub.py

@@ -241,7 +241,8 @@ class ToolSub(FlatCAMTool):
 
         self.target_grb_obj_name = self.target_gerber_combo.currentText()
         if self.target_grb_obj_name == '':
-            self.app.inform.emit(_("[ERROR_NOTCL] No Target object loaded."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("No Target object loaded."))
             return
 
         # Get target object.
@@ -249,12 +250,14 @@ class ToolSub(FlatCAMTool):
             self.target_grb_obj = self.app.collection.get_by_name(self.target_grb_obj_name)
         except Exception as e:
             log.debug("ToolSub.on_grb_intersection_click() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Could not retrieve object"), self.obj_name))
             return "Could not retrieve object: %s" % self.target_grb_obj_name
 
         self.sub_grb_obj_name = self.sub_gerber_combo.currentText()
         if self.sub_grb_obj_name == '':
-            self.app.inform.emit(_("[ERROR_NOTCL] No Substractor object loaded."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("No Substractor object loaded."))
             return
 
         # Get substractor object.
@@ -262,7 +265,8 @@ class ToolSub(FlatCAMTool):
             self.sub_grb_obj = self.app.collection.get_by_name(self.sub_grb_obj_name)
         except Exception as e:
             log.debug("ToolSub.on_grb_intersection_click() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Could not retrieve object"), self.obj_name))
             return "Could not retrieve object: %s" % self.sub_grb_obj_name
 
         # crate the new_apertures dict structure
@@ -412,11 +416,13 @@ class ToolSub(FlatCAMTool):
         with self.app.proc_container.new(_("Generating new object ...")):
             ret = self.app.new_object('gerber', outname, obj_init, autoselected=False)
             if ret == 'fail':
-                self.app.inform.emit(_('[ERROR_NOTCL] Generating new object failed.'))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _('Generating new object failed.'))
                 return
 
             # GUI feedback
-            self.app.inform.emit(_("[success] Created: %s") % outname)
+            self.app.inform.emit('[success] %s: %s' %
+                                 (_("Created"), outname))
 
             # cleanup
             self.new_apertures.clear()
@@ -437,7 +443,8 @@ class ToolSub(FlatCAMTool):
 
         self.target_geo_obj_name = self.target_geo_combo.currentText()
         if self.target_geo_obj_name == '':
-            self.app.inform.emit(_("[ERROR_NOTCL] No Target object loaded."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("No Target object loaded."))
             return
 
         # Get target object.
@@ -445,12 +452,14 @@ class ToolSub(FlatCAMTool):
             self.target_geo_obj = self.app.collection.get_by_name(self.target_geo_obj_name)
         except Exception as e:
             log.debug("ToolSub.on_geo_intersection_click() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.target_geo_obj_name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Could not retrieve object"), self.target_geo_obj_name))
             return "Could not retrieve object: %s" % self.target_grb_obj_name
 
         self.sub_geo_obj_name = self.sub_geo_combo.currentText()
         if self.sub_geo_obj_name == '':
-            self.app.inform.emit(_("[ERROR_NOTCL] No Substractor object loaded."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("No Substractor object loaded."))
             return
 
         # Get substractor object.
@@ -458,11 +467,13 @@ class ToolSub(FlatCAMTool):
             self.sub_geo_obj = self.app.collection.get_by_name(self.sub_geo_obj_name)
         except Exception as e:
             log.debug("ToolSub.on_geo_intersection_click() --> %s" % str(e))
-            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.sub_geo_obj_name)
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' %
+                                 (_("Could not retrieve object"), self.sub_geo_obj_name))
             return "Could not retrieve object: %s" % self.sub_geo_obj_name
 
         if self.sub_geo_obj.multigeo:
-            self.app.inform.emit(_("[ERROR_NOTCL] Currently, the Substractor geometry cannot be of type Multigeo."))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _("Currently, the Substractor geometry cannot be of type Multigeo."))
             return
 
         # create the target_options obj
@@ -608,12 +619,14 @@ class ToolSub(FlatCAMTool):
         with self.app.proc_container.new(_("Generating new object ...")):
             ret = self.app.new_object('geometry', outname, obj_init, autoselected=False)
             if ret == 'fail':
-                self.app.inform.emit(_('[ERROR_NOTCL] Generating new object failed.'))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _('Generating new object failed.'))
                 return
             # Register recent file
             self.app.file_opened.emit('geometry', outname)
             # GUI feedback
-            self.app.inform.emit(_("[success] Created: %s") % outname)
+            self.app.inform.emit('[success] %s: %s' %
+                                 (_("Created"), outname))
 
             # cleanup
             self.new_tools.clear()
@@ -687,7 +700,8 @@ class ToolSub(FlatCAMTool):
                 self.app.worker_task.emit({'fcn': self.new_geo_object,
                                            'params': [outname]})
         else:
-            self.app.inform.emit(_('[ERROR_NOTCL] Generating new object failed.'))
+            self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                 _('Generating new object failed.'))
 
     def reset_fields(self):
         self.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))

+ 44 - 30
flatcamTools/ToolTransform.py

@@ -470,8 +470,8 @@ class ToolTransform(FlatCAMTool):
             try:
                 value = float(self.rotate_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Rotate, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
         self.app.worker_task.emit({'fcn': self.on_rotate_action,
                                    'params': [value]})
@@ -504,8 +504,8 @@ class ToolTransform(FlatCAMTool):
             try:
                 value = float(self.skewx_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew X, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         # self.on_skew("X", value)
@@ -522,8 +522,8 @@ class ToolTransform(FlatCAMTool):
             try:
                 value = float(self.skewy_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew Y, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         # self.on_skew("Y", value)
@@ -540,8 +540,8 @@ class ToolTransform(FlatCAMTool):
             try:
                 xvalue = float(self.scalex_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale X, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
@@ -574,8 +574,8 @@ class ToolTransform(FlatCAMTool):
             try:
                 yvalue = float(self.scaley_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale Y, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
@@ -603,8 +603,8 @@ class ToolTransform(FlatCAMTool):
             try:
                 value = float(self.offx_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset X, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         # self.on_offset("X", value)
@@ -621,8 +621,8 @@ class ToolTransform(FlatCAMTool):
             try:
                 value = float(self.offy_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset Y, "
-                                       "use a number."))
+                self.app.inform.emit('[ERROR_NOTCL] %s' %
+                                     _("Wrong value format entered, use a number."))
                 return
 
         # self.on_offset("Y", value)
@@ -639,7 +639,8 @@ class ToolTransform(FlatCAMTool):
         ymaxlist = []
 
         if not obj_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to rotate!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No object selected. Please Select an object to rotate!"))
             return
         else:
             with self.app.proc_container.new(_("Appying Rotate")):
@@ -675,11 +676,12 @@ class ToolTransform(FlatCAMTool):
                         # add information to the object that it was changed and how much
                         sel_obj.options['rotate'] = num
                         sel_obj.plot()
-                    self.app.inform.emit(_('[success] Rotate done ...'))
+                    self.app.inform.emit('[success] %s...' % _('Rotate done'))
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, rotation movement was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
                     return
 
     def on_flip(self, axis):
@@ -690,7 +692,8 @@ class ToolTransform(FlatCAMTool):
         ymaxlist = []
 
         if not obj_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to flip!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s!' %
+                                 _("No object selected. Please Select an object to flip"))
             return
         else:
             with self.app.proc_container.new(_("Applying Flip")):
@@ -735,7 +738,8 @@ class ToolTransform(FlatCAMTool):
                                     sel_obj.options['mirror_y'] = not sel_obj.options['mirror_y']
                                 else:
                                     sel_obj.options['mirror_y'] = True
-                                self.app.inform.emit(_('[success] Flip on the Y axis done ...'))
+                                self.app.inform.emit('[success] %s...' %
+                                                     _('Flip on the Y axis done'))
                             elif axis is 'Y':
                                 sel_obj.mirror('Y', (px, py))
                                 # add information to the object that it was changed and how much
@@ -744,13 +748,15 @@ class ToolTransform(FlatCAMTool):
                                     sel_obj.options['mirror_x'] = not sel_obj.options['mirror_x']
                                 else:
                                     sel_obj.options['mirror_x'] = True
-                                self.app.inform.emit(_('[success] Flip on the X axis done ...'))
+                                self.app.inform.emit('[success] %s...' %
+                                                     _('Flip on the X axis done'))
                             self.app.object_changed.emit(sel_obj)
                         sel_obj.plot()
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Flip action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
                     return
 
     def on_skew(self, axis, num):
@@ -759,7 +765,8 @@ class ToolTransform(FlatCAMTool):
         yminlist = []
 
         if not obj_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to shear/skew!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No object selected. Please Select an object to shear/skew!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Skew")):
@@ -797,7 +804,8 @@ class ToolTransform(FlatCAMTool):
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Skew action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
                     return
 
     def on_scale(self, axis, xfactor, yfactor, point=None):
@@ -808,7 +816,8 @@ class ToolTransform(FlatCAMTool):
         ymaxlist = []
 
         if not obj_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to scale!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No object selected. Please Select an object to scale!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Scale")):
@@ -850,17 +859,20 @@ class ToolTransform(FlatCAMTool):
                             self.app.object_changed.emit(sel_obj)
                         sel_obj.plot()
 
-                    self.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis))
+                    self.app.inform.emit('[success] %s %s %s...' %
+                                         (_('Scale on the'), str(axis), _('axis done')))
                     self.app.progress.emit(100)
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Scale action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e), _("action was not executed.")))
                     return
 
     def on_offset(self, axis, num):
         obj_list = self.app.collection.get_selected()
 
         if not obj_list:
-            self.app.inform.emit(_("[WARNING_NOTCL] No object selected. Please Select an object to offset!"))
+            self.app.inform.emit('[WARNING_NOTCL] %s' %
+                                 _("No object selected. Please Select an object to offset!"))
             return
         else:
             with self.app.proc_container.new(_("Applying Offset")):
@@ -869,7 +881,7 @@ class ToolTransform(FlatCAMTool):
 
                     for sel_obj in obj_list:
                         if isinstance(sel_obj, FlatCAMCNCjob):
-                            self.app.inform.emit(_("CNCJob objects can't be offseted."))
+                            self.app.inform.emit(_("CNCJob objects can't be offset."))
                         else:
                             if axis is 'X':
                                 sel_obj.offset((num, 0))
@@ -882,11 +894,13 @@ class ToolTransform(FlatCAMTool):
                             self.app.object_changed.emit(sel_obj)
                         sel_obj.plot()
 
-                    self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis))
+                    self.app.inform.emit('[success] %s %s %s...' %
+                                         (_('Offset on the'), str(axis), _('axis done')))
                     self.app.progress.emit(100)
 
                 except Exception as e:
-                    self.app.inform.emit(_("[ERROR_NOTCL] Due of %s, Offset action was not executed.") % str(e))
+                    self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' %
+                                         (_("Due of"), str(e),  _("action was not executed.")))
                     return
 
 # end of file

BIN
locale/de/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 479 - 458
locale/de/LC_MESSAGES/strings.po


BIN
locale/en/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 487 - 347
locale/en/LC_MESSAGES/strings.po


BIN
locale/ro/LC_MESSAGES/strings.mo


Разница между файлами не показана из-за своего большого размера
+ 474 - 476
locale/ro/LC_MESSAGES/strings.po


Разница между файлами не показана из-за своего большого размера
+ 355 - 325
locale_template/strings.pot


Некоторые файлы не были показаны из-за большого количества измененных файлов