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

Merged in test_beta8.913 (pull request #142)

Test beta8.913
Marius Stanciu 6 лет назад
Родитель
Сommit
9d508a6840
48 измененных файлов с 22178 добавлено и 4910 удалено
  1. 457 127
      FlatCAMApp.py
  2. 88 227
      FlatCAMObj.py
  3. 12 4
      FlatCAMTranslation.py
  4. 2 2
      FlatCAMWorkerStack.py
  5. 15 0
      ObjectCollection.py
  6. 178 0
      README.md
  7. 371 114
      camlib.py
  8. 2557 0
      flatcamEditors/FlatCAMExcEditor.py
  9. 772 3161
      flatcamEditors/FlatCAMGeoEditor.py
  10. 3803 0
      flatcamEditors/FlatCAMGrbEditor.py
  11. 0 0
      flatcamEditors/__init__.py
  12. 490 49
      flatcamGUI/FlatCAMGUI.py
  13. 326 99
      flatcamGUI/GUIElements.py
  14. 7 89
      flatcamGUI/ObjectUI.py
  15. 5 2
      flatcamGUI/PlotCanvas.py
  16. 5 2
      flatcamGUI/VisPyVisuals.py
  17. 21 25
      flatcamParsers/ParseSVG.py
  18. 9 9
      flatcamTools/ToolCalculators.py
  19. 44 24
      flatcamTools/ToolCutOut.py
  20. 2 2
      flatcamTools/ToolDblSided.py
  21. 4 4
      flatcamTools/ToolFilm.py
  22. 127 186
      flatcamTools/ToolMeasurement.py
  23. 3 3
      flatcamTools/ToolMove.py
  24. 15 9
      flatcamTools/ToolNonCopperClear.py
  25. 23 16
      flatcamTools/ToolPaint.py
  26. 12 12
      flatcamTools/ToolPanelize.py
  27. 6 0
      flatcamTools/ToolProperties.py
  28. 2 2
      flatcamTools/ToolSolderPaste.py
  29. 12 12
      flatcamTools/ToolTransform.py
  30. BIN
      locale/de/LC_MESSAGES/strings.mo
  31. 11783 0
      locale/de/LC_MESSAGES/strings.po
  32. BIN
      locale/en/LC_MESSAGES/strings.mo
  33. 341 230
      locale/en/LC_MESSAGES/strings.po
  34. BIN
      locale/ro/LC_MESSAGES/strings.mo
  35. 361 256
      locale/ro/LC_MESSAGES/strings.po
  36. 317 236
      locale_template/strings.pot
  37. BIN
      share/aperture16.png
  38. BIN
      share/aperture32.png
  39. BIN
      share/padarray32.png
  40. BIN
      share/scale32.png
  41. BIN
      share/script_new16.png
  42. BIN
      share/script_open16.png
  43. BIN
      share/track32.png
  44. 1 1
      tclCommands/TclCommand.py
  45. 1 1
      tclCommands/TclCommandCutout.py
  46. 3 3
      tclCommands/TclCommandGeoCutout.py
  47. 1 1
      tclCommands/TclCommandOpenGerber.py
  48. 2 2
      tclCommands/TclCommandPanelize.py

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


+ 88 - 227
FlatCAMObj.py

@@ -178,7 +178,7 @@ class FlatCAMObj(QtCore.QObject):
         self.muted_ui = False
 
     def on_name_activate(self):
-        old_name = copy.copy(self.options["name"])
+        old_name = copy(self.options["name"])
         new_name = self.ui.name_entry.get_value()
 
         # update the SHELL auto-completer model data
@@ -186,11 +186,12 @@ class FlatCAMObj(QtCore.QObject):
             self.app.myKeywords.remove(old_name)
             self.app.myKeywords.append(new_name)
             self.app.shell._edit.set_model_data(self.app.myKeywords)
+            self.app.ui.code_editor.set_model_data(self.app.myKeywords)
         except:
             log.debug("on_name_activate() --> Could not remove the old object name from auto-completer model list")
 
         self.options["name"] = self.ui.name_entry.get_value()
-        self.app.inform.emit(_("[success]Name changed from {old} to {new}").format(old=old_name, new=new_name))
+        self.app.inform.emit(_("[success] Name changed from {old} to {new}").format(old=old_name, new=new_name))
 
     def on_offset_button_click(self):
         self.app.report_usage("obj_on_offset_button")
@@ -410,18 +411,27 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                         except:
                             log.warning("Failed to copy option.", option)
 
-                for geos in grb.solid_geometry:
-                    grb_final.solid_geometry.append(geos)
-                    grb_final.follow_geometry.append(geos)
+                try:
+                    for geos in grb.solid_geometry:
+                        grb_final.solid_geometry.append(geos)
+                        grb_final.follow_geometry.append(geos)
+                except TypeError:
+                    grb_final.solid_geometry.append(grb.solid_geometry)
+                    grb_final.follow_geometry.append(grb.solid_geometry)
 
                 for ap in grb.apertures:
                     if ap not in grb_final.apertures:
                         grb_final.apertures[ap] = grb.apertures[ap]
                     else:
-                        if 'solid_geometry' not in grb_final.apertures[ap]:
-                            grb_final.apertures[ap]['solid_geometry'] = []
-                        for geo in grb.apertures[ap]['solid_geometry']:
-                            grb_final.apertures[ap]['solid_geometry'].append(geo)
+                        # create a list of integers out of the grb.apertures keys and find the max of that value
+                        # then, the aperture duplicate is assigned an id value incremented with 1,
+                        # and finally made string because the apertures dict keys are strings
+                        max_ap = str(max([int(k) for k in grb_final.apertures.keys()]) + 1)
+                        grb_final.apertures[max_ap] = {}
+                        grb_final.apertures[max_ap]['solid_geometry'] = []
+
+                        for k, v in grb.apertures[ap].items():
+                            grb_final.apertures[max_ap][k] = v
 
         grb_final.solid_geometry = MultiPolygon(grb_final.solid_geometry)
         grb_final.follow_geometry = MultiPolygon(grb_final.follow_geometry)
@@ -448,8 +458,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             "bboxmargin": 0.0,
             "bboxrounded": False,
             "aperture_display": False,
-            "aperture_scale_factor": 1.0,
-            "aperture_buffer_factor": 0.0,
             "follow": False
         })
 
@@ -501,8 +509,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             "bboxmargin": self.ui.bbmargin_entry,
             "bboxrounded": self.ui.bbrounded_cb,
             "aperture_display": self.ui.aperture_table_visibility_cb,
-            "aperture_scale_factor": self.ui.scale_aperture_entry,
-            "aperture_buffer_factor": self.ui.buffer_aperture_entry,
             "follow": self.ui.follow_cb
         })
 
@@ -522,9 +528,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click)
         self.ui.aperture_table_visibility_cb.stateChanged.connect(self.on_aperture_table_visibility_change)
         self.ui.follow_cb.stateChanged.connect(self.on_follow_cb_click)
-        self.ui.scale_aperture_button.clicked.connect(self.on_scale_aperture_click)
-        self.ui.buffer_aperture_button.clicked.connect(self.on_buffer_aperture_click)
-        self.ui.new_grb_button.clicked.connect(self.on_new_modified_gerber)
 
         # Show/Hide Advanced Options
         if self.app.defaults["global_app_level"] == 'b':
@@ -896,7 +899,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                 for g in geo_obj.solid_geometry:
                     if g:
                         app_obj.inform.emit(_(
-                            "[success]Isolation geometry created: %s"
+                            "[success] Isolation geometry created: %s"
                         ) % geo_obj.options["name"])
                         break
                     else:
@@ -951,7 +954,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                     for g in geo_obj.solid_geometry:
                         if g:
                             app_obj.inform.emit(_(
-                                "[success]Isolation geometry created: %s"
+                                "[success] Isolation geometry created: %s"
                             ) % geo_obj.options["name"])
                             break
                         else:
@@ -989,30 +992,12 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
     def on_aperture_table_visibility_change(self):
         if self.ui.aperture_table_visibility_cb.isChecked():
             self.ui.apertures_table.setVisible(True)
-            self.ui.scale_aperture_label.setVisible(True)
-            self.ui.scale_aperture_entry.setVisible(True)
-            self.ui.scale_aperture_button.setVisible(True)
-
-            self.ui.buffer_aperture_label.setVisible(True)
-            self.ui.buffer_aperture_entry.setVisible(True)
-            self.ui.buffer_aperture_button.setVisible(True)
 
-            self.ui.new_grb_label.setVisible(True)
-            self.ui.new_grb_button.setVisible(True)
             self.ui.mark_all_cb.setVisible(True)
             self.ui.mark_all_cb.setChecked(False)
         else:
             self.ui.apertures_table.setVisible(False)
-            self.ui.scale_aperture_label.setVisible(False)
-            self.ui.scale_aperture_entry.setVisible(False)
-            self.ui.scale_aperture_button.setVisible(False)
 
-            self.ui.buffer_aperture_label.setVisible(False)
-            self.ui.buffer_aperture_entry.setVisible(False)
-            self.ui.buffer_aperture_button.setVisible(False)
-
-            self.ui.new_grb_label.setVisible(False)
-            self.ui.new_grb_button.setVisible(False)
             self.ui.mark_all_cb.setVisible(False)
 
             # on hide disable all mark plots
@@ -1020,136 +1005,6 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                 self.ui.apertures_table.cellWidget(row, 5).set_value(False)
             self.clear_plot_apertures()
 
-    def on_scale_aperture_click(self, signal):
-        try:
-            factor = self.ui.scale_aperture_entry.get_value()
-        except Exception as e:
-            log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
-            self.app.inform.emit(_(
-                "[ERROR_NOTCL] The aperture scale factor value is missing or wrong format."
-            ))
-            return
-
-        def scale_recursion(geom):
-            if type(geom) == list or type(geom) is MultiPolygon:
-                geoms=list()
-                for local_geom in geom:
-                    geoms.append(scale_recursion(local_geom))
-                return geoms
-            else:
-                return  affinity.scale(geom, factor, factor, origin='center')
-
-        if not self.ui.apertures_table.selectedItems():
-            self.app.inform.emit(_(
-                "[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again."
-            ))
-            return
-
-        for x in self.ui.apertures_table.selectedItems():
-            try:
-                apid = self.ui.apertures_table.item(x.row(), 1).text()
-            except Exception as e:
-                log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
-
-            self.apertures[apid]['solid_geometry'] = scale_recursion(self.apertures[apid]['solid_geometry'])
-
-        self.on_mark_cb_click_table()
-
-    def on_buffer_aperture_click(self, signal):
-        try:
-            buff_value = self.ui.buffer_aperture_entry.get_value()
-        except Exception as e:
-            log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
-            self.app.inform.emit(_(
-                "[ERROR_NOTCL] The aperture buffer value is missing or wrong format."
-            ))
-            return
-
-        def buffer_recursion(geom):
-            if type(geom) == list or type(geom) is MultiPolygon:
-                geoms=list()
-                for local_geom in geom:
-                    geoms.append(buffer_recursion(local_geom))
-                return geoms
-            else:
-                return  geom.buffer(buff_value, join_style=2)
-
-        if not self.ui.apertures_table.selectedItems():
-            self.app.inform.emit(_(
-                "[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again."
-            ))
-            return
-
-        for x in self.ui.apertures_table.selectedItems():
-            try:
-                apid = self.ui.apertures_table.item(x.row(), 1).text()
-            except Exception as e:
-                log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e))
-
-            self.apertures[apid]['solid_geometry'] = buffer_recursion(self.apertures[apid]['solid_geometry'])
-
-        self.on_mark_cb_click_table()
-
-    def on_new_modified_gerber(self, signal):
-
-        name = '%s_ap_mod' % str(self.options['name'])
-        apertures = deepcopy(self.apertures)
-        options = self.options
-
-        # geometry storage
-        poly_buff = []
-
-        # How the object should be initialized
-        def obj_init(gerber_obj, app_obj):
-            assert isinstance(gerber_obj, FlatCAMGerber), \
-                "Expected to initialize a FlatCAMGerber but got %s" % type(gerber_obj)
-
-            gerber_obj.source_file = self.source_file
-            gerber_obj.multigeo = False
-            gerber_obj.follow = False
-
-            gerber_obj.apertures = apertures
-            for option in options:
-                # we don't want to overwrite the new name and we don't want to share the 'plot' state
-                # because the new object should ve visible even if the source is not visible
-                if option != 'name' and option != 'plot':
-                    gerber_obj.options[option] = options[option]
-
-            # regenerate solid_geometry
-            app_obj.log.debug("Creating new Gerber object. Joining %s polygons.")
-            # for ap in apertures:
-                # for geo in apertures[ap]['solid_geometry']:
-                #     poly_buff.append(geo)
-            poly_buff = [geo for ap in apertures for geo in apertures[ap]['solid_geometry']]
-
-            # buffering the poly_buff
-            new_geo = MultiPolygon(poly_buff)
-            new_geo = new_geo.buffer(0.0000001)
-            new_geo = new_geo.buffer(-0.0000001)
-
-            gerber_obj.solid_geometry = new_geo
-
-            app_obj.log.debug("Finished creation of a new Gerber object. Polygons joined.")
-
-        log.debug("on_new_modified_gerber()")
-
-        with self.app.proc_container.new(_("Generating Gerber")) as proc:
-
-            self.app.progress.emit(10)
-
-            ### Object creation ###
-            ret = self.app.new_object("gerber", name, obj_init, autoselected=False)
-            if ret == 'fail':
-                self.app.inform.emit(_(
-                    '[ERROR_NOTCL] Cretion of Gerber failed.'
-                ))
-                return
-
-            self.app.progress.emit(100)
-
-            # GUI feedback
-            self.app.inform.emit(_("[success] Created: %s") % name)
-
     def convert_units(self, units):
         """
         Converts the units of the object by scaling dimensions in all geometry
@@ -1317,7 +1172,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
 
                 aperture = self.ui.apertures_table.item(row, 1).text()
                 # self.plot_apertures(color='#2d4606bf', marked_aperture=aperture, visible=True)
-                self.plot_apertures(color='#FD6A02', marked_aperture=aperture, visible=True)
+                self.plot_apertures(color=self.app.defaults['global_sel_draw_color'], marked_aperture=aperture, visible=True)
             else:
                 self.marked_rows.append(False)
 
@@ -1354,7 +1209,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         if mark_all:
             for aperture in self.apertures:
                 # self.plot_apertures(color='#2d4606bf', marked_aperture=aperture, visible=True)
-                self.plot_apertures(color='#FD6A02', marked_aperture=aperture, visible=True)
+                self.plot_apertures(color=self.app.defaults['global_sel_draw_color'], marked_aperture=aperture, visible=True)
         else:
             self.clear_plot_apertures()
 
@@ -1955,7 +1810,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                         self.ui.tools_table.currentItem().text().replace(',', '.'))
                 except ValueError:
                     self.app.inform.emit(_(
-                        "[ERROR_NOTCL]Wrong value format entered, use a number."
+                        "[ERROR_NOTCL] Wrong value format entered, use a number."
                     ))
                     self.ui.tools_table.currentItem().setText(str(self.tool_offset[dia]))
                     return
@@ -2179,7 +2034,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
         if len(tools) == 0:
             self.app.inform.emit(_(
-                "[ERROR_NOTCL]Please select one or more tools from the list and try again."
+                "[ERROR_NOTCL] Please select one or more tools from the list and try again."
             ))
             return False, "Error: No tools."
 
@@ -2270,7 +2125,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
         if len(tools) == 0:
             self.app.inform.emit(_(
-                "[ERROR_NOTCL]Please select one or more tools from the list and try again."
+                "[ERROR_NOTCL] Please select one or more tools from the list and try again."
             ))
             return False, "Error: No tools."
 
@@ -2385,7 +2240,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                 tools.append(self.ui.tools_table.item(0, 0).text())
             else:
                 self.app.inform.emit(_(
-                    "[ERROR_NOTCL]Please select one or more tools from the list and try again."
+                    "[ERROR_NOTCL] Please select one or more tools from the list and try again."
                 ))
                 return
 
@@ -2409,6 +2264,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
             ### Add properties to the object
 
+            job_obj.origin_kind = 'excellon'
+
             job_obj.options['Tools_in_use'] = tool_table_items
             job_obj.options['type'] = 'Excellon'
             job_obj.options['ppname_e'] = pp_excellon_name
@@ -2443,7 +2300,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
                         ))
 
             try:
@@ -2455,7 +2312,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
                             'or self.options["feedrate_probe"]'
                         )
                     )
@@ -2608,7 +2465,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
     #     except TypeError:  # Element is not iterable...
     #         self.add_shape(shape=element, color=color, visible=visible, layer=0)
 
-    def plot(self):
+    def plot(self, kind=None):
 
         # Does all the required setup and returns False
         # if the 'ptint' option is set to False.
@@ -3218,7 +3075,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                             )
                         except ValueError:
                             self.app.inform.emit(_(
-                                "[ERROR_NOTCL]Wrong value format entered, "
+                                "[ERROR_NOTCL] Wrong value format entered, "
                                 "use a number."
                             )
                             )
@@ -3439,7 +3296,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         else:
             change_message = False
             self.app.inform.emit(_(
-                "[ERROR_NOTCL]Default Tool added. Wrong value format entered."
+                "[ERROR_NOTCL] Default Tool added. Wrong value format entered."
             ))
         self.build_ui()
 
@@ -3469,7 +3326,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                         self.tools[int(max_uid)] = deepcopy(self.tools[tooluid_copy])
                     except AttributeError:
                         self.app.inform.emit(_(
-                            "[WARNING_NOTCL]Failed. Select a tool to copy."
+                            "[WARNING_NOTCL] Failed. Select a tool to copy."
                         ))
                         self.build_ui()
                         return
@@ -3479,7 +3336,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 # self.ui.geo_tools_table.clearSelection()
             else:
                 self.app.inform.emit(_(
-                    "[WARNING_NOTCL]Failed. Select a tool to copy."
+                    "[WARNING_NOTCL] Failed. Select a tool to copy."
                 ))
                 self.build_ui()
                 return
@@ -3524,7 +3381,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 d = float(self.ui.geo_tools_table.item(current_row, 1).text().replace(',', '.'))
             except ValueError:
                 self.app.inform.emit(_(
-                    "[ERROR_NOTCL]Wrong value format entered, "
+                    "[ERROR_NOTCL] Wrong value format entered, "
                     "use a number."
                 ))
                 return
@@ -3572,7 +3429,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                         temp_tools.clear()
                     except AttributeError:
                         self.app.inform.emit(_(
-                            "[WARNING_NOTCL]Failed. Select a tool to delete."
+                            "[WARNING_NOTCL] Failed. Select a tool to delete."
                         ))
                         self.build_ui()
                         return
@@ -3582,7 +3439,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 # self.ui.geo_tools_table.clearSelection()
             else:
                 self.app.inform.emit(_(
-                    "[WARNING_NOTCL]Failed. Select a tool to delete."
+                    "[WARNING_NOTCL] Failed. Select a tool to delete."
                 ))
                 self.build_ui()
                 return
@@ -3711,7 +3568,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 vdia = float(self.ui.tipdia_entry.get_value().replace(',', '.'))
             except ValueError:
                 self.app.inform.emit(_(
-                    "[ERROR_NOTCL]Wrong value format entered, "
+                    "[ERROR_NOTCL] Wrong value format entered, "
                     "use a number."
                 ))
                 return
@@ -3724,7 +3581,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 half_vangle = float(self.ui.tipangle_entry.get_value().replace(',', '.')) / 2
             except ValueError:
                 self.app.inform.emit(_(
-                    "[ERROR_NOTCL]Wrong value format entered, "
+                    "[ERROR_NOTCL] Wrong value format entered, "
                     "use a number."
                 ))
                 return
@@ -3841,7 +3698,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                                      )
             except ValueError:
                 self.app.inform.emit(_(
-                    "[ERROR_NOTCL]Wrong value format entered, "
+                    "[ERROR_NOTCL] Wrong value format entered, "
                     "use a number."
                 ))
                 return
@@ -4020,7 +3877,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         try:
             if self.special_group:
                 self.app.inform.emit(_(
-                    "[WARNING_NOTCL]This Geometry can't be processed because it is %s geometry."
+                    "[WARNING_NOTCL] This Geometry can't be processed because it is %s geometry."
                 ) % str(self.special_group))
                 return
         except AttributeError:
@@ -4037,7 +3894,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                         tooldia = float(self.ui.geo_tools_table.item(x.row(), 1).text().replace(',', '.'))
                     except ValueError:
                         self.app.inform.emit(_(
-                            "[ERROR_NOTCL]Wrong Tool Dia value format entered, "
+                            "[ERROR_NOTCL] Wrong Tool Dia value format entered, "
                             "use a number."
                         ))
                         return
@@ -4137,7 +3994,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
                         ))
 
             try:
@@ -4149,7 +4006,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
                             'or self.options["feedrate_probe"]'
                         ))
 
@@ -4249,7 +4106,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                                                  )
                         except ValueError:
                             self.app.inform.emit(_(
-                                "[ERROR_NOTCL]Wrong value format entered, "
+                                "[ERROR_NOTCL] Wrong value format entered, "
                                 "use a number."
                             ))
                             return
@@ -4348,7 +4205,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
                         ))
 
             try:
@@ -4360,7 +4217,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
                             'or self.options["feedrate_probe"]'
                         ))
 
@@ -4372,7 +4229,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                         a += 1
                 if a == len(self.tools):
                     self.app.inform.emit(_(
-                        '[ERROR_NOTCL]Cancelled. Empty file, it has no geometry...'
+                        '[ERROR_NOTCL] Cancelled. Empty file, it has no geometry...'
                     ))
                     return 'fail'
 
@@ -4482,7 +4339,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                                                   )
                         except ValueError:
                             self.app.inform.emit(_(
-                                "[ERROR_NOTCL]Wrong value format entered, "
+                                "[ERROR_NOTCL] Wrong value format entered, "
                                 "use a number."
                             ))
                             return
@@ -4552,12 +4409,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 if self.solid_geometry:
                     with self.app.proc_container.new(_("Generating CNC Code")):
                         if app_obj.new_object("cncjob", outname, job_init_single_geometry) != 'fail':
-                            app_obj.inform.emit("[success]CNCjob created: %s" % outname)
+                            app_obj.inform.emit("[success] CNCjob created: %s" % outname)
                             app_obj.progress.emit(100)
                 else:
                     with self.app.proc_container.new(_("Generating CNC Code")):
                         if app_obj.new_object("cncjob", outname, job_init_multi_geometry) != 'fail':
-                            app_obj.inform.emit("[success]CNCjob created: %s" % outname)
+                            app_obj.inform.emit("[success] CNCjob created: %s" % outname)
                             app_obj.progress.emit(100)
 
             # Create a promise with the name
@@ -4663,7 +4520,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["z_pdepth"] or self.options["z_pdepth"]'
                         ))
 
             try:
@@ -4675,7 +4532,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 except ValueError:
                     self.app.inform.emit(
                         _(
-                            '[ERROR_NOTCL]Wrong value format for self.defaults["feedrate_probe"] '
+                            '[ERROR_NOTCL] Wrong value format for self.defaults["feedrate_probe"] '
                             'or self.options["feedrate_probe"]'
                         ))
 
@@ -4703,7 +4560,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             def job_thread(app_obj):
                 with self.app.proc_container.new(_("Generating CNC Code")):
                     app_obj.new_object("cncjob", outname, job_init)
-                    app_obj.inform.emit("[success]CNCjob created: %s" % outname)
+                    app_obj.inform.emit("[success] CNCjob created: %s" % outname)
                     app_obj.progress.emit(100)
 
             # Create a promise with the name
@@ -4764,7 +4621,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         # else:
         #     self.solid_geometry = affinity.scale(self.solid_geometry, xfactor, yfactor,
         #                                          origin=(px, py))
-        # self.app.inform.emit("[success]Geometry Scale done.")
+        # self.app.inform.emit("[success] Geometry Scale done.")
 
         def scale_recursion(geom):
             if type(geom) == list:
@@ -4782,7 +4639,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             self.solid_geometry=scale_recursion(self.solid_geometry)
 
         self.app.inform.emit(_(
-            "[success]Geometry Scale done."
+            "[success] Geometry Scale done."
         ))
 
     def offset(self, vect):
@@ -4799,7 +4656,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
             dx, dy = vect
         except TypeError:
             self.app.inform.emit(_(
-                "[ERROR_NOTCL]An (x,y) pair of values are needed. "
+                "[ERROR_NOTCL] An (x,y) pair of values are needed. "
                 "Probable you entered only one value in the Offset field."
             ))
             return
@@ -4819,7 +4676,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         else:
             self.solid_geometry=translate_recursion(self.solid_geometry)
         self.app.inform.emit(_(
-            "[success]Geometry Offset done."
+            "[success] Geometry Offset done."
         ))
 
     def convert_units(self, units):
@@ -4888,7 +4745,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                                 )
                             except ValueError:
                                 self.app.inform.emit(_(
-                                    "[ERROR_NOTCL]Wrong value format entered, "
+                                    "[ERROR_NOTCL] Wrong value format entered, "
                                     "use a number."
                                 ))
                                 return
@@ -4948,11 +4805,14 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         except TypeError:  # Element is not iterable...
             self.add_shape(shape=element, color=color, visible=visible, layer=0)
 
-    def plot(self, visible=None):
+    def plot(self, visible=None, kind=None):
         """
-        Adds the object into collection.
+        Plot the object.
 
-        :return: None
+        :param visible: Controls if the added shape is visible of not
+        :param kind: added so there is no error when a project is loaded and it has both geometry and CNCJob, because
+        CNCJob require the 'kind' parameter. Perhaps the FlatCAMObj.plot() has to be rewrited
+        :return:
         """
 
         # Does all the required setup and returns False
@@ -5305,7 +5165,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
         self.ui.updateplot_button.clicked.connect(self.on_updateplot_button_click)
         self.ui.export_gcode_button.clicked.connect(self.on_exportgcode_button_click)
-        self.ui.modify_gcode_button.clicked.connect(self.on_modifygcode_button_click)
+        self.ui.modify_gcode_button.clicked.connect(self.on_edit_code_click)
 
         self.ui.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
 
@@ -5372,7 +5232,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
         if filename == '':
             self.app.inform.emit(_(
-                "[WARNING_NOTCL]Export Machine Code cancelled ..."))
+                "[WARNING_NOTCL] Export Machine Code cancelled ..."))
             return
 
         preamble = str(self.ui.prepend_text.get_value())
@@ -5385,7 +5245,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         self.app.file_saved.emit("gcode", filename)
         self.app.inform.emit(_("[success] Machine Code file saved to: %s") % filename)
 
-    def on_modifygcode_button_click(self, *args):
+    def on_edit_code_click(self, *args):
         preamble = str(self.ui.prepend_text.get_value())
         postamble = str(self.ui.append_text.get_value())
         gc = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
@@ -5394,18 +5254,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         else:
             self.app.gcode_edited = gc
 
-        # add the tab if it was closed
-        self.app.ui.plot_tab_area.addTab(self.app.ui.cncjob_tab, _("Code Editor"))
-
-        # delete the absolute and relative position and messages in the infobar
-        self.app.ui.position_label.setText("")
-        self.app.ui.rel_position_label.setText("")
-
-        # Switch plot_area to CNCJob tab
-        self.app.ui.plot_tab_area.setCurrentWidget(self.app.ui.cncjob_tab)
-
-        # first clear previous text in text editor (if any)
-        self.app.ui.code_editor.clear()
+        self.app.init_code_editor(name=_("Code Editor"))
+        self.app.ui.buttonOpen.clicked.connect(self.app.handleOpen)
+        self.app.ui.buttonSave.clicked.connect(self.app.handleSaveGCode)
 
         # then append the text from GCode to the text editor
         try:
@@ -5413,8 +5264,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
                 proc_line = str(line).strip('\n')
                 self.app.ui.code_editor.append(proc_line)
         except Exception as e:
-            log.debug('FlatCAMCNNJob.on_modifygcode_button_click() -->%s' % str(e))
-            self.app.inform.emit(_('[ERROR]FlatCAMCNNJob.on_modifygcode_button_click() -->%s') % str(e))
+            log.debug('FlatCAMCNNJob.on_edit_code_click() -->%s' % str(e))
+            self.app.inform.emit(_('[ERROR]FlatCAMCNNJob.on_edit_code_click() -->%s') % str(e))
             return
 
         self.app.ui.code_editor.moveCursor(QtGui.QTextCursor.Start)
@@ -5513,6 +5364,17 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
         return gcode
 
+    def gcode_footer(self, end_command=None):
+        """
+
+        :param end_command: 'M02' or 'M30' - String
+        :return:
+        """
+        if end_command:
+            return end_command
+        else:
+            return 'M02'
+
     def export_gcode(self, filename=None, preamble='', postamble='', to_file=False):
         gcode = ''
         roland = False
@@ -5520,7 +5382,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
 
         try:
             if self.special_group:
-                self.app.inform.emit(_("[WARNING_NOTCL]This CNCJob object can't be processed because "
+                self.app.inform.emit(_("[WARNING_NOTCL] This CNCJob object can't be processed because "
                                      "it is a %s CNCJob object.") % str(self.special_group))
                 return 'fail'
         except AttributeError:
@@ -5577,7 +5439,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
                 ))
                 return
 
-            g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble
+            g = gcode[:g_idx] + preamble + '\n' + gcode[g_idx:] + postamble + self.gcode_footer()
 
         # if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box
         if self.ui.toolchange_cb.get_value() is True:
@@ -5711,7 +5573,6 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
             self.ui.plot_cb.setChecked(True)
         self.ui_connect()
 
-
     def plot(self, visible=None, kind='all'):
 
         # Does all the required setup and returns False

+ 12 - 4
FlatCAMTranslation.py

@@ -86,12 +86,14 @@ def on_language_apply_click(app, restart=False):
         msgbox.setInformativeText("Are you sure do you want to change the current language to %s?" % name.capitalize())
         msgbox.setWindowTitle("Apply Language ...")
         msgbox.setWindowIcon(QtGui.QIcon('share/language32.png'))
-        msgbox.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel)
-        msgbox.setDefaultButton(QtWidgets.QMessageBox.Yes)
+        bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
+        bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
 
-        response = msgbox.exec_()
+        msgbox.setDefaultButton(bt_yes)
+        msgbox.exec_()
+        response = msgbox.clickedButton()
 
-        if response == QtWidgets.QMessageBox.Cancel:
+        if response == bt_no:
             return
         else:
             settings = QSettings("Open Source", "FlatCAM")
@@ -112,6 +114,12 @@ def apply_language(domain, lang=None):
             name = settings.value('language')
         else:
             name = settings.value('English')
+            # in case the 'language' parameter is not in QSettings add it to QSettings and it's value is
+            # the default language, English
+            settings.setValue('language', 'English')
+
+            # This will write the setting to the platform specific storage.
+            del settings
     else:
         name = str(lang)
 

+ 2 - 2
FlatCAMWorkerStack.py

@@ -8,7 +8,7 @@ class WorkerStack(QtCore.QObject):
     worker_task = QtCore.pyqtSignal(dict)               # 'worker_name', 'func', 'params'
     thread_exception = QtCore.pyqtSignal(object)
 
-    def __init__(self):
+    def __init__(self, workers_number):
         super(WorkerStack, self).__init__()
 
         self.workers = []
@@ -16,7 +16,7 @@ class WorkerStack(QtCore.QObject):
         self.load = {}                                  # {'worker_name': tasks_count}
 
         # Create workers crew
-        for i in range(0, 2):
+        for i in range(0, workers_number):
             worker = Worker(self, 'Slogger-' + str(i))
             thread = QtCore.QThread()
 

+ 15 - 0
ObjectCollection.py

@@ -240,6 +240,9 @@ class ObjectCollection(QtCore.QAbstractItemModel):
         # tasks know that they have to wait until available.
         self.promises = set()
 
+        # same as above only for objects that are plotted
+        self.plot_promises = set()
+
         self.app = app
 
         ### View
@@ -275,6 +278,16 @@ class ObjectCollection(QtCore.QAbstractItemModel):
     def has_promises(self):
         return len(self.promises) > 0
 
+    def plot_promise(self, plot_obj_name):
+        self.plot_promises.add(plot_obj_name)
+
+    def plot_remove_promise(self, plot_obj_name):
+        if plot_obj_name in self.plot_promises:
+            self.plot_promises.remove(plot_obj_name)
+
+    def has_plot_promises(self):
+        return len(self.plot_promises) > 0
+
     def on_mouse_down(self, event):
         FlatCAMApp.App.log.debug("Mouse button pressed on list")
 
@@ -394,6 +407,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
                     self.app.myKeywords.remove(old_name)
                     self.app.myKeywords.append(new_name)
                     self.app.shell._edit.set_model_data(self.app.myKeywords)
+                    self.app.ui.code_editor.set_model_data(self.app.myKeywords)
                 except:
                     log.debug(
                         "setData() --> Could not remove the old object name from auto-completer model list")
@@ -550,6 +564,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
         try:
             self.app.myKeywords.remove(name)
             self.app.shell._edit.set_model_data(self.app.myKeywords)
+            self.app.ui.code_editor.set_model_data(self.app.myKeywords)
         except:
             log.debug(
                 "delete_active() --> Could not remove the old object name from auto-completer model list")

+ 178 - 0
README.md

@@ -9,6 +9,184 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+13.04.2019
+
+- updating the German translation
+- Gerber Editor: added ability to change on the fly the aperture after one of the tools: Add Pad or Add Pad Array is activated
+- Gerber Editor: if a tool is cancelled via key shortcut ESCAPE, the selection is now deleted and any other action require a new selection
+- finished German translation (Google translated with some adjustments)
+- final fix for issue #277. Previous fix was applied only for one case out of three.
+- RELEASE 8.913
+
+12.04.2019
+
+- Gerber Editor: added support for Oblong type of aperture
+- fixed an issue with automatically filled in aperture code when the edited Gerber file has no apertures; established an default with value 10 (according to Gerber specifications)
+- fixed a bug in editing a blank Gerber object
+- added handlers for the Gerber Editor context menu
+- updated the translation template POT file and the EN PO/MO files
+- Gerber Editor: added toggle effect to the Transform Tool
+- Gerber Editor: added shortcut for Transform Tool and also toggle effect here, too
+- updated the shortcut list with the Gerber Editor shortcut keys
+- Gerber Editor: fixed error when adding an aperture with code value lower than the ones that already exists
+- when adding an aperture with code '0' (zero) it will automatically be set with size zero and type: 'REG' (from region); here we store all the regions from a Gerber file, the ones without a declared aperture
+- Gerber Editor: added support for Gerber polarity change commands (LPD, LPC)
+- moved the polarity change processing from FlatCAMGrbEditor() class to camlib.Gerber().parse_lines()
+- made optional the saving of an edited object. Now the user can cancel the changes to the object.
+- replaced the standard buttons in the QMessageBox's used in the app with custom ones that can have text translated
+- updated the POT translation file and the MO/PO files for English and Romanian language
+
+11.04.2019
+
+- changed the color of the marked apertures to the global_selection_color
+- Gerber Editor: added Transformation Tool and Rotation key shortcut
+- in all Editors, manually deactivating a button in the editor toolbar will automatically select the 'Select' button
+- fixed Excellon Editor selection: when a tool is selected in Tools Table, all the drills belonging to that tool are selected. When a drill is selected on canvas, the associated tool will be selected without automatically selecting all other drills with same tool
+- Gerber Editor: added Add Pad Array tool
+- Gerber Editor: in Add Pad Array tool, if the pad is not circular type, for circular array the pad will be rotated to match the array angle
+- Gerber Editor: fixed multiple selection with key modifier such that first click selects, second deselects
+
+10.04.2019
+
+- Gerber Editor: added Add Track and Add Region functions
+- Gerber Editor: fixed key shortcuts
+- fixed setting the Layout combobox in Preferences according to the current layout
+- created menu links and shortcut keys for adding a new empty Gerber objects; on update of the edited Gerber, if the source object was an empty one (new blank one) this source obj will be deleted
+- removed the old apertures editing from Gerber Obj selected tab
+- Gerber Editor: added Add Pad (circular or rectangular type only)
+- Gerber Editor: autoincrement aperture code when adding new apertures
+- Gerber Editor: automatically calculate the size of the rectangular aperture
+
+9.04.2019
+
+- Gerber Editor: added buffer and scale tools
+- Gerber Editor: working on aperture selection to show on Aperture Table
+- Gerber Editor: finished the selection on canvas; should be used as an template for the other Editors
+- Gerber Editor: finished the Copy, Aperture Add, Buffer, Scale, Move including the Utility geometry
+- Trying to fix bug in Measurement Tool: the mouse events don't disconnect
+- fixed above bug in Measurement Tool (but there is a TODO there)
+
+7.04.2019
+
+- default values for Jump To function is jumping to origin (0, 0)
+
+6.04.2019
+
+- fixed bug in Geometry Editor in buffer_int() function that created an Circular Reference Error when applying buffer interior on a geometry.
+- fixed issue with not possible to close the app after a project save.
+- preliminary Gerber Editor.on_aperture_delete() 
+- fixed 'circular reference' error when creating the new Gerber file in Gerber Editor
+- preliminary Gerber Editor.on_aperture_add()
+
+5.04.2019
+
+- Gerber Editor: made geometry transfer (which is slow) to Editor to be multithreaded
+- Gerber Editor: plotting process is showed in the status bar
+- increased the number of workers in FlatCAM and made the number of workers customizable from Preferences
+- WIP in Gerber Editor: geometry is no longer stored in a Rtree storage as it is not needed
+- changed the way delayed plot is working in Gerber Editor to use a Qtimer instead of python threading module
+- WIP in Gerber Editor
+- fixed bug in saving the maximized state
+- fixed bug in applying default language on first start
+~~- on activating 'V' key shortcut (zoom fit) the mouse cursor is now jumping to origin (0, 0)~~
+- fixed bug in saving toolbars state; the file was saved before setting the self.defaults['global_toolbar_view]
+
+4.04.2019
+
+- added support for Gerber format specification D (no zero suppression) - PCBWizard Gerber files support
+- added support for Excellon file with no info about tool diameters - PCB Wizard Excellon file support
+- modified the bogus diameters series for Excellon objects that do not have tool diameter info
+- made Excellon Editor aware of the fact that the Excellon object that is edited has fake (bogus) tool diameters and therefore it will not sort the tools based on diameter but based on tool number
+- fixed bug on Excellon Editor: when diameter is edited in Tools Table and the target diameter is already in the tool table, the drills from current tool are moved to the new tool (with new dia) - before it crashed
+- fixed offset after editing drill diameters in Excellon Editor.
+
+3.04.2019
+
+- fixed plotting in Gerber Editor
+- working on GUI in Gerber Editor
+- added a Gcode end_command: default is M02
+- modified the calling of the editor2object() slot function to fix an issue with updating geometry imported from SVG file, after edit
+- working on Gerber Editor - added the key shortcuts: wip
+- made saving of the project file non-blocking and also while saving the project file, if the user tries again to close the app while project file is being saved, the app will close only after saving is complete (the project file size is non zero)
+- fixed the camlib.Geometry.import_svg() and camlib.Gerber.bounds() to work when importing SVG files as Gerber
+
+31.03.2019
+
+- fixed issue #281 by making generation of a convex shape for the freeform cutout in Tool Cutout a choice rather than the default
+- fixed bug in Tool Cutout, now in manual cutout mode the gap size reflect the value set
+- changed Measuring Tool to use the mouse click release instead of mouse click press; also fixed a bug when using the ESC key.
+- fixed errors when the File -> New Project is initiated while an Editor is still active.
+- the File->Exit action handler is now self.final_save() 
+- wip in Gerber editor
+
+29.03.2019
+
+- update the TCL keyword list
+- fix on the Gerber parser that makes searching for '%%' char optional when doing regex search for mode, units or image polarity. This allow loading Gerber files generated by the ECAD software TCl4.4
+- fix error in plotting Excellon when toggling units
+- FlatCAM editors now are separated each in it's own file
+- fixed TextTool in Geometry Editor so it will open the notebook on activation and close it after finishing text adding
+- started to work on a Gerber Editor
+- added a fix in the Excellon parser by allowing a comma in the tool definitions between the diameter and the rest
+
+28.03.2019
+
+- About 45% progress in German translation
+- new feature: added ability to edit MultiGeo geometry (geometry from Paint Tool)
+- changed all the info messages that are of type warning, error or success so they have a space added after the keyword
+- changed the Romanian translation by adding more diacritics  
+- modified Gerber parser to copy the follow_geometry in the self.apertures
+- modified the Properties Tool to show the number of elements in the follow_geometry for each aperture
+- modified the copy functions to copy the follow_geometry and also the apertures if it's possible (only for Gerber objects)
+
+27.03.2019
+
+- added new feature: user can delete apertures in Advanced mode and then create a new FlatCAM Gerber object
+- progress in German translation. About 27% done.
+- fixed issue #278. Crash on name change in the Name field in the Selected Tab.
+
+26.03.2019
+
+- fixed an issue where the Geometry plot function protested that it does not have an parameter that is used by the CNCJob plot function. But both inherit from FaltCAMObj plot function which does not have that parameter so something may need to be changed. Until then I provided a phony keyboard parameter to make that function 'shut up'
+- fixed bug: after using Paint Tool shortcut keys are disabled
+- added CNCJob geometry for the holes created by the drills from Excellon objects
+
+25.03.2019
+
+- in the TCL completer if the word is already complete don't add it again but add a space
+- added all the TCL keywords in the completer keyword list
+- work in progress in German translation ~7%
+- after any autocomplete in TCL completer, a space is added
+- fixed an module import issue in NCC Tool
+- minor change (optimization) of the CNCJob UI
+- work in progress in German translation ~20%
+
+22.03.2019
+
+- fixed an error that created a situation that when saving a project with some of the CNCJob objects disabled, on project reload the CNCJob objects are no longer loaded
+- fixed the Gerber.merge() function. When some of the Gerber files have apertures with same id, create a new aperture id for the object that is fused because each aperture id may hold different geometries.
+- changed the autoname for saving Preferences, Project and PNG file
+
+20.03.2019
+
+- added autocomplete finish with ENTER key for the TCL Shell
+- made sure that the autocomplete function works only for FlatCAM Scripts
+- ESC key will trigger normal view if in full screen and the ESC key is pressed
+- added an icon and title text for the Toggle Units QMessageBox
+
+19.03.2019
+
+- added autocomplete for Code editor;
+- autocomplete in Code Editor is finished by hitting either TAB key or ENTER key
+- fixed the Gerber.merge() to work for the case when one of the merged Gerber objects solid_geometry type is Polygon and not a list
+
+18.03.2019
+
+- added ability to create new scripts and open scripts in FlatCAM Script Editor
+- the Code Editor tab name is changed according to the task; 'save' and 'open' buttons will have filters installed for the QOpenDialog fit to the task
+- added ability to run a FlatCAM Tcl script by double-clicking on the file
+- in Code Editor added shortcut combo key CTRL+SHIFT+V to function as a Special Paste that will replace the '\' char with '/' so the Windows paths will be pasted correctly for TCL Shell. Also doing SHIFT + LMB on the Paste in contextual menu is doing the same.
+
 17.03.2019
 
 - remade the layout in 2Sided Tool

+ 371 - 114
camlib.py

@@ -621,7 +621,6 @@ class Geometry(object):
 
         # flatten the self.solid_geometry list for import_svg() to import SVG as Gerber
         self.solid_geometry = list(self.flatten_list(self.solid_geometry))
-        self.solid_geometry = cascaded_union(self.solid_geometry)
 
         geos_text = getsvgtext(svg_root, object_type, units=units)
         if geos_text is not None:
@@ -632,7 +631,8 @@ class Geometry(object):
                     _, minimy, _, maximy = i.bounds
                     h2 = (maximy - minimy) * 0.5
                     geos_text_f.append(translate(scale(i, 1.0, -1.0, origin=(0, 0)), yoff=(h + h2)))
-            self.solid_geometry = [self.solid_geometry, geos_text_f]
+            if geos_text_f:
+                self.solid_geometry = self.solid_geometry + geos_text_f
 
     def import_dxf(self, filename, object_type=None, units='MM'):
         """
@@ -1384,7 +1384,7 @@ class Geometry(object):
                     self.tools[tool]['solid_geometry'] = mirror_geom(self.tools[tool]['solid_geometry'])
             else:
                 self.solid_geometry = mirror_geom(self.solid_geometry)
-            self.app.inform.emit(_('[success]Object was mirrored ...'))
+            self.app.inform.emit(_('[success] Object was mirrored ...'))
         except AttributeError:
             self.app.inform.emit(_("[ERROR_NOTCL] Failed to mirror. No object selected"))
 
@@ -1422,7 +1422,7 @@ class Geometry(object):
                     self.tools[tool]['solid_geometry'] = rotate_geom(self.tools[tool]['solid_geometry'])
             else:
                 self.solid_geometry = rotate_geom(self.solid_geometry)
-            self.app.inform.emit(_('[success]Object was rotated ...'))
+            self.app.inform.emit(_('[success] Object was rotated ...'))
         except AttributeError:
             self.app.inform.emit(_("[ERROR_NOTCL] Failed to rotate. No object selected"))
 
@@ -1458,7 +1458,7 @@ class Geometry(object):
                     self.tools[tool]['solid_geometry'] = skew_geom(self.tools[tool]['solid_geometry'])
             else:
                 self.solid_geometry = skew_geom(self.solid_geometry)
-            self.app.inform.emit(_('[success]Object was skewed ...'))
+            self.app.inform.emit(_('[success] Object was skewed ...'))
         except AttributeError:
             self.app.inform.emit(_("[ERROR_NOTCL] Failed to skew. No object selected"))
 
@@ -1951,14 +1951,14 @@ class Gerber (Geometry):
         #### Parser patterns ####
         # FS - Format Specification
         # The format of X and Y must be the same!
-        # L-omit leading zeros, T-omit trailing zeros
+        # L-omit leading zeros, T-omit trailing zeros, D-no zero supression
         # A-absolute notation, I-incremental notation
-        self.fmt_re = re.compile(r'%FS([LT])([AI])X(\d)(\d)Y\d\d\*%$')
+        self.fmt_re = re.compile(r'%?FS([LTD])([AI])X(\d)(\d)Y\d\d\*%?$')
         self.fmt_re_alt = re.compile(r'%FS([LT])([AI])X(\d)(\d)Y\d\d\*MO(IN|MM)\*%$')
         self.fmt_re_orcad = re.compile(r'(G\d+)*\**%FS([LT])([AI]).*X(\d)(\d)Y\d\d\*%$')
 
         # Mode (IN/MM)
-        self.mode_re = re.compile(r'^%MO(IN|MM)\*%$')
+        self.mode_re = re.compile(r'^%?MO(IN|MM)\*%?$')
 
         # Comment G04|G4
         self.comm_re = re.compile(r'^G0?4(.*)$')
@@ -2013,7 +2013,7 @@ class Gerber (Geometry):
         self.eof_re = re.compile(r'^M02\*')
 
         # IP - Image polarity
-        self.pol_re = re.compile(r'^%IP(POS|NEG)\*%$')
+        self.pol_re = re.compile(r'^%?IP(POS|NEG)\*%?$')
 
         # LP - Level polarity
         self.lpol_re = re.compile(r'^%LP([DC])\*%$')
@@ -2169,6 +2169,10 @@ class Gerber (Geometry):
         # applying a union for every new polygon.
         poly_buffer = []
 
+        # made True when the LPC command is encountered in Gerber parsing
+        # it allows adding data into the clear_geometry key of the self.apertures[aperture] dict
+        self.is_lpc = False
+
         # store here the follow geometry
         follow_buffer = []
 
@@ -2242,15 +2246,27 @@ class Gerber (Geometry):
                         geo = LineString(path)
                         if not geo.is_empty:
                             follow_buffer.append(geo)
+                            try:
+                                self.apertures[last_path_aperture]['follow_geometry'].append(geo)
+                            except KeyError:
+                                self.apertures[last_path_aperture]['follow_geometry'] = []
+                                self.apertures[last_path_aperture]['follow_geometry'].append(geo)
 
                         geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                         if not geo.is_empty:
                             poly_buffer.append(geo)
-                            try:
-                                self.apertures[current_aperture]['solid_geometry'].append(geo)
-                            except KeyError:
-                                self.apertures[current_aperture]['solid_geometry'] = []
-                                self.apertures[current_aperture]['solid_geometry'].append(geo)
+                            if self.is_lpc is True:
+                                try:
+                                    self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[last_path_aperture]['clear_geometry'] = []
+                                    self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                            else:
+                                try:
+                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[last_path_aperture]['solid_geometry'] = []
+                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
                         path = [path[-1]]
 
@@ -2259,10 +2275,12 @@ class Gerber (Geometry):
                     # TODO: Remove when bug fixed
                     if len(poly_buffer) > 0:
                         if current_polarity == 'D':
+                            self.is_lpc = True
                             # self.follow_geometry = self.follow_geometry.union(cascaded_union(follow_buffer))
                             self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer))
 
                         else:
+                            self.is_lpc = False
                             # self.follow_geometry = self.follow_geometry.difference(cascaded_union(follow_buffer))
                             self.solid_geometry = self.solid_geometry.difference(cascaded_union(poly_buffer))
 
@@ -2284,8 +2302,8 @@ class Gerber (Geometry):
                     log.debug("Gerber format found. (%s) " % str(gline))
 
                     log.debug(
-                        "Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros)" %
-                        self.gerber_zeros)
+                        "Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros, "
+                        "D-no zero supression)" % self.gerber_zeros)
                     log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
                     continue
 
@@ -2308,8 +2326,8 @@ class Gerber (Geometry):
                     self.frac_digits = int(match.group(4))
                     log.debug("Gerber format found. (%s) " % str(gline))
                     log.debug(
-                        "Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros)" %
-                        self.gerber_zeros)
+                        "Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros, "
+                        "D-no zero suppression)" % self.gerber_zeros)
                     log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
 
                     gerber_units = match.group(1)
@@ -2332,8 +2350,8 @@ class Gerber (Geometry):
                         self.frac_digits = int(match.group(5))
                         log.debug("Gerber format found. (%s) " % str(gline))
                         log.debug(
-                            "Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros)" %
-                            self.gerber_zeros)
+                            "Gerber format found. Gerber zeros = %s (L-omit leading zeros, T-omit trailing zeros, "
+                            "D-no zerosuppressionn)" % self.gerber_zeros)
                         log.debug("Gerber format found. Coordinates type = %s (Absolute or Relative)" % absolute)
 
                         gerber_units = match.group(1)
@@ -2415,11 +2433,18 @@ class Gerber (Geometry):
                                 int(self.steps_per_circle))
                             if not flash.is_empty:
                                 poly_buffer.append(flash)
-                                try:
-                                    self.apertures[current_aperture]['solid_geometry'].append(flash)
-                                except KeyError:
-                                    self.apertures[current_aperture]['solid_geometry'] = []
-                                    self.apertures[current_aperture]['solid_geometry'].append(flash)
+                                if self.is_lpc is True:
+                                    try:
+                                        self.apertures[current_aperture]['clear_geometry'].append(flash)
+                                    except KeyError:
+                                        self.apertures[current_aperture]['clear_geometry'] = []
+                                        self.apertures[current_aperture]['clear_geometry'].append(flash)
+                                else:
+                                    try:
+                                        self.apertures[current_aperture]['solid_geometry'].append(flash)
+                                    except KeyError:
+                                        self.apertures[current_aperture]['solid_geometry'] = []
+                                        self.apertures[current_aperture]['solid_geometry'].append(flash)
                         except IndexError:
                             log.warning("Line %d: %s -> Nothing there to flash!" % (line_num, gline))
 
@@ -2453,15 +2478,27 @@ class Gerber (Geometry):
                             geo = LineString(path)
                             if not geo.is_empty:
                                 follow_buffer.append(geo)
+                                try:
+                                    self.apertures[current_aperture]['follow_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[current_aperture]['follow_geometry'] = []
+                                    self.apertures[current_aperture]['follow_geometry'].append(geo)
 
                             geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                             if not geo.is_empty:
                                 poly_buffer.append(geo)
-                                try:
-                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
-                                except KeyError:
-                                    self.apertures[last_path_aperture]['solid_geometry'] = []
-                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                if self.is_lpc is True:
+                                    try:
+                                        self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[last_path_aperture]['clear_geometry'] = []
+                                        self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                else:
+                                    try:
+                                        self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[last_path_aperture]['solid_geometry'] = []
+                                        self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
                             path = [path[-1]]
 
@@ -2478,15 +2515,27 @@ class Gerber (Geometry):
                         geo = LineString(path)
                         if not geo.is_empty:
                             follow_buffer.append(geo)
+                            try:
+                                self.apertures[current_aperture]['follow_geometry'].append(geo)
+                            except KeyError:
+                                self.apertures[current_aperture]['follow_geometry'] = []
+                                self.apertures[current_aperture]['follow_geometry'].append(geo)
 
                         geo = LineString(path).buffer(width/1.999, int(self.steps_per_circle / 4))
                         if not geo.is_empty:
                             poly_buffer.append(geo)
-                            try:
-                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
-                            except KeyError:
-                                self.apertures[last_path_aperture]['solid_geometry'] = []
-                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                            if self.is_lpc is True:
+                                try:
+                                    self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[last_path_aperture]['clear_geometry'] = []
+                                    self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                            else:
+                                try:
+                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[last_path_aperture]['solid_geometry'] = []
+                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
                         path = [path[-1]]
 
@@ -2503,12 +2552,25 @@ class Gerber (Geometry):
                         if geo:
                             if not geo.is_empty:
                                 follow_buffer.append(geo)
-                                poly_buffer.append(geo)
                                 try:
-                                    self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                    self.apertures[current_aperture]['follow_geometry'].append(geo)
                                 except KeyError:
-                                    self.apertures[current_aperture]['solid_geometry'] = []
-                                    self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                    self.apertures[current_aperture]['follow_geometry'] = []
+                                    self.apertures[current_aperture]['follow_geometry'].append(geo)
+
+                                poly_buffer.append(geo)
+                                if self.is_lpc is True:
+                                    try:
+                                        self.apertures[current_aperture]['clear_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[current_aperture]['clear_geometry'] = []
+                                        self.apertures[current_aperture]['clear_geometry'].append(geo)
+                                else:
+                                    try:
+                                        self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[current_aperture]['solid_geometry'] = []
+                                        self.apertures[current_aperture]['solid_geometry'].append(geo)
                             continue
 
                     # Only one path defines region?
@@ -2531,6 +2593,11 @@ class Gerber (Geometry):
                     region = Polygon()
                     if not region.is_empty:
                         follow_buffer.append(region)
+                        try:
+                            self.apertures[current_aperture]['follow_geometry'].append(region)
+                        except KeyError:
+                            self.apertures[current_aperture]['follow_geometry'] = []
+                            self.apertures[current_aperture]['follow_geometry'].append(region)
 
                     region = Polygon(path)
                     if not region.is_valid:
@@ -2552,11 +2619,18 @@ class Gerber (Geometry):
                                 self.apertures['0']['solid_geometry'] = []
                             used_aperture = '0'
 
-                        try:
-                            self.apertures[used_aperture]['solid_geometry'].append(region)
-                        except KeyError:
-                            self.apertures[used_aperture]['solid_geometry'] = []
-                            self.apertures[used_aperture]['solid_geometry'].append(region)
+                        if self.is_lpc is True:
+                            try:
+                                self.apertures[used_aperture]['clear_geometry'].append(region)
+                            except KeyError:
+                                self.apertures[used_aperture]['clear_geometry'] = []
+                                self.apertures[used_aperture]['clear_geometry'].append(region)
+                        else:
+                            try:
+                                self.apertures[used_aperture]['solid_geometry'].append(region)
+                            except KeyError:
+                                self.apertures[used_aperture]['solid_geometry'] = []
+                                self.apertures[used_aperture]['solid_geometry'].append(region)
 
                     path = [[current_x, current_y]]  # Start new path
                     continue
@@ -2627,11 +2701,18 @@ class Gerber (Geometry):
 
                                         geo = shply_box(minx, miny, maxx, maxy)
                                         poly_buffer.append(geo)
-                                        try:
-                                            self.apertures[current_aperture]['solid_geometry'].append(geo)
-                                        except KeyError:
-                                            self.apertures[current_aperture]['solid_geometry'] = []
-                                            self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                        if self.is_lpc is True:
+                                            try:
+                                                self.apertures[current_aperture]['clear_geometry'].append(geo)
+                                            except KeyError:
+                                                self.apertures[current_aperture]['clear_geometry'] = []
+                                                self.apertures[current_aperture]['clear_geometry'].append(geo)
+                                        else:
+                                            try:
+                                                self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                            except KeyError:
+                                                self.apertures[current_aperture]['solid_geometry'] = []
+                                                self.apertures[current_aperture]['solid_geometry'].append(geo)
                                 except:
                                     pass
                             last_path_aperture = current_aperture
@@ -2670,10 +2751,20 @@ class Gerber (Geometry):
                                 if self.apertures[last_path_aperture]["type"] != 'R':
                                     if not geo.is_empty:
                                         follow_buffer.append(geo)
+                                        try:
+                                            self.apertures[current_aperture]['follow_geometry'].append(geo)
+                                        except KeyError:
+                                            self.apertures[current_aperture]['follow_geometry'] = []
+                                            self.apertures[current_aperture]['follow_geometry'].append(geo)
                             except Exception as e:
                                 log.debug("camlib.Gerber.parse_lines() --> %s" % str(e))
                                 if not geo.is_empty:
                                     follow_buffer.append(geo)
+                                    try:
+                                        self.apertures[current_aperture]['follow_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[current_aperture]['follow_geometry'] = []
+                                        self.apertures[current_aperture]['follow_geometry'].append(geo)
 
                             # this treats the case when we are storing geometry as solids
                             if making_region:
@@ -2706,19 +2797,33 @@ class Gerber (Geometry):
                                 if self.apertures[last_path_aperture]["type"] != 'R':
                                     if not geo.is_empty:
                                         poly_buffer.append(geo)
-                                        try:
-                                            self.apertures[last_path_aperture]['solid_geometry'].append(geo)
-                                        except KeyError:
-                                            self.apertures[last_path_aperture]['solid_geometry'] = []
-                                            self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                        if self.is_lpc is True:
+                                            try:
+                                                self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                            except KeyError:
+                                                self.apertures[last_path_aperture]['clear_geometry'] = []
+                                                self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                        else:
+                                            try:
+                                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                            except KeyError:
+                                                self.apertures[last_path_aperture]['solid_geometry'] = []
+                                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
                             except Exception as e:
                                 log.debug("camlib.Gerber.parse_lines() --> %s" % str(e))
                                 poly_buffer.append(geo)
-                                try:
-                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
-                                except KeyError:
-                                    self.apertures[last_path_aperture]['solid_geometry'] = []
-                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                if self.is_lpc is True:
+                                    try:
+                                        self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[last_path_aperture]['clear_geometry'] = []
+                                        self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                else:
+                                    try:
+                                        self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[last_path_aperture]['solid_geometry'] = []
+                                        self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
                         # if linear_x or linear_y are None, ignore those
                         if linear_x is not None and linear_y is not None:
@@ -2741,8 +2846,18 @@ class Gerber (Geometry):
                                 try:
                                     if self.apertures[last_path_aperture]["type"] != 'R':
                                         follow_buffer.append(geo)
+                                        try:
+                                            self.apertures[current_aperture]['follow_geometry'].append(geo)
+                                        except KeyError:
+                                            self.apertures[current_aperture]['follow_geometry'] = []
+                                            self.apertures[current_aperture]['follow_geometry'].append(geo)
                                 except:
                                     follow_buffer.append(geo)
+                                    try:
+                                        self.apertures[current_aperture]['follow_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[current_aperture]['follow_geometry'] = []
+                                        self.apertures[current_aperture]['follow_geometry'].append(geo)
 
                             # this treats the case when we are storing geometry as solids
                             width = self.apertures[last_path_aperture]["size"]
@@ -2751,18 +2866,32 @@ class Gerber (Geometry):
                                 try:
                                     if self.apertures[last_path_aperture]["type"] != 'R':
                                         poly_buffer.append(geo)
+                                        if self.is_lpc is True:
+                                            try:
+                                                self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                            except KeyError:
+                                                self.apertures[last_path_aperture]['clear_geometry'] = []
+                                                self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                        else:
+                                            try:
+                                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                            except KeyError:
+                                                self.apertures[last_path_aperture]['solid_geometry'] = []
+                                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                except:
+                                    poly_buffer.append(geo)
+                                    if self.is_lpc is True:
+                                        try:
+                                            self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                        except KeyError:
+                                            self.apertures[last_path_aperture]['clear_geometry'] = []
+                                            self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                                    else:
                                         try:
                                             self.apertures[last_path_aperture]['solid_geometry'].append(geo)
                                         except KeyError:
                                             self.apertures[last_path_aperture]['solid_geometry'] = []
                                             self.apertures[last_path_aperture]['solid_geometry'].append(geo)
-                                except:
-                                    poly_buffer.append(geo)
-                                    try:
-                                        self.apertures[last_path_aperture]['solid_geometry'].append(geo)
-                                    except KeyError:
-                                        self.apertures[last_path_aperture]['solid_geometry'] = []
-                                        self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
                         # Reset path starting point
                         path = [[linear_x, linear_y]]
@@ -2770,7 +2899,13 @@ class Gerber (Geometry):
                         # --- BUFFERED ---
                         # Draw the flash
                         # this treats the case when we are storing geometry as paths
-                        follow_buffer.append(Point([linear_x, linear_y]))
+                        geo_flash = Point([linear_x, linear_y])
+                        follow_buffer.append(geo_flash)
+                        try:
+                            self.apertures[current_aperture]['follow_geometry'].append(geo_flash)
+                        except KeyError:
+                            self.apertures[current_aperture]['follow_geometry'] = []
+                            self.apertures[current_aperture]['follow_geometry'].append(geo_flash)
 
                         # this treats the case when we are storing geometry as solids
                         flash = Gerber.create_flash_geometry(
@@ -2780,11 +2915,18 @@ class Gerber (Geometry):
                         )
                         if not flash.is_empty:
                             poly_buffer.append(flash)
-                            try:
-                                self.apertures[current_aperture]['solid_geometry'].append(flash)
-                            except KeyError:
-                                self.apertures[current_aperture]['solid_geometry'] = []
-                                self.apertures[current_aperture]['solid_geometry'].append(flash)
+                            if self.is_lpc is True:
+                                try:
+                                    self.apertures[current_aperture]['clear_geometry'].append(flash)
+                                except KeyError:
+                                    self.apertures[current_aperture]['clear_geometry'] = []
+                                    self.apertures[current_aperture]['clear_geometry'].append(flash)
+                            else:
+                                try:
+                                    self.apertures[current_aperture]['solid_geometry'].append(flash)
+                                except KeyError:
+                                    self.apertures[current_aperture]['solid_geometry'] = []
+                                    self.apertures[current_aperture]['solid_geometry'].append(flash)
 
                     # maybe those lines are not exactly needed but it is easier to read the program as those coordinates
                     # are used in case that circular interpolation is encountered within the Gerber file
@@ -2869,16 +3011,28 @@ class Gerber (Geometry):
                             geo = LineString(path)
                             if not geo.is_empty:
                                 follow_buffer.append(geo)
+                                try:
+                                    self.apertures[current_aperture]['follow_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[current_aperture]['follow_geometry'] = []
+                                    self.apertures[current_aperture]['follow_geometry'].append(geo)
 
                             # this treats the case when we are storing geometry as solids
                             buffered = LineString(path).buffer(width / 1.999, int(self.steps_per_circle))
                             if not buffered.is_empty:
                                 poly_buffer.append(buffered)
-                                try:
-                                    self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
-                                except KeyError:
-                                    self.apertures[last_path_aperture]['solid_geometry'] = []
-                                    self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
+                                if self.is_lpc is True:
+                                    try:
+                                        self.apertures[last_path_aperture]['clear_geometry'].append(buffered)
+                                    except KeyError:
+                                        self.apertures[last_path_aperture]['clear_geometry'] = []
+                                        self.apertures[last_path_aperture]['clear_geometry'].append(buffered)
+                                else:
+                                    try:
+                                        self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
+                                    except KeyError:
+                                        self.apertures[last_path_aperture]['solid_geometry'] = []
+                                        self.apertures[last_path_aperture]['solid_geometry'].append(buffered)
 
                         current_x = circular_x
                         current_y = circular_y
@@ -3000,20 +3154,46 @@ class Gerber (Geometry):
                     geo = LineString(path)
                     if not geo.is_empty:
                         follow_buffer.append(geo)
+                        try:
+                            self.apertures[current_aperture]['follow_geometry'].append(geo)
+                        except KeyError:
+                            self.apertures[current_aperture]['follow_geometry'] = []
+                            self.apertures[current_aperture]['follow_geometry'].append(geo)
 
                     # this treats the case when we are storing geometry as solids
                     width = self.apertures[last_path_aperture]["size"]
                     geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                     if not geo.is_empty:
                         poly_buffer.append(geo)
-                        try:
-                            self.apertures[last_path_aperture]['solid_geometry'].append(geo)
-                        except KeyError:
-                            self.apertures[last_path_aperture]['solid_geometry'] = []
-                            self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                        if self.is_lpc is True:
+                            try:
+                                self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                            except KeyError:
+                                self.apertures[last_path_aperture]['clear_geometry'] = []
+                                self.apertures[last_path_aperture]['clear_geometry'].append(geo)
+                        else:
+                            try:
+                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                            except KeyError:
+                                self.apertures[last_path_aperture]['solid_geometry'] = []
+                                self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
-            # --- Apply buffer ---
+            # first check if we have any clear_geometry (LPC) and if yes then we need to substract it
+            # from the apertures solid_geometry
+            temp_geo = []
+            for apid in self.apertures:
+                if 'clear_geometry' in self.apertures[apid]:
+                    for clear_geo in self.apertures[apid]['clear_geometry']:
+                        for solid_geo in self.apertures[apid]['solid_geometry']:
+                            if solid_geo.intersects(clear_geo):
+                                res_geo = clear_geo.symmetric_difference(solid_geo)
+                                temp_geo.append(res_geo)
+                            else:
+                                temp_geo.append(solid_geo)
+                    self.apertures[apid]['solid_geometry'] = deepcopy(temp_geo)
+                    self.apertures[apid].pop('clear_geometry', None)
 
+            # --- Apply buffer ---
             # this treats the case when we are storing geometry as paths
             self.follow_geometry = follow_buffer
 
@@ -3124,7 +3304,7 @@ class Gerber (Geometry):
         :rtype : None
         :return: None
         """
-
+        pass
         # self.buffer_paths()
         #
         # self.fix_regions()
@@ -3184,16 +3364,17 @@ class Gerber (Geometry):
                             maxx = max(maxx, maxx_)
                             maxy = max(maxy, maxy_)
                     else:
-                        try:
-                            minx_, miny_, maxx_, maxy_ = bounds_rec(k)
-                        except Exception as e:
-                            log.debug("camlib.Geometry.bounds() --> %s" % str(e))
-                            return
+                        if not k.is_empty:
+                            try:
+                                minx_, miny_, maxx_, maxy_ = bounds_rec(k)
+                            except Exception as e:
+                                log.debug("camlib.Gerber.bounds() --> %s" % str(e))
+                                return
 
-                        minx = min(minx, minx_)
-                        miny = min(miny, miny_)
-                        maxx = max(maxx, maxx_)
-                        maxy = max(maxy, maxy_)
+                            minx = min(minx, minx_)
+                            miny = min(miny, miny_)
+                            maxx = max(maxx, maxx_)
+                            maxy = max(maxy, maxy_)
                 return minx, miny, maxx, maxy
             else:
                 # it's a Shapely object, return it's bounds
@@ -3264,7 +3445,7 @@ class Gerber (Geometry):
         except Exception as e:
             log.debug('FlatCAMGeometry.scale() --> %s' % str(e))
 
-        self.app.inform.emit(_("[success]Gerber Scale done."))
+        self.app.inform.emit(_("[success] Gerber Scale done."))
 
 
         ## solid_geometry ???
@@ -3297,7 +3478,7 @@ class Gerber (Geometry):
         try:
             dx, dy = vect
         except TypeError:
-            self.app.inform.emit(_("[ERROR_NOTCL]An (x,y) pair of values are needed. "
+            self.app.inform.emit(_("[ERROR_NOTCL] An (x,y) pair of values are needed. "
                                  "Probable you entered only one value in the Offset field."))
             return
 
@@ -3321,7 +3502,7 @@ class Gerber (Geometry):
         except Exception as e:
             log.debug('FlatCAMGeometry.offset() --> %s' % str(e))
 
-        self.app.inform.emit(_("[success]Gerber Offset done."))
+        self.app.inform.emit(_("[success] Gerber Offset done."))
 
     def mirror(self, axis, point):
         """
@@ -3371,7 +3552,6 @@ class Gerber (Geometry):
         # self.solid_geometry = affinity.scale(self.solid_geometry,
         #                                      xscale, yscale, origin=(px, py))
 
-
     def skew(self, angle_x, angle_y, point):
         """
         Shear/Skew the geometries of an object by angles along x and y dimensions.
@@ -3530,6 +3710,12 @@ class Excellon(Geometry):
         self.zeros_found = self.zeros
         self.units_found = self.units
 
+        # this will serve as a default if the Excellon file has no info regarding of tool diameters (this info may be
+        # in another file like for PCB WIzard ECAD software
+        self.toolless_diam = 1.0
+        # signal that the Excellon file has no tool diameter informations and the tools have bogus (random) diameter
+        self.diameterless = False
+
         # Excellon format
         self.excellon_format_upper_in = excellon_format_upper_in or self.defaults["excellon_format_upper_in"]
         self.excellon_format_lower_in = excellon_format_lower_in or self.defaults["excellon_format_lower_in"]
@@ -3575,7 +3761,7 @@ class Excellon(Geometry):
         #                              r'(?=.*F(\d*\.?\d*))?(?=.*S(\d*\.?\d*))?' +
         #                              r'(?=.*B(\d*\.?\d*))?(?=.*H(\d*\.?\d*))?' +
         #                              r'(?=.*Z([-\+]?\d*\.?\d*))?[CFSBHT]')
-        self.toolset_re = re.compile(r'^T(\d+)(?=.*C(\d*\.?\d*))?' +
+        self.toolset_re = re.compile(r'^T(\d+)(?=.*C,?(\d*\.?\d*))?' +
                                      r'(?=.*F(\d*\.?\d*))?(?=.*S(\d*\.?\d*))?' +
                                      r'(?=.*B(\d*\.?\d*))?(?=.*H(\d*\.?\d*))?' +
                                      r'(?=.*Z([-\+]?\d*\.?\d*))?[CFSBHT]')
@@ -3590,7 +3776,8 @@ class Excellon(Geometry):
         self.toolsel_re = re.compile(r'^T(\d+)')
 
         # Headerless toolset
-        self.toolset_hl_re = re.compile(r'^T(\d+)(?=.*C(\d*\.?\d*))')
+        # self.toolset_hl_re = re.compile(r'^T(\d+)(?=.*C(\d*\.?\d*))')
+        self.toolset_hl_re = re.compile(r'^T(\d+)(?:.?C(\d+\.?\d*))?')
 
         # Comment
         self.comm_re = re.compile(r'^;(.*)$')
@@ -3739,10 +3926,9 @@ class Excellon(Geometry):
                             continue
                     else:
                         log.warning("Line ignored, it's a comment: %s" % eline)
-
                 else:
                     if self.hend_re.search(eline):
-                        if in_header is False:
+                        if in_header is False or bool(self.tools) is False:
                             log.warning("Found end of the header but there is no header: %s" % eline)
                             log.warning("The only useful data in header are tools, units and format.")
                             log.warning("Therefore we will create units and format based on defaults.")
@@ -3787,12 +3973,27 @@ class Excellon(Geometry):
                     if match:
                         current_tool = str(int(match.group(1)))
                         log.debug("Tool change: %s" % current_tool)
-                        if headerless is True:
+                        if bool(headerless):
                             match = self.toolset_hl_re.search(eline)
                             if match:
                                 name = str(int(match.group(1)))
+                                try:
+                                    diam = float(match.group(2))
+                                except:
+                                    # it's possible that tool definition has only tool number and no diameter info
+                                    # (those could be in another file like PCB Wizard do)
+                                    # then match.group(2) = None and float(None) will create the exception
+                                    # the bellow construction is so each tool will have a slightly different diameter
+                                    # starting with a default value, to allow Excellon editing after that
+                                    self.diameterless = True
+
+                                    if self.excellon_units == 'MM':
+                                        diam = self.toolless_diam + (int(current_tool) - 1) / 100
+                                    else:
+                                        diam = (self.toolless_diam + (int(current_tool) - 1) / 100) / 25.4
+
                                 spec = {
-                                    "C": float(match.group(2)),
+                                    "C": diam,
                                 }
                                 spec['solid_geometry'] = []
                                 self.tools[name] = spec
@@ -4719,6 +4920,8 @@ class CNCjob(Geometry):
         Geometry.__init__(self, geo_steps_per_circle=int(steps_per_circle))
 
         self.kind = kind
+        self.origin_kind = None
+
         self.units = units
 
         self.z_cut = z_cut
@@ -4783,6 +4986,10 @@ class CNCjob(Geometry):
 
         self.tool = 0.0
 
+        # used for creating drill CCode geometry; will be updated in the generate_from_excellon_by_tool()
+        self.exc_drills = None
+        self.exc_tools = None
+
         # search for toolchange parameters in the Toolchange Custom Code
         self.re_toolchange_custom = re.compile(r'(%[a-zA-Z0-9\-_]+%)')
 
@@ -4904,6 +5111,11 @@ class CNCjob(Geometry):
         :return: None
         :rtype: None
         """
+
+        # create a local copy of the exobj.drills so it can be used for creating drill CCode geometry
+        self.exc_drills = deepcopy(exobj.drills)
+        self.exc_tools = deepcopy(exobj.tools)
+
         if drillz > 0:
             self.app.inform.emit(_("[WARNING] The Cut Z parameter has positive value. "
                                  "It is the depth value to drill into material.\n"
@@ -5109,7 +5321,13 @@ class CNCjob(Geometry):
                                 current_tooldia = float('%.2f' % float(exobj.tools[tool]["C"]))
                             else:
                                 current_tooldia = float('%.3f' % float(exobj.tools[tool]["C"]))
-                            z_offset = float(self.tool_offset[current_tooldia]) * (-1)
+
+                            # TODO apply offset only when using the GUI, for TclCommand this will create an error
+                            # because the values for Z offset are created in build_ui()
+                            try:
+                                z_offset = float(self.tool_offset[current_tooldia]) * (-1)
+                            except KeyError:
+                                z_offset = 0
                             self.z_cut += z_offset
 
                             # Drillling!
@@ -5128,7 +5346,7 @@ class CNCjob(Geometry):
                 else:
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                               "The loaded Excellon file has no drills ...")
-                    self.app.inform.emit(_('[ERROR_NOTCL]The loaded Excellon file has no drills ...'))
+                    self.app.inform.emit(_('[ERROR_NOTCL] The loaded Excellon file has no drills ...'))
                     return 'fail'
 
                 log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))
@@ -5223,7 +5441,7 @@ class CNCjob(Geometry):
                 else:
                     log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                               "The loaded Excellon file has no drills ...")
-                    self.app.inform.emit(_('[ERROR_NOTCL]The loaded Excellon file has no drills ...'))
+                    self.app.inform.emit(_('[ERROR_NOTCL] The loaded Excellon file has no drills ...'))
                     return 'fail'
 
                 log.debug("The total travel distance with OR-TOOLS Basic Algorithm is: %s" % str(measured_distance))
@@ -5255,8 +5473,15 @@ class CNCjob(Geometry):
                             current_tooldia = float('%.2f' % float(exobj.tools[tool]["C"]))
                         else:
                             current_tooldia = float('%.3f' % float(exobj.tools[tool]["C"]))
-                        z_offset = float(self.tool_offset[current_tooldia]) * (-1)
+
+                        # TODO apply offset only when using the GUI, for TclCommand this will create an error
+                        # because the values for Z offset are created in build_ui()
+                        try:
+                            z_offset = float(self.tool_offset[current_tooldia]) * (-1)
+                        except KeyError:
+                            z_offset = 0
                         self.z_cut += z_offset
+
                         # Drillling!
                         altPoints = []
                         for point in points[tool]:
@@ -5274,7 +5499,7 @@ class CNCjob(Geometry):
                     else:
                         log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> "
                                   "The loaded Excellon file has no drills ...")
-                        self.app.inform.emit(_('[ERROR_NOTCL]The loaded Excellon file has no drills ...'))
+                        self.app.inform.emit(_('[ERROR_NOTCL] The loaded Excellon file has no drills ...'))
                         return 'fail'
             log.debug("The total travel distance with Travelling Salesman Algorithm is: %s" % str(measured_distance))
 
@@ -5537,7 +5762,7 @@ class CNCjob(Geometry):
 
         # if solid_geometry is empty raise an exception
         if not geometry.solid_geometry:
-            self.app.inform.emit(_("[ERROR_NOTCL]Trying to generate a CNC Job "
+            self.app.inform.emit(_("[ERROR_NOTCL] Trying to generate a CNC Job "
                                  "from a Geometry object without solid_geometry."))
 
         temp_solid_geometry = []
@@ -5576,7 +5801,7 @@ class CNCjob(Geometry):
                 # if the offset is less than half of the total length or less than half of the total width of the
                 # solid geometry it's obvious we can't do the offset
                 if -offset > ((c - a) / 2) or -offset > ((d - b) / 2):
-                    self.app.inform.emit(_("[ERROR_NOTCL]The Tool Offset value is too negative to use "
+                    self.app.inform.emit(_("[ERROR_NOTCL] The Tool Offset value is too negative to use "
                                          "for the current_geometry.\n"
                                          "Raise the value (in module) and try again."))
                     return 'fail'
@@ -6098,7 +6323,7 @@ class CNCjob(Geometry):
 
         # Process every instruction
         for line in StringIO(self.gcode):
-            if '%MO' in line or '%' in line:
+            if '%MO' in line or '%' in line or 'MOIN' in line or 'MOMM' in line:
                 return "fail"
 
             gobj = self.codes_split(line)
@@ -6130,6 +6355,23 @@ class CNCjob(Geometry):
                                      "kind": kind})
                     path = [path[-1]]  # Start with the last point of last path.
 
+                # create the geometry for the holes created when drilling Excellon drills
+                if self.origin_kind == 'excellon':
+                    if current['Z'] < 0:
+                        current_drill_point_coords = (float('%.4f' % current['X']), float('%.4f' % current['Y']))
+                        # find the drill diameter knowing the drill coordinates
+                        for pt_dict in self.exc_drills:
+                            point_in_dict_coords = (float('%.4f' % pt_dict['point'].x),
+                                                   float('%.4f' % pt_dict['point'].y))
+                            if point_in_dict_coords == current_drill_point_coords:
+                                tool = pt_dict['tool']
+                                dia = self.exc_tools[tool]['C']
+                                kind = ['C', 'F']
+                                geometry.append({"geom": Point(current_drill_point_coords).
+                                                buffer(dia/2).exterior,
+                                                 "kind": kind})
+                                break
+
             if 'G' in gobj:
                 current['G'] = int(gobj['G'])
                 
@@ -6256,7 +6498,17 @@ class CNCjob(Geometry):
                 text.append(str(path_num))
                 pos.append(geo['geom'].coords[0])
 
-                poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance)
+                # plot the geometry of Excellon objects
+                if self.origin_kind == 'excellon':
+                    try:
+                        poly = Polygon(geo['geom'])
+                    except ValueError:
+                        # if the geos are travel lines it will enter into Exception
+                        poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance)
+                else:
+                    # plot the geometry of any objects other than Excellon
+                    poly = geo['geom'].buffer(tooldia / 2.0).simplify(tool_tolerance)
+
                 if kind == 'all':
                     obj.add_shape(shape=poly, color=color[geo['kind'][0]][1], face_color=color[geo['kind'][0]][0],
                               visible=visible, layer=1 if geo['kind'][0] == 'C' else 2)
@@ -7075,17 +7327,22 @@ def parse_gerber_number(strnumber, int_digits, frac_digits, zeros):
     :param frac_digits: Number of digits used for the fractional
     part of the number
     :type frac_digits: int
-    :param zeros: If 'L', leading zeros are removed and trailing zeros are kept. If 'T', is in reverse.
+    :param zeros: If 'L', leading zeros are removed and trailing zeros are kept. Same situation for 'D' when
+    no zero suppression is done. If 'T', is in reverse.
     :type zeros: str
     :return: The number in floating point.
     :rtype: float
     """
-    if zeros == 'L':
+
+    ret_val = None
+
+    if zeros == 'L' or zeros == 'D':
         ret_val = int(strnumber) * (10 ** (-frac_digits))
 
     if zeros == 'T':
         int_val = int(strnumber)
         ret_val = (int_val * (10 ** ((int_digits + frac_digits) - len(strnumber)))) * (10 ** (-frac_digits))
+
     return ret_val
 
 

+ 2557 - 0
flatcamEditors/FlatCAMExcEditor.py

@@ -0,0 +1,2557 @@
+from PyQt5 import QtGui, QtCore, QtWidgets
+from PyQt5.QtCore import Qt, QSettings
+
+from shapely.geometry import LineString, LinearRing, MultiLineString
+from shapely.ops import cascaded_union
+import shapely.affinity as affinity
+
+from numpy import arctan2, Inf, array, sqrt, sign, dot
+from rtree import index as rtindex
+
+from camlib import *
+from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, SpinBoxDelegate
+from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+
+fcTranslate.apply_language('strings')
+import builtins
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+
+class FCDrillAdd(FCShapeTool):
+    """
+    Resulting type: MultiLineString
+    """
+
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'drill_add'
+
+        self.selected_dia = None
+        try:
+            self.draw_app.app.inform.emit(self.start_msg)
+            # self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.tools_table_exc.currentRow() + 1]
+            self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.last_tool_selected]
+            # as a visual marker, select again in tooltable the actual tool that we are using
+            # remember that it was deselected when clicking on canvas
+            item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
+            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.select_tool("select")
+            return
+
+        geo = self.utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y))
+
+        if isinstance(geo, DrawToolShape) and geo.geo is not None:
+            self.draw_app.draw_utility_geometry(geo=geo)
+
+        self.draw_app.app.inform.emit(_("Click on target location ..."))
+
+        # Switch notebook to Selected page
+        self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab)
+
+    def click(self, point):
+        self.make()
+        return "Done."
+
+    def utility_geometry(self, data=None):
+        self.points = data
+        return DrawToolUtilityShape(self.util_shape(data))
+
+    def util_shape(self, point):
+        if point[0] is None and point[1] is None:
+            point_x = self.draw_app.x
+            point_y = self.draw_app.y
+        else:
+            point_x = point[0]
+            point_y = point[1]
+
+        start_hor_line = ((point_x - (self.selected_dia / 2)), point_y)
+        stop_hor_line = ((point_x + (self.selected_dia / 2)), point_y)
+        start_vert_line = (point_x, (point_y - (self.selected_dia / 2)))
+        stop_vert_line = (point_x, (point_y + (self.selected_dia / 2)))
+
+        return MultiLineString([(start_hor_line, stop_hor_line), (start_vert_line, stop_vert_line)])
+
+    def make(self):
+
+        # add the point to drills if the diameter is a key in the dict, if not, create it add the drill location
+        # to the value, as a list of itself
+        if self.selected_dia in self.draw_app.points_edit:
+            self.draw_app.points_edit[self.selected_dia].append(self.points)
+        else:
+            self.draw_app.points_edit[self.selected_dia] = [self.points]
+
+        self.draw_app.current_storage = self.draw_app.storage_dict[self.selected_dia]
+        self.geometry = DrawToolShape(self.util_shape(self.points))
+        self.complete = True
+        self.draw_app.app.inform.emit(_("[success] Done. Drill added."))
+
+
+class FCDrillArray(FCShapeTool):
+    """
+    Resulting type: MultiLineString
+    """
+
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'drill_array'
+
+        self.draw_app.array_frame.show()
+
+        self.selected_dia = None
+        self.drill_axis = 'X'
+        self.drill_array = 'linear'
+        self.drill_array_size = None
+        self.drill_pitch = None
+        self.drill_linear_angle = None
+
+        self.drill_angle = None
+        self.drill_direction = None
+        self.drill_radius = None
+
+        self.origin = None
+        self.destination = None
+        self.flag_for_circ_array = None
+
+        self.last_dx = 0
+        self.last_dy = 0
+
+        self.pt = []
+
+        try:
+            self.draw_app.app.inform.emit(self.start_msg)
+            self.selected_dia = self.draw_app.tool2tooldia[self.draw_app.last_tool_selected]
+            # as a visual marker, select again in tooltable the actual tool that we are using
+            # remember that it was deselected when clicking on canvas
+            item = self.draw_app.tools_table_exc.item((self.draw_app.last_tool_selected - 1), 1)
+            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"))
+            return
+
+        geo = self.utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y), static=True)
+
+        if isinstance(geo, DrawToolShape) and geo.geo is not None:
+            self.draw_app.draw_utility_geometry(geo=geo)
+
+        self.draw_app.app.inform.emit(_("Click on target location ..."))
+
+        # Switch notebook to Selected page
+        self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab)
+
+    def click(self, point):
+
+        if self.drill_array == 'Linear':
+            self.make()
+            return
+        else:
+            if self.flag_for_circ_array is None:
+                self.draw_app.in_action = True
+                self.pt.append(point)
+
+                self.flag_for_circ_array = True
+                self.set_origin(point)
+                self.draw_app.app.inform.emit(_("Click on the Drill Circular Array Start position"))
+            else:
+                self.destination = point
+                self.make()
+                self.flag_for_circ_array = None
+                return
+
+    def set_origin(self, origin):
+        self.origin = origin
+
+    def utility_geometry(self, data=None, static=None):
+        self.drill_axis = self.draw_app.drill_axis_radio.get_value()
+        self.drill_direction = self.draw_app.drill_direction_radio.get_value()
+        self.drill_array = self.draw_app.array_type_combo.get_value()
+        try:
+            self.drill_array_size = int(self.draw_app.drill_array_size_entry.get_value())
+            try:
+                self.drill_pitch = float(self.draw_app.drill_pitch_entry.get_value())
+                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."))
+                return
+        except Exception as e:
+            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] The value is mistyped. Check the value."))
+            return
+
+        if self.drill_array == 'Linear':
+            if data[0] is None and data[1] is None:
+                dx = self.draw_app.x
+                dy = self.draw_app.y
+            else:
+                dx = data[0]
+                dy = data[1]
+
+            geo_list = []
+            geo = None
+            self.points = [dx, dy]
+
+            for item in range(self.drill_array_size):
+                if self.drill_axis == 'X':
+                    geo = self.util_shape(((dx + (self.drill_pitch * item)), dy))
+                if self.drill_axis == 'Y':
+                    geo = self.util_shape((dx, (dy + (self.drill_pitch * item))))
+                if self.drill_axis == 'A':
+                    x_adj = self.drill_pitch * math.cos(math.radians(self.drill_linear_angle))
+                    y_adj = self.drill_pitch * math.sin(math.radians(self.drill_linear_angle))
+                    geo = self.util_shape(
+                        ((dx + (x_adj * item)), (dy + (y_adj * item)))
+                    )
+
+                if static is None or static is False:
+                    geo_list.append(affinity.translate(geo, xoff=(dx - self.last_dx), yoff=(dy - self.last_dy)))
+                else:
+                    geo_list.append(geo)
+            # self.origin = data
+
+            self.last_dx = dx
+            self.last_dy = dy
+            return DrawToolUtilityShape(geo_list)
+        else:
+            if data[0] is None and data[1] is None:
+                cdx = self.draw_app.x
+                cdy = self.draw_app.y
+            else:
+                cdx = data[0]
+                cdy = data[1]
+
+            if len(self.pt) > 0:
+                temp_points = [x for x in self.pt]
+                temp_points.append([cdx, cdy])
+                return DrawToolUtilityShape(LineString(temp_points))
+
+    def util_shape(self, point):
+        if point[0] is None and point[1] is None:
+            point_x = self.draw_app.x
+            point_y = self.draw_app.y
+        else:
+            point_x = point[0]
+            point_y = point[1]
+
+        start_hor_line = ((point_x - (self.selected_dia / 2)), point_y)
+        stop_hor_line = ((point_x + (self.selected_dia / 2)), point_y)
+        start_vert_line = (point_x, (point_y - (self.selected_dia / 2)))
+        stop_vert_line = (point_x, (point_y + (self.selected_dia / 2)))
+
+        return MultiLineString([(start_hor_line, stop_hor_line), (start_vert_line, stop_vert_line)])
+
+    def make(self):
+        self.geometry = []
+        geo = None
+
+        # add the point to drills if the diameter is a key in the dict, if not, create it add the drill location
+        # to the value, as a list of itself
+        if self.selected_dia not in self.draw_app.points_edit:
+            self.draw_app.points_edit[self.selected_dia] = []
+        for i in range(self.drill_array_size):
+            self.draw_app.points_edit[self.selected_dia].append(self.points)
+
+        self.draw_app.current_storage = self.draw_app.storage_dict[self.selected_dia]
+
+        if self.drill_array == 'Linear':
+            for item in range(self.drill_array_size):
+                if self.drill_axis == 'X':
+                    geo = self.util_shape(((self.points[0] + (self.drill_pitch * item)), self.points[1]))
+                if self.drill_axis == 'Y':
+                    geo = self.util_shape((self.points[0], (self.points[1] + (self.drill_pitch * item))))
+                if self.drill_axis == 'A':
+                    x_adj = self.drill_pitch * math.cos(math.radians(self.drill_linear_angle))
+                    y_adj = self.drill_pitch * math.sin(math.radians(self.drill_linear_angle))
+                    geo = self.util_shape(
+                        ((self.points[0] + (x_adj * item)), (self.points[1] + (y_adj * item)))
+                    )
+
+                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."))
+                return
+
+            radius = distance(self.destination, self.origin)
+            initial_angle = math.asin((self.destination[1] - self.origin[1]) / radius)
+            for i in range(self.drill_array_size):
+                angle_radians = math.radians(self.drill_angle * i)
+                if self.drill_direction == 'CW':
+                    x = self.origin[0] + radius * math.cos(-angle_radians + initial_angle)
+                    y = self.origin[1] + radius * math.sin(-angle_radians + initial_angle)
+                else:
+                    x = self.origin[0] + radius * math.cos(angle_radians + initial_angle)
+                    y = self.origin[1] + radius * math.sin(angle_radians + initial_angle)
+
+                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.in_action = True
+        self.draw_app.array_frame.hide()
+        return
+
+
+class FCDrillResize(FCShapeTool):
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'drill_resize'
+
+        self.draw_app.app.inform.emit(_("Click on the Drill(s) to resize ..."))
+        self.resize_dia = None
+        self.draw_app.resize_frame.show()
+        self.points = None
+        self.selected_dia_list = []
+        self.current_storage = None
+        self.geometry = []
+        self.destination_storage = None
+
+        self.draw_app.resize_btn.clicked.connect(self.make)
+
+        # Switch notebook to Selected page
+        self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab)
+
+    def make(self):
+        self.draw_app.is_modified = True
+
+        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."))
+            return
+
+        if new_dia not in self.draw_app.olddia_newdia:
+            self.destination_storage = FlatCAMGeoEditor.make_storage()
+            self.draw_app.storage_dict[new_dia] = self.destination_storage
+
+            # self.olddia_newdia dict keeps the evidence on current tools diameters as keys and gets updated on values
+            # each time a tool diameter is edited or added
+            self.draw_app.olddia_newdia[new_dia] = new_dia
+        else:
+            self.destination_storage = self.draw_app.storage_dict[new_dia]
+
+        for index in self.draw_app.tools_table_exc.selectedIndexes():
+            row = index.row()
+            # on column 1 in tool tables we hold the diameters, and we retrieve them as strings
+            # therefore below we convert to float
+            dia_on_row = self.draw_app.tools_table_exc.item(row, 1).text()
+            self.selected_dia_list.append(float(dia_on_row))
+
+        # since we add a new tool, we update also the intial state of the tool_table through it's dictionary
+        # we add a new entry in the tool2tooldia dict
+        self.draw_app.tool2tooldia[len(self.draw_app.olddia_newdia)] = new_dia
+
+        sel_shapes_to_be_deleted = []
+
+        if self.selected_dia_list:
+            for sel_dia in self.selected_dia_list:
+                self.current_storage = self.draw_app.storage_dict[sel_dia]
+                for select_shape in self.draw_app.get_selected():
+                    if select_shape in self.current_storage.get_objects():
+                        factor = new_dia / sel_dia
+                        self.geometry.append(
+                            DrawToolShape(affinity.scale(select_shape.geo, xfact=factor, yfact=factor, origin='center'))
+                        )
+                        self.current_storage.remove(select_shape)
+                        # a hack to make the tool_table display less drills per diameter when shape(drill) is deleted
+                        # self.points_edit it's only useful first time when we load the data into the storage
+                        # but is still used as reference when building tool_table in self.build_ui()
+                        # the number of drills displayed in column 2 is just a len(self.points_edit) therefore
+                        # deleting self.points_edit elements (doesn't matter who but just the number)
+                        # solved the display issue.
+                        del self.draw_app.points_edit[sel_dia][0]
+
+                        sel_shapes_to_be_deleted.append(select_shape)
+
+                        self.draw_app.on_exc_shape_complete(self.destination_storage)
+                        # a hack to make the tool_table display more drills per diameter when shape(drill) is added
+                        # self.points_edit it's only useful first time when we load the data into the storage
+                        # but is still used as reference when building tool_table in self.build_ui()
+                        # the number of drills displayed in column 2 is just a len(self.points_edit) therefore
+                        # deleting self.points_edit elements (doesn't matter who but just the number)
+                        # solved the display issue.
+                        if new_dia not in self.draw_app.points_edit:
+                            self.draw_app.points_edit[new_dia] = [(0, 0)]
+                        else:
+                            self.draw_app.points_edit[new_dia].append((0,0))
+                        self.geometry = []
+
+                        # if following the resize of the drills there will be no more drills for the selected tool then
+                        # delete that tool
+                        if not self.draw_app.points_edit[sel_dia]:
+                            self.draw_app.on_tool_delete(sel_dia)
+
+            for shp in sel_shapes_to_be_deleted:
+                self.draw_app.selected.remove(shp)
+            sel_shapes_to_be_deleted = []
+
+            self.draw_app.build_ui()
+            self.draw_app.replot()
+            self.draw_app.app.inform.emit(_("[success] Done. Drill Resize completed."))
+
+        else:
+            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Cancelled. No drills selected for resize ..."))
+
+        self.draw_app.resize_frame.hide()
+        self.complete = True
+
+        # MS: always return to the Select Tool
+        self.draw_app.select_tool("select")
+
+
+class FCDrillMove(FCShapeTool):
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'drill_move'
+
+        # self.shape_buffer = self.draw_app.shape_buffer
+        self.origin = None
+        self.destination = None
+        self.selected_dia_list = []
+
+        if self.draw_app.launched_from_shortcuts is True:
+            self.draw_app.launched_from_shortcuts = False
+            self.draw_app.app.inform.emit(_("Click on target location ..."))
+        else:
+            self.draw_app.app.inform.emit(_("Click on reference location ..."))
+        self.current_storage = None
+        self.geometry = []
+
+        for index in self.draw_app.tools_table_exc.selectedIndexes():
+            row = index.row()
+            # on column 1 in tool tables we hold the diameters, and we retrieve them as strings
+            # therefore below we convert to float
+            dia_on_row = self.draw_app.tools_table_exc.item(row, 1).text()
+            self.selected_dia_list.append(float(dia_on_row))
+
+        # Switch notebook to Selected page
+        self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab)
+
+    def set_origin(self, origin):
+        self.origin = origin
+
+    def click(self, point):
+        if len(self.draw_app.get_selected()) == 0:
+            return "Nothing to move."
+
+        if self.origin is None:
+            self.set_origin(point)
+            self.draw_app.app.inform.emit(_("Click on target location ..."))
+            return
+        else:
+            self.destination = point
+            self.make()
+
+            # MS: always return to the Select Tool
+            self.draw_app.select_tool("select")
+            return
+
+    def make(self):
+        # Create new geometry
+        dx = self.destination[0] - self.origin[0]
+        dy = self.destination[1] - self.origin[1]
+        sel_shapes_to_be_deleted = []
+
+        for sel_dia in self.selected_dia_list:
+            self.current_storage = self.draw_app.storage_dict[sel_dia]
+            for select_shape in self.draw_app.get_selected():
+                if select_shape in self.current_storage.get_objects():
+
+                    self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy)))
+                    self.current_storage.remove(select_shape)
+                    sel_shapes_to_be_deleted.append(select_shape)
+                    self.draw_app.on_exc_shape_complete(self.current_storage)
+                    self.geometry = []
+
+            for shp in sel_shapes_to_be_deleted:
+                self.draw_app.selected.remove(shp)
+            sel_shapes_to_be_deleted = []
+
+        self.draw_app.build_ui()
+        self.draw_app.app.inform.emit(_("[success] Done. Drill(s) Move completed."))
+
+    def utility_geometry(self, data=None):
+        """
+        Temporary geometry on screen while using this tool.
+
+        :param data:
+        :return:
+        """
+        geo_list = []
+
+        if self.origin is None:
+            return None
+
+        if len(self.draw_app.get_selected()) == 0:
+            return None
+
+        dx = data[0] - self.origin[0]
+        dy = data[1] - self.origin[1]
+        for geom in self.draw_app.get_selected():
+            geo_list.append(affinity.translate(geom.geo, xoff=dx, yoff=dy))
+        return DrawToolUtilityShape(geo_list)
+
+
+class FCDrillCopy(FCDrillMove):
+    def __init__(self, draw_app):
+        FCDrillMove.__init__(self, draw_app)
+        self.name = 'drill_copy'
+
+    def make(self):
+        # Create new geometry
+        dx = self.destination[0] - self.origin[0]
+        dy = self.destination[1] - self.origin[1]
+        sel_shapes_to_be_deleted = []
+
+        for sel_dia in self.selected_dia_list:
+            self.current_storage = self.draw_app.storage_dict[sel_dia]
+            for select_shape in self.draw_app.get_selected():
+                if select_shape in self.current_storage.get_objects():
+                    self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy)))
+
+                    # add some fake drills into the self.draw_app.points_edit to update the drill count in tool table
+                    self.draw_app.points_edit[sel_dia].append((0, 0))
+
+                    sel_shapes_to_be_deleted.append(select_shape)
+                    self.draw_app.on_exc_shape_complete(self.current_storage)
+                    self.geometry = []
+
+            for shp in sel_shapes_to_be_deleted:
+                self.draw_app.selected.remove(shp)
+            sel_shapes_to_be_deleted = []
+
+        self.draw_app.build_ui()
+        self.draw_app.app.inform.emit(_("[success] Done. Drill(s) copied."))
+
+
+class FCDrillSelect(DrawTool):
+    def __init__(self, exc_editor_app):
+        DrawTool.__init__(self, exc_editor_app)
+        self.name = 'drill_select'
+
+        self.exc_editor_app = exc_editor_app
+        self.storage = self.exc_editor_app.storage_dict
+        # self.selected = self.exc_editor_app.selected
+
+        # here we store all shapes that were selected so we can search for the nearest to our click location
+        self.sel_storage = FlatCAMExcEditor.make_storage()
+
+        self.exc_editor_app.resize_frame.hide()
+        self.exc_editor_app.array_frame.hide()
+
+    def click(self, point):
+        key_modifier = QtWidgets.QApplication.keyboardModifiers()
+        if self.exc_editor_app.app.defaults["global_mselect_key"] == 'Control':
+            if key_modifier == Qt.ControlModifier:
+                pass
+            else:
+                self.exc_editor_app.selected = []
+        else:
+            if key_modifier == Qt.ShiftModifier:
+                pass
+            else:
+                self.exc_editor_app.selected = []
+
+    def click_release(self, pos):
+        self.exc_editor_app.tools_table_exc.clearSelection()
+
+        try:
+            for storage in self.exc_editor_app.storage_dict:
+                for shape in self.exc_editor_app.storage_dict[storage].get_objects():
+                    self.sel_storage.insert(shape)
+
+            _, closest_shape = self.sel_storage.nearest(pos)
+
+            # constrain selection to happen only within a certain bounding box
+            x_coord, y_coord = closest_shape.geo[0].xy
+            delta = (x_coord[1] - x_coord[0])
+            # closest_shape_coords = (((x_coord[0] + delta / 2)), y_coord[0])
+            xmin = x_coord[0] - (0.7 * delta)
+            xmax = x_coord[0] + (1.7 * delta)
+            ymin = y_coord[0] - (0.7 * delta)
+            ymax = y_coord[0] + (1.7 * delta)
+        except StopIteration:
+            return ""
+
+        if pos[0] < xmin or pos[0] > xmax or pos[1] < ymin or pos[1] > ymax:
+            self.exc_editor_app.selected = []
+        else:
+            modifiers = QtWidgets.QApplication.keyboardModifiers()
+            mod_key = 'Control'
+            if modifiers == QtCore.Qt.ShiftModifier:
+                mod_key = 'Shift'
+            elif modifiers == QtCore.Qt.ControlModifier:
+                mod_key = 'Control'
+
+            if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
+                if closest_shape in self.exc_editor_app.selected:
+                    self.exc_editor_app.selected.remove(closest_shape)
+                else:
+                    self.exc_editor_app.selected.append(closest_shape)
+            else:
+                self.draw_app.selected = []
+                self.draw_app.selected.append(closest_shape)
+
+            # select the diameter of the selected shape in the tool table
+            try:
+                self.draw_app.tools_table_exc.cellPressed.disconnect()
+            except:
+                pass
+
+            sel_tools = set()
+            self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+            for shape_s in self.exc_editor_app.selected:
+                for storage in self.exc_editor_app.storage_dict:
+                    if shape_s in self.exc_editor_app.storage_dict[storage].get_objects():
+                        sel_tools.add(storage)
+
+            for storage in sel_tools:
+                self.exc_editor_app.tools_table_exc.selectRow(int(storage))
+                self.draw_app.last_tool_selected = int(storage)
+
+            self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+
+            self.draw_app.tools_table_exc.cellPressed.connect(self.draw_app.on_row_selected)
+
+        # delete whatever is in selection storage, there is no longer need for those shapes
+        self.sel_storage = FlatCAMExcEditor.make_storage()
+
+        return ""
+
+        # pos[0] and pos[1] are the mouse click coordinates (x, y)
+        # for storage in self.exc_editor_app.storage_dict:
+        #     for obj_shape in self.exc_editor_app.storage_dict[storage].get_objects():
+        #         minx, miny, maxx, maxy = obj_shape.geo.bounds
+        #         if (minx <= pos[0] <= maxx) and (miny <= pos[1] <= maxy):
+        #             over_shape_list.append(obj_shape)
+        #
+        # try:
+        #     # if there is no shape under our click then deselect all shapes
+        #     if not over_shape_list:
+        #         self.exc_editor_app.selected = []
+        #         FlatCAMExcEditor.draw_shape_idx = -1
+        #         self.exc_editor_app.tools_table_exc.clearSelection()
+        #     else:
+        #         # if there are shapes under our click then advance through the list of them, one at the time in a
+        #         # circular way
+        #         FlatCAMExcEditor.draw_shape_idx = (FlatCAMExcEditor.draw_shape_idx + 1) % len(over_shape_list)
+        #         obj_to_add = over_shape_list[int(FlatCAMExcEditor.draw_shape_idx)]
+        #
+        #         if self.exc_editor_app.app.defaults["global_mselect_key"] == 'Shift':
+        #             if self.exc_editor_app.modifiers == Qt.ShiftModifier:
+        #                 if obj_to_add in self.exc_editor_app.selected:
+        #                     self.exc_editor_app.selected.remove(obj_to_add)
+        #                 else:
+        #                     self.exc_editor_app.selected.append(obj_to_add)
+        #             else:
+        #                 self.exc_editor_app.selected = []
+        #                 self.exc_editor_app.selected.append(obj_to_add)
+        #         else:
+        #             # if CONTROL key is pressed then we add to the selected list the current shape but if it's already
+        #             # in the selected list, we removed it. Therefore first click selects, second deselects.
+        #             if self.exc_editor_app.modifiers == Qt.ControlModifier:
+        #                 if obj_to_add in self.exc_editor_app.selected:
+        #                     self.exc_editor_app.selected.remove(obj_to_add)
+        #                 else:
+        #                     self.exc_editor_app.selected.append(obj_to_add)
+        #             else:
+        #                 self.exc_editor_app.selected = []
+        #                 self.exc_editor_app.selected.append(obj_to_add)
+        #
+        #     for storage in self.exc_editor_app.storage_dict:
+        #         for shape in self.exc_editor_app.selected:
+        #             if shape in self.exc_editor_app.storage_dict[storage].get_objects():
+        #                 for key in self.exc_editor_app.tool2tooldia:
+        #                     if self.exc_editor_app.tool2tooldia[key] == storage:
+        #                         item = self.exc_editor_app.tools_table_exc.item((key - 1), 1)
+        #                         item.setSelected(True)
+        #                         # self.exc_editor_app.tools_table_exc.selectItem(key - 1)
+        #
+        # except Exception as e:
+        #     log.error("[ERROR] Something went bad. %s" % str(e))
+        #     raise
+
+
+class FlatCAMExcEditor(QtCore.QObject):
+
+    draw_shape_idx = -1
+
+    def __init__(self, app):
+        assert isinstance(app, FlatCAMApp.App), \
+            "Expected the app to be a FlatCAMApp.App, got %s" % type(app)
+
+        super(FlatCAMExcEditor, self).__init__()
+
+        self.app = app
+        self.canvas = self.app.plotcanvas
+
+        ## Current application units in Upper Case
+        self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
+
+        self.exc_edit_widget = QtWidgets.QWidget()
+        layout = QtWidgets.QVBoxLayout()
+        self.exc_edit_widget.setLayout(layout)
+
+        ## Page Title box (spacing between children)
+        self.title_box = QtWidgets.QHBoxLayout()
+        layout.addLayout(self.title_box)
+
+        ## Page Title icon
+        pixmap = QtGui.QPixmap('share/flatcam_icon32.png')
+        self.icon = QtWidgets.QLabel()
+        self.icon.setPixmap(pixmap)
+        self.title_box.addWidget(self.icon, stretch=0)
+
+        ## Title label
+        self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Excellon Editor'))
+        self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+        self.title_box.addWidget(self.title_label, stretch=1)
+
+        ## Object name
+        self.name_box = QtWidgets.QHBoxLayout()
+        layout.addLayout(self.name_box)
+        name_label = QtWidgets.QLabel(_("Name:"))
+        self.name_box.addWidget(name_label)
+        self.name_entry = FCEntry()
+        self.name_box.addWidget(self.name_entry)
+
+        ## Box box for custom widgets
+        # This gets populated in offspring implementations.
+        self.custom_box = QtWidgets.QVBoxLayout()
+        layout.addLayout(self.custom_box)
+
+        # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
+        # this way I can hide/show the frame
+        self.drills_frame = QtWidgets.QFrame()
+        self.drills_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.drills_frame)
+        self.tools_box = QtWidgets.QVBoxLayout()
+        self.tools_box.setContentsMargins(0, 0, 0, 0)
+        self.drills_frame.setLayout(self.tools_box)
+
+        #### Tools Drills ####
+        self.tools_table_label = QtWidgets.QLabel("<b>%s</b>" % _('Tools Table'))
+        self.tools_table_label.setToolTip(
+           _( "Tools in this Excellon object\n"
+            "when are used for drilling.")
+        )
+        self.tools_box.addWidget(self.tools_table_label)
+
+        self.tools_table_exc = FCTable()
+        # delegate = SpinBoxDelegate(units=self.units)
+        # self.tools_table_exc.setItemDelegateForColumn(1, delegate)
+
+        self.tools_box.addWidget(self.tools_table_exc)
+
+        self.tools_table_exc.setColumnCount(4)
+        self.tools_table_exc.setHorizontalHeaderLabels(['#', _('Diameter'), 'D', 'S'])
+        self.tools_table_exc.setSortingEnabled(False)
+        self.tools_table_exc.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
+
+        self.empty_label = QtWidgets.QLabel('')
+        self.tools_box.addWidget(self.empty_label)
+
+        #### Add a new Tool ####
+        self.addtool_label = QtWidgets.QLabel('<b>%s</b>' % _('Add/Delete Tool'))
+        self.addtool_label.setToolTip(
+            _("Add/Delete a tool to the tool list\n"
+            "for this Excellon object.")
+        )
+        self.tools_box.addWidget(self.addtool_label)
+
+        grid1 = QtWidgets.QGridLayout()
+        self.tools_box.addLayout(grid1)
+
+        addtool_entry_lbl = QtWidgets.QLabel(_('Tool Dia:'))
+        addtool_entry_lbl.setToolTip(
+        _("Diameter for the new tool")
+        )
+        grid1.addWidget(addtool_entry_lbl, 0, 0)
+
+        hlay = QtWidgets.QHBoxLayout()
+        self.addtool_entry = FCEntry()
+        self.addtool_entry.setValidator(QtGui.QDoubleValidator(0.0001, 99.9999, 4))
+        hlay.addWidget(self.addtool_entry)
+
+        self.addtool_btn = QtWidgets.QPushButton(_('Add Tool'))
+        self.addtool_btn.setToolTip(
+           _( "Add a new tool to the tool list\n"
+            "with the diameter specified above.")
+        )
+        self.addtool_btn.setFixedWidth(80)
+        hlay.addWidget(self.addtool_btn)
+        grid1.addLayout(hlay, 0, 1)
+
+        grid2 = QtWidgets.QGridLayout()
+        self.tools_box.addLayout(grid2)
+
+        self.deltool_btn = QtWidgets.QPushButton(_('Delete Tool'))
+        self.deltool_btn.setToolTip(
+           _( "Delete a tool in the tool list\n"
+            "by selecting a row in the tool table.")
+        )
+        grid2.addWidget(self.deltool_btn, 0, 1)
+
+        # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
+        # this way I can hide/show the frame
+        self.resize_frame = QtWidgets.QFrame()
+        self.resize_frame.setContentsMargins(0, 0, 0, 0)
+        self.tools_box.addWidget(self.resize_frame)
+        self.resize_box = QtWidgets.QVBoxLayout()
+        self.resize_box.setContentsMargins(0, 0, 0, 0)
+        self.resize_frame.setLayout(self.resize_box)
+
+        #### Resize a  drill ####
+        self.emptyresize_label = QtWidgets.QLabel('')
+        self.resize_box.addWidget(self.emptyresize_label)
+
+        self.drillresize_label = QtWidgets.QLabel('<b>%s</b>' % _("Resize Drill(s)"))
+        self.drillresize_label.setToolTip(
+            _("Resize a drill or a selection of drills.")
+        )
+        self.resize_box.addWidget(self.drillresize_label)
+
+        grid3 = QtWidgets.QGridLayout()
+        self.resize_box.addLayout(grid3)
+
+        res_entry_lbl = QtWidgets.QLabel(_('Resize Dia:'))
+        res_entry_lbl.setToolTip(
+           _( "Diameter to resize to.")
+        )
+        grid3.addWidget(res_entry_lbl, 0, 0)
+
+        hlay2 = QtWidgets.QHBoxLayout()
+        self.resdrill_entry = LengthEntry()
+        hlay2.addWidget(self.resdrill_entry)
+
+        self.resize_btn = QtWidgets.QPushButton(_('Resize'))
+        self.resize_btn.setToolTip(
+            _("Resize drill(s)")
+        )
+        self.resize_btn.setFixedWidth(80)
+        hlay2.addWidget(self.resize_btn)
+        grid3.addLayout(hlay2, 0, 1)
+
+        self.resize_frame.hide()
+
+        # add a frame and inside add a vertical box layout. Inside this vbox layout I add
+        # all the add drill array  widgets
+        # this way I can hide/show the frame
+        self.array_frame = QtWidgets.QFrame()
+        self.array_frame.setContentsMargins(0, 0, 0, 0)
+        self.tools_box.addWidget(self.array_frame)
+        self.array_box = QtWidgets.QVBoxLayout()
+        self.array_box.setContentsMargins(0, 0, 0, 0)
+        self.array_frame.setLayout(self.array_box)
+
+        #### Add DRILL Array ####
+        self.emptyarray_label = QtWidgets.QLabel('')
+        self.array_box.addWidget(self.emptyarray_label)
+
+        self.drillarray_label = QtWidgets.QLabel('<b>%s</b>' % _("Add Drill Array"))
+        self.drillarray_label.setToolTip(
+            _("Add an array of drills (linear or circular array)")
+        )
+        self.array_box.addWidget(self.drillarray_label)
+
+        self.array_type_combo = FCComboBox()
+        self.array_type_combo.setToolTip(
+           _( "Select the type of drills array to create.\n"
+            "It can be Linear X(Y) or Circular")
+        )
+        self.array_type_combo.addItem(_("Linear"))
+        self.array_type_combo.addItem(_("Circular"))
+
+        self.array_box.addWidget(self.array_type_combo)
+
+        self.array_form = QtWidgets.QFormLayout()
+        self.array_box.addLayout(self.array_form)
+
+        self.drill_array_size_label = QtWidgets.QLabel(_('Nr of drills:'))
+        self.drill_array_size_label.setToolTip(
+            _("Specify how many drills to be in the array.")
+        )
+        self.drill_array_size_label.setFixedWidth(100)
+
+        self.drill_array_size_entry = LengthEntry()
+        self.array_form.addRow(self.drill_array_size_label, self.drill_array_size_entry)
+
+        self.array_linear_frame = QtWidgets.QFrame()
+        self.array_linear_frame.setContentsMargins(0, 0, 0, 0)
+        self.array_box.addWidget(self.array_linear_frame)
+        self.linear_box = QtWidgets.QVBoxLayout()
+        self.linear_box.setContentsMargins(0, 0, 0, 0)
+        self.array_linear_frame.setLayout(self.linear_box)
+
+        self.linear_form = QtWidgets.QFormLayout()
+        self.linear_box.addLayout(self.linear_form)
+
+        self.drill_axis_label = QtWidgets.QLabel(_('Direction:'))
+        self.drill_axis_label.setToolTip(
+            _("Direction on which the linear array is oriented:\n"
+            "- 'X' - horizontal axis \n"
+            "- 'Y' - vertical axis or \n"
+            "- 'Angle' - a custom angle for the array inclination")
+        )
+        self.drill_axis_label.setFixedWidth(100)
+
+        self.drill_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
+                                          {'label': 'Y', 'value': 'Y'},
+                                          {'label': _('Angle'), 'value': 'A'}])
+        self.drill_axis_radio.set_value('X')
+        self.linear_form.addRow(self.drill_axis_label, self.drill_axis_radio)
+
+        self.drill_pitch_label = QtWidgets.QLabel(_('Pitch:'))
+        self.drill_pitch_label.setToolTip(
+            _("Pitch = Distance between elements of the array.")
+        )
+        self.drill_pitch_label.setFixedWidth(100)
+
+        self.drill_pitch_entry = LengthEntry()
+        self.linear_form.addRow(self.drill_pitch_label, self.drill_pitch_entry)
+
+        self.linear_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.linear_angle_label.setToolTip(
+           _( "Angle at which the linear array is placed.\n"
+            "The precision is of max 2 decimals.\n"
+            "Min value is: -359.99 degrees.\n"
+            "Max value is:  360.00 degrees.")
+        )
+        self.linear_angle_label.setFixedWidth(100)
+
+        self.linear_angle_spinner = FCDoubleSpinner()
+        self.linear_angle_spinner.set_precision(2)
+        self.linear_angle_spinner.setRange(-359.99, 360.00)
+        self.linear_form.addRow(self.linear_angle_label, self.linear_angle_spinner)
+
+        self.array_circular_frame = QtWidgets.QFrame()
+        self.array_circular_frame.setContentsMargins(0, 0, 0, 0)
+        self.array_box.addWidget(self.array_circular_frame)
+        self.circular_box = QtWidgets.QVBoxLayout()
+        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.setToolTip(
+           _( "Direction for circular array."
+            "Can be CW = clockwise or CCW = counter clockwise.")
+        )
+        self.drill_direction_label.setFixedWidth(100)
+
+        self.circular_form = QtWidgets.QFormLayout()
+        self.circular_box.addLayout(self.circular_form)
+
+        self.drill_direction_radio = RadioSet([{'label': 'CW', 'value': 'CW'},
+                                               {'label': 'CCW.', 'value': 'CCW'}])
+        self.drill_direction_radio.set_value('CW')
+        self.circular_form.addRow(self.drill_direction_label, self.drill_direction_radio)
+
+        self.drill_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.drill_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.drill_angle_label.setFixedWidth(100)
+
+        self.drill_angle_entry = LengthEntry()
+        self.circular_form.addRow(self.drill_angle_label, self.drill_angle_entry)
+
+        self.array_circular_frame.hide()
+
+        self.linear_angle_spinner.hide()
+        self.linear_angle_label.hide()
+
+        self.array_frame.hide()
+        self.tools_box.addStretch()
+
+        ## Toolbar events and properties
+        self.tools_exc = {
+            "select": {"button": self.app.ui.select_drill_btn,
+                       "constructor": FCDrillSelect},
+            "drill_add": {"button": self.app.ui.add_drill_btn,
+                    "constructor": FCDrillAdd},
+            "drill_array": {"button": self.app.ui.add_drill_array_btn,
+                          "constructor": FCDrillArray},
+            "drill_resize": {"button": self.app.ui.resize_drill_btn,
+                       "constructor": FCDrillResize},
+            "drill_copy": {"button": self.app.ui.copy_drill_btn,
+                     "constructor": FCDrillCopy},
+            "drill_move": {"button": self.app.ui.move_drill_btn,
+                     "constructor": FCDrillMove},
+        }
+
+        ### Data
+        self.active_tool = None
+
+        self.storage_dict = {}
+        self.current_storage = []
+
+        # build the data from the Excellon point into a dictionary
+        #  {tool_dia: [geometry_in_points]}
+        self.points_edit = {}
+        self.sorted_diameters =[]
+
+        self.new_drills = []
+        self.new_tools = {}
+        self.new_slots = {}
+        self.new_tool_offset = {}
+
+        # dictionary to store the tool_row and diameters in Tool_table
+        # it will be updated everytime self.build_ui() is called
+        self.olddia_newdia = {}
+
+        self.tool2tooldia = {}
+
+        # this will store the value for the last selected tool, for use after clicking on canvas when the selection
+        # is cleared but as a side effect also the selected tool is cleared
+        self.last_tool_selected = None
+        self.utility = []
+
+        # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
+        self.launched_from_shortcuts = False
+
+        # this var will store the state of the toolbar before starting the editor
+        self.toolbar_old_state = False
+
+        self.app.ui.delete_drill_btn.triggered.connect(self.on_delete_btn)
+        self.name_entry.returnPressed.connect(self.on_name_activate)
+        self.addtool_btn.clicked.connect(self.on_tool_add)
+        # self.addtool_entry.editingFinished.connect(self.on_tool_add)
+        self.deltool_btn.clicked.connect(self.on_tool_delete)
+        # self.tools_table_exc.selectionModel().currentChanged.connect(self.on_row_selected)
+        self.tools_table_exc.cellPressed.connect(self.on_row_selected)
+
+        self.array_type_combo.currentIndexChanged.connect(self.on_array_type_combo)
+
+        self.drill_axis_radio.activated_custom.connect(self.on_linear_angle_radio)
+
+        self.app.ui.exc_add_array_drill_menuitem.triggered.connect(self.exc_add_drill_array)
+        self.app.ui.exc_add_drill_menuitem.triggered.connect(self.exc_add_drill)
+
+        self.app.ui.exc_resize_drill_menuitem.triggered.connect(self.exc_resize_drills)
+        self.app.ui.exc_copy_drill_menuitem.triggered.connect(self.exc_copy_drills)
+        self.app.ui.exc_delete_drill_menuitem.triggered.connect(self.on_delete_btn)
+
+        self.app.ui.exc_move_drill_menuitem.triggered.connect(self.exc_move_drills)
+
+
+        # Init GUI
+        self.drill_array_size_entry.set_value(5)
+        self.drill_pitch_entry.set_value(2.54)
+        self.drill_angle_entry.set_value(12)
+        self.drill_direction_radio.set_value('CW')
+        self.drill_axis_radio.set_value('X')
+        self.exc_obj = None
+
+        # VisPy Visuals
+        self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
+        self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
+        self.app.pool_recreated.connect(self.pool_recreated)
+
+        # Remove from scene
+        self.shapes.enabled = False
+        self.tool_shape.enabled = False
+
+        ## List of selected shapes.
+        self.selected = []
+
+        self.move_timer = QtCore.QTimer()
+        self.move_timer.setSingleShot(True)
+
+        self.key = None  # Currently pressed key
+        self.modifiers = None
+        self.x = None  # Current mouse cursor pos
+        self.y = None
+        # Current snapped mouse pos
+        self.snap_x = None
+        self.snap_y = None
+        self.pos = None
+
+        def make_callback(thetool):
+            def f():
+                self.on_tool_select(thetool)
+            return f
+
+        for tool in self.tools_exc:
+            self.tools_exc[tool]["button"].triggered.connect(make_callback(tool))  # Events
+            self.tools_exc[tool]["button"].setCheckable(True)  # Checkable
+
+        self.options = {
+            "global_gridx": 0.1,
+            "global_gridy": 0.1,
+            "snap_max": 0.05,
+            "grid_snap": True,
+            "corner_snap": False,
+            "grid_gap_link": True
+        }
+        self.app.options_read_form()
+
+        for option in self.options:
+            if option in self.app.options:
+                self.options[option] = self.app.options[option]
+
+        self.rtree_exc_index = rtindex.Index()
+        # flag to show if the object was modified
+        self.is_modified = False
+
+        self.edited_obj_name = ""
+
+        # variable to store the total amount of drills per job
+        self.tot_drill_cnt = 0
+        self.tool_row = 0
+
+        # variable to store the total amount of slots per job
+        self.tot_slot_cnt = 0
+        self.tool_row_slots = 0
+
+        self.tool_row = 0
+
+        # store the status of the editor so the Delete at object level will not work until the edit is finished
+        self.editor_active = False
+
+        def entry2option(option, entry):
+            self.options[option] = float(entry.text())
+
+        # store the status of the editor so the Delete at object level will not work until the edit is finished
+        self.editor_active = False
+
+    def pool_recreated(self, pool):
+        self.shapes.pool = pool
+        self.tool_shape.pool = pool
+
+    @staticmethod
+    def make_storage():
+
+        ## Shape storage.
+        storage = FlatCAMRTreeStorage()
+        storage.get_points = DrawToolShape.get_pts
+
+        return storage
+
+    def set_ui(self):
+        # updated units
+        self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
+
+        self.olddia_newdia.clear()
+        self.tool2tooldia.clear()
+
+        # build the self.points_edit dict {dimaters: [point_list]}
+        for drill in self.exc_obj.drills:
+            if drill['tool'] in self.exc_obj.tools:
+                if self.units == 'IN':
+                    tool_dia = float('%.3f' % self.exc_obj.tools[drill['tool']]['C'])
+                else:
+                    tool_dia = float('%.2f' % self.exc_obj.tools[drill['tool']]['C'])
+
+                try:
+                    self.points_edit[tool_dia].append(drill['point'])
+                except KeyError:
+                    self.points_edit[tool_dia] = [drill['point']]
+        # update the olddia_newdia dict to make sure we have an updated state of the tool_table
+        for key in self.points_edit:
+            self.olddia_newdia[key] = key
+
+        sort_temp = []
+        for diam in self.olddia_newdia:
+            sort_temp.append(float(diam))
+        self.sorted_diameters = sorted(sort_temp)
+
+        # populate self.intial_table_rows dict with the tool number as keys and tool diameters as values
+        if self.exc_obj.diameterless is False:
+            for i in range(len(self.sorted_diameters)):
+                tt_dia = self.sorted_diameters[i]
+                self.tool2tooldia[i + 1] = tt_dia
+        else:
+            # the Excellon object has diameters that are bogus information, added by the application because the
+            # Excellon file has no tool diameter information. In this case do not order the diameter in the table
+            # but use the real order found in the exc_obj.tools
+            for k, v in self.exc_obj.tools.items():
+                if self.units == 'IN':
+                    tool_dia = float('%.3f' % v['C'])
+                else:
+                    tool_dia = float('%.2f' % v['C'])
+                self.tool2tooldia[int(k)] = tool_dia
+
+    def build_ui(self):
+
+        try:
+            # if connected, disconnect the signal from the slot on item_changed as it creates issues
+            self.tools_table_exc.itemChanged.disconnect()
+        except:
+            pass
+
+        try:
+            self.tools_table_exc.cellPressed.disconnect()
+        except:
+            pass
+
+        # updated units
+        self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
+
+        # make a new name for the new Excellon object (the one with edited content)
+        self.edited_obj_name = self.exc_obj.options['name']
+        self.name_entry.set_value(self.edited_obj_name)
+
+        if self.units == "IN":
+            self.addtool_entry.set_value(0.039)
+        else:
+            self.addtool_entry.set_value(1.00)
+
+        sort_temp = []
+
+        for diam in self.olddia_newdia:
+            sort_temp.append(float(diam))
+        self.sorted_diameters = sorted(sort_temp)
+
+        # here, self.sorted_diameters will hold in a oblique way, the number of tools
+        n = len(self.sorted_diameters)
+        # we have (n+2) rows because there are 'n' tools, each a row, plus the last 2 rows for totals.
+        self.tools_table_exc.setRowCount(n + 2)
+
+        self.tot_drill_cnt = 0
+        self.tot_slot_cnt = 0
+
+        self.tool_row = 0
+        # this variable will serve as the real tool_number
+        tool_id = 0
+
+        for tool_no in self.sorted_diameters:
+            tool_id += 1
+            drill_cnt = 0  # variable to store the nr of drills per tool
+            slot_cnt = 0  # variable to store the nr of slots per tool
+
+            # Find no of drills for the current tool
+            for tool_dia in self.points_edit:
+                if float(tool_dia) == tool_no:
+                    drill_cnt = len(self.points_edit[tool_dia])
+
+            self.tot_drill_cnt += drill_cnt
+
+            try:
+                # Find no of slots for the current tool
+                for slot in self.slots:
+                    if slot['tool'] == tool_no:
+                        slot_cnt += 1
+
+                self.tot_slot_cnt += slot_cnt
+            except AttributeError:
+                # log.debug("No slots in the Excellon file")
+                # slot editing not implemented
+                pass
+
+            id = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
+            id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            self.tools_table_exc.setItem(self.tool_row, 0, id)  # Tool name/id
+
+            # Make sure that the drill diameter when in MM is with no more than 2 decimals
+            # There are no drill bits in MM with more than 3 decimals diameter
+            # For INCH the decimals should be no more than 3. There are no drills under 10mils
+            if self.units == 'MM':
+                dia = QtWidgets.QTableWidgetItem('%.2f' % self.olddia_newdia[tool_no])
+            else:
+                dia = QtWidgets.QTableWidgetItem('%.3f' % self.olddia_newdia[tool_no])
+
+            dia.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            drill_count = QtWidgets.QTableWidgetItem('%d' % drill_cnt)
+            drill_count.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            # if the slot number is zero is better to not clutter the GUI with zero's so we print a space
+            if slot_cnt > 0:
+                slot_count = QtWidgets.QTableWidgetItem('%d' % slot_cnt)
+            else:
+                slot_count = QtWidgets.QTableWidgetItem('')
+            slot_count.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            self.tools_table_exc.setItem(self.tool_row, 1, dia)  # Diameter
+            self.tools_table_exc.setItem(self.tool_row, 2, drill_count)  # Number of drills per tool
+            self.tools_table_exc.setItem(self.tool_row, 3, slot_count)  # Number of drills per tool
+            self.tool_row += 1
+
+        # make the diameter column editable
+        for row in range(self.tool_row):
+            self.tools_table_exc.item(row, 1).setFlags(
+                QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            self.tools_table_exc.item(row, 2).setForeground(QtGui.QColor(0, 0, 0))
+            self.tools_table_exc.item(row, 3).setForeground(QtGui.QColor(0, 0, 0))
+
+        # add a last row with the Total number of drills
+        # HACK: made the text on this cell '9999' such it will always be the one before last when sorting
+        # it will have to have the foreground color (font color) white
+        empty = QtWidgets.QTableWidgetItem('9998')
+        empty.setForeground(QtGui.QColor(255, 255, 255))
+
+        empty.setFlags(empty.flags() ^ QtCore.Qt.ItemIsEnabled)
+        empty_b = QtWidgets.QTableWidgetItem('')
+        empty_b.setFlags(empty_b.flags() ^ QtCore.Qt.ItemIsEnabled)
+
+        label_tot_drill_count = QtWidgets.QTableWidgetItem(_('Total Drills'))
+        tot_drill_count = QtWidgets.QTableWidgetItem('%d' % self.tot_drill_cnt)
+
+        label_tot_drill_count.setFlags(label_tot_drill_count.flags() ^ QtCore.Qt.ItemIsEnabled)
+        tot_drill_count.setFlags(tot_drill_count.flags() ^ QtCore.Qt.ItemIsEnabled)
+
+        self.tools_table_exc.setItem(self.tool_row, 0, empty)
+        self.tools_table_exc.setItem(self.tool_row, 1, label_tot_drill_count)
+        self.tools_table_exc.setItem(self.tool_row, 2, tot_drill_count)  # Total number of drills
+        self.tools_table_exc.setItem(self.tool_row, 3, empty_b)
+
+        font = QtGui.QFont()
+        font.setBold(True)
+        font.setWeight(75)
+
+        for k in [1, 2]:
+            self.tools_table_exc.item(self.tool_row, k).setForeground(QtGui.QColor(127, 0, 255))
+            self.tools_table_exc.item(self.tool_row, k).setFont(font)
+
+        self.tool_row += 1
+
+        # add a last row with the Total number of slots
+        # HACK: made the text on this cell '9999' such it will always be the last when sorting
+        # it will have to have the foreground color (font color) white
+        empty_2 = QtWidgets.QTableWidgetItem('9999')
+        empty_2.setForeground(QtGui.QColor(255, 255, 255))
+
+        empty_2.setFlags(empty_2.flags() ^ QtCore.Qt.ItemIsEnabled)
+
+        empty_3 = QtWidgets.QTableWidgetItem('')
+        empty_3.setFlags(empty_3.flags() ^ QtCore.Qt.ItemIsEnabled)
+
+        label_tot_slot_count = QtWidgets.QTableWidgetItem(_('Total Slots'))
+        tot_slot_count = QtWidgets.QTableWidgetItem('%d' % self.tot_slot_cnt)
+        label_tot_slot_count.setFlags(label_tot_slot_count.flags() ^ QtCore.Qt.ItemIsEnabled)
+        tot_slot_count.setFlags(tot_slot_count.flags() ^ QtCore.Qt.ItemIsEnabled)
+
+        self.tools_table_exc.setItem(self.tool_row, 0, empty_2)
+        self.tools_table_exc.setItem(self.tool_row, 1, label_tot_slot_count)
+        self.tools_table_exc.setItem(self.tool_row, 2, empty_3)
+        self.tools_table_exc.setItem(self.tool_row, 3, tot_slot_count)  # Total number of slots
+
+        for kl in [1, 2, 3]:
+            self.tools_table_exc.item(self.tool_row, kl).setFont(font)
+            self.tools_table_exc.item(self.tool_row, kl).setForeground(QtGui.QColor(0, 70, 255))
+
+
+        # all the tools are selected by default
+        self.tools_table_exc.selectColumn(0)
+        #
+        self.tools_table_exc.resizeColumnsToContents()
+        self.tools_table_exc.resizeRowsToContents()
+
+        vertical_header = self.tools_table_exc.verticalHeader()
+        # vertical_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
+        vertical_header.hide()
+        self.tools_table_exc.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+
+        horizontal_header = self.tools_table_exc.horizontalHeader()
+        horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
+        horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
+        # horizontal_header.setStretchLastSection(True)
+
+        # self.tools_table_exc.setSortingEnabled(True)
+        # sort by tool diameter
+        self.tools_table_exc.sortItems(1)
+
+        # After sorting, to display also the number of drills in the right row we need to update self.initial_rows dict
+        # with the new order. Of course the last 2 rows in the tool table are just for display therefore we don't
+        # use them
+        self.tool2tooldia.clear()
+        for row in range(self.tools_table_exc.rowCount() - 2):
+            tool = int(self.tools_table_exc.item(row, 0).text())
+            diameter = float(self.tools_table_exc.item(row, 1).text())
+            self.tool2tooldia[tool] = diameter
+
+        self.tools_table_exc.setMinimumHeight(self.tools_table_exc.getHeight())
+        self.tools_table_exc.setMaximumHeight(self.tools_table_exc.getHeight())
+
+        # make sure no rows are selected so the user have to click the correct row, meaning selecting the correct tool
+        self.tools_table_exc.clearSelection()
+
+        # Remove anything else in the GUI Selected Tab
+        self.app.ui.selected_scroll_area.takeWidget()
+        # Put ourself in the GUI Selected Tab
+        self.app.ui.selected_scroll_area.setWidget(self.exc_edit_widget)
+        # Switch notebook to Selected page
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
+
+        # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
+        self.tools_table_exc.itemChanged.connect(self.on_tool_edit)
+        self.tools_table_exc.cellPressed.connect(self.on_row_selected)
+
+    def on_tool_add(self, tooldia=None):
+        self.is_modified = True
+        if tooldia:
+            tool_dia = tooldia
+        else:
+            try:
+                tool_dia = float(self.addtool_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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.")
+                                         )
+                    return
+
+        if tool_dia not in self.olddia_newdia:
+            storage_elem = FlatCAMGeoEditor.make_storage()
+            self.storage_dict[tool_dia] = storage_elem
+
+            # self.olddia_newdia dict keeps the evidence on current tools diameters as keys and gets updated on values
+            # 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. ")
+                                 )
+            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.build_ui()
+
+        # make a quick sort through the tool2tooldia dict so we find which row to select
+        row_to_be_selected = None
+        for key in sorted(self.tool2tooldia):
+            if self.tool2tooldia[key] == tool_dia:
+                row_to_be_selected = int(key) - 1
+                break
+
+        self.tools_table_exc.selectRow(row_to_be_selected)
+
+    def on_tool_delete(self, dia=None):
+        self.is_modified = True
+        deleted_tool_dia_list = []
+        deleted_tool_offset_list = []
+
+        try:
+            if dia is None or dia is False:
+                # deleted_tool_dia = float(self.tools_table_exc.item(self.tools_table_exc.currentRow(), 1).text())
+                for index in self.tools_table_exc.selectionModel().selectedRows():
+                    row = index.row()
+                    deleted_tool_dia_list.append(float(self.tools_table_exc.item(row, 1).text()))
+            else:
+                if isinstance(dia, list):
+                    for dd in dia:
+                        deleted_tool_dia_list.append(float('%.4f' % dd))
+                else:
+                    deleted_tool_dia_list.append(float('%.4f' % dia))
+        except:
+            self.app.inform.emit(_("[WARNING_NOTCL] Select a tool in Tool Table"))
+            return
+
+        for deleted_tool_dia in deleted_tool_dia_list:
+
+            # delete de tool offset
+            self.exc_obj.tool_offset.pop(float(deleted_tool_dia), None)
+
+            # delete the storage used for that tool
+            storage_elem = FlatCAMGeoEditor.make_storage()
+            self.storage_dict[deleted_tool_dia] = storage_elem
+            self.storage_dict.pop(deleted_tool_dia, None)
+
+            # I've added this flag_del variable because dictionary don't like
+            # having keys deleted while iterating through them
+            flag_del = []
+            # self.points_edit.pop(deleted_tool_dia, None)
+            for deleted_tool in self.tool2tooldia:
+                if self.tool2tooldia[deleted_tool] == deleted_tool_dia:
+                    flag_del.append(deleted_tool)
+
+            if flag_del:
+                for tool_to_be_deleted in flag_del:
+                    # delete the tool
+                    self.tool2tooldia.pop(tool_to_be_deleted, None)
+
+                    # delete also the drills from points_edit dict just in case we add the tool again, we don't want to show the
+                    # number of drills from before was deleter
+                    self.points_edit[deleted_tool_dia] = []
+                flag_del = []
+
+            self.olddia_newdia.pop(deleted_tool_dia, None)
+
+            self.app.inform.emit(_("[success] Deleted tool with dia: {del_dia} {units}").format(del_dia=str(deleted_tool_dia), units=str(self.units)))
+
+        self.replot()
+        # self.app.inform.emit("Could not delete selected tool")
+
+        self.build_ui()
+
+    def on_tool_edit(self, item_changed):
+
+        # if connected, disconnect the signal from the slot on item_changed as it creates issues
+        self.tools_table_exc.itemChanged.disconnect()
+        self.tools_table_exc.cellPressed.disconnect()
+
+        # self.tools_table_exc.selectionModel().currentChanged.disconnect()
+
+        self.is_modified = True
+        geometry = []
+        current_table_dia_edited = None
+
+        if self.tools_table_exc.currentItem() is not None:
+            try:
+                current_table_dia_edited = float(self.tools_table_exc.currentItem().text())
+            except ValueError as e:
+                log.debug("FlatCAMExcEditor.on_tool_edit() --> %s" % str(e))
+                self.tools_table_exc.setCurrentItem(None)
+                return
+
+        row_of_item_changed = self.tools_table_exc.currentRow()
+
+        # rows start with 0, tools start with 1 so we adjust the value by 1
+        key_in_tool2tooldia = row_of_item_changed + 1
+
+        dia_changed = self.tool2tooldia[key_in_tool2tooldia]
+
+        # tool diameter is not used so we create a new tool with the desired diameter
+        if current_table_dia_edited not in self.olddia_newdia.values():
+            # update the dict that holds as keys our initial diameters and as values the edited diameters
+            self.olddia_newdia[dia_changed] = current_table_dia_edited
+            # update the dict that holds tool_no as key and tool_dia as value
+            self.tool2tooldia[key_in_tool2tooldia] = current_table_dia_edited
+
+            # update the tool offset
+            modified_offset = self.exc_obj.tool_offset.pop(dia_changed)
+            self.exc_obj.tool_offset[current_table_dia_edited] = modified_offset
+
+            self.replot()
+        else:
+            # tool diameter is already in use so we move the drills from the prior tool to the new tool
+            factor = current_table_dia_edited / dia_changed
+            geometry = []
+
+            for shape in self.storage_dict[dia_changed].get_objects():
+                geometry.append(DrawToolShape(
+                    MultiLineString([affinity.scale(subgeo, xfact=factor, yfact=factor, origin='center')
+                                     for subgeo in shape.geo])))
+
+                # add bogus drill points (for total count of drills)
+                for k, v in self.olddia_newdia.items():
+                    if v == current_table_dia_edited:
+                        self.points_edit[k].append((0, 0))
+                        break
+
+            # search for the oldia that correspond to the newdia and add the drills in it's storage
+            # everything will be sort out later, when the edited excellon is updated
+            for k, v in self.olddia_newdia.items():
+                if v == current_table_dia_edited:
+                    self.add_exc_shape(geometry, self.storage_dict[k])
+                    break
+
+            # delete the old tool from which we moved the drills
+            self.on_tool_delete(dia=dia_changed)
+
+            # delete the tool offset
+            self.exc_obj.tool_offset.pop(dia_changed, None)
+
+        # we reactivate the signals after the after the tool editing
+        self.tools_table_exc.itemChanged.connect(self.on_tool_edit)
+        self.tools_table_exc.cellPressed.connect(self.on_row_selected)
+
+        # self.tools_table_exc.selectionModel().currentChanged.connect(self.on_row_selected)
+
+    def on_name_activate(self):
+        self.edited_obj_name = self.name_entry.get_value()
+
+    def activate(self):
+        self.connect_canvas_event_handlers()
+
+        # initialize working objects
+        self.storage_dict = {}
+        self.current_storage = []
+        self.points_edit = {}
+        self.sorted_diameters = []
+        self.new_drills = []
+        self.new_tools = {}
+        self.new_slots = {}
+        self.new_tool_offset = {}
+        self.olddia_newdia = {}
+
+        self.shapes.enabled = True
+        self.tool_shape.enabled = True
+        # self.app.app_cursor.enabled = True
+
+        self.app.ui.snap_max_dist_entry.setEnabled(True)
+        self.app.ui.corner_snap_btn.setEnabled(True)
+        self.app.ui.snap_magnet.setVisible(True)
+        self.app.ui.corner_snap_btn.setVisible(True)
+
+        self.app.ui.exc_editor_menu.setDisabled(False)
+        self.app.ui.exc_editor_menu.menuAction().setVisible(True)
+
+        self.app.ui.update_obj_btn.setEnabled(True)
+        self.app.ui.e_editor_cmenu.setEnabled(True)
+
+        self.app.ui.exc_edit_toolbar.setDisabled(False)
+        self.app.ui.exc_edit_toolbar.setVisible(True)
+        # self.app.ui.snap_toolbar.setDisabled(False)
+
+        # start with GRID toolbar activated
+        if self.app.ui.grid_snap_btn.isChecked() is False:
+            self.app.ui.grid_snap_btn.trigger()
+
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(False)
+        self.app.ui.popmenu_save.setVisible(True)
+
+        # Tell the App that the editor is active
+        self.editor_active = True
+
+    def deactivate(self):
+        self.disconnect_canvas_event_handlers()
+        self.clear()
+        self.app.ui.exc_edit_toolbar.setDisabled(True)
+
+        settings = QSettings("Open Source", "FlatCAM")
+        if settings.contains("layout"):
+            layout = settings.value('layout', type=str)
+            if layout == 'standard':
+                # self.app.ui.exc_edit_toolbar.setVisible(False)
+
+                self.app.ui.snap_max_dist_entry.setEnabled(False)
+                self.app.ui.corner_snap_btn.setEnabled(False)
+                self.app.ui.snap_magnet.setVisible(False)
+                self.app.ui.corner_snap_btn.setVisible(False)
+            elif layout == 'compact':
+                # self.app.ui.exc_edit_toolbar.setVisible(True)
+
+                self.app.ui.snap_max_dist_entry.setEnabled(False)
+                self.app.ui.corner_snap_btn.setEnabled(False)
+                self.app.ui.snap_magnet.setVisible(True)
+                self.app.ui.corner_snap_btn.setVisible(True)
+        else:
+            # self.app.ui.exc_edit_toolbar.setVisible(False)
+
+            self.app.ui.snap_max_dist_entry.setEnabled(False)
+            self.app.ui.corner_snap_btn.setEnabled(False)
+            self.app.ui.snap_magnet.setVisible(False)
+            self.app.ui.corner_snap_btn.setVisible(False)
+
+        # set the Editor Toolbar visibility to what was before entering in the Editor
+        self.app.ui.exc_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
+            else self.app.ui.exc_edit_toolbar.setVisible(True)
+
+        # Disable visuals
+        self.shapes.enabled = False
+        self.tool_shape.enabled = False
+        # self.app.app_cursor.enabled = False
+
+        # Tell the app that the editor is no longer active
+        self.editor_active = False
+
+        self.app.ui.exc_editor_menu.setDisabled(True)
+        self.app.ui.exc_editor_menu.menuAction().setVisible(False)
+
+        self.app.ui.update_obj_btn.setEnabled(False)
+
+        self.app.ui.g_editor_cmenu.setEnabled(False)
+        self.app.ui.e_editor_cmenu.setEnabled(False)
+
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(True)
+        self.app.ui.popmenu_save.setVisible(False)
+
+        # Show original geometry
+        if self.exc_obj:
+            self.exc_obj.visible = True
+
+    def connect_canvas_event_handlers(self):
+        ## Canvas events
+
+        # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
+        # but those from FlatCAMGeoEditor
+
+        self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.collection.view.clicked.disconnect()
+
+        self.canvas.vis_connect('mouse_press', self.on_canvas_click)
+        self.canvas.vis_connect('mouse_move', self.on_canvas_move)
+        self.canvas.vis_connect('mouse_release', self.on_canvas_click_release)
+
+    def disconnect_canvas_event_handlers(self):
+        self.canvas.vis_disconnect('mouse_press', self.on_canvas_click)
+        self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
+        self.canvas.vis_disconnect('mouse_release', self.on_canvas_click_release)
+
+        # we restore the key and mouse control to FlatCAMApp method
+        self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.vis_connect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
+
+    def clear(self):
+        self.active_tool = None
+        # self.shape_buffer = []
+        self.selected = []
+
+        self.points_edit = {}
+        self.new_tools = {}
+        self.new_drills = []
+
+        # self.storage_dict = {}
+
+        self.shapes.clear(update=True)
+        self.tool_shape.clear(update=True)
+
+        # self.storage = FlatCAMExcEditor.make_storage()
+        self.replot()
+
+    def edit_fcexcellon(self, exc_obj):
+        """
+        Imports the geometry from the given FlatCAM Excellon object
+        into the editor.
+
+        :param fcgeometry: FlatCAMExcellon
+        :return: None
+        """
+
+        assert isinstance(exc_obj, Excellon), \
+            "Expected an Excellon Object, got %s" % type(exc_obj)
+
+        self.deactivate()
+        self.activate()
+
+        # Hide original geometry
+        self.exc_obj = exc_obj
+        exc_obj.visible = False
+
+        # Set selection tolerance
+        # DrawToolShape.tolerance = fc_excellon.drawing_tolerance * 10
+
+        self.select_tool("select")
+
+        self.set_ui()
+
+        # now that we hava data, create the GUI interface and add it to the Tool Tab
+        self.build_ui()
+
+        # we activate this after the initial build as we don't need to see the tool been populated
+        self.tools_table_exc.itemChanged.connect(self.on_tool_edit)
+
+        # build the geometry for each tool-diameter, each drill will be represented by a '+' symbol
+        # and then add it to the storage elements (each storage elements is a member of a list
+        for tool_dia in self.points_edit:
+            storage_elem = FlatCAMGeoEditor.make_storage()
+            for point in self.points_edit[tool_dia]:
+                # make a '+' sign, the line length is the tool diameter
+                start_hor_line = ((point.x - (tool_dia / 2)), point.y)
+                stop_hor_line = ((point.x + (tool_dia / 2)), point.y)
+                start_vert_line = (point.x, (point.y - (tool_dia / 2)))
+                stop_vert_line = (point.x, (point.y + (tool_dia / 2)))
+                shape = MultiLineString([(start_hor_line, stop_hor_line),(start_vert_line, stop_vert_line)])
+                if shape is not None:
+                    self.add_exc_shape(DrawToolShape(shape), storage_elem)
+            self.storage_dict[tool_dia] = storage_elem
+
+        self.replot()
+
+        # add a first tool in the Tool Table but only if the Excellon Object is empty
+        if not self.tool2tooldia:
+            self.on_tool_add(tooldia=1.00)
+
+    def update_fcexcellon(self, exc_obj):
+        """
+        Create a new Excellon object that contain the edited content of the source Excellon object
+
+        :param exc_obj: FlatCAMExcellon
+        :return: None
+        """
+
+        # this dictionary will contain tooldia's as keys and a list of coordinates tuple as values
+        # the values of this dict are coordinates of the holes (drills)
+        edited_points = {}
+        for storage_tooldia in self.storage_dict:
+            for x in self.storage_dict[storage_tooldia].get_objects():
+
+                # all x.geo in self.storage_dict[storage] are MultiLinestring objects
+                # each MultiLineString is made out of Linestrings
+                # select first Linestring object in the current MultiLineString
+                first_linestring = x.geo[0]
+                # get it's coordinates
+                first_linestring_coords = first_linestring.coords
+                x_coord = first_linestring_coords[0][0] + (float(first_linestring.length / 2))
+                y_coord = first_linestring_coords[0][1]
+
+                # create a tuple with the coordinates (x, y) and add it to the list that is the value of the
+                # edited_points dictionary
+                point = (x_coord, y_coord)
+                if not storage_tooldia in edited_points:
+                    edited_points[storage_tooldia] = [point]
+                else:
+                    edited_points[storage_tooldia].append(point)
+
+        # recreate the drills and tools to be added to the new Excellon edited object
+        # first, we look in the tool table if one of the tool diameters was changed then
+        # append that a tuple formed by (old_dia, edited_dia) to a list
+        changed_key = []
+        for initial_dia in self.olddia_newdia:
+            edited_dia = self.olddia_newdia[initial_dia]
+            if edited_dia != initial_dia:
+                for old_dia in edited_points:
+                    if old_dia == initial_dia:
+                        changed_key.append((old_dia, edited_dia))
+            # if the initial_dia is not in edited_points it means it is a new tool with no drill points
+            # (and we have to add it)
+            # because in case we have drill points it will have to be already added in edited_points
+            # if initial_dia not in edited_points.keys():
+            #     edited_points[initial_dia] = []
+
+        for el in changed_key:
+            edited_points[el[1]] = edited_points.pop(el[0])
+
+        # Let's sort the edited_points dictionary by keys (diameters) and store the result in a zipped list
+        # ordered_edited_points is a ordered list of tuples;
+        # element[0] of the tuple is the diameter and
+        # element[1] of the tuple is a list of coordinates (a tuple themselves)
+        ordered_edited_points = sorted(zip(edited_points.keys(), edited_points.values()))
+
+        current_tool = 0
+        for tool_dia in ordered_edited_points:
+            current_tool += 1
+
+            # create the self.tools for the new Excellon object (the one with edited content)
+            name = str(current_tool)
+            spec = {"C": float(tool_dia[0])}
+            self.new_tools[name] = spec
+
+            # add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
+            self.new_tools[name]['solid_geometry'] = []
+
+            # create the self.drills for the new Excellon object (the one with edited content)
+            for point in tool_dia[1]:
+                self.new_drills.append(
+                    {
+                        'point': Point(point),
+                        'tool': str(current_tool)
+                    }
+                )
+                # repopulate the 'solid_geometry' for each tool
+                poly = Point(point).buffer(float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4))
+                self.new_tools[name]['solid_geometry'].append(poly)
+
+        if self.is_modified is True:
+            if "_edit" in self.edited_obj_name:
+                try:
+                    id = int(self.edited_obj_name[-1]) + 1
+                    self.edited_obj_name = self.edited_obj_name[:-1] + str(id)
+                except ValueError:
+                    self.edited_obj_name += "_1"
+            else:
+                self.edited_obj_name += "_edit"
+
+        self.app.worker_task.emit({'fcn': self.new_edited_excellon,
+                                   'params': [self.edited_obj_name]})
+
+        if self.exc_obj.slots:
+            self.new_slots = self.exc_obj.slots
+
+        self.new_tool_offset = self.exc_obj.tool_offset
+
+        # reset the tool table
+        self.tools_table_exc.clear()
+        self.tools_table_exc.setHorizontalHeaderLabels(['#', _('Diameter'), 'D', 'S'])
+        self.last_tool_selected = None
+
+        # delete the edited Excellon object which will be replaced by a new one having the edited content of the first
+        self.app.collection.set_active(self.exc_obj.options['name'])
+        self.app.collection.delete_active()
+
+        # restore GUI to the Selected TAB
+        # Remove anything else in the GUI
+        self.app.ui.tool_scroll_area.takeWidget()
+        # Switch notebook to Selected page
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
+
+    def update_options(self, obj):
+        try:
+            if not obj.options:
+                obj.options = {}
+                obj.options['xmin'] = 0
+                obj.options['ymin'] = 0
+                obj.options['xmax'] = 0
+                obj.options['ymax'] = 0
+                return True
+            else:
+                return False
+        except AttributeError:
+            obj.options = {}
+            return True
+
+    def new_edited_excellon(self, outname):
+        """
+        Creates a new Excellon object for the edited Excellon. Thread-safe.
+
+        :param outname: Name of the resulting object. None causes the
+            name to be that of the file.
+        :type outname: str
+        :return: None
+        """
+
+        self.app.log.debug("Update the Excellon object with edited content. Source is %s" %
+                           self.exc_obj.options['name'])
+
+        # How the object should be initialized
+        def obj_init(excellon_obj, app_obj):
+            # self.progress.emit(20)
+            excellon_obj.drills = self.new_drills
+            excellon_obj.tools = self.new_tools
+            excellon_obj.slots = self.new_slots
+            excellon_obj.tool_offset = self.new_tool_offset
+            excellon_obj.options['name'] = outname
+
+            try:
+                excellon_obj.create_geometry()
+            except KeyError:
+                self.app.inform.emit(
+                   _( "[ERROR_NOTCL] There are no Tools definitions in the file. Aborting Excellon creation.")
+                )
+            except:
+                msg = _("[ERROR] An internal error has ocurred. See shell.\n")
+                msg += traceback.format_exc()
+                app_obj.inform.emit(msg)
+                raise
+                # raise
+
+        with self.app.proc_container.new(_("Creating Excellon.")):
+
+            try:
+                self.app.new_object("excellon", outname, obj_init)
+            except Exception as e:
+                log.error("Error on object creation: %s" % str(e))
+                self.app.progress.emit(100)
+                return
+
+            self.app.inform.emit(_("[success] Excellon editing finished."))
+            # self.progress.emit(100)
+
+    def on_tool_select(self, tool):
+        """
+        Behavior of the toolbar. Tool initialization.
+
+        :rtype : None
+        """
+        current_tool = tool
+
+        self.app.log.debug("on_tool_select('%s')" % tool)
+
+        if self.last_tool_selected is None and current_tool is not 'select':
+            # self.draw_app.select_tool('select')
+            self.complete = True
+            current_tool = 'select'
+            self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. There is no Tool/Drill selected"))
+
+        # This is to make the group behave as radio group
+        if current_tool in self.tools_exc:
+            if self.tools_exc[current_tool]["button"].isChecked():
+                self.app.log.debug("%s is checked." % current_tool)
+                for t in self.tools_exc:
+                    if t != current_tool:
+                        self.tools_exc[t]["button"].setChecked(False)
+
+                # this is where the Editor toolbar classes (button's) are instantiated
+                self.active_tool = self.tools_exc[current_tool]["constructor"](self)
+                # self.app.inform.emit(self.active_tool.start_msg)
+            else:
+                self.app.log.debug("%s is NOT checked." % current_tool)
+                for t in self.tools_exc:
+                    self.tools_exc[t]["button"].setChecked(False)
+
+                self.select_tool('select')
+                self.active_tool = FCDrillSelect(self)
+
+    def on_row_selected(self, row, col):
+        if col == 0:
+            key_modifier = QtWidgets.QApplication.keyboardModifiers()
+            if self.app.defaults["global_mselect_key"] == 'Control':
+                modifier_to_use = Qt.ControlModifier
+            else:
+                modifier_to_use = Qt.ShiftModifier
+
+            if key_modifier == modifier_to_use:
+                pass
+            else:
+                self.selected = []
+
+            try:
+                selected_dia = self.tool2tooldia[self.tools_table_exc.currentRow() + 1]
+                self.last_tool_selected = self.tools_table_exc.currentRow() + 1
+                for obj in self.storage_dict[selected_dia].get_objects():
+                    self.selected.append(obj)
+            except Exception as e:
+                self.app.log.debug(str(e))
+
+            self.replot()
+
+    def toolbar_tool_toggle(self, key):
+        self.options[key] = self.sender().isChecked()
+        if self.options[key] == True:
+            return 1
+        else:
+            return 0
+
+    def on_canvas_click(self, event):
+        """
+        event.x and .y have canvas coordinates
+        event.xdaya and .ydata have plot coordinates
+
+        :param event: Event object dispatched by Matplotlib
+        :return: None
+        """
+
+        if event.button is 1:
+            self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
+            self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
+
+            ### Snap coordinates
+            x, y = self.app.geo_editor.snap(self.pos[0], self.pos[1])
+
+            self.pos = (x, y)
+            # print(self.active_tool)
+
+            # Selection with left mouse button
+            if self.active_tool is not None and event.button is 1:
+                # Dispatch event to active_tool
+                # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
+                msg = self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
+
+                # If it is a shape generating tool
+                if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
+                    if self.current_storage is not None:
+                        self.on_exc_shape_complete(self.current_storage)
+                        self.build_ui()
+                    # MS: always return to the Select Tool if modifier key is not pressed
+                    # else return to the current tool
+                    key_modifier = QtWidgets.QApplication.keyboardModifiers()
+                    if self.app.defaults["global_mselect_key"] == 'Control':
+                        modifier_to_use = Qt.ControlModifier
+                    else:
+                        modifier_to_use = Qt.ShiftModifier
+                    # if modifier key is pressed then we add to the selected list the current shape but if it's already
+                    # in the selected list, we removed it. Therefore first click selects, second deselects.
+                    if key_modifier == modifier_to_use:
+                        self.select_tool(self.active_tool.name)
+                    else:
+                        self.select_tool("select")
+                        return
+
+                if isinstance(self.active_tool, FCDrillSelect):
+                    # self.app.log.debug("Replotting after click.")
+                    self.replot()
+            else:
+                self.app.log.debug("No active tool to respond to click!")
+
+    def on_exc_shape_complete(self, storage):
+        self.app.log.debug("on_shape_complete()")
+
+        # Add shape
+        if type(storage) is list:
+            for item_storage in storage:
+                self.add_exc_shape(self.active_tool.geometry, item_storage)
+        else:
+            self.add_exc_shape(self.active_tool.geometry, storage)
+
+        # Remove any utility shapes
+        self.delete_utility_geometry()
+        self.tool_shape.clear(update=True)
+
+        # Replot and reset tool.
+        self.replot()
+        # self.active_tool = type(self.active_tool)(self)
+
+    def add_exc_shape(self, shape, storage):
+        """
+        Adds a shape to the shape storage.
+
+        :param shape: Shape to be added.
+        :type shape: DrawToolShape
+        :return: None
+        """
+        # List of DrawToolShape?
+        if isinstance(shape, list):
+            for subshape in shape:
+                self.add_exc_shape(subshape, storage)
+            return
+
+        assert isinstance(shape, DrawToolShape), \
+            "Expected a DrawToolShape, got %s" % str(type(shape))
+
+        assert shape.geo is not None, \
+            "Shape object has empty geometry (None)"
+
+        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
+               not isinstance(shape.geo, list), \
+            "Shape objects has empty geometry ([])"
+
+        if isinstance(shape, DrawToolUtilityShape):
+            self.utility.append(shape)
+        else:
+            storage.insert(shape)  # TODO: Check performance
+
+    def add_shape(self, shape):
+        """
+        Adds a shape to the shape storage.
+
+        :param shape: Shape to be added.
+        :type shape: DrawToolShape
+        :return: None
+        """
+
+        # List of DrawToolShape?
+        if isinstance(shape, list):
+            for subshape in shape:
+                self.add_shape(subshape)
+            return
+
+        assert isinstance(shape, DrawToolShape), \
+            "Expected a DrawToolShape, got %s" % type(shape)
+
+        assert shape.geo is not None, \
+            "Shape object has empty geometry (None)"
+
+        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
+               not isinstance(shape.geo, list), \
+            "Shape objects has empty geometry ([])"
+
+        if isinstance(shape, DrawToolUtilityShape):
+            self.utility.append(shape)
+        else:
+            self.storage.insert(shape)  # TODO: Check performance
+
+    def on_canvas_click_release(self, event):
+        pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
+
+        self.modifiers = QtWidgets.QApplication.keyboardModifiers()
+
+        if self.app.grid_status():
+            pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
+        else:
+            pos = (pos_canvas[0], pos_canvas[1])
+
+        # if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context
+        # canvas menu
+        try:
+            if event.button == 2:  # right click
+                if self.app.panning_action is True:
+                    self.app.panning_action = False
+                else:
+                    self.app.cursor = QtGui.QCursor()
+                    self.app.ui.popMenu.popup(self.app.cursor.pos())
+        except Exception as e:
+            log.warning("Error: %s" % str(e))
+            raise
+
+        # if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right
+        # selection and then select a type of selection ("enclosing" or "touching")
+        try:
+            if event.button == 1:  # left click
+                if self.app.selection_type is not None:
+                    self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
+                    self.app.selection_type = None
+                elif isinstance(self.active_tool, FCDrillSelect):
+                    # Dispatch event to active_tool
+                    # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
+                    # msg = self.active_tool.click_release((self.pos[0], self.pos[1]))
+                    # self.app.inform.emit(msg)
+                    self.active_tool.click_release((self.pos[0], self.pos[1]))
+                    self.replot()
+        except Exception as e:
+            log.warning("Error: %s" % str(e))
+            raise
+
+    def draw_selection_area_handler(self, start_pos, end_pos, sel_type):
+        """
+        :param start_pos: mouse position when the selection LMB click was done
+        :param end_pos: mouse position when the left mouse button is released
+        :param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
+        :type Bool
+        :return:
+        """
+        poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
+
+        self.app.delete_selection_shape()
+        for storage in self.storage_dict:
+            for obj in self.storage_dict[storage].get_objects():
+                if (sel_type is True and poly_selection.contains(obj.geo)) or \
+                        (sel_type is False and poly_selection.intersects(obj.geo)):
+                    if self.key == self.app.defaults["global_mselect_key"]:
+                        if obj in self.selected:
+                            self.selected.remove(obj)
+                        else:
+                            # add the object to the selected shapes
+                            self.selected.append(obj)
+                    else:
+                        self.selected.append(obj)
+
+        try:
+            self.tools_table_exc.cellPressed.disconnect()
+        except:
+            pass
+        # select the diameter of the selected shape in the tool table
+        self.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+        for storage in self.storage_dict:
+            for shape_s in self.selected:
+                if shape_s in self.storage_dict[storage].get_objects():
+                    for key in self.tool2tooldia:
+                        if self.tool2tooldia[key] == storage:
+                            item = self.tools_table_exc.item((key - 1), 1)
+                            self.tools_table_exc.setCurrentItem(item)
+                            self.last_tool_selected = key
+
+        self.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+
+        self.tools_table_exc.cellPressed.connect(self.on_row_selected)
+        self.replot()
+
+    def on_canvas_move(self, event):
+        """
+        Called on 'mouse_move' event
+
+        event.pos have canvas screen coordinates
+
+        :param event: Event object dispatched by VisPy SceneCavas
+        :return: None
+        """
+
+        pos = self.canvas.vispy_canvas.translate_coords(event.pos)
+        event.xdata, event.ydata = pos[0], pos[1]
+
+        self.x = event.xdata
+        self.y = event.ydata
+
+        # Prevent updates on pan
+        # if len(event.buttons) > 0:
+        #     return
+
+        # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
+        if event.button == 2:
+            self.app.panning_action = True
+            return
+        else:
+            self.app.panning_action = False
+
+        try:
+            x = float(event.xdata)
+            y = float(event.ydata)
+        except TypeError:
+            return
+
+        if self.active_tool is None:
+            return
+
+        ### Snap coordinates
+        x, y = self.app.geo_editor.app.geo_editor.snap(x, y)
+
+        self.snap_x = x
+        self.snap_y = y
+
+        # update the position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                       "<b>Y</b>: %.4f" % (x, y))
+
+        if self.pos is None:
+            self.pos = (0, 0)
+        dx = x - self.pos[0]
+        dy = y - self.pos[1]
+
+        # update the reference position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                           "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
+
+        ### Utility geometry (animated)
+        geo = self.active_tool.utility_geometry(data=(x, y))
+
+        if isinstance(geo, DrawToolShape) and geo.geo is not None:
+
+            # Remove any previous utility shape
+            self.tool_shape.clear(update=True)
+            self.draw_utility_geometry(geo=geo)
+
+        ### Selection area on canvas section ###
+        dx = pos[0] - self.pos[0]
+        if event.is_dragging == 1 and event.button == 1:
+            self.app.delete_selection_shape()
+            if dx < 0:
+                self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y),
+                     color=self.app.defaults["global_alt_sel_line"],
+                     face_color=self.app.defaults['global_alt_sel_fill'])
+                self.app.selection_type = False
+            else:
+                self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y))
+                self.app.selection_type = True
+        else:
+            self.app.selection_type = None
+
+        # Update cursor
+        self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
+
+    def on_canvas_key_release(self, event):
+        self.key = None
+
+    def draw_utility_geometry(self, geo):
+            # Add the new utility shape
+            try:
+                # this case is for the Font Parse
+                for el in list(geo.geo):
+                    if type(el) == MultiPolygon:
+                        for poly in el:
+                            self.tool_shape.add(
+                                shape=poly,
+                                color=(self.app.defaults["global_draw_color"] + '80'),
+                                update=False,
+                                layer=0,
+                                tolerance=None
+                            )
+                    elif type(el) == MultiLineString:
+                        for linestring in el:
+                            self.tool_shape.add(
+                                shape=linestring,
+                                color=(self.app.defaults["global_draw_color"] + '80'),
+                                update=False,
+                                layer=0,
+                                tolerance=None
+                            )
+                    else:
+                        self.tool_shape.add(
+                            shape=el,
+                            color=(self.app.defaults["global_draw_color"] + '80'),
+                            update=False,
+                            layer=0,
+                            tolerance=None
+                        )
+            except TypeError:
+                self.tool_shape.add(
+                    shape=geo.geo, color=(self.app.defaults["global_draw_color"] + '80'),
+                    update=False, layer=0, tolerance=None)
+
+            self.tool_shape.redraw()
+
+
+    def replot(self):
+        self.plot_all()
+
+    def plot_all(self):
+        """
+        Plots all shapes in the editor.
+
+        :return: None
+        :rtype: None
+        """
+        # self.app.log.debug("plot_all()")
+        self.shapes.clear(update=True)
+
+        for storage in self.storage_dict:
+            for shape_plus in self.storage_dict[storage].get_objects():
+                if shape_plus.geo is None:
+                    continue
+
+                if shape_plus in self.selected:
+                    self.plot_shape(geometry=shape_plus.geo, color=self.app.defaults['global_sel_draw_color'],
+                                    linewidth=2)
+                    continue
+                self.plot_shape(geometry=shape_plus.geo, color=self.app.defaults['global_draw_color'])
+
+        # for shape in self.storage.get_objects():
+        #     if shape.geo is None:  # TODO: This shouldn't have happened
+        #         continue
+        #
+        #     if shape in self.selected:
+        #         self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_sel_draw_color'], linewidth=2)
+        #         continue
+        #
+        #     self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_draw_color'])
+
+
+
+        for shape in self.utility:
+            self.plot_shape(geometry=shape.geo, linewidth=1)
+            continue
+
+        self.shapes.redraw()
+
+    def plot_shape(self, geometry=None, color='black', linewidth=1):
+        """
+        Plots a geometric object or list of objects without rendering. Plotted objects
+        are returned as a list. This allows for efficient/animated rendering.
+
+        :param geometry: Geometry to be plotted (Any Shapely.geom kind or list of such)
+        :param color: Shape color
+        :param linewidth: Width of lines in # of pixels.
+        :return: List of plotted elements.
+        """
+        plot_elements = []
+
+        if geometry is None:
+            geometry = self.active_tool.geometry
+
+        try:
+            for geo in geometry:
+                plot_elements += self.plot_shape(geometry=geo, color=color, linewidth=linewidth)
+
+        ## Non-iterable
+        except TypeError:
+            ## DrawToolShape
+            if isinstance(geometry, DrawToolShape):
+                plot_elements += self.plot_shape(geometry=geometry.geo, color=color, linewidth=linewidth)
+
+            ## Polygon: Descend into exterior and each interior.
+            if type(geometry) == Polygon:
+                plot_elements += self.plot_shape(geometry=geometry.exterior, color=color, linewidth=linewidth)
+                plot_elements += self.plot_shape(geometry=geometry.interiors, color=color, linewidth=linewidth)
+
+            if type(geometry) == LineString or type(geometry) == LinearRing:
+                plot_elements.append(self.shapes.add(shape=geometry, color=color, layer=0))
+
+            if type(geometry) == Point:
+                pass
+
+        return plot_elements
+
+    def on_shape_complete(self):
+        self.app.log.debug("on_shape_complete()")
+
+        # Add shape
+        self.add_shape(self.active_tool.geometry)
+
+        # Remove any utility shapes
+        self.delete_utility_geometry()
+        self.tool_shape.clear(update=True)
+
+        # Replot and reset tool.
+        self.replot()
+        # self.active_tool = type(self.active_tool)(self)
+
+    def get_selected(self):
+        """
+        Returns list of shapes that are selected in the editor.
+
+        :return: List of shapes.
+        """
+        # return [shape for shape in self.shape_buffer if shape["selected"]]
+        return self.selected
+
+    def delete_selected(self):
+        temp_ref = [s for s in self.selected]
+        for shape_sel in temp_ref:
+            self.delete_shape(shape_sel)
+
+        self.selected = []
+        self.build_ui()
+        self.app.inform.emit(_("[success] Done. Drill(s) deleted."))
+
+    def delete_shape(self, shape):
+        self.is_modified = True
+
+        if shape in self.utility:
+            self.utility.remove(shape)
+            return
+
+        for storage in self.storage_dict:
+            # try:
+            #     self.storage_dict[storage].remove(shape)
+            # except:
+            #     pass
+            if shape in self.storage_dict[storage].get_objects():
+                self.storage_dict[storage].remove(shape)
+                # a hack to make the tool_table display less drills per diameter
+                # self.points_edit it's only useful first time when we load the data into the storage
+                # but is still used as referecen when building tool_table in self.build_ui()
+                # the number of drills displayed in column 2 is just a len(self.points_edit) therefore
+                # deleting self.points_edit elements (doesn't matter who but just the number) solved the display issue.
+                del self.points_edit[storage][0]
+
+        if shape in self.selected:
+            self.selected.remove(shape)  # TODO: Check performance
+
+    def delete_utility_geometry(self):
+        # for_deletion = [shape for shape in self.shape_buffer if shape.utility]
+        # for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
+        for_deletion = [shape for shape in self.utility]
+        for shape in for_deletion:
+            self.delete_shape(shape)
+
+        self.tool_shape.clear(update=True)
+        self.tool_shape.redraw()
+
+    def on_delete_btn(self):
+        self.delete_selected()
+        self.replot()
+
+    def select_tool(self, toolname):
+        """
+        Selects a drawing tool. Impacts the object and GUI.
+
+        :param toolname: Name of the tool.
+        :return: None
+        """
+        self.tools_exc[toolname]["button"].setChecked(True)
+        self.on_tool_select(toolname)
+
+    def set_selected(self, shape):
+
+        # Remove and add to the end.
+        if shape in self.selected:
+            self.selected.remove(shape)
+
+        self.selected.append(shape)
+
+    def set_unselected(self, shape):
+        if shape in self.selected:
+            self.selected.remove(shape)
+
+    def on_array_type_combo(self):
+        if self.array_type_combo.currentIndex() == 0:
+            self.array_circular_frame.hide()
+            self.array_linear_frame.show()
+        else:
+            self.delete_utility_geometry()
+            self.array_circular_frame.show()
+            self.array_linear_frame.hide()
+            self.app.inform.emit(_("Click on the circular array Center position"))
+
+    def on_linear_angle_radio(self):
+        val = self.drill_axis_radio.get_value()
+        if val == 'A':
+            self.linear_angle_spinner.show()
+            self.linear_angle_label.show()
+        else:
+            self.linear_angle_spinner.hide()
+            self.linear_angle_label.hide()
+
+    def exc_add_drill(self):
+        self.select_tool('add')
+        return
+
+    def exc_add_drill_array(self):
+        self.select_tool('add_array')
+        return
+
+    def exc_resize_drills(self):
+        self.select_tool('resize')
+        return
+
+    def exc_copy_drills(self):
+        self.select_tool('copy')
+        return
+
+    def exc_move_drills(self):
+        self.select_tool('move')
+        return

Разница между файлами не показана из-за своего большого размера
+ 772 - 3161
flatcamEditors/FlatCAMGeoEditor.py


+ 3803 - 0
flatcamEditors/FlatCAMGrbEditor.py

@@ -0,0 +1,3803 @@
+from PyQt5 import QtGui, QtCore, QtWidgets
+from PyQt5.QtCore import Qt, QSettings
+
+from shapely.geometry import LineString, LinearRing, MultiLineString
+from shapely.ops import cascaded_union
+import shapely.affinity as affinity
+
+from numpy import arctan2, Inf, array, sqrt, sign, dot
+from rtree import index as rtindex
+import threading, time
+import copy
+
+from camlib import *
+from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, LengthEntry, RadioSet, \
+    SpinBoxDelegate, EvalEntry, EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
+from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
+from FlatCAMObj import FlatCAMGerber
+from FlatCAMTool import FlatCAMTool
+
+import gettext
+import FlatCAMTranslation as fcTranslate
+
+fcTranslate.apply_language('strings')
+import builtins
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+
+class FCPad(FCShapeTool):
+    """
+    Resulting type: Polygon
+    """
+
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'pad'
+        self.draw_app = draw_app
+
+        self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['solid_geometry']
+        self.radius = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) / 2
+        self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"]
+
+        # if those cause KeyError exception it means that the aperture type is not 'R'. Only 'R' type has those keys
+        try:
+            self.half_width = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['width']) / 2
+        except KeyError:
+            pass
+        try:
+            self.half_height = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['height']) / 2
+        except KeyError:
+            pass
+
+        geo = self.utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y))
+
+        if isinstance(geo, DrawToolShape) and geo.geo is not None:
+            self.draw_app.draw_utility_geometry(geo=geo)
+
+        self.draw_app.app.inform.emit(_("Click to place ..."))
+
+        # Switch notebook to Selected page
+        self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab)
+
+        self.start_msg = _("Click to place ...")
+
+    def click(self, point):
+        self.make()
+        return "Done."
+
+    def utility_geometry(self, data=None):
+        self.points = data
+        geo_data = self.util_shape(data)
+        if geo_data:
+            return DrawToolUtilityShape(geo_data)
+        else:
+            return None
+
+    def util_shape(self, point):
+        # updating values here allows us to change the aperture on the fly, after the Tool has been started
+        self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['solid_geometry']
+        self.radius = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) / 2
+        self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"]
+
+        # if those cause KeyError exception it means that the aperture type is not 'R'. Only 'R' type has those keys
+        try:
+            self.half_width = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['width']) / 2
+        except KeyError:
+            pass
+        try:
+            self.half_height = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['height']) / 2
+        except KeyError:
+            pass
+
+        if point[0] is None and point[1] is None:
+            point_x = self.draw_app.x
+            point_y = self.draw_app.y
+        else:
+            point_x = point[0]
+            point_y = point[1]
+
+        ap_type = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['type']
+        if ap_type == 'C':
+            center = Point([point_x, point_y])
+            return center.buffer(self.radius)
+        elif ap_type == 'R':
+            p1 = (point_x - self.half_width, point_y - self.half_height)
+            p2 = (point_x + self.half_width, point_y - self.half_height)
+            p3 = (point_x + self.half_width, point_y + self.half_height)
+            p4 = (point_x - self.half_width, point_y + self.half_height)
+            return Polygon([p1, p2, p3, p4, p1])
+        elif ap_type == 'O':
+            geo = []
+            if self.half_height > self.half_width:
+                p1 = (point_x - self.half_width, point_y - self.half_height + self.half_width)
+                p2 = (point_x + self.half_width, point_y - self.half_height + self.half_width)
+                p3 = (point_x + self.half_width, point_y + self.half_height - self.half_width)
+                p4 = (point_x - self.half_width, point_y + self.half_height - self.half_width)
+
+                down_center = (point_x, point_y - self.half_height + self.half_width)
+                d_start_angle = math.pi
+                d_stop_angle = 0.0
+                down_arc = arc(down_center, self.half_width, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
+
+                up_center = (point_x, point_y + self.half_height - self.half_width)
+                u_start_angle = 0.0
+                u_stop_angle = math.pi
+                up_arc = arc(up_center, self.half_width, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
+
+                geo.append(p1)
+                for pt in down_arc:
+                    geo.append(pt)
+                geo.append(p2)
+                geo.append(p3)
+                for pt in up_arc:
+                    geo.append(pt)
+                geo.append(p4)
+                return Polygon(geo)
+            else:
+                p1 = (point_x - self.half_width + self.half_height, point_y - self.half_height)
+                p2 = (point_x + self.half_width - self.half_height, point_y - self.half_height)
+                p3 = (point_x + self.half_width - self.half_height, point_y + self.half_height)
+                p4 = (point_x - self.half_width + self.half_height, point_y + self.half_height)
+
+                left_center = (point_x - self.half_width + self.half_height, point_y)
+                d_start_angle = math.pi / 2
+                d_stop_angle = 1.5 * math.pi
+                left_arc = arc(left_center, self.half_height, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
+
+                right_center = (point_x + self.half_width - self.half_height, point_y)
+                u_start_angle = 1.5 * math.pi
+                u_stop_angle = math.pi / 2
+                right_arc = arc(right_center, self.half_height, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
+
+                geo.append(p1)
+                geo.append(p2)
+                for pt in right_arc:
+                    geo.append(pt)
+                geo.append(p3)
+                geo.append(p4)
+                for pt in left_arc:
+                    geo.append(pt)
+                return Polygon(geo)
+        else:
+            self.draw_app.app.inform.emit(_(
+                "Incompatible aperture type. Select an aperture with type 'C', 'R' or 'O'."))
+            return None
+
+    def make(self):
+        self.draw_app.current_storage = self.storage_obj
+        try:
+            self.geometry = DrawToolShape(self.util_shape(self.points))
+        except Exception as e:
+            log.debug("FCPad.make() --> %s" % str(e))
+
+        self.draw_app.in_action = False
+        self.complete = True
+        self.draw_app.app.inform.emit(_("[success] Done. Adding Pad completed."))
+
+    def clean_up(self):
+        self.draw_app.selected = []
+        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.plot_all()
+
+
+class FCPadArray(FCShapeTool):
+    """
+    Resulting type: MultiPolygon
+    """
+
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'array'
+        self.draw_app = draw_app
+
+        self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['solid_geometry']
+        self.radius = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) / 2
+        self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"]
+
+        # if those cause KeyError exception it means that the aperture type is not 'R'. Only 'R' type has those keys
+        try:
+            self.half_width = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['width']) / 2
+        except KeyError:
+            pass
+        try:
+            self.half_height = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['height']) / 2
+        except KeyError:
+            pass
+
+        self.draw_app.array_frame.show()
+
+        self.selected_size = None
+        self.pad_axis = 'X'
+        self.pad_array = 'linear'
+        self.pad_array_size = None
+        self.pad_pitch = None
+        self.pad_linear_angle = None
+
+        self.pad_angle = None
+        self.pad_direction = None
+        self.pad_radius = None
+
+        self.origin = None
+        self.destination = None
+        self.flag_for_circ_array = None
+
+        self.last_dx = 0
+        self.last_dy = 0
+
+        self.pt = []
+
+        self.draw_app.app.inform.emit(self.start_msg)
+
+        try:
+            self.selected_size = self.draw_app.tool2tooldia[self.draw_app.last_aperture_selected]
+        except KeyError:
+            self.draw_app.app.inform.emit(_("[WARNING_NOTCL] To add an Pad Array first select a tool in Tool Table"))
+            return
+
+        geo = self.utility_geometry(data=(self.draw_app.snap_x, self.draw_app.snap_y), static=True)
+
+        if isinstance(geo, DrawToolShape) and geo.geo is not None:
+            self.draw_app.draw_utility_geometry(geo=geo)
+
+        self.draw_app.app.inform.emit(_("Click on target location ..."))
+
+        # Switch notebook to Selected page
+        self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab)
+
+    def click(self, point):
+
+        if self.pad_array == 'Linear':
+            self.make()
+            return
+        else:
+            if self.flag_for_circ_array is None:
+                self.draw_app.in_action = True
+                self.pt.append(point)
+
+                self.flag_for_circ_array = True
+                self.set_origin(point)
+                self.draw_app.app.inform.emit(_("Click on the Pad Circular Array Start position"))
+            else:
+                self.destination = point
+                self.make()
+                self.flag_for_circ_array = None
+                return
+
+    def set_origin(self, origin):
+        self.origin = origin
+
+    def utility_geometry(self, data=None, static=None):
+        self.pad_axis = self.draw_app.pad_axis_radio.get_value()
+        self.pad_direction = self.draw_app.pad_direction_radio.get_value()
+        self.pad_array = self.draw_app.array_type_combo.get_value()
+        try:
+            self.pad_array_size = int(self.draw_app.pad_array_size_entry.get_value())
+            try:
+                self.pad_pitch = float(self.draw_app.pad_pitch_entry.get_value())
+                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."))
+                return
+        except Exception as e:
+            self.draw_app.app.inform.emit(_("[ERROR_NOTCL] The value is mistyped. Check the value."))
+            return
+
+        if self.pad_array == 'Linear':
+            if data[0] is None and data[1] is None:
+                dx = self.draw_app.x
+                dy = self.draw_app.y
+            else:
+                dx = data[0]
+                dy = data[1]
+
+            geo_list = []
+            geo = None
+            self.points = [dx, dy]
+
+            for item in range(self.pad_array_size):
+                if self.pad_axis == 'X':
+                    geo = self.util_shape(((dx + (self.pad_pitch * item)), dy))
+                if self.pad_axis == 'Y':
+                    geo = self.util_shape((dx, (dy + (self.pad_pitch * item))))
+                if self.pad_axis == 'A':
+                    x_adj = self.pad_pitch * math.cos(math.radians(self.pad_linear_angle))
+                    y_adj = self.pad_pitch * math.sin(math.radians(self.pad_linear_angle))
+                    geo = self.util_shape(
+                        ((dx + (x_adj * item)), (dy + (y_adj * item)))
+                    )
+
+                if static is None or static is False:
+                    geo_list.append(affinity.translate(geo, xoff=(dx - self.last_dx), yoff=(dy - self.last_dy)))
+                else:
+                    geo_list.append(geo)
+            # self.origin = data
+
+            self.last_dx = dx
+            self.last_dy = dy
+            return DrawToolUtilityShape(geo_list)
+        else:
+            if data[0] is None and data[1] is None:
+                cdx = self.draw_app.x
+                cdy = self.draw_app.y
+            else:
+                cdx = data[0]
+                cdy = data[1]
+
+            if len(self.pt) > 0:
+                temp_points = [x for x in self.pt]
+                temp_points.append([cdx, cdy])
+                return DrawToolUtilityShape(LineString(temp_points))
+
+    def util_shape(self, point):
+        # updating values here allows us to change the aperture on the fly, after the Tool has been started
+        self.storage_obj = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['solid_geometry']
+        self.radius = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size']) / 2
+        self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"]
+
+        # if those cause KeyError exception it means that the aperture type is not 'R'. Only 'R' type has those keys
+        try:
+            self.half_width = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['width']) / 2
+        except KeyError:
+            pass
+        try:
+            self.half_height = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['height']) / 2
+        except KeyError:
+            pass
+
+        if point[0] is None and point[1] is None:
+            point_x = self.draw_app.x
+            point_y = self.draw_app.y
+        else:
+            point_x = point[0]
+            point_y = point[1]
+
+        ap_type = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['type']
+        if ap_type == 'C':
+            center = Point([point_x, point_y])
+            return center.buffer(self.radius)
+        elif ap_type == 'R':
+            p1 = (point_x - self.half_width, point_y - self.half_height)
+            p2 = (point_x + self.half_width, point_y - self.half_height)
+            p3 = (point_x + self.half_width, point_y + self.half_height)
+            p4 = (point_x - self.half_width, point_y + self.half_height)
+            return Polygon([p1, p2, p3, p4, p1])
+        elif ap_type == 'O':
+            geo = []
+            if self.half_height > self.half_width:
+                p1 = (point_x - self.half_width, point_y - self.half_height + self.half_width)
+                p2 = (point_x + self.half_width, point_y - self.half_height + self.half_width)
+                p3 = (point_x + self.half_width, point_y + self.half_height - self.half_width)
+                p4 = (point_x - self.half_width, point_y + self.half_height - self.half_width)
+
+                down_center = (point_x, point_y - self.half_height + self.half_width)
+                d_start_angle = math.pi
+                d_stop_angle = 0.0
+                down_arc = arc(down_center, self.half_width, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
+
+                up_center = (point_x, point_y + self.half_height - self.half_width)
+                u_start_angle = 0.0
+                u_stop_angle = math.pi
+                up_arc = arc(up_center, self.half_width, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
+
+                geo.append(p1)
+                for pt in down_arc:
+                    geo.append(pt)
+                geo.append(p2)
+                geo.append(p3)
+                for pt in up_arc:
+                    geo.append(pt)
+                geo.append(p4)
+                return Polygon(geo)
+            else:
+                p1 = (point_x - self.half_width + self.half_height, point_y - self.half_height)
+                p2 = (point_x + self.half_width - self.half_height, point_y - self.half_height)
+                p3 = (point_x + self.half_width - self.half_height, point_y + self.half_height)
+                p4 = (point_x - self.half_width + self.half_height, point_y + self.half_height)
+
+                left_center = (point_x - self.half_width + self.half_height, point_y)
+                d_start_angle = math.pi / 2
+                d_stop_angle = 1.5 * math.pi
+                left_arc = arc(left_center, self.half_height, d_start_angle, d_stop_angle, 'ccw', self.steps_per_circ)
+
+                right_center = (point_x + self.half_width - self.half_height, point_y)
+                u_start_angle = 1.5 * math.pi
+                u_stop_angle = math.pi / 2
+                right_arc = arc(right_center, self.half_height, u_start_angle, u_stop_angle, 'ccw', self.steps_per_circ)
+
+                geo.append(p1)
+                geo.append(p2)
+                for pt in right_arc:
+                    geo.append(pt)
+                geo.append(p3)
+                geo.append(p4)
+                for pt in left_arc:
+                    geo.append(pt)
+                return Polygon(geo)
+        else:
+            self.draw_app.app.inform.emit(_(
+                "Incompatible aperture type. Select an aperture with type 'C', 'R' or 'O'."))
+            return None
+
+    def make(self):
+        self.geometry = []
+        geo = None
+
+        self.draw_app.current_storage = self.storage_obj
+
+        if self.pad_array == 'Linear':
+            for item in range(self.pad_array_size):
+                if self.pad_axis == 'X':
+                    geo = self.util_shape(((self.points[0] + (self.pad_pitch * item)), self.points[1]))
+                if self.pad_axis == 'Y':
+                    geo = self.util_shape((self.points[0], (self.points[1] + (self.pad_pitch * item))))
+                if self.pad_axis == 'A':
+                    x_adj = self.pad_pitch * math.cos(math.radians(self.pad_linear_angle))
+                    y_adj = self.pad_pitch * math.sin(math.radians(self.pad_linear_angle))
+                    geo = self.util_shape(
+                        ((self.points[0] + (x_adj * item)), (self.points[1] + (y_adj * item)))
+                    )
+
+                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."))
+                return
+
+            radius = distance(self.destination, self.origin)
+            initial_angle = math.asin((self.destination[1] - self.origin[1]) / radius)
+            for i in range(self.pad_array_size):
+                angle_radians = math.radians(self.pad_angle * i)
+                if self.pad_direction == 'CW':
+                    x = self.origin[0] + radius * math.cos(-angle_radians + initial_angle)
+                    y = self.origin[1] + radius * math.sin(-angle_radians + initial_angle)
+                else:
+                    x = self.origin[0] + radius * math.cos(angle_radians + initial_angle)
+                    y = self.origin[1] + radius * math.sin(angle_radians + initial_angle)
+
+                geo = self.util_shape((x, y))
+                if self.pad_direction == 'CW':
+                    geo = affinity.rotate(geo, angle=(math.pi - angle_radians), use_radians=True)
+                else:
+                    geo = affinity.rotate(geo, angle=(angle_radians - math.pi), use_radians=True)
+
+                self.geometry.append(DrawToolShape(geo))
+        self.complete = True
+        self.draw_app.app.inform.emit(_("[success] Done. Pad Array added."))
+        self.draw_app.in_action = True
+        self.draw_app.array_frame.hide()
+        return
+
+    def clean_up(self):
+        self.draw_app.selected = []
+        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.plot_all()
+
+
+class FCRegion(FCShapeTool):
+    """
+    Resulting type: Polygon
+    """
+
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'region'
+        self.draw_app = draw_app
+
+        size_ap = float(self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['size'])
+        self.buf_val = (size_ap / 2) if size_ap > 0 else 0.0000001
+
+        self.start_msg = _("Click on 1st point ...")
+
+    def click(self, point):
+        self.draw_app.in_action = True
+        self.points.append(point)
+
+        if len(self.points) > 0:
+            self.draw_app.app.inform.emit(_("Click on next Point or click Right mouse button to complete ..."))
+            return "Click on next point or hit ENTER to complete ..."
+
+        return ""
+
+    def utility_geometry(self, data=None):
+
+        if len(self.points) == 1:
+            temp_points = [x for x in self.points]
+            temp_points.append(data)
+            return DrawToolUtilityShape(LineString(temp_points).buffer(self.buf_val, join_style=1))
+
+        if len(self.points) > 1:
+            temp_points = [x for x in self.points]
+            temp_points.append(data)
+            return DrawToolUtilityShape(LinearRing(temp_points).buffer(self.buf_val, join_style=1))
+        return None
+
+    def make(self):
+        # self.geometry = LinearRing(self.points)
+        self.geometry = DrawToolShape(Polygon(self.points).buffer(self.buf_val, join_style=2))
+        self.draw_app.in_action = False
+        self.complete = True
+        self.draw_app.app.inform.emit(_("[success] Done. Region completed."))
+
+    def clean_up(self):
+        self.draw_app.selected = []
+        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.plot_all()
+
+    def on_key(self, key):
+        if key == 'backspace':
+            if len(self.points) > 0:
+                self.points = self.points[0:-1]
+
+
+class FCTrack(FCRegion):
+    """
+    Resulting type: Polygon
+    """
+
+    def make(self):
+
+        self.geometry = DrawToolShape(LineString(self.points).buffer(self.buf_val))
+        self.name = 'track'
+
+        self.draw_app.in_action = False
+        self.complete = True
+        self.draw_app.app.inform.emit(_("[success] Done. Path completed."))
+
+    def clean_up(self):
+        self.draw_app.selected = []
+        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.plot_all()
+
+    def utility_geometry(self, data=None):
+        if len(self.points) > 0:
+            temp_points = [x for x in self.points]
+            temp_points.append(data)
+
+            return DrawToolUtilityShape(LineString(temp_points).buffer(self.buf_val))
+        return None
+
+    def on_key(self, key):
+        if key == 'backspace':
+            if len(self.points) > 0:
+                self.points = self.points[0:-1]
+
+
+class FCScale(FCShapeTool):
+    def __init__(self, draw_app):
+        FCShapeTool.__init__(self, draw_app)
+        self.name = 'scale'
+
+        # self.shape_buffer = self.draw_app.shape_buffer
+        self.draw_app = draw_app
+        self.app = draw_app.app
+
+        self.start_msg = _("Scale the selected Gerber apertures ...")
+        self.origin = (0, 0)
+
+        if self.draw_app.app.ui.splitter.sizes()[0] == 0:
+            self.draw_app.app.ui.splitter.setSizes([1, 1])
+        self.activate_scale()
+
+    def activate_scale(self):
+        self.draw_app.hide_tool('all')
+        self.draw_app.scale_tool_frame.show()
+
+        try:
+            self.draw_app.scale_button.clicked.disconnect()
+        except TypeError:
+            pass
+        self.draw_app.scale_button.clicked.connect(self.on_scale_click)
+
+    def deactivate_scale(self):
+        self.draw_app.scale_button.clicked.disconnect()
+        self.complete = True
+        self.draw_app.select_tool("select")
+        self.draw_app.hide_tool(self.name)
+
+    def on_scale_click(self):
+        self.draw_app.on_scale()
+        self.deactivate_scale()
+
+
+class FCBuffer(FCShapeTool):
+    def __init__(self, draw_app):
+        FCShapeTool.__init__(self, draw_app)
+        self.name = 'buffer'
+
+        # self.shape_buffer = self.draw_app.shape_buffer
+        self.draw_app = draw_app
+        self.app = draw_app.app
+
+        self.start_msg = _("Buffer the selected apertures ...")
+        self.origin = (0, 0)
+
+        if self.draw_app.app.ui.splitter.sizes()[0] == 0:
+            self.draw_app.app.ui.splitter.setSizes([1, 1])
+        self.activate_buffer()
+
+    def activate_buffer(self):
+        self.draw_app.hide_tool('all')
+        self.draw_app.buffer_tool_frame.show()
+
+        try:
+            self.draw_app.buffer_button.clicked.disconnect()
+        except TypeError:
+            pass
+        self.draw_app.buffer_button.clicked.connect(self.on_buffer_click)
+
+    def deactivate_buffer(self):
+        self.draw_app.buffer_button.clicked.disconnect()
+        self.complete = True
+        self.draw_app.select_tool("select")
+        self.draw_app.hide_tool(self.name)
+
+    def on_buffer_click(self):
+        self.draw_app.on_buffer()
+        self.deactivate_buffer()
+
+
+class FCApertureMove(FCShapeTool):
+    def __init__(self, draw_app):
+        DrawTool.__init__(self, draw_app)
+        self.name = 'move'
+
+        # self.shape_buffer = self.draw_app.shape_buffer
+        self.origin = None
+        self.destination = None
+        self.selected_apertures = []
+
+        if self.draw_app.launched_from_shortcuts is True:
+            self.draw_app.launched_from_shortcuts = False
+            self.draw_app.app.inform.emit(_("Click on target location ..."))
+        else:
+            self.draw_app.app.inform.emit(_("Click on reference location ..."))
+        self.current_storage = None
+        self.geometry = []
+
+        for index in self.draw_app.apertures_table.selectedIndexes():
+            row = index.row()
+            # on column 1 in tool tables we hold the aperture codes, and we retrieve them as strings
+            aperture_on_row = self.draw_app.apertures_table.item(row, 1).text()
+            self.selected_apertures.append(aperture_on_row)
+
+        # Switch notebook to Selected page
+        self.draw_app.app.ui.notebook.setCurrentWidget(self.draw_app.app.ui.selected_tab)
+
+    def set_origin(self, origin):
+        self.origin = origin
+
+    def click(self, point):
+        if len(self.draw_app.get_selected()) == 0:
+            return "Nothing to move."
+
+        if self.origin is None:
+            self.set_origin(point)
+            self.draw_app.app.inform.emit(_("Click on target location ..."))
+            return
+        else:
+            self.destination = point
+            self.make()
+
+            # MS: always return to the Select Tool
+            self.draw_app.select_tool("select")
+            return
+
+    def make(self):
+        # Create new geometry
+        dx = self.destination[0] - self.origin[0]
+        dy = self.destination[1] - self.origin[1]
+        sel_shapes_to_be_deleted = []
+
+        for sel_dia in self.selected_apertures:
+            self.current_storage = self.draw_app.storage_dict[sel_dia]['solid_geometry']
+            for select_shape in self.draw_app.get_selected():
+                if select_shape in self.current_storage:
+
+                    self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy)))
+                    self.current_storage.remove(select_shape)
+                    sel_shapes_to_be_deleted.append(select_shape)
+                    self.draw_app.on_grb_shape_complete(self.current_storage)
+                    self.geometry = []
+
+            for shp in sel_shapes_to_be_deleted:
+                self.draw_app.selected.remove(shp)
+            sel_shapes_to_be_deleted = []
+
+        self.draw_app.build_ui()
+        self.draw_app.app.inform.emit(_("[success] Done. Apertures Move completed."))
+
+    def clean_up(self):
+        self.draw_app.selected = []
+        self.draw_app.apertures_table.clearSelection()
+        self.draw_app.plot_all()
+
+    def utility_geometry(self, data=None):
+        """
+        Temporary geometry on screen while using this tool.
+
+        :param data:
+        :return:
+        """
+        geo_list = []
+
+        if self.origin is None:
+            return None
+
+        if len(self.draw_app.get_selected()) == 0:
+            return None
+
+        dx = data[0] - self.origin[0]
+        dy = data[1] - self.origin[1]
+        for geom in self.draw_app.get_selected():
+            geo_list.append(affinity.translate(geom.geo, xoff=dx, yoff=dy))
+        return DrawToolUtilityShape(geo_list)
+
+
+class FCApertureCopy(FCApertureMove):
+    def __init__(self, draw_app):
+        FCApertureMove.__init__(self, draw_app)
+        self.name = 'copy'
+
+    def make(self):
+        # Create new geometry
+        dx = self.destination[0] - self.origin[0]
+        dy = self.destination[1] - self.origin[1]
+        sel_shapes_to_be_deleted = []
+
+        for sel_dia in self.selected_apertures:
+            self.current_storage = self.draw_app.storage_dict[sel_dia]['solid_geometry']
+            for select_shape in self.draw_app.get_selected():
+                if select_shape in self.current_storage:
+                    self.geometry.append(DrawToolShape(affinity.translate(select_shape.geo, xoff=dx, yoff=dy)))
+
+                    sel_shapes_to_be_deleted.append(select_shape)
+                    self.draw_app.on_grb_shape_complete(self.current_storage)
+                    self.geometry = []
+
+            for shp in sel_shapes_to_be_deleted:
+                self.draw_app.selected.remove(shp)
+            sel_shapes_to_be_deleted = []
+
+        self.draw_app.build_ui()
+        self.draw_app.app.inform.emit(_("[success] Done. Apertures copied."))
+
+
+class FCApertureSelect(DrawTool):
+    def __init__(self, grb_editor_app):
+        DrawTool.__init__(self, grb_editor_app)
+        self.name = 'select'
+        self.origin = None
+
+        self.grb_editor_app = grb_editor_app
+        self.storage = self.grb_editor_app.storage_dict
+        # self.selected = self.grb_editor_app.selected
+
+        # here we store all shapes that were selected
+        self.sel_storage = []
+
+        self.grb_editor_app.apertures_table.clearSelection()
+        self.grb_editor_app.hide_tool('all')
+        self.grb_editor_app.hide_tool('select')
+
+    def set_origin(self, origin):
+        self.origin = origin
+
+    def click(self, point):
+        key_modifier = QtWidgets.QApplication.keyboardModifiers()
+        if self.grb_editor_app.app.defaults["global_mselect_key"] == 'Control':
+            if key_modifier == Qt.ControlModifier:
+                pass
+            else:
+                self.grb_editor_app.selected = []
+        else:
+            if key_modifier == Qt.ShiftModifier:
+                pass
+            else:
+                self.grb_editor_app.selected = []
+
+    def click_release(self, point):
+        self.grb_editor_app.apertures_table.clearSelection()
+        sel_aperture = set()
+        key_modifier = QtWidgets.QApplication.keyboardModifiers()
+
+        for storage in self.grb_editor_app.storage_dict:
+            for shape in self.grb_editor_app.storage_dict[storage]['solid_geometry']:
+                if Point(point).within(shape.geo):
+                    if (self.grb_editor_app.app.defaults["global_mselect_key"] == 'Control' and
+                        key_modifier == Qt.ControlModifier) or \
+                            (self.grb_editor_app.app.defaults["global_mselect_key"] == 'Shift' and
+                             key_modifier == Qt.ShiftModifier):
+
+                        if shape in self.draw_app.selected:
+                            self.draw_app.selected.remove(shape)
+                        else:
+                            # add the object to the selected shapes
+                            self.draw_app.selected.append(shape)
+                            sel_aperture.add(storage)
+                    else:
+                        self.draw_app.selected.append(shape)
+                        sel_aperture.add(storage)
+
+        # select the aperture in the Apertures Table that is associated with the selected shape
+        try:
+            self.draw_app.apertures_table.cellPressed.disconnect()
+        except:
+            pass
+
+        self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+        for aper in sel_aperture:
+            for row in range(self.grb_editor_app.apertures_table.rowCount()):
+                if str(aper) == self.grb_editor_app.apertures_table.item(row, 1).text():
+                    self.grb_editor_app.apertures_table.selectRow(row)
+                    self.draw_app.last_aperture_selected = aper
+        self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+
+        self.draw_app.apertures_table.cellPressed.connect(self.draw_app.on_row_selected)
+
+        return ""
+
+    def clean_up(self):
+        self.draw_app.plot_all()
+
+
+class FCTransform(FCShapeTool):
+    def __init__(self, draw_app):
+        FCShapeTool.__init__(self, draw_app)
+        self.name = 'transformation'
+
+        # self.shape_buffer = self.draw_app.shape_buffer
+        self.draw_app = draw_app
+        self.app = draw_app.app
+
+        self.start_msg = _("Shape transformations ...")
+        self.origin = (0, 0)
+        self.draw_app.transform_tool.run()
+
+
+class FlatCAMGrbEditor(QtCore.QObject):
+
+    draw_shape_idx = -1
+
+    def __init__(self, app):
+        assert isinstance(app, FlatCAMApp.App), \
+            "Expected the app to be a FlatCAMApp.App, got %s" % type(app)
+
+        super(FlatCAMGrbEditor, self).__init__()
+
+        self.app = app
+        self.canvas = self.app.plotcanvas
+
+        ## Current application units in Upper Case
+        self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
+
+        self.grb_edit_widget = QtWidgets.QWidget()
+        layout = QtWidgets.QVBoxLayout()
+        self.grb_edit_widget.setLayout(layout)
+
+        ## Page Title box (spacing between children)
+        self.title_box = QtWidgets.QHBoxLayout()
+        layout.addLayout(self.title_box)
+
+        ## Page Title icon
+        pixmap = QtGui.QPixmap('share/flatcam_icon32.png')
+        self.icon = QtWidgets.QLabel()
+        self.icon.setPixmap(pixmap)
+        self.title_box.addWidget(self.icon, stretch=0)
+
+        ## Title label
+        self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Gerber Editor'))
+        self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+        self.title_box.addWidget(self.title_label, stretch=1)
+
+        ## Object name
+        self.name_box = QtWidgets.QHBoxLayout()
+        layout.addLayout(self.name_box)
+        name_label = QtWidgets.QLabel(_("Name:"))
+        self.name_box.addWidget(name_label)
+        self.name_entry = FCEntry()
+        self.name_box.addWidget(self.name_entry)
+
+        ## Box for custom widgets
+        # This gets populated in offspring implementations.
+        self.custom_box = QtWidgets.QVBoxLayout()
+        layout.addLayout(self.custom_box)
+
+
+        #### Gerber Apertures ####
+        self.apertures_table_label = QtWidgets.QLabel(_('<b>Apertures:</b>'))
+        self.apertures_table_label.setToolTip(
+            _("Apertures Table for the Gerber Object.")
+        )
+        self.custom_box.addWidget(self.apertures_table_label)
+
+        self.apertures_table = FCTable()
+        # delegate = SpinBoxDelegate(units=self.units)
+        # self.apertures_table.setItemDelegateForColumn(1, delegate)
+
+        self.custom_box.addWidget(self.apertures_table)
+
+        self.apertures_table.setColumnCount(5)
+        self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
+        self.apertures_table.setSortingEnabled(False)
+
+        self.apertures_table.horizontalHeaderItem(0).setToolTip(
+            _("Index"))
+        self.apertures_table.horizontalHeaderItem(1).setToolTip(
+            _("Aperture Code"))
+        self.apertures_table.horizontalHeaderItem(2).setToolTip(
+            _("Type of aperture: circular, rectangle, macros etc"))
+        self.apertures_table.horizontalHeaderItem(4).setToolTip(
+            _("Aperture Size:"))
+        self.apertures_table.horizontalHeaderItem(4).setToolTip(
+            _("Aperture Dimensions:\n"
+              " - (width, height) for R, O type.\n"
+              " - (dia, nVertices) for P type"))
+
+        self.empty_label = QtWidgets.QLabel('')
+        self.custom_box.addWidget(self.empty_label)
+
+        # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Apertures widgets
+        # this way I can hide/show the frame
+        self.apertures_frame = QtWidgets.QFrame()
+        self.apertures_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.apertures_frame)
+        self.apertures_box = QtWidgets.QVBoxLayout()
+        self.apertures_box.setContentsMargins(0, 0, 0, 0)
+        self.apertures_frame.setLayout(self.apertures_box)
+
+        #### Add/Delete an new Aperture ####
+
+        grid1 = QtWidgets.QGridLayout()
+        self.apertures_box.addLayout(grid1)
+
+        apcode_lbl = QtWidgets.QLabel(_('Aperture Code:'))
+        apcode_lbl.setToolTip(
+        _("Code for the new aperture")
+        )
+        grid1.addWidget(apcode_lbl, 1, 0)
+
+        self.apcode_entry = FCEntry()
+        self.apcode_entry.setValidator(QtGui.QIntValidator(0, 999))
+        grid1.addWidget(self.apcode_entry, 1, 1)
+
+        apsize_lbl = QtWidgets.QLabel(_('Aperture Size:'))
+        apsize_lbl.setToolTip(
+        _("Size for the new aperture.\n"
+          "If aperture type is 'R' or 'O' then\n"
+          "this value is automatically\n"
+          "calculated as:\n"
+          "sqrt(width**2 + height**2)")
+        )
+        grid1.addWidget(apsize_lbl, 2, 0)
+
+        self.apsize_entry = FCEntry()
+        self.apsize_entry.setValidator(QtGui.QDoubleValidator(0.0001, 99.9999, 4))
+        grid1.addWidget(self.apsize_entry, 2, 1)
+
+        aptype_lbl = QtWidgets.QLabel(_('Aperture Type:'))
+        aptype_lbl.setToolTip(
+        _("Select the type of new aperture. Can be:\n"
+          "C = circular\n"
+          "R = rectangular\n"
+          "O = oblong")
+        )
+        grid1.addWidget(aptype_lbl, 3, 0)
+
+        self.aptype_cb = FCComboBox()
+        self.aptype_cb.addItems(['C', 'R', 'O'])
+        grid1.addWidget(self.aptype_cb, 3, 1)
+
+        self.apdim_lbl = QtWidgets.QLabel(_('Aperture Dim:'))
+        self.apdim_lbl.setToolTip(
+        _("Dimensions for the new aperture.\n"
+          "Active only for rectangular apertures (type R).\n"
+          "The format is (width, height)")
+        )
+        grid1.addWidget(self.apdim_lbl, 4, 0)
+
+        self.apdim_entry = EvalEntry()
+        grid1.addWidget(self.apdim_entry, 4, 1)
+
+        apadd_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Add Aperture:'))
+        apadd_lbl.setToolTip(
+            _("Add an aperture to the aperture list")
+        )
+        grid1.addWidget(apadd_lbl, 5, 0)
+
+        self.addaperture_btn = QtWidgets.QPushButton(_('Go'))
+        self.addaperture_btn.setToolTip(
+           _( "Add a new aperture to the aperture list.")
+        )
+        grid1.addWidget(self.addaperture_btn, 5, 1)
+
+        apdelete_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Del Aperture:'))
+        apdelete_lbl.setToolTip(
+            _( "Delete a aperture in the aperture list.\n"
+               "It will delete also the associated geometry.")
+        )
+        grid1.addWidget(apdelete_lbl, 6, 0)
+
+        self.delaperture_btn = QtWidgets.QPushButton(_('Go'))
+        self.delaperture_btn.setToolTip(
+           _( "Delete a aperture in the aperture list")
+        )
+        grid1.addWidget(self.delaperture_btn, 6, 1)
+
+        ### BUFFER TOOL ###
+
+        self.buffer_tool_frame = QtWidgets.QFrame()
+        self.buffer_tool_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.buffer_tool_frame)
+        self.buffer_tools_box = QtWidgets.QVBoxLayout()
+        self.buffer_tools_box.setContentsMargins(0, 0, 0, 0)
+        self.buffer_tool_frame.setLayout(self.buffer_tools_box)
+        self.buffer_tool_frame.hide()
+
+        # Title
+        buf_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Buffer Aperture:'))
+        buf_title_lbl.setToolTip(
+            _("Buffer a aperture in the aperture list")
+        )
+        self.buffer_tools_box.addWidget(buf_title_lbl)
+
+        # Form Layout
+        buf_form_layout = QtWidgets.QFormLayout()
+        self.buffer_tools_box.addLayout(buf_form_layout)
+
+        # Buffer distance
+        self.buffer_distance_entry = FCEntry()
+        buf_form_layout.addRow(_("Buffer distance:"), self.buffer_distance_entry)
+        self.buffer_corner_lbl = QtWidgets.QLabel(_("Buffer corner:"))
+        self.buffer_corner_lbl.setToolTip(
+            _("There are 3 types of corners:\n"
+              " - 'Round': the corner is rounded.\n"
+              " - 'Square:' the corner is met in a sharp angle.\n"
+              " - 'Beveled:' the corner is a line that directly connects the features meeting in the corner")
+        )
+        self.buffer_corner_cb = FCComboBox()
+        self.buffer_corner_cb.addItem(_("Round"))
+        self.buffer_corner_cb.addItem(_("Square"))
+        self.buffer_corner_cb.addItem(_("Beveled"))
+        buf_form_layout.addRow(self.buffer_corner_lbl, self.buffer_corner_cb)
+
+        # Buttons
+        hlay_buf = QtWidgets.QHBoxLayout()
+        self.buffer_tools_box.addLayout(hlay_buf)
+
+        self.buffer_button = QtWidgets.QPushButton(_("Buffer"))
+        hlay_buf.addWidget(self.buffer_button)
+
+        ### SCALE TOOL ###
+
+        self.scale_tool_frame = QtWidgets.QFrame()
+        self.scale_tool_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.scale_tool_frame)
+        self.scale_tools_box = QtWidgets.QVBoxLayout()
+        self.scale_tools_box.setContentsMargins(0, 0, 0, 0)
+        self.scale_tool_frame.setLayout(self.scale_tools_box)
+        self.scale_tool_frame.hide()
+
+        # Title
+        scale_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _('Scale Aperture:'))
+        scale_title_lbl.setToolTip(
+            _("Scale a aperture in the aperture list")
+        )
+        self.scale_tools_box.addWidget(scale_title_lbl)
+
+        # Form Layout
+        scale_form_layout = QtWidgets.QFormLayout()
+        self.scale_tools_box.addLayout(scale_form_layout)
+
+        self.scale_factor_lbl = QtWidgets.QLabel(_("Scale factor:"))
+        self.scale_factor_lbl.setToolTip(
+            _("The factor by which to scale the selected aperture.\n"
+              "Values can be between 0.0000 and 999.9999")
+        )
+        self.scale_factor_entry = FCEntry()
+        self.scale_factor_entry.setValidator(QtGui.QDoubleValidator(0.0000, 999.9999, 4))
+        scale_form_layout.addRow(self.scale_factor_lbl, self.scale_factor_entry)
+
+        # Buttons
+        hlay_scale = QtWidgets.QHBoxLayout()
+        self.scale_tools_box.addLayout(hlay_scale)
+
+        self.scale_button = QtWidgets.QPushButton(_("Scale"))
+        hlay_scale.addWidget(self.scale_button)
+
+        # add a frame and inside add a vertical box layout. Inside this vbox layout I add
+        # all the add Pad array  widgets
+        # this way I can hide/show the frame
+        self.array_frame = QtWidgets.QFrame()
+        self.array_frame.setContentsMargins(0, 0, 0, 0)
+        self.custom_box.addWidget(self.array_frame)
+        self.array_box = QtWidgets.QVBoxLayout()
+        self.array_box.setContentsMargins(0, 0, 0, 0)
+        self.array_frame.setLayout(self.array_box)
+
+        #### Add Pad Array ####
+        self.emptyarray_label = QtWidgets.QLabel('')
+        self.array_box.addWidget(self.emptyarray_label)
+
+        self.padarray_label = QtWidgets.QLabel('<b>%s</b>' % _("Add Pad Array"))
+        self.padarray_label.setToolTip(
+            _("Add an array of pads (linear or circular array)")
+        )
+        self.array_box.addWidget(self.padarray_label)
+
+        self.array_type_combo = FCComboBox()
+        self.array_type_combo.setToolTip(
+           _( "Select the type of pads array to create.\n"
+            "It can be Linear X(Y) or Circular")
+        )
+        self.array_type_combo.addItem(_("Linear"))
+        self.array_type_combo.addItem(_("Circular"))
+
+        self.array_box.addWidget(self.array_type_combo)
+
+        self.array_form = QtWidgets.QFormLayout()
+        self.array_box.addLayout(self.array_form)
+
+        self.pad_array_size_label = QtWidgets.QLabel(_('Nr of pads:'))
+        self.pad_array_size_label.setToolTip(
+            _("Specify how many pads to be in the array.")
+        )
+        self.pad_array_size_label.setFixedWidth(100)
+
+        self.pad_array_size_entry = LengthEntry()
+        self.array_form.addRow(self.pad_array_size_label, self.pad_array_size_entry)
+
+        self.array_linear_frame = QtWidgets.QFrame()
+        self.array_linear_frame.setContentsMargins(0, 0, 0, 0)
+        self.array_box.addWidget(self.array_linear_frame)
+        self.linear_box = QtWidgets.QVBoxLayout()
+        self.linear_box.setContentsMargins(0, 0, 0, 0)
+        self.array_linear_frame.setLayout(self.linear_box)
+
+        self.linear_form = QtWidgets.QFormLayout()
+        self.linear_box.addLayout(self.linear_form)
+
+        self.pad_axis_label = QtWidgets.QLabel(_('Direction:'))
+        self.pad_axis_label.setToolTip(
+            _("Direction on which the linear array is oriented:\n"
+            "- 'X' - horizontal axis \n"
+            "- 'Y' - vertical axis or \n"
+            "- 'Angle' - a custom angle for the array inclination")
+        )
+        self.pad_axis_label.setFixedWidth(100)
+
+        self.pad_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
+                                          {'label': 'Y', 'value': 'Y'},
+                                          {'label': _('Angle'), 'value': 'A'}])
+        self.pad_axis_radio.set_value('X')
+        self.linear_form.addRow(self.pad_axis_label, self.pad_axis_radio)
+
+        self.pad_pitch_label = QtWidgets.QLabel(_('Pitch:'))
+        self.pad_pitch_label.setToolTip(
+            _("Pitch = Distance between elements of the array.")
+        )
+        self.pad_pitch_label.setFixedWidth(100)
+
+        self.pad_pitch_entry = LengthEntry()
+        self.linear_form.addRow(self.pad_pitch_label, self.pad_pitch_entry)
+
+        self.linear_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.linear_angle_label.setToolTip(
+           _( "Angle at which the linear array is placed.\n"
+            "The precision is of max 2 decimals.\n"
+            "Min value is: -359.99 degrees.\n"
+            "Max value is:  360.00 degrees.")
+        )
+        self.linear_angle_label.setFixedWidth(100)
+
+        self.linear_angle_spinner = FCDoubleSpinner()
+        self.linear_angle_spinner.set_precision(2)
+        self.linear_angle_spinner.setRange(-359.99, 360.00)
+        self.linear_form.addRow(self.linear_angle_label, self.linear_angle_spinner)
+
+        self.array_circular_frame = QtWidgets.QFrame()
+        self.array_circular_frame.setContentsMargins(0, 0, 0, 0)
+        self.array_box.addWidget(self.array_circular_frame)
+        self.circular_box = QtWidgets.QVBoxLayout()
+        self.circular_box.setContentsMargins(0, 0, 0, 0)
+        self.array_circular_frame.setLayout(self.circular_box)
+
+        self.pad_direction_label = QtWidgets.QLabel(_('Direction:'))
+        self.pad_direction_label.setToolTip(
+           _( "Direction for circular array."
+            "Can be CW = clockwise or CCW = counter clockwise.")
+        )
+        self.pad_direction_label.setFixedWidth(100)
+
+        self.circular_form = QtWidgets.QFormLayout()
+        self.circular_box.addLayout(self.circular_form)
+
+        self.pad_direction_radio = RadioSet([{'label': 'CW', 'value': 'CW'},
+                                               {'label': 'CCW.', 'value': 'CCW'}])
+        self.pad_direction_radio.set_value('CW')
+        self.circular_form.addRow(self.pad_direction_label, self.pad_direction_radio)
+
+        self.pad_angle_label = QtWidgets.QLabel(_('Angle:'))
+        self.pad_angle_label.setToolTip(
+            _("Angle at which each element in circular array is placed.")
+        )
+        self.pad_angle_label.setFixedWidth(100)
+
+        self.pad_angle_entry = LengthEntry()
+        self.circular_form.addRow(self.pad_angle_label, self.pad_angle_entry)
+
+        self.array_circular_frame.hide()
+
+        self.linear_angle_spinner.hide()
+        self.linear_angle_label.hide()
+
+        self.array_frame.hide()
+
+        self.custom_box.addStretch()
+
+        ## Toolbar events and properties
+        self.tools_gerber = {
+            "select": {"button": self.app.ui.grb_select_btn,
+                       "constructor": FCApertureSelect},
+            "pad": {"button": self.app.ui.grb_add_pad_btn,
+                              "constructor": FCPad},
+            "array": {"button": self.app.ui.add_pad_ar_btn,
+                    "constructor": FCPadArray},
+            "track": {"button": self.app.ui.grb_add_track_btn,
+                              "constructor": FCTrack},
+            "region": {"button": self.app.ui.grb_add_region_btn,
+                              "constructor": FCRegion},
+            "buffer": {"button": self.app.ui.aperture_buffer_btn,
+                                "constructor": FCBuffer},
+            "scale": {"button": self.app.ui.aperture_scale_btn,
+                                "constructor": FCScale},
+            "copy": {"button": self.app.ui.aperture_copy_btn,
+                     "constructor": FCApertureCopy},
+            "transform": {"button": self.app.ui.grb_transform_btn,
+                          "constructor": FCTransform},
+            "move": {"button": self.app.ui.aperture_move_btn,
+                     "constructor": FCApertureMove},
+        }
+
+        ### Data
+        self.active_tool = None
+
+        self.storage_dict = {}
+        self.current_storage = []
+
+        self.sorted_apid =[]
+
+        self.new_apertures = {}
+        self.new_aperture_macros = {}
+
+        # store here the plot promises, if empty the delayed plot will be activated
+        self.grb_plot_promises = []
+
+        # dictionary to store the tool_row and aperture codes in Tool_table
+        # it will be updated everytime self.build_ui() is called
+        self.olddia_newdia = {}
+
+        self.tool2tooldia = {}
+
+        # this will store the value for the last selected tool, for use after clicking on canvas when the selection
+        # is cleared but as a side effect also the selected tool is cleared
+        self.last_aperture_selected = None
+        self.utility = []
+
+        # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
+        self.launched_from_shortcuts = False
+
+        # this var will store the state of the toolbar before starting the editor
+        self.toolbar_old_state = False
+
+        # Init GUI
+        self.apdim_lbl.hide()
+        self.apdim_entry.hide()
+        self.gerber_obj = None
+        self.gerber_obj_options = {}
+
+        self.buffer_distance_entry.set_value(0.01)
+        self.scale_factor_entry.set_value(1.0)
+
+        # VisPy Visuals
+        self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
+        self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
+        self.app.pool_recreated.connect(self.pool_recreated)
+
+        # Remove from scene
+        self.shapes.enabled = False
+        self.tool_shape.enabled = False
+
+        ## List of selected shapes.
+        self.selected = []
+
+        self.key = None  # Currently pressed key
+        self.modifiers = None
+        self.x = None  # Current mouse cursor pos
+        self.y = None
+        # Current snapped mouse pos
+        self.snap_x = None
+        self.snap_y = None
+        self.pos = None
+
+        # signal that there is an action active like polygon or path
+        self.in_action = False
+        # this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
+        self.launched_from_shortcuts = False
+
+        def make_callback(thetool):
+            def f():
+                self.on_tool_select(thetool)
+            return f
+
+        for tool in self.tools_gerber:
+            self.tools_gerber[tool]["button"].triggered.connect(make_callback(tool))  # Events
+            self.tools_gerber[tool]["button"].setCheckable(True)  # Checkable
+
+        self.options = {
+            "global_gridx": 0.1,
+            "global_gridy": 0.1,
+            "snap_max": 0.05,
+            "grid_snap": True,
+            "corner_snap": False,
+            "grid_gap_link": True
+        }
+        self.app.options_read_form()
+
+        for option in self.options:
+            if option in self.app.options:
+                self.options[option] = self.app.options[option]
+
+        # flag to show if the object was modified
+        self.is_modified = False
+
+        self.edited_obj_name = ""
+
+        self.tool_row = 0
+
+        # store the status of the editor so the Delete at object level will not work until the edit is finished
+        self.editor_active = False
+
+        def entry2option(option, entry):
+            self.options[option] = float(entry.text())
+
+        self.transform_tool = TransformEditorTool(self.app, self)
+
+        # Signals
+        self.buffer_button.clicked.connect(self.on_buffer)
+        self.scale_button.clicked.connect(self.on_scale)
+
+        self.app.ui.aperture_delete_btn.triggered.connect(self.on_delete_btn)
+        self.name_entry.returnPressed.connect(self.on_name_activate)
+
+        self.aptype_cb.currentIndexChanged[str].connect(self.on_aptype_changed)
+
+        self.addaperture_btn.clicked.connect(self.on_aperture_add)
+        self.delaperture_btn.clicked.connect(self.on_aperture_delete)
+        self.apertures_table.cellPressed.connect(self.on_row_selected)
+
+        self.app.ui.grb_add_pad_menuitem.triggered.connect(self.on_pad_add)
+        self.app.ui.grb_add_pad_array_menuitem.triggered.connect(self.on_pad_add_array)
+
+        self.app.ui.grb_add_track_menuitem.triggered.connect(self.on_track_add)
+        self.app.ui.grb_add_region_menuitem.triggered.connect(self.on_region_add)
+
+        self.app.ui.grb_add_buffer_menuitem.triggered.connect(self.on_buffer)
+        self.app.ui.grb_add_scale_menuitem.triggered.connect(self.on_scale)
+        self.app.ui.grb_transform_menuitem.triggered.connect(self.transform_tool.run)
+
+        self.app.ui.grb_copy_menuitem.triggered.connect(self.on_copy_button)
+        self.app.ui.grb_delete_menuitem.triggered.connect(self.on_delete_btn)
+
+        self.app.ui.grb_move_menuitem.triggered.connect(self.on_move_button)
+
+        self.array_type_combo.currentIndexChanged.connect(self.on_array_type_combo)
+        self.pad_axis_radio.activated_custom.connect(self.on_linear_angle_radio)
+
+        # store the status of the editor so the Delete at object level will not work until the edit is finished
+        self.editor_active = False
+
+    def pool_recreated(self, pool):
+        self.shapes.pool = pool
+        self.tool_shape.pool = pool
+
+    def set_ui(self):
+        # updated units
+        self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
+
+        self.olddia_newdia.clear()
+        self.tool2tooldia.clear()
+
+        # update the olddia_newdia dict to make sure we have an updated state of the tool_table
+        for key in self.storage_dict:
+            self.olddia_newdia[key] = key
+
+        sort_temp = []
+        for aperture in self.olddia_newdia:
+            sort_temp.append(int(aperture))
+        self.sorted_apid = sorted(sort_temp)
+
+        # populate self.intial_table_rows dict with the tool number as keys and aperture codes as values
+        for i in range(len(self.sorted_apid)):
+            tt_aperture = self.sorted_apid[i]
+            self.tool2tooldia[i + 1] = tt_aperture
+
+        if self.units == "IN":
+            self.apsize_entry.set_value(0.039)
+        else:
+            self.apsize_entry.set_value(1.00)
+
+        # Init GUI
+        self.pad_array_size_entry.set_value(5)
+        self.pad_pitch_entry.set_value(2.54)
+        self.pad_angle_entry.set_value(12)
+        self.pad_direction_radio.set_value('CW')
+        self.pad_axis_radio.set_value('X')
+
+    def build_ui(self):
+
+        try:
+            # if connected, disconnect the signal from the slot on item_changed as it creates issues
+            self.apertures_table.itemChanged.disconnect()
+        except:
+            pass
+
+        try:
+            self.apertures_table.cellPressed.disconnect()
+        except:
+            pass
+
+        # updated units
+        self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper()
+
+        # make a new name for the new Excellon object (the one with edited content)
+        self.edited_obj_name = self.gerber_obj.options['name']
+        self.name_entry.set_value(self.edited_obj_name)
+
+        self.apertures_row = 0
+        aper_no = self.apertures_row + 1
+
+        sort = []
+        for k, v in list(self.storage_dict.items()):
+            sort.append(int(k))
+
+        sorted_apertures = sorted(sort)
+
+        sort = []
+        for k, v in list(self.gerber_obj.aperture_macros.items()):
+            sort.append(k)
+        sorted_macros = sorted(sort)
+
+        n = len(sorted_apertures) + len(sorted_macros)
+        self.apertures_table.setRowCount(n)
+
+        for ap_code in sorted_apertures:
+            ap_code = str(ap_code)
+
+            ap_id_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
+            ap_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            self.apertures_table.setItem(self.apertures_row, 0, ap_id_item)  # Tool name/id
+
+            ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
+            ap_code_item.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            ap_type_item = QtWidgets.QTableWidgetItem(str(self.storage_dict[ap_code]['type']))
+            ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            if str(self.storage_dict[ap_code]['type']) == 'R' or str(self.storage_dict[ap_code]['type']) == 'O':
+                ap_dim_item = QtWidgets.QTableWidgetItem(
+                    '%.4f, %.4f' % (self.storage_dict[ap_code]['width'] * self.gerber_obj.file_units_factor,
+                                    self.storage_dict[ap_code]['height'] * self.gerber_obj.file_units_factor
+                                    )
+                )
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled)
+            elif str(self.storage_dict[ap_code]['type']) == 'P':
+                ap_dim_item = QtWidgets.QTableWidgetItem(
+                    '%.4f, %.4f' % (self.storage_dict[ap_code]['diam'] * self.gerber_obj.file_units_factor,
+                                    self.storage_dict[ap_code]['nVertices'] * self.gerber_obj.file_units_factor)
+                )
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled)
+            else:
+                ap_dim_item = QtWidgets.QTableWidgetItem('')
+                ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            try:
+                if self.storage_dict[ap_code]['size'] is not None:
+                    ap_size_item = QtWidgets.QTableWidgetItem('%.4f' %
+                                                              float(self.storage_dict[ap_code]['size'] *
+                                                                    self.gerber_obj.file_units_factor))
+                else:
+                    ap_size_item = QtWidgets.QTableWidgetItem('')
+            except KeyError:
+                ap_size_item = QtWidgets.QTableWidgetItem('')
+            ap_size_item.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            self.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
+            self.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
+            self.apertures_table.setItem(self.apertures_row, 3, ap_size_item)  # Aperture Dimensions
+            self.apertures_table.setItem(self.apertures_row, 4, ap_dim_item)  # Aperture Dimensions
+
+            self.apertures_row += 1
+
+        for ap_code in sorted_macros:
+            ap_code = str(ap_code)
+
+            ap_id_item = QtWidgets.QTableWidgetItem('%d' % int(self.apertures_row + 1))
+            ap_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+            self.apertures_table.setItem(self.apertures_row, 0, ap_id_item)  # Tool name/id
+
+            ap_code_item = QtWidgets.QTableWidgetItem(ap_code)
+
+            ap_type_item = QtWidgets.QTableWidgetItem('AM')
+            ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
+
+            self.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
+            self.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
+
+            self.apertures_row += 1
+
+        self.apertures_table.selectColumn(0)
+        self.apertures_table.resizeColumnsToContents()
+        self.apertures_table.resizeRowsToContents()
+
+        vertical_header = self.apertures_table.verticalHeader()
+        # vertical_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
+        vertical_header.hide()
+        self.apertures_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+
+        horizontal_header = self.apertures_table.horizontalHeader()
+        horizontal_header.setMinimumSectionSize(10)
+        horizontal_header.setDefaultSectionSize(70)
+        horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
+        horizontal_header.resizeSection(0, 20)
+        horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.Stretch)
+
+        self.apertures_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+        self.apertures_table.setSortingEnabled(False)
+        self.apertures_table.setMinimumHeight(self.apertures_table.getHeight())
+        self.apertures_table.setMaximumHeight(self.apertures_table.getHeight())
+
+        # make sure no rows are selected so the user have to click the correct row, meaning selecting the correct tool
+        self.apertures_table.clearSelection()
+
+        # Remove anything else in the GUI Selected Tab
+        self.app.ui.selected_scroll_area.takeWidget()
+        # Put ourself in the GUI Selected Tab
+        self.app.ui.selected_scroll_area.setWidget(self.grb_edit_widget)
+        # Switch notebook to Selected page
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
+
+        # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
+        self.apertures_table.itemChanged.connect(self.on_tool_edit)
+        self.apertures_table.cellPressed.connect(self.on_row_selected)
+
+        # for convenience set the next aperture code in the apcode field
+        try:
+            self.apcode_entry.set_value(max(self.tool2tooldia.values()) + 1)
+        except ValueError:
+            # this means that the edited object has no apertures so we start with 10 (Gerber specifications)
+            self.apcode_entry.set_value(10)
+
+    def on_aperture_add(self, apid=None):
+        self.is_modified = True
+        if apid:
+            ap_id = apid
+        else:
+            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."))
+                return
+            if ap_id == '':
+                self.app.inform.emit(_("[WARNING_NOTCL] Aperture code value is missing or wrong format. "
+                                       "Add it and retry."))
+                return
+
+        if ap_id == '0':
+            if ap_id not in self.olddia_newdia:
+                self.storage_dict[ap_id] = {}
+                self.storage_dict[ap_id]['type'] = 'REG'
+                size_val = 0
+                self.apsize_entry.set_value(size_val)
+                self.storage_dict[ap_id]['size'] = size_val
+
+                self.storage_dict[ap_id]['solid_geometry'] = []
+                self.storage_dict[ap_id]['follow_geometry'] = []
+
+                # self.olddia_newdia dict keeps the evidence on current aperture codes as keys and gets updated on values
+                # each time a aperture code is edited or added
+                self.olddia_newdia[ap_id] = ap_id
+        else:
+            if ap_id not in self.olddia_newdia:
+                self.storage_dict[ap_id] = {}
+
+                type_val = self.aptype_cb.currentText()
+                self.storage_dict[ap_id]['type'] = type_val
+
+                if type_val == 'R' or type_val == 'O':
+                    try:
+                        dims = self.apdim_entry.get_value()
+                        self.storage_dict[ap_id]['width'] = dims[0]
+                        self.storage_dict[ap_id]['height'] = dims[1]
+
+                        size_val = math.sqrt((dims[0] ** 2) + (dims[1] ** 2))
+                        self.apsize_entry.set_value(size_val)
+
+                    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. "
+                                               "Add it in format (width, height) and retry."))
+                        return
+                else:
+                    try:
+                        size_val = float(self.apsize_entry.get_value())
+                    except ValueError:
+                        # try to convert comma to decimal point. if it's still not working error message and return
+                        try:
+                            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."))
+                            return
+                self.storage_dict[ap_id]['size'] = size_val
+
+                self.storage_dict[ap_id]['solid_geometry'] = []
+                self.storage_dict[ap_id]['follow_geometry'] = []
+
+                # self.olddia_newdia dict keeps the evidence on current aperture codes as keys and gets updated on 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."))
+                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.build_ui()
+
+        # make a quick sort through the tool2tooldia dict so we find which row to select
+        row_to_be_selected = None
+        for key in sorted(self.tool2tooldia):
+            if self.tool2tooldia[key] == int(ap_id):
+                row_to_be_selected = int(key) - 1
+                break
+        self.apertures_table.selectRow(row_to_be_selected)
+
+    def on_aperture_delete(self, apid=None):
+        self.is_modified = True
+        deleted_apcode_list = []
+        deleted_tool_offset_list = []
+
+        try:
+            if apid is None or apid is False:
+                # deleted_tool_dia = float(self.apertures_table.item(self.apertures_table.currentRow(), 1).text())
+                for index in self.apertures_table.selectionModel().selectedRows():
+                    row = index.row()
+                    deleted_apcode_list.append(self.apertures_table.item(row, 1).text())
+            else:
+                if isinstance(apid, list):
+                    for dd in apid:
+                        deleted_apcode_list.append(dd)
+                else:
+                    deleted_apcode_list.append(apid)
+        except:
+            self.app.inform.emit(_("[WARNING_NOTCL] Select a tool in Tool Table"))
+            return
+
+        for deleted_aperture in deleted_apcode_list:
+            # delete the storage used for that tool
+            self.storage_dict.pop(deleted_aperture, None)
+
+            # I've added this flag_del variable because dictionary don't like
+            # having keys deleted while iterating through them
+            flag_del = []
+            for deleted_tool in self.tool2tooldia:
+                if self.tool2tooldia[deleted_tool] == deleted_aperture:
+                    flag_del.append(deleted_tool)
+
+            if flag_del:
+                for aperture_to_be_deleted in flag_del:
+                    # delete the tool
+                    self.tool2tooldia.pop(aperture_to_be_deleted, None)
+                flag_del = []
+
+            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.plot_all()
+        self.build_ui()
+
+    def on_tool_edit(self, item_changed):
+
+        # if connected, disconnect the signal from the slot on item_changed as it creates issues
+        self.apertures_table.itemChanged.disconnect()
+        # self.apertures_table.cellPressed.disconnect()
+
+        self.is_modified = True
+        geometry = []
+        current_table_dia_edited = None
+
+        if self.apertures_table.currentItem() is not None:
+            try:
+                current_table_dia_edited = float(self.apertures_table.currentItem().text())
+            except ValueError as e:
+                log.debug("FlatCAMExcEditor.on_tool_edit() --> %s" % str(e))
+                self.apertures_table.setCurrentItem(None)
+                return
+
+        row_of_item_changed = self.apertures_table.currentRow()
+
+        # rows start with 0, tools start with 1 so we adjust the value by 1
+        key_in_tool2tooldia = row_of_item_changed + 1
+
+        dia_changed = self.tool2tooldia[key_in_tool2tooldia]
+
+        # aperture code is not used so we create a new tool with the desired diameter
+        if current_table_dia_edited not in self.olddia_newdia.values():
+            # update the dict that holds as keys our initial diameters and as values the edited diameters
+            self.olddia_newdia[dia_changed] = current_table_dia_edited
+            # update the dict that holds tool_no as key and tool_dia as value
+            self.tool2tooldia[key_in_tool2tooldia] = current_table_dia_edited
+
+            # update the tool offset
+            modified_offset = self.gerber_obj.tool_offset.pop(dia_changed)
+            self.gerber_obj.tool_offset[current_table_dia_edited] = modified_offset
+
+            self.plot_all()
+        else:
+            # aperture code is already in use so we move the pads from the prior tool to the new tool
+            factor = current_table_dia_edited / dia_changed
+            for shape in self.storage_dict[dia_changed].get_objects():
+                geometry.append(DrawToolShape(
+                    MultiLineString([affinity.scale(subgeo, xfact=factor, yfact=factor) for subgeo in shape.geo])))
+
+                self.points_edit[current_table_dia_edited].append((0, 0))
+            self.add_gerber_shape(geometry, self.storage_dict[current_table_dia_edited])
+
+            self.on_aperture_delete(apid=dia_changed)
+
+            # delete the tool offset
+            self.gerber_obj.tool_offset.pop(dia_changed, None)
+
+        # we reactivate the signals after the after the tool editing
+        self.apertures_table.itemChanged.connect(self.on_tool_edit)
+        # self.apertures_table.cellPressed.connect(self.on_row_selected)
+
+    def on_name_activate(self):
+        self.edited_obj_name = self.name_entry.get_value()
+
+    def on_aptype_changed(self, current_text):
+        if current_text == 'R' or current_text == 'O':
+            self.apdim_lbl.show()
+            self.apdim_entry.show()
+            self.apsize_entry.setReadOnly(True)
+        else:
+            self.apdim_lbl.hide()
+            self.apdim_entry.hide()
+            self.apsize_entry.setReadOnly(False)
+
+    def activate_grb_editor(self):
+        self.connect_canvas_event_handlers()
+
+        # init working objects
+        self.storage_dict = {}
+        self.current_storage = []
+        self.sorted_apid = []
+        self.new_apertures = {}
+        self.new_aperture_macros = {}
+        self.grb_plot_promises = []
+        self.olddia_newdia = {}
+        self.tool2tooldia = {}
+
+        self.shapes.enabled = True
+        self.tool_shape.enabled = True
+
+        self.app.ui.snap_max_dist_entry.setEnabled(True)
+        self.app.ui.corner_snap_btn.setEnabled(True)
+        self.app.ui.snap_magnet.setVisible(True)
+        self.app.ui.corner_snap_btn.setVisible(True)
+
+        self.app.ui.grb_editor_menu.setDisabled(False)
+        self.app.ui.grb_editor_menu.menuAction().setVisible(True)
+
+        self.app.ui.update_obj_btn.setEnabled(True)
+        self.app.ui.grb_editor_cmenu.setEnabled(True)
+
+        self.app.ui.grb_edit_toolbar.setDisabled(False)
+        self.app.ui.grb_edit_toolbar.setVisible(True)
+        # self.app.ui.snap_toolbar.setDisabled(False)
+
+        # start with GRID toolbar activated
+        if self.app.ui.grid_snap_btn.isChecked() is False:
+            self.app.ui.grid_snap_btn.trigger()
+
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(False)
+        self.app.ui.popmenu_save.setVisible(True)
+
+        # Tell the App that the editor is active
+        self.editor_active = True
+
+    def deactivate_grb_editor(self):
+        self.disconnect_canvas_event_handlers()
+        self.clear()
+        self.app.ui.grb_edit_toolbar.setDisabled(True)
+
+        settings = QSettings("Open Source", "FlatCAM")
+        if settings.contains("layout"):
+            layout = settings.value('layout', type=str)
+            if layout == 'standard':
+                # self.app.ui.exc_edit_toolbar.setVisible(False)
+
+                self.app.ui.snap_max_dist_entry.setEnabled(False)
+                self.app.ui.corner_snap_btn.setEnabled(False)
+                self.app.ui.snap_magnet.setVisible(False)
+                self.app.ui.corner_snap_btn.setVisible(False)
+            elif layout == 'compact':
+                # self.app.ui.exc_edit_toolbar.setVisible(True)
+
+                self.app.ui.snap_max_dist_entry.setEnabled(False)
+                self.app.ui.corner_snap_btn.setEnabled(False)
+                self.app.ui.snap_magnet.setVisible(True)
+                self.app.ui.corner_snap_btn.setVisible(True)
+        else:
+            # self.app.ui.exc_edit_toolbar.setVisible(False)
+
+            self.app.ui.snap_max_dist_entry.setEnabled(False)
+            self.app.ui.corner_snap_btn.setEnabled(False)
+            self.app.ui.snap_magnet.setVisible(False)
+            self.app.ui.corner_snap_btn.setVisible(False)
+
+        # set the Editor Toolbar visibility to what was before entering in the Editor
+        self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
+            else self.app.ui.grb_edit_toolbar.setVisible(True)
+
+        # Disable visuals
+        self.shapes.enabled = False
+        self.tool_shape.enabled = False
+        # self.app.app_cursor.enabled = False
+
+        # Tell the app that the editor is no longer active
+        self.editor_active = False
+
+        self.app.ui.grb_editor_menu.setDisabled(True)
+        self.app.ui.grb_editor_menu.menuAction().setVisible(False)
+
+        self.app.ui.update_obj_btn.setEnabled(False)
+
+        self.app.ui.g_editor_cmenu.setEnabled(False)
+        self.app.ui.grb_editor_cmenu.setEnabled(False)
+        self.app.ui.e_editor_cmenu.setEnabled(False)
+
+        # adjust the visibility of some of the canvas context menu
+        self.app.ui.popmenu_edit.setVisible(True)
+        self.app.ui.popmenu_save.setVisible(False)
+
+        # Show original geometry
+        if self.gerber_obj:
+            self.gerber_obj.visible = True
+
+    def connect_canvas_event_handlers(self):
+        ## Canvas events
+
+        # make sure that the shortcuts key and mouse events will no longer be linked to the methods from FlatCAMApp
+        # but those from FlatCAMGeoEditor
+
+        self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.vis_disconnect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.collection.view.clicked.disconnect()
+
+        self.canvas.vis_connect('mouse_press', self.on_canvas_click)
+        self.canvas.vis_connect('mouse_move', self.on_canvas_move)
+        self.canvas.vis_connect('mouse_release', self.on_canvas_click_release)
+
+    def disconnect_canvas_event_handlers(self):
+        self.canvas.vis_disconnect('mouse_press', self.on_canvas_click)
+        self.canvas.vis_disconnect('mouse_move', self.on_canvas_move)
+        self.canvas.vis_disconnect('mouse_release', self.on_canvas_click_release)
+
+        # we restore the key and mouse control to FlatCAMApp method
+        self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
+        self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
+        self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        self.app.plotcanvas.vis_connect('mouse_double_click', self.app.on_double_click_over_plot)
+        self.app.collection.view.clicked.connect(self.app.collection.on_mouse_down)
+
+    def clear(self):
+        self.active_tool = None
+        # self.shape_buffer = []
+        self.selected = []
+
+        self.shapes.clear(update=True)
+        self.tool_shape.clear(update=True)
+
+    def edit_fcgerber(self, orig_grb_obj):
+        """
+        Imports the geometry found in self.apertures from the given FlatCAM Gerber object
+        into the editor.
+
+        :param fcgeometry: FlatCAMExcellon
+        :return: None
+        """
+
+        self.deactivate_grb_editor()
+        self.activate_grb_editor()
+
+        # create a reference to the source object
+        self.gerber_obj = orig_grb_obj
+        self.gerber_obj_options = orig_grb_obj.options
+
+        # Hide original geometry
+        orig_grb_obj.visible = False
+
+        # Set selection tolerance
+        # DrawToolShape.tolerance = fc_excellon.drawing_tolerance * 10
+
+        self.select_tool("select")
+
+        # we activate this after the initial build as we don't need to see the tool been populated
+        self.apertures_table.itemChanged.connect(self.on_tool_edit)
+
+        # and then add it to the storage elements (each storage elements is a member of a list
+
+        def job_thread(self, apid):
+            with self.app.proc_container.new(_("Adding aperture: %s geo ...") % str(apid)):
+                solid_storage_elem = []
+                follow_storage_elem = []
+
+                self.storage_dict[apid] = {}
+
+                # add the Gerber geometry to editor storage
+                for k, v in self.gerber_obj.apertures[apid].items():
+                    try:
+                        if k == 'solid_geometry':
+                            for geo in v:
+                                if geo:
+                                    self.add_gerber_shape(DrawToolShape(geo), solid_storage_elem)
+                            self.storage_dict[apid][k] = solid_storage_elem
+                        elif k == 'follow_geometry':
+                            for geo in v:
+                                if geo is not None:
+                                    self.add_gerber_shape(DrawToolShape(geo), follow_storage_elem)
+                            self.storage_dict[apid][k] = follow_storage_elem
+                        else:
+                            self.storage_dict[apid][k] = v
+                    except Exception as e:
+                        log.debug("FlatCAMGrbEditor.edit_fcgerber().job_thread() --> %s" % str(e))
+                # Check promises and clear if exists
+                while True:
+                    try:
+                        self.grb_plot_promises.remove(apid)
+                        time.sleep(0.5)
+                    except ValueError:
+                        break
+
+        for apid in self.gerber_obj.apertures:
+            self.grb_plot_promises.append(apid)
+            self.app.worker_task.emit({'fcn': job_thread, 'params': [self, apid]})
+
+        self.start_delayed_plot(check_period=1000)
+
+    def update_fcgerber(self, grb_obj):
+        """
+        Create a new Gerber object that contain the edited content of the source Gerber object
+
+        :param grb_obj: FlatCAMGerber
+        :return: None
+        """
+
+        new_grb_name = self.edited_obj_name
+
+        # if the 'delayed plot' malfunctioned stop the QTimer
+        try:
+            self.plot_thread.stop()
+        except:
+            pass
+
+        if "_edit" in self.edited_obj_name:
+            try:
+                id = int(self.edited_obj_name[-1]) + 1
+                new_grb_name= self.edited_obj_name[:-1] + str(id)
+            except ValueError:
+                new_grb_name += "_1"
+        else:
+            new_grb_name = self.edited_obj_name + "_edit"
+
+        self.app.worker_task.emit({'fcn': self.new_edited_gerber,
+                                   'params': [new_grb_name]})
+
+        # reset the tool table
+        self.apertures_table.clear()
+
+        self.apertures_table.setHorizontalHeaderLabels(['#', _('Code'), _('Type'), _('Size'), _('Dim')])
+        self.last_aperture_selected = None
+
+        # restore GUI to the Selected TAB
+        # Remove anything else in the GUI
+        self.app.ui.selected_scroll_area.takeWidget()
+        # Switch notebook to Selected page
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
+
+    def update_options(self, obj):
+        try:
+            if not obj.options:
+                obj.options = {}
+                obj.options['xmin'] = 0
+                obj.options['ymin'] = 0
+                obj.options['xmax'] = 0
+                obj.options['ymax'] = 0
+                return True
+            else:
+                return False
+        except AttributeError:
+            obj.options = {}
+            return True
+
+    def new_edited_gerber(self, outname):
+        """
+        Creates a new Gerber object for the edited Gerber. Thread-safe.
+
+        :param outname: Name of the resulting object. None causes the name to be that of the file.
+        :type outname: str
+        :return: None
+        """
+
+        self.app.log.debug("Update the Gerber object with edited content. Source is: %s" %
+                           self.gerber_obj.options['name'].upper())
+
+        out_name = outname
+        local_storage_dict = deepcopy(self.storage_dict)
+
+        # How the object should be initialized
+        def obj_init(grb_obj, app_obj):
+
+            poly_buffer = []
+            follow_buffer = []
+
+            for storage_apid, storage_val in local_storage_dict.items():
+                grb_obj.apertures[storage_apid] = {}
+
+                for k, v in storage_val.items():
+                    if k == 'solid_geometry':
+                        grb_obj.apertures[storage_apid][k] = []
+                        for geo in v:
+                            new_geo = deepcopy(geo.geo)
+                            grb_obj.apertures[storage_apid][k].append(new_geo)
+                            poly_buffer.append(new_geo)
+
+                    elif k == 'follow_geometry':
+                        grb_obj.apertures[storage_apid][k] = []
+                        for geo in v:
+                            new_geo = deepcopy(geo.geo)
+                            grb_obj.apertures[storage_apid][k].append(new_geo)
+                            follow_buffer.append(new_geo)
+                    else:
+                        grb_obj.apertures[storage_apid][k] = deepcopy(v)
+
+            grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros)
+
+            new_poly = MultiPolygon(poly_buffer)
+            new_poly = new_poly.buffer(0.00000001)
+            new_poly = new_poly.buffer(-0.00000001)
+            grb_obj.solid_geometry = new_poly
+
+            grb_obj.follow_geometry = deepcopy(follow_buffer)
+
+            for k, v in self.gerber_obj_options.items():
+                if k == 'name':
+                    grb_obj.options[k] = out_name
+                else:
+                    grb_obj.options[k] = deepcopy(v)
+
+            grb_obj.source_file = []
+            grb_obj.multigeo = False
+            grb_obj.follow = False
+
+            try:
+                grb_obj.create_geometry()
+            except KeyError:
+                self.app.inform.emit(
+                   _( "[ERROR_NOTCL] There are no Aperture definitions in the file. Aborting Gerber creation.")
+                )
+            except:
+                msg = _("[ERROR] An internal error has ocurred. See shell.\n")
+                msg += traceback.format_exc()
+                app_obj.inform.emit(msg)
+                raise
+                # raise
+
+        with self.app.proc_container.new(_("Creating Gerber.")):
+            try:
+                self.app.new_object("gerber", outname, obj_init)
+            except Exception as e:
+                log.error("Error on object creation: %s" % str(e))
+                self.app.progress.emit(100)
+                return
+
+            self.app.inform.emit(_("[success] Gerber editing finished."))
+            # self.progress.emit(100)
+
+    def on_tool_select(self, tool):
+        """
+        Behavior of the toolbar. Tool initialization.
+
+        :rtype : None
+        """
+        current_tool = tool
+
+        self.app.log.debug("on_tool_select('%s')" % tool)
+
+        if self.last_aperture_selected is None and current_tool is not 'select':
+            # self.draw_app.select_tool('select')
+            self.complete = True
+            current_tool = 'select'
+            self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. No aperture is selected"))
+
+        # This is to make the group behave as radio group
+        if current_tool in self.tools_gerber:
+            if self.tools_gerber[current_tool]["button"].isChecked():
+                self.app.log.debug("%s is checked." % current_tool)
+                for t in self.tools_gerber:
+                    if t != current_tool:
+                        self.tools_gerber[t]["button"].setChecked(False)
+
+                # this is where the Editor toolbar classes (button's) are instantiated
+                self.active_tool = self.tools_gerber[current_tool]["constructor"](self)
+                # self.app.inform.emit(self.active_tool.start_msg)
+            else:
+                self.app.log.debug("%s is NOT checked." % current_tool)
+                for t in self.tools_gerber:
+                    self.tools_gerber[t]["button"].setChecked(False)
+
+                self.select_tool('select')
+                self.active_tool = FCApertureSelect(self)
+
+    def on_row_selected(self, row, col):
+        if col == 0:
+            key_modifier = QtWidgets.QApplication.keyboardModifiers()
+            if self.app.defaults["global_mselect_key"] == 'Control':
+                modifier_to_use = Qt.ControlModifier
+            else:
+                modifier_to_use = Qt.ShiftModifier
+
+            if key_modifier == modifier_to_use:
+                pass
+            else:
+                self.selected = []
+
+            try:
+                # selected_apid = str(self.tool2tooldia[row + 1])
+                selected_apid = self.apertures_table.item(row, 1).text()
+                self.last_aperture_selected = selected_apid
+
+                for obj in self.storage_dict[selected_apid]['solid_geometry']:
+                    self.selected.append(obj)
+            except Exception as e:
+                self.app.log.debug(str(e))
+
+            self.plot_all()
+
+    def toolbar_tool_toggle(self, key):
+        self.options[key] = self.sender().isChecked()
+        return self.options[key]
+
+    def on_grb_shape_complete(self, storage=None):
+        self.app.log.debug("on_shape_complete()")
+
+        if storage is not None:
+            # Add shape
+            self.add_gerber_shape(self.active_tool.geometry, storage)
+        else:
+            stora = self.storage_dict[self.last_aperture_selected]['solid_geometry']
+            self.add_gerber_shape(self.active_tool.geometry, storage=stora)
+
+        # Remove any utility shapes
+        self.delete_utility_geometry()
+        self.tool_shape.clear(update=True)
+
+        # Replot and reset tool.
+        self.plot_all()
+
+    def add_gerber_shape(self, shape, storage):
+        """
+        Adds a shape to the shape storage.
+
+        :param shape: Shape to be added.
+        :type shape: DrawToolShape
+        :return: None
+        """
+        # List of DrawToolShape?
+        if isinstance(shape, list):
+            for subshape in shape:
+                self.add_gerber_shape(subshape, storage)
+            return
+
+        assert isinstance(shape, DrawToolShape), \
+            "Expected a DrawToolShape, got %s" % str(type(shape))
+
+        assert shape.geo is not None, \
+            "Shape object has empty geometry (None)"
+
+        assert (isinstance(shape.geo, list) and len(shape.geo) > 0) or \
+               not isinstance(shape.geo, list), \
+            "Shape objects has empty geometry ([])"
+
+        if isinstance(shape, DrawToolUtilityShape):
+            self.utility.append(shape)
+        else:
+            storage.append(shape)  # TODO: Check performance
+
+    def on_canvas_click(self, event):
+        """
+        event.x and .y have canvas coordinates
+        event.xdaya and .ydata have plot coordinates
+
+        :param event: Event object dispatched by Matplotlib
+        :return: None
+        """
+
+        if event.button is 1:
+            self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
+            self.pos = self.canvas.vispy_canvas.translate_coords(event.pos)
+
+            ### Snap coordinates
+            x, y = self.app.geo_editor.snap(self.pos[0], self.pos[1])
+
+            self.pos = (x, y)
+
+            # Selection with left mouse button
+            if self.active_tool is not None and event.button is 1:
+                # Dispatch event to active_tool
+                # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
+                msg = self.active_tool.click(self.app.geo_editor.snap(self.pos[0], self.pos[1]))
+
+                # If it is a shape generating tool
+                if isinstance(self.active_tool, FCShapeTool) and self.active_tool.complete:
+                    if self.current_storage is not None:
+                        self.on_grb_shape_complete(self.current_storage)
+                        self.build_ui()
+                    # MS: always return to the Select Tool if modifier key is not pressed
+                    # else return to the current tool
+                    key_modifier = QtWidgets.QApplication.keyboardModifiers()
+                    if self.app.defaults["global_mselect_key"] == 'Control':
+                        modifier_to_use = Qt.ControlModifier
+                    else:
+                        modifier_to_use = Qt.ShiftModifier
+                    # if modifier key is pressed then we add to the selected list the current shape but if it's already
+                    # in the selected list, we removed it. Therefore first click selects, second deselects.
+                    if key_modifier == modifier_to_use:
+                        self.select_tool(self.active_tool.name)
+                    else:
+                        self.select_tool("select")
+                        return
+
+                if isinstance(self.active_tool, FCApertureSelect):
+                    # self.app.log.debug("Replotting after click.")
+                    self.plot_all()
+            else:
+                self.app.log.debug("No active tool to respond to click!")
+
+    def on_canvas_click_release(self, event):
+        pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
+
+        self.modifiers = QtWidgets.QApplication.keyboardModifiers()
+
+        if self.app.grid_status():
+            pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
+        else:
+            pos = (pos_canvas[0], pos_canvas[1])
+
+        # if the released mouse button was RMB then test if it was a panning motion or not, if not it was a context
+        # canvas menu
+        try:
+            if event.button == 2:  # right click
+                if self.app.panning_action is True:
+                    self.app.panning_action = False
+                else:
+                    if self.in_action is False:
+                        self.app.cursor = QtGui.QCursor()
+                        self.app.ui.popMenu.popup(self.app.cursor.pos())
+                    else:
+                        # if right click on canvas and the active tool need to be finished (like Path or Polygon)
+                        # right mouse click will finish the action
+                        if isinstance(self.active_tool, FCShapeTool):
+                            self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
+                            self.active_tool.make()
+                            if self.active_tool.complete:
+                                self.on_grb_shape_complete()
+                                self.app.inform.emit(_("[success] Done."))
+
+                                # MS: always return to the Select Tool if modifier key is not pressed
+                                # else return to the current tool
+                                key_modifier = QtWidgets.QApplication.keyboardModifiers()
+                                if (self.app.defaults["global_mselect_key"] == 'Control' and
+                                    key_modifier == Qt.ControlModifier) or \
+                                        (self.app.defaults["global_mselect_key"] == 'Shift' and
+                                         key_modifier == Qt.ShiftModifier):
+
+                                    self.select_tool(self.active_tool.name)
+                                else:
+                                    self.select_tool("select")
+        except Exception as e:
+            log.warning("Error: %s" % str(e))
+            raise
+
+        # if the released mouse button was LMB then test if we had a right-to-left selection or a left-to-right
+        # selection and then select a type of selection ("enclosing" or "touching")
+        try:
+            if event.button == 1:  # left click
+                if self.app.selection_type is not None:
+                    self.draw_selection_area_handler(self.pos, pos, self.app.selection_type)
+                    self.app.selection_type = None
+
+                elif isinstance(self.active_tool, FCApertureSelect):
+                    # Dispatch event to active_tool
+                    # msg = self.active_tool.click(self.app.geo_editor.snap(event.xdata, event.ydata))
+                    # msg = self.active_tool.click_release((self.pos[0], self.pos[1]))
+                    # self.app.inform.emit(msg)
+                    self.active_tool.click_release((self.pos[0], self.pos[1]))
+
+                    # if there are selected objects then plot them
+                    if self.selected:
+                        self.plot_all()
+        except Exception as e:
+            log.warning("Error: %s" % str(e))
+            raise
+
+    def draw_selection_area_handler(self, start_pos, end_pos, sel_type):
+        """
+        :param start_pos: mouse position when the selection LMB click was done
+        :param end_pos: mouse position when the left mouse button is released
+        :param sel_type: if True it's a left to right selection (enclosure), if False it's a 'touch' selection
+        :type Bool
+        :return:
+        """
+        poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])])
+
+        sel_aperture = set()
+        self.apertures_table.clearSelection()
+
+        self.app.delete_selection_shape()
+        for storage in self.storage_dict:
+            for obj in self.storage_dict[storage]['solid_geometry']:
+                if (sel_type is True and poly_selection.contains(obj.geo)) or \
+                        (sel_type is False and poly_selection.intersects(obj.geo)):
+                    if self.key == self.app.defaults["global_mselect_key"]:
+                        if obj in self.selected:
+                            self.selected.remove(obj)
+                        else:
+                            # add the object to the selected shapes
+                            self.selected.append(obj)
+                            sel_aperture.add(storage)
+                    else:
+                        self.selected.append(obj)
+                        sel_aperture.add(storage)
+
+        try:
+            self.apertures_table.cellPressed.disconnect()
+        except:
+            pass
+        # select the aperture code of the selected geometry, in the tool table
+        self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+        for aper in sel_aperture:
+            for row in range(self.apertures_table.rowCount()):
+                if str(aper) == self.apertures_table.item(row, 1).text():
+                    self.apertures_table.selectRow(row)
+                    self.last_aperture_selected = aper
+        self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
+
+        self.apertures_table.cellPressed.connect(self.on_row_selected)
+        self.plot_all()
+
+    def on_canvas_move(self, event):
+        """
+        Called on 'mouse_move' event
+
+        event.pos have canvas screen coordinates
+
+        :param event: Event object dispatched by VisPy SceneCavas
+        :return: None
+        """
+
+        pos = self.canvas.vispy_canvas.translate_coords(event.pos)
+        event.xdata, event.ydata = pos[0], pos[1]
+
+        self.x = event.xdata
+        self.y = event.ydata
+
+        # Prevent updates on pan
+        # if len(event.buttons) > 0:
+        #     return
+
+        # if the RMB is clicked and mouse is moving over plot then 'panning_action' is True
+        if event.button == 2:
+            self.app.panning_action = True
+            return
+        else:
+            self.app.panning_action = False
+
+        try:
+            x = float(event.xdata)
+            y = float(event.ydata)
+        except TypeError:
+            return
+
+        if self.active_tool is None:
+            return
+
+        ### Snap coordinates
+        x, y = self.app.geo_editor.app.geo_editor.snap(x, y)
+
+        self.snap_x = x
+        self.snap_y = y
+
+        # update the position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                       "<b>Y</b>: %.4f" % (x, y))
+
+        if self.pos is None:
+            self.pos = (0, 0)
+        dx = x - self.pos[0]
+        dy = y - self.pos[1]
+
+        # update the reference position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                           "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
+
+        ### Utility geometry (animated)
+        geo = self.active_tool.utility_geometry(data=(x, y))
+
+        if isinstance(geo, DrawToolShape) and geo.geo is not None:
+
+            # Remove any previous utility shape
+            self.tool_shape.clear(update=True)
+            self.draw_utility_geometry(geo=geo)
+
+        ### Selection area on canvas section ###
+        dx = pos[0] - self.pos[0]
+        if event.is_dragging == 1 and event.button == 1:
+            self.app.delete_selection_shape()
+            if dx < 0:
+                self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y),
+                     color=self.app.defaults["global_alt_sel_line"],
+                     face_color=self.app.defaults['global_alt_sel_fill'])
+                self.app.selection_type = False
+            else:
+                self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x,y))
+                self.app.selection_type = True
+        else:
+            self.app.selection_type = None
+
+        # Update cursor
+        self.app.app_cursor.set_data(np.asarray([(x, y)]), symbol='++', edge_color='black', size=20)
+
+    def on_canvas_key_release(self, event):
+        self.key = None
+
+    def draw_utility_geometry(self, geo):
+        if type(geo.geo) == list:
+            for el in geo.geo:
+                # Add the new utility shape
+                self.tool_shape.add(
+                    shape=el, color=(self.app.defaults["global_draw_color"] + '80'),
+                    update=False, layer=0, tolerance=None)
+        else:
+            # Add the new utility shape
+            self.tool_shape.add(
+                shape=geo.geo, color=(self.app.defaults["global_draw_color"] + '80'),
+                update=False, layer=0, tolerance=None)
+
+        self.tool_shape.redraw()
+
+    def plot_all(self):
+        """
+        Plots all shapes in the editor.
+
+        :return: None
+        :rtype: None
+        """
+        with self.app.proc_container.new("Plotting"):
+            # self.app.log.debug("plot_all()")
+            self.shapes.clear(update=True)
+
+            for storage in self.storage_dict:
+                for shape in self.storage_dict[storage]['solid_geometry']:
+                    if shape.geo is None:
+                        continue
+
+                    if shape in self.selected:
+                        self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_sel_draw_color'],
+                                        linewidth=2)
+                        continue
+                    self.plot_shape(geometry=shape.geo, color=self.app.defaults['global_draw_color'])
+
+            for shape in self.utility:
+                self.plot_shape(geometry=shape.geo, linewidth=1)
+                continue
+
+            self.shapes.redraw()
+
+    def plot_shape(self, geometry=None, color='black', linewidth=1):
+        """
+        Plots a geometric object or list of objects without rendering. Plotted objects
+        are returned as a list. This allows for efficient/animated rendering.
+
+        :param geometry: Geometry to be plotted (Any Shapely.geom kind or list of such)
+        :param color: Shape color
+        :param linewidth: Width of lines in # of pixels.
+        :return: List of plotted elements.
+        """
+        # plot_elements = []
+
+        if geometry is None:
+            geometry = self.active_tool.geometry
+
+        try:
+            self.shapes.add(shape=geometry.geo, color=color, face_color=color, layer=0)
+        except AttributeError:
+            if type(geometry) == Point:
+                return
+            self.shapes.add(shape=geometry, color=color, face_color=color+'AF', layer=0)
+
+    def start_delayed_plot(self, check_period):
+        # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period))
+        # self.plot_thread.start()
+        log.debug("FlatCAMGrbEditor --> Delayed Plot started.")
+        self.plot_thread = QtCore.QTimer()
+        self.plot_thread.setInterval(check_period)
+        self.plot_thread.timeout.connect(self.check_plot_finished)
+        self.plot_thread.start()
+
+    def check_plot_finished(self):
+        # print(self.grb_plot_promises)
+        try:
+            if not self.grb_plot_promises:
+                self.plot_thread.stop()
+
+                self.set_ui()
+                # now that we hava data, create the GUI interface and add it to the Tool Tab
+                self.build_ui()
+
+                self.plot_all()
+                log.debug("FlatCAMGrbEditor --> delayed_plot finished")
+        except Exception:
+            traceback.print_exc()
+
+    def on_shape_complete(self):
+        self.app.log.debug("on_shape_complete()")
+
+        # Add shape
+        self.add_gerber_shape(self.active_tool.geometry)
+
+        # Remove any utility shapes
+        self.delete_utility_geometry()
+        self.tool_shape.clear(update=True)
+
+        # Replot and reset tool.
+        self.plot_all()
+        # self.active_tool = type(self.active_tool)(self)
+
+    def get_selected(self):
+        """
+        Returns list of shapes that are selected in the editor.
+
+        :return: List of shapes.
+        """
+        # return [shape for shape in self.shape_buffer if shape["selected"]]
+        return self.selected
+
+    def delete_selected(self):
+        temp_ref = [s for s in self.selected]
+        for shape_sel in temp_ref:
+            self.delete_shape(shape_sel)
+
+        self.selected = []
+        self.build_ui()
+        self.app.inform.emit(_("[success] Done. Apertures deleted."))
+
+    def delete_shape(self, shape):
+        self.is_modified = True
+
+        if shape in self.utility:
+            self.utility.remove(shape)
+            return
+
+        for storage in self.storage_dict:
+            # try:
+            #     self.storage_dict[storage].remove(shape)
+            # except:
+            #     pass
+            if shape in self.storage_dict[storage]['solid_geometry']:
+                self.storage_dict[storage]['solid_geometry'].remove(shape)
+
+        if shape in self.selected:
+            self.selected.remove(shape)  # TODO: Check performance
+
+    def delete_utility_geometry(self):
+        # for_deletion = [shape for shape in self.shape_buffer if shape.utility]
+        # for_deletion = [shape for shape in self.storage.get_objects() if shape.utility]
+        for_deletion = [shape for shape in self.utility]
+        for shape in for_deletion:
+            self.delete_shape(shape)
+
+        self.tool_shape.clear(update=True)
+        self.tool_shape.redraw()
+
+    def on_delete_btn(self):
+        self.delete_selected()
+        self.plot_all()
+
+    def select_tool(self, toolname):
+        """
+        Selects a drawing tool. Impacts the object and GUI.
+
+        :param toolname: Name of the tool.
+        :return: None
+        """
+        self.tools_gerber[toolname]["button"].setChecked(True)
+        self.on_tool_select(toolname)
+
+    def set_selected(self, shape):
+
+        # Remove and add to the end.
+        if shape in self.selected:
+            self.selected.remove(shape)
+
+        self.selected.append(shape)
+
+    def set_unselected(self, shape):
+        if shape in self.selected:
+            self.selected.remove(shape)
+
+    def on_array_type_combo(self):
+        if self.array_type_combo.currentIndex() == 0:
+            self.array_circular_frame.hide()
+            self.array_linear_frame.show()
+        else:
+            self.delete_utility_geometry()
+            self.array_circular_frame.show()
+            self.array_linear_frame.hide()
+            self.app.inform.emit(_("Click on the circular array Center position"))
+
+    def on_linear_angle_radio(self):
+        val = self.pad_axis_radio.get_value()
+        if val == 'A':
+            self.linear_angle_spinner.show()
+            self.linear_angle_label.show()
+        else:
+            self.linear_angle_spinner.hide()
+            self.linear_angle_label.hide()
+
+    def on_copy_button(self):
+        self.select_tool('copy')
+        return
+
+    def on_move_button(self):
+        self.select_tool('move')
+        return
+
+    def on_pad_add(self):
+        self.select_tool('pad')
+
+    def on_pad_add_array(self):
+        self.select_tool('array')
+
+    def on_track_add(self):
+        self.select_tool('track')
+
+    def on_region_add(self):
+        self.select_tool('region')
+
+    def on_buffer(self):
+        buff_value = 0.01
+        log.debug("FlatCAMGrbEditor.on_buffer()")
+
+        try:
+            buff_value = float(self.buffer_distance_entry.get_value())
+        except ValueError:
+            # try to convert comma to decimal point. if it's still not working error message and return
+            try:
+                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."))
+                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)
+        join_style = self.buffer_corner_cb.currentIndex() + 1
+
+        def buffer_recursion(geom, selection):
+            if type(geom) == list or type(geom) is MultiPolygon:
+                geoms = list()
+                for local_geom in geom:
+                    geoms.append(buffer_recursion(local_geom, selection=selection))
+                return geoms
+            else:
+                if geom in selection:
+                    return DrawToolShape(geom.geo.buffer(buff_value, join_style=join_style))
+                else:
+                    return geom
+
+        if not self.apertures_table.selectedItems():
+            self.app.inform.emit(_(
+                "[WARNING_NOTCL] No aperture to buffer. Select at least one aperture and try again."
+            ))
+            return
+
+        for x in self.apertures_table.selectedItems():
+            try:
+                apid = self.apertures_table.item(x.row(), 1).text()
+
+                temp_storage = deepcopy(buffer_recursion(self.storage_dict[apid]['solid_geometry'], self.selected))
+                self.storage_dict[apid]['solid_geometry'] = []
+                self.storage_dict[apid]['solid_geometry'] = temp_storage
+
+            except Exception as e:
+                log.debug("FlatCAMGrbEditor.buffer() --> %s" % str(e))
+        self.plot_all()
+        self.app.inform.emit(_("[success] Done. Buffer Tool completed."))
+
+    def on_scale(self):
+        scale_factor = 1.0
+        log.debug("FlatCAMGrbEditor.on_scale()")
+
+        try:
+            scale_factor = float(self.scale_factor_entry.get_value())
+        except ValueError:
+            # try to convert comma to decimal point. if it's still not working error message and return
+            try:
+                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."))
+                return
+
+        def scale_recursion(geom, selection):
+            if type(geom) == list or type(geom) is MultiPolygon:
+                geoms = list()
+                for local_geom in geom:
+                    geoms.append(scale_recursion(local_geom, selection=selection))
+                return geoms
+            else:
+                if geom in selection:
+                    return DrawToolShape(affinity.scale(geom.geo, scale_factor, scale_factor, origin='center'))
+                else:
+                    return geom
+
+        if not self.apertures_table.selectedItems():
+            self.app.inform.emit(_(
+                "[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again."
+            ))
+            return
+
+        for x in self.apertures_table.selectedItems():
+            try:
+                apid = self.apertures_table.item(x.row(), 1).text()
+
+                temp_storage = deepcopy(scale_recursion(self.storage_dict[apid]['solid_geometry'], self.selected))
+                self.storage_dict[apid]['solid_geometry'] = []
+                self.storage_dict[apid]['solid_geometry'] = temp_storage
+
+            except Exception as e:
+                log.debug("FlatCAMGrbEditor.on_scale() --> %s" % str(e))
+
+        self.plot_all()
+        self.app.inform.emit(_("[success] Done. Scale Tool completed."))
+
+    def on_transform(self):
+        if type(self.active_tool) == FCTransform:
+            self.select_tool('select')
+        else:
+            self.select_tool('transform')
+
+    def hide_tool(self, tool_name):
+        # self.app.ui.notebook.setTabText(2, _("Tools"))
+
+        if tool_name == 'all':
+            self.apertures_frame.hide()
+        if tool_name == 'select':
+            self.apertures_frame.show()
+        if tool_name == 'buffer' or tool_name == 'all':
+            self.buffer_tool_frame.hide()
+        if tool_name == 'scale' or tool_name == 'all':
+            self.scale_tool_frame.hide()
+
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
+
+
+class TransformEditorTool(FlatCAMTool):
+    """
+    Inputs to specify how to paint the selected polygons.
+    """
+
+    toolName = _("Transform Tool")
+    rotateName = _("Rotate")
+    skewName = _("Skew/Shear")
+    scaleName = _("Scale")
+    flipName = _("Mirror (Flip)")
+    offsetName = _("Offset")
+
+    def __init__(self, app, draw_app):
+        FlatCAMTool.__init__(self, app)
+
+        self.app = app
+        self.draw_app = draw_app
+
+        self.transform_lay = QtWidgets.QVBoxLayout()
+        self.layout.addLayout(self.transform_lay)
+        ## Title
+        title_label = QtWidgets.QLabel("%s" % (_('Editor %s') % self.toolName))
+        title_label.setStyleSheet("""
+                QLabel
+                {
+                    font-size: 16px;
+                    font-weight: bold;
+                }
+                """)
+        self.transform_lay.addWidget(title_label)
+
+        self.empty_label = QtWidgets.QLabel("")
+        self.empty_label.setFixedWidth(50)
+
+        self.empty_label1 = QtWidgets.QLabel("")
+        self.empty_label1.setFixedWidth(70)
+        self.empty_label2 = QtWidgets.QLabel("")
+        self.empty_label2.setFixedWidth(70)
+        self.empty_label3 = QtWidgets.QLabel("")
+        self.empty_label3.setFixedWidth(70)
+        self.empty_label4 = QtWidgets.QLabel("")
+        self.empty_label4.setFixedWidth(70)
+        self.transform_lay.addWidget(self.empty_label)
+
+        ## Rotate Title
+        rotate_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.rotateName)
+        self.transform_lay.addWidget(rotate_title_label)
+
+        ## Layout
+        form_layout = QtWidgets.QFormLayout()
+        self.transform_lay.addLayout(form_layout)
+        form_child = QtWidgets.QHBoxLayout()
+
+        self.rotate_label = QtWidgets.QLabel(_("Angle:"))
+        self.rotate_label.setToolTip(
+            _("Angle for Rotation action, in degrees.\n"
+              "Float number between -360 and 359.\n"
+              "Positive numbers for CW motion.\n"
+              "Negative numbers for CCW motion.")
+        )
+        self.rotate_label.setFixedWidth(50)
+
+        self.rotate_entry = FCEntry()
+        # self.rotate_entry.setFixedWidth(60)
+        self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+
+        self.rotate_button = FCButton()
+        self.rotate_button.set_value(_("Rotate"))
+        self.rotate_button.setToolTip(
+            _("Rotate the selected shape(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected shapes.")
+        )
+        self.rotate_button.setFixedWidth(60)
+
+        form_child.addWidget(self.rotate_entry)
+        form_child.addWidget(self.rotate_button)
+
+        form_layout.addRow(self.rotate_label, form_child)
+
+        self.transform_lay.addWidget(self.empty_label1)
+
+        ## Skew Title
+        skew_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.skewName)
+        self.transform_lay.addWidget(skew_title_label)
+
+        ## Form Layout
+        form1_layout = QtWidgets.QFormLayout()
+        self.transform_lay.addLayout(form1_layout)
+        form1_child_1 = QtWidgets.QHBoxLayout()
+        form1_child_2 = QtWidgets.QHBoxLayout()
+
+        self.skewx_label = QtWidgets.QLabel(_("Angle X:"))
+        self.skewx_label.setToolTip(
+            _("Angle for Skew action, in degrees.\n"
+              "Float number between -360 and 359.")
+        )
+        self.skewx_label.setFixedWidth(50)
+        self.skewx_entry = FCEntry()
+        self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        # self.skewx_entry.setFixedWidth(60)
+
+        self.skewx_button = FCButton()
+        self.skewx_button.set_value(_("Skew X"))
+        self.skewx_button.setToolTip(
+            _("Skew/shear the selected shape(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected shapes."))
+        self.skewx_button.setFixedWidth(60)
+
+        self.skewy_label = QtWidgets.QLabel(_("Angle Y:"))
+        self.skewy_label.setToolTip(
+            _("Angle for Skew action, in degrees.\n"
+              "Float number between -360 and 359.")
+        )
+        self.skewy_label.setFixedWidth(50)
+        self.skewy_entry = FCEntry()
+        self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        # self.skewy_entry.setFixedWidth(60)
+
+        self.skewy_button = FCButton()
+        self.skewy_button.set_value(_("Skew Y"))
+        self.skewy_button.setToolTip(
+            _("Skew/shear the selected shape(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected shapes."))
+        self.skewy_button.setFixedWidth(60)
+
+        form1_child_1.addWidget(self.skewx_entry)
+        form1_child_1.addWidget(self.skewx_button)
+
+        form1_child_2.addWidget(self.skewy_entry)
+        form1_child_2.addWidget(self.skewy_button)
+
+        form1_layout.addRow(self.skewx_label, form1_child_1)
+        form1_layout.addRow(self.skewy_label, form1_child_2)
+
+        self.transform_lay.addWidget(self.empty_label2)
+
+        ## Scale Title
+        scale_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.scaleName)
+        self.transform_lay.addWidget(scale_title_label)
+
+        ## Form Layout
+        form2_layout = QtWidgets.QFormLayout()
+        self.transform_lay.addLayout(form2_layout)
+        form2_child_1 = QtWidgets.QHBoxLayout()
+        form2_child_2 = QtWidgets.QHBoxLayout()
+
+        self.scalex_label = QtWidgets.QLabel(_("Factor X:"))
+        self.scalex_label.setToolTip(
+            _("Factor for Scale action over X axis.")
+        )
+        self.scalex_label.setFixedWidth(50)
+        self.scalex_entry = FCEntry()
+        self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        # self.scalex_entry.setFixedWidth(60)
+
+        self.scalex_button = FCButton()
+        self.scalex_button.set_value(_("Scale X"))
+        self.scalex_button.setToolTip(
+            _("Scale the selected shape(s).\n"
+              "The point of reference depends on \n"
+              "the Scale reference checkbox state."))
+        self.scalex_button.setFixedWidth(60)
+
+        self.scaley_label = QtWidgets.QLabel(_("Factor Y:"))
+        self.scaley_label.setToolTip(
+            _("Factor for Scale action over Y axis.")
+        )
+        self.scaley_label.setFixedWidth(50)
+        self.scaley_entry = FCEntry()
+        self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        # self.scaley_entry.setFixedWidth(60)
+
+        self.scaley_button = FCButton()
+        self.scaley_button.set_value(_("Scale Y"))
+        self.scaley_button.setToolTip(
+            _("Scale the selected shape(s).\n"
+              "The point of reference depends on \n"
+              "the Scale reference checkbox state."))
+        self.scaley_button.setFixedWidth(60)
+
+        self.scale_link_cb = FCCheckBox()
+        self.scale_link_cb.set_value(True)
+        self.scale_link_cb.setText(_("Link"))
+        self.scale_link_cb.setToolTip(
+            _("Scale the selected shape(s)\n"
+              "using the Scale Factor X for both axis."))
+        self.scale_link_cb.setFixedWidth(50)
+
+        self.scale_zero_ref_cb = FCCheckBox()
+        self.scale_zero_ref_cb.set_value(True)
+        self.scale_zero_ref_cb.setText(_("Scale Reference"))
+        self.scale_zero_ref_cb.setToolTip(
+            _("Scale the selected shape(s)\n"
+              "using the origin reference when checked,\n"
+              "and the center of the biggest bounding box\n"
+              "of the selected shapes when unchecked."))
+
+        form2_child_1.addWidget(self.scalex_entry)
+        form2_child_1.addWidget(self.scalex_button)
+
+        form2_child_2.addWidget(self.scaley_entry)
+        form2_child_2.addWidget(self.scaley_button)
+
+        form2_layout.addRow(self.scalex_label, form2_child_1)
+        form2_layout.addRow(self.scaley_label, form2_child_2)
+        form2_layout.addRow(self.scale_link_cb, self.scale_zero_ref_cb)
+        self.ois_scale = OptionalInputSection(self.scale_link_cb, [self.scaley_entry, self.scaley_button],
+                                              logic=False)
+
+        self.transform_lay.addWidget(self.empty_label3)
+
+        ## Offset Title
+        offset_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.offsetName)
+        self.transform_lay.addWidget(offset_title_label)
+
+        ## Form Layout
+        form3_layout = QtWidgets.QFormLayout()
+        self.transform_lay.addLayout(form3_layout)
+        form3_child_1 = QtWidgets.QHBoxLayout()
+        form3_child_2 = QtWidgets.QHBoxLayout()
+
+        self.offx_label = QtWidgets.QLabel(_("Value X:"))
+        self.offx_label.setToolTip(
+            _("Value for Offset action on X axis.")
+        )
+        self.offx_label.setFixedWidth(50)
+        self.offx_entry = FCEntry()
+        self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        # self.offx_entry.setFixedWidth(60)
+
+        self.offx_button = FCButton()
+        self.offx_button.set_value(_("Offset X"))
+        self.offx_button.setToolTip(
+            _("Offset the selected shape(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected shapes.\n")
+        )
+        self.offx_button.setFixedWidth(60)
+
+        self.offy_label = QtWidgets.QLabel(_("Value Y:"))
+        self.offy_label.setToolTip(
+            _("Value for Offset action on Y axis.")
+        )
+        self.offy_label.setFixedWidth(50)
+        self.offy_entry = FCEntry()
+        self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        # self.offy_entry.setFixedWidth(60)
+
+        self.offy_button = FCButton()
+        self.offy_button.set_value(_("Offset Y"))
+        self.offy_button.setToolTip(
+            _("Offset the selected shape(s).\n"
+              "The point of reference is the middle of\n"
+              "the bounding box for all selected shapes.\n")
+        )
+        self.offy_button.setFixedWidth(60)
+
+        form3_child_1.addWidget(self.offx_entry)
+        form3_child_1.addWidget(self.offx_button)
+
+        form3_child_2.addWidget(self.offy_entry)
+        form3_child_2.addWidget(self.offy_button)
+
+        form3_layout.addRow(self.offx_label, form3_child_1)
+        form3_layout.addRow(self.offy_label, form3_child_2)
+
+        self.transform_lay.addWidget(self.empty_label4)
+
+        ## Flip Title
+        flip_title_label = QtWidgets.QLabel("<font size=3><b>%s</b></font>" % self.flipName)
+        self.transform_lay.addWidget(flip_title_label)
+
+        ## Form Layout
+        form4_layout = QtWidgets.QFormLayout()
+        form4_child_hlay = QtWidgets.QHBoxLayout()
+        self.transform_lay.addLayout(form4_child_hlay)
+        self.transform_lay.addLayout(form4_layout)
+        form4_child_1 = QtWidgets.QHBoxLayout()
+
+        self.flipx_button = FCButton()
+        self.flipx_button.set_value(_("Flip on X"))
+        self.flipx_button.setToolTip(
+            _("Flip the selected shape(s) over the X axis.\n"
+              "Does not create a new shape.")
+        )
+        self.flipx_button.setFixedWidth(60)
+
+        self.flipy_button = FCButton()
+        self.flipy_button.set_value(_("Flip on Y"))
+        self.flipy_button.setToolTip(
+            _("Flip the selected shape(s) over the X axis.\n"
+              "Does not create a new shape.")
+        )
+        self.flipy_button.setFixedWidth(60)
+
+        self.flip_ref_cb = FCCheckBox()
+        self.flip_ref_cb.set_value(True)
+        self.flip_ref_cb.setText(_("Ref Pt"))
+        self.flip_ref_cb.setToolTip(
+            _("Flip the selected shape(s)\n"
+              "around the point in Point Entry Field.\n"
+              "\n"
+              "The point coordinates can be captured by\n"
+              "left click on canvas together with pressing\n"
+              "SHIFT key. \n"
+              "Then click Add button to insert coordinates.\n"
+              "Or enter the coords in format (x, y) in the\n"
+              "Point Entry field and click Flip on X(Y)")
+        )
+        self.flip_ref_cb.setFixedWidth(50)
+
+        self.flip_ref_label = QtWidgets.QLabel(_("Point:"))
+        self.flip_ref_label.setToolTip(
+            _("Coordinates in format (x, y) used as reference for mirroring.\n"
+              "The 'x' in (x, y) will be used when using Flip on X and\n"
+              "the 'y' in (x, y) will be used when using Flip on Y.")
+        )
+        self.flip_ref_label.setFixedWidth(50)
+        self.flip_ref_entry = EvalEntry2("(0, 0)")
+        self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
+        # self.flip_ref_entry.setFixedWidth(60)
+
+        self.flip_ref_button = FCButton()
+        self.flip_ref_button.set_value(_("Add"))
+        self.flip_ref_button.setToolTip(
+            _("The point coordinates can be captured by\n"
+              "left click on canvas together with pressing\n"
+              "SHIFT key. Then click Add button to insert.")
+        )
+        self.flip_ref_button.setFixedWidth(60)
+
+        form4_child_hlay.addStretch()
+        form4_child_hlay.addWidget(self.flipx_button)
+        form4_child_hlay.addWidget(self.flipy_button)
+
+        form4_child_1.addWidget(self.flip_ref_entry)
+        form4_child_1.addWidget(self.flip_ref_button)
+
+        form4_layout.addRow(self.flip_ref_cb)
+        form4_layout.addRow(self.flip_ref_label, form4_child_1)
+        self.ois_flip = OptionalInputSection(self.flip_ref_cb,
+                                             [self.flip_ref_entry, self.flip_ref_button], logic=True)
+
+        self.transform_lay.addStretch()
+
+        ## Signals
+        self.rotate_button.clicked.connect(self.on_rotate)
+        self.skewx_button.clicked.connect(self.on_skewx)
+        self.skewy_button.clicked.connect(self.on_skewy)
+        self.scalex_button.clicked.connect(self.on_scalex)
+        self.scaley_button.clicked.connect(self.on_scaley)
+        self.offx_button.clicked.connect(self.on_offx)
+        self.offy_button.clicked.connect(self.on_offy)
+        self.flipx_button.clicked.connect(self.on_flipx)
+        self.flipy_button.clicked.connect(self.on_flipy)
+        self.flip_ref_button.clicked.connect(self.on_flip_add_coords)
+
+        self.rotate_entry.returnPressed.connect(self.on_rotate)
+        self.skewx_entry.returnPressed.connect(self.on_skewx)
+        self.skewy_entry.returnPressed.connect(self.on_skewy)
+        self.scalex_entry.returnPressed.connect(self.on_scalex)
+        self.scaley_entry.returnPressed.connect(self.on_scaley)
+        self.offx_entry.returnPressed.connect(self.on_offx)
+        self.offy_entry.returnPressed.connect(self.on_offy)
+
+        self.set_tool_ui()
+
+    def run(self, toggle=True):
+        self.app.report_usage("Geo Editor Transform Tool()")
+
+        # if the splitter is hidden, display it, else hide it but only if the current widget is the same
+        if self.app.ui.splitter.sizes()[0] == 0:
+            self.app.ui.splitter.setSizes([1, 1])
+
+        if toggle:
+            try:
+                if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
+                    self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
+                else:
+                    self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
+            except AttributeError:
+                pass
+
+        FlatCAMTool.run(self)
+        self.set_tool_ui()
+
+        self.app.ui.notebook.setTabText(2, _("Transform Tool"))
+
+    def install(self, icon=None, separator=None, **kwargs):
+        FlatCAMTool.install(self, icon, separator, shortcut='ALT+T', **kwargs)
+
+    def set_tool_ui(self):
+        ## Initialize form
+        if self.app.defaults["tools_transform_rotate"]:
+            self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])
+        else:
+            self.rotate_entry.set_value(0.0)
+
+        if self.app.defaults["tools_transform_skew_x"]:
+            self.skewx_entry.set_value(self.app.defaults["tools_transform_skew_x"])
+        else:
+            self.skewx_entry.set_value(0.0)
+
+        if self.app.defaults["tools_transform_skew_y"]:
+            self.skewy_entry.set_value(self.app.defaults["tools_transform_skew_y"])
+        else:
+            self.skewy_entry.set_value(0.0)
+
+        if self.app.defaults["tools_transform_scale_x"]:
+            self.scalex_entry.set_value(self.app.defaults["tools_transform_scale_x"])
+        else:
+            self.scalex_entry.set_value(1.0)
+
+        if self.app.defaults["tools_transform_scale_y"]:
+            self.scaley_entry.set_value(self.app.defaults["tools_transform_scale_y"])
+        else:
+            self.scaley_entry.set_value(1.0)
+
+        if self.app.defaults["tools_transform_scale_link"]:
+            self.scale_link_cb.set_value(self.app.defaults["tools_transform_scale_link"])
+        else:
+            self.scale_link_cb.set_value(True)
+
+        if self.app.defaults["tools_transform_scale_reference"]:
+            self.scale_zero_ref_cb.set_value(self.app.defaults["tools_transform_scale_reference"])
+        else:
+            self.scale_zero_ref_cb.set_value(True)
+
+        if self.app.defaults["tools_transform_offset_x"]:
+            self.offx_entry.set_value(self.app.defaults["tools_transform_offset_x"])
+        else:
+            self.offx_entry.set_value(0.0)
+
+        if self.app.defaults["tools_transform_offset_y"]:
+            self.offy_entry.set_value(self.app.defaults["tools_transform_offset_y"])
+        else:
+            self.offy_entry.set_value(0.0)
+
+        if self.app.defaults["tools_transform_mirror_reference"]:
+            self.flip_ref_cb.set_value(self.app.defaults["tools_transform_mirror_reference"])
+        else:
+            self.flip_ref_cb.set_value(False)
+
+        if self.app.defaults["tools_transform_mirror_point"]:
+            self.flip_ref_entry.set_value(self.app.defaults["tools_transform_mirror_point"])
+        else:
+            self.flip_ref_entry.set_value((0, 0))
+
+    def template(self):
+        if not self.fcdraw.selected:
+            self.app.inform.emit(_("[WARNING_NOTCL] Transformation cancelled. No shape selected."))
+            return
+
+        self.draw_app.select_tool("select")
+        self.app.ui.notebook.setTabText(2, "Tools")
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
+
+        self.app.ui.splitter.setSizes([0, 1])
+
+    def on_rotate(self, sig=None, val=None):
+        if val:
+            value = val
+        else:
+            try:
+                value = float(self.rotate_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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."))
+                    return
+        self.app.worker_task.emit({'fcn': self.on_rotate_action,
+                                   'params': [value]})
+        # self.on_rotate_action(value)
+        return
+
+    def on_flipx(self):
+        # self.on_flip("Y")
+        axis = 'Y'
+        self.app.worker_task.emit({'fcn': self.on_flip,
+                                   'params': [axis]})
+        return
+
+    def on_flipy(self):
+        # self.on_flip("X")
+        axis = 'X'
+        self.app.worker_task.emit({'fcn': self.on_flip,
+                                   'params': [axis]})
+        return
+
+    def on_flip_add_coords(self):
+        val = self.app.clipboard.text()
+        self.flip_ref_entry.set_value(val)
+
+    def on_skewx(self, sig=None, val=None):
+        if val:
+            value = val
+        else:
+            try:
+                value = float(self.skewx_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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."))
+                    return
+
+        # self.on_skew("X", value)
+        axis = 'X'
+        self.app.worker_task.emit({'fcn': self.on_skew,
+                                   'params': [axis, value]})
+        return
+
+    def on_skewy(self, sig=None, val=None):
+        if val:
+            value = val
+        else:
+            try:
+                value = float(self.skewy_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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."))
+                    return
+
+        # self.on_skew("Y", value)
+        axis = 'Y'
+        self.app.worker_task.emit({'fcn': self.on_skew,
+                                   'params': [axis, value]})
+        return
+
+    def on_scalex(self, sig=None, val=None):
+        if val:
+            xvalue = val
+        else:
+            try:
+                xvalue = float(self.scalex_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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."))
+                    return
+
+        # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
+        if xvalue == 0:
+            xvalue = 1
+        if self.scale_link_cb.get_value():
+            yvalue = xvalue
+        else:
+            yvalue = 1
+
+        axis = 'X'
+        point = (0, 0)
+        if self.scale_zero_ref_cb.get_value():
+            self.app.worker_task.emit({'fcn': self.on_scale,
+                                       'params': [axis, xvalue, yvalue, point]})
+            # self.on_scale("X", xvalue, yvalue, point=(0,0))
+        else:
+            # self.on_scale("X", xvalue, yvalue)
+            self.app.worker_task.emit({'fcn': self.on_scale,
+                                       'params': [axis, xvalue, yvalue]})
+
+        return
+
+    def on_scaley(self, sig=None, val=None):
+        xvalue = 1
+        if val:
+            yvalue = val
+        else:
+            try:
+                yvalue = float(self.scaley_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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."))
+                    return
+
+        # scaling to zero has no sense so we remove it, because scaling with 1 does nothing
+        if yvalue == 0:
+            yvalue = 1
+
+        axis = 'Y'
+        point = (0, 0)
+        if self.scale_zero_ref_cb.get_value():
+            self.app.worker_task.emit({'fcn': self.on_scale,
+                                       'params': [axis, xvalue, yvalue, point]})
+            # self.on_scale("Y", xvalue, yvalue, point=(0,0))
+        else:
+            # self.on_scale("Y", xvalue, yvalue)
+            self.app.worker_task.emit({'fcn': self.on_scale,
+                                       'params': [axis, xvalue, yvalue]})
+
+        return
+
+    def on_offx(self, sig=None, val=None):
+        if val:
+            value = val
+        else:
+            try:
+                value = float(self.offx_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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."))
+                    return
+
+        # self.on_offset("X", value)
+        axis = 'X'
+        self.app.worker_task.emit({'fcn': self.on_offset,
+                                   'params': [axis, value]})
+        return
+
+    def on_offy(self, sig=None, val=None):
+        if val:
+            value = val
+        else:
+            try:
+                value = float(self.offy_entry.get_value())
+            except ValueError:
+                # try to convert comma to decimal point. if it's still not working error message and return
+                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."))
+                    return
+
+        # self.on_offset("Y", value)
+        axis = 'Y'
+        self.app.worker_task.emit({'fcn': self.on_offset,
+                                   'params': [axis, value]})
+        return
+
+    def on_rotate_action(self, num):
+        shape_list = self.draw_app.selected
+        xminlist = []
+        yminlist = []
+        xmaxlist = []
+        ymaxlist = []
+
+        if not shape_list:
+            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to rotate!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Appying Rotate")):
+                try:
+                    # first get a bounding box to fit all
+                    for sha in shape_list:
+                        xmin, ymin, xmax, ymax = sha.bounds()
+                        xminlist.append(xmin)
+                        yminlist.append(ymin)
+                        xmaxlist.append(xmax)
+                        ymaxlist.append(ymax)
+
+                    # get the minimum x,y and maximum x,y for all objects selected
+                    xminimal = min(xminlist)
+                    yminimal = min(yminlist)
+                    xmaximal = max(xmaxlist)
+                    ymaximal = max(ymaxlist)
+
+                    self.app.progress.emit(20)
+
+                    for sel_sha in shape_list:
+                        px = 0.5 * (xminimal + xmaximal)
+                        py = 0.5 * (yminimal + ymaximal)
+
+                        sel_sha.rotate(-num, point=(px, py))
+                        self.draw_app.plot_all()
+                        # self.draw_app.add_shape(DrawToolShape(sel_sha.geo))
+
+                    # self.draw_app.transform_complete.emit()
+
+                    self.app.inform.emit(_("[success] 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))
+                    return
+
+    def on_flip(self, axis):
+        shape_list = self.draw_app.selected
+        xminlist = []
+        yminlist = []
+        xmaxlist = []
+        ymaxlist = []
+
+        if not shape_list:
+            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to flip!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Flip")):
+                try:
+                    # get mirroring coords from the point entry
+                    if self.flip_ref_cb.isChecked():
+                        px, py = eval('{}'.format(self.flip_ref_entry.text()))
+                    # get mirroing coords from the center of an all-enclosing bounding box
+                    else:
+                        # first get a bounding box to fit all
+                        for sha in shape_list:
+                            xmin, ymin, xmax, ymax = sha.bounds()
+                            xminlist.append(xmin)
+                            yminlist.append(ymin)
+                            xmaxlist.append(xmax)
+                            ymaxlist.append(ymax)
+
+                        # get the minimum x,y and maximum x,y for all objects selected
+                        xminimal = min(xminlist)
+                        yminimal = min(yminlist)
+                        xmaximal = max(xmaxlist)
+                        ymaximal = max(ymaxlist)
+
+                        px = 0.5 * (xminimal + xmaximal)
+                        py = 0.5 * (yminimal + ymaximal)
+
+                    self.app.progress.emit(20)
+
+                    # execute mirroring
+                    for sha in shape_list:
+                        if axis is 'X':
+                            sha.mirror('X', (px, py))
+                            self.app.inform.emit(_('[success] 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.draw_app.plot_all()
+
+                    #     self.draw_app.add_shape(DrawToolShape(sha.geo))
+                    #
+                    # self.draw_app.transform_complete.emit()
+
+                    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))
+                    return
+
+    def on_skew(self, axis, num):
+        shape_list = self.draw_app.selected
+        xminlist = []
+        yminlist = []
+
+        if not shape_list:
+            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to shear/skew!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Skew")):
+                try:
+                    # first get a bounding box to fit all
+                    for sha in shape_list:
+                        xmin, ymin, xmax, ymax = sha.bounds()
+                        xminlist.append(xmin)
+                        yminlist.append(ymin)
+
+                    # get the minimum x,y and maximum x,y for all objects selected
+                    xminimal = min(xminlist)
+                    yminimal = min(yminlist)
+
+                    self.app.progress.emit(20)
+
+                    for sha in shape_list:
+                        if axis is 'X':
+                            sha.skew(num, 0, point=(xminimal, yminimal))
+                        elif axis is 'Y':
+                            sha.skew(0, num, point=(xminimal, yminimal))
+                        self.draw_app.plot_all()
+
+                    #     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))
+                    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))
+                    return
+
+    def on_scale(self, axis, xfactor, yfactor, point=None):
+        shape_list = self.draw_app.selected
+        xminlist = []
+        yminlist = []
+        xmaxlist = []
+        ymaxlist = []
+
+        if not shape_list:
+            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to scale!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Scale")):
+                try:
+                    # first get a bounding box to fit all
+                    for sha in shape_list:
+                        xmin, ymin, xmax, ymax = sha.bounds()
+                        xminlist.append(xmin)
+                        yminlist.append(ymin)
+                        xmaxlist.append(xmax)
+                        ymaxlist.append(ymax)
+
+                    # get the minimum x,y and maximum x,y for all objects selected
+                    xminimal = min(xminlist)
+                    yminimal = min(yminlist)
+                    xmaximal = max(xmaxlist)
+                    ymaximal = max(ymaxlist)
+
+                    self.app.progress.emit(20)
+
+                    if point is None:
+                        px = 0.5 * (xminimal + xmaximal)
+                        py = 0.5 * (yminimal + ymaximal)
+                    else:
+                        px = 0
+                        py = 0
+
+                    for sha in shape_list:
+                        sha.scale(xfactor, yfactor, point=(px, py))
+                        self.draw_app.plot_all()
+
+                    #     self.draw_app.add_shape(DrawToolShape(sha.geo))
+                    #
+                    # self.draw_app.transform_complete.emit()
+
+                    self.app.inform.emit(_('[success] Scale on the %s axis done ...') % str(axis))
+                    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))
+                    return
+
+    def on_offset(self, axis, num):
+        shape_list = self.draw_app.selected
+        xminlist = []
+        yminlist = []
+
+        if not shape_list:
+            self.app.inform.emit(_("[WARNING_NOTCL] No shape selected. Please Select a shape to offset!"))
+            return
+        else:
+            with self.app.proc_container.new(_("Applying Offset")):
+                try:
+                    # first get a bounding box to fit all
+                    for sha in shape_list:
+                        xmin, ymin, xmax, ymax = sha.bounds()
+                        xminlist.append(xmin)
+                        yminlist.append(ymin)
+
+                    # get the minimum x,y and maximum x,y for all objects selected
+                    xminimal = min(xminlist)
+                    yminimal = min(yminlist)
+                    self.app.progress.emit(20)
+
+                    for sha in shape_list:
+                        if axis is 'X':
+                            sha.offset((num, 0))
+                        elif axis is 'Y':
+                            sha.offset((0, num))
+                        self.draw_app.plot_all()
+
+                    #     self.draw_app.add_shape(DrawToolShape(sha.geo))
+                    #
+                    # self.draw_app.transform_complete.emit()
+
+                    self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis))
+                    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))
+                    return
+
+    def on_rotate_key(self):
+        val_box = FCInputDialog(title=_("Rotate ..."),
+                                text=_('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'))
+
+        val, ok = val_box.get_value()
+        if ok:
+            self.on_rotate(val=val)
+            self.app.inform.emit(
+                _("[success] Geometry shape rotate done...")
+            )
+            return
+        else:
+            self.app.inform.emit(
+                _("[WARNING_NOTCL] 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)),
+                                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'))
+
+        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..."))
+            return
+        else:
+            self.app.inform.emit(
+                _("[WARNING_NOTCL] 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)),
+                                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'))
+
+        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..."))
+            return
+        else:
+            self.app.inform.emit(
+                _("[WARNING_NOTCL] Geometry shape offset Y cancelled..."))
+
+    def on_skewx_key(self):
+        val_box = FCInputDialog(title=_("Skew on X axis ..."),
+                                text=_('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'))
+
+        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..."))
+            return
+        else:
+            self.app.inform.emit(
+                _("[WARNING_NOTCL] Geometry shape skew X cancelled..."))
+
+    def on_skewy_key(self):
+        val_box = FCInputDialog(title=_("Skew on Y axis ..."),
+                                text=_('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'))
+
+        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..."))
+            return
+        else:
+            self.app.inform.emit(
+                _("[WARNING_NOTCL] Geometry shape skew Y cancelled..."))

+ 0 - 0
flatcamEditors/__init__.py


+ 490 - 49
flatcamGUI/FlatCAMGUI.py

@@ -16,7 +16,7 @@ from flatcamGUI.GUIElements import *
 import platform
 import webbrowser
 
-from FlatCAMEditor import FCShapeTool
+from flatcamEditors.FlatCAMGeoEditor import FCShapeTool
 
 import gettext
 import FlatCAMTranslation as fcTranslate
@@ -66,6 +66,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.menufilenewgeo.setToolTip(
             _("Will create a new, empty Geometry Object.")
         )
+        self.menufilenewgrb = self.menufilenew.addAction(QtGui.QIcon('share/flatcam_icon32.png'), _('Gerber\tB'))
+        self.menufilenewgrb.setToolTip(
+            _("Will create a new, empty Gerber Object.")
+        )
         self.menufilenewexc = self.menufilenew.addAction(QtGui.QIcon('share/drill16.png'), _('Excellon\tL'))
         self.menufilenewexc.setToolTip(
             _("Will create a new, empty Excellon Object.")
@@ -107,7 +111,14 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         # Separator
         self.menufile.addSeparator()
 
-        # Run Scripts
+        # Scripting
+        self.menufile_scripting = self.menufile.addMenu(QtGui.QIcon('share/script16.png'), _('Scripting'))
+        self.menufile_scripting.setToolTipsVisible(True)
+
+        self.menufilenewscript = QtWidgets.QAction(QtGui.QIcon('share/script_new16.png'), _('New Script ...'),
+                                                   self)
+        self.menufileopenscript = QtWidgets.QAction(QtGui.QIcon('share/script_open16.png'), _('Open Script ...'),
+                                                   self)
         self.menufilerunscript = QtWidgets.QAction(QtGui.QIcon('share/script16.png'), _('Run Script ...\tSHIFT+S'),
                                                    self)
         self.menufilerunscript.setToolTip(
@@ -115,7 +126,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             "enabling the automation of certain\n"
             "functions of FlatCAM.")
         )
-        self.menufile.addAction(self.menufilerunscript)
+        self.menufile_scripting.addAction(self.menufilenewscript)
+        self.menufile_scripting.addAction(self.menufileopenscript)
+        self.menufile_scripting.addSeparator()
+        self.menufile_scripting.addAction(self.menufilerunscript)
 
         # Separator
         self.menufile.addSeparator()
@@ -209,7 +223,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         # Separator
         self.menuedit.addSeparator()
         self.menueditedit = self.menuedit.addAction(QtGui.QIcon('share/edit16.png'), _('Edit Object\tE'))
-        self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), _('Save && Close Editor\tCTRL+S'))
+        self.menueditok = self.menuedit.addAction(QtGui.QIcon('share/edit_ok16.png'), _('Close Editor\tCTRL+S'))
 
         # adjust the initial state of the menu entries related to the editor
         self.menueditedit.setDisabled(False)
@@ -441,13 +455,48 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.exc_move_drill_menuitem = self.exc_editor_menu.addAction(
             QtGui.QIcon('share/move32.png'),_( 'Move Drill(s)\tM'))
 
+        ### APPLICATION GERBER EDITOR MENU ###
+
+        self.grb_editor_menu = QtWidgets.QMenu(_(">Gerber Editor<"))
+        self.menu.addMenu(self.grb_editor_menu)
+
+        self.grb_add_pad_menuitem = self.grb_editor_menu.addAction(
+            QtGui.QIcon('share/aperture16.png'), _('Add Pad\tP'))
+        self.grb_add_pad_array_menuitem = self.grb_editor_menu.addAction(
+            QtGui.QIcon('share/padarray32.png'), _('Add Pad Array\tA'))
+        self.grb_add_track_menuitem = self.grb_editor_menu.addAction(
+            QtGui.QIcon('share/track32.png'), _('Add Track\tT'))
+        self.grb_add_region_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/rectangle32.png'),
+                                                                     _('Add Region\tN'))
+        self.grb_editor_menu.addSeparator()
+
+        self.grb_add_buffer_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/buffer16-2.png'),
+                                                                    _('Buffer\tB'))
+        self.grb_add_scale_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/scale32.png'),
+                                                                    _('Scale\tS'))
+        self.grb_transform_menuitem = self.grb_editor_menu.addAction(
+            QtGui.QIcon('share/transform.png'),_( "Transform\tALT+R")
+        )
+        self.grb_editor_menu.addSeparator()
+
+        self.grb_copy_menuitem = self.grb_editor_menu.addAction(QtGui.QIcon('share/copy32.png'), _('Copy\tC'))
+        self.grb_delete_menuitem = self.grb_editor_menu.addAction(
+            QtGui.QIcon('share/deleteshape32.png'), _('Delete\tDEL')
+        )
+        self.grb_editor_menu.addSeparator()
+
+        self.grb_move_menuitem = self.grb_editor_menu.addAction(
+            QtGui.QIcon('share/move32.png'),_( 'Move\tM'))
+
+        self.grb_editor_menu.menuAction().setVisible(False)
+        self.grb_editor_menu.setDisabled(True)
+
         self.geo_editor_menu.menuAction().setVisible(False)
         self.geo_editor_menu.setDisabled(True)
 
         self.exc_editor_menu.menuAction().setVisible(False)
         self.exc_editor_menu.setDisabled(True)
 
-
         ################################
         ### Project Tab Context menu ###
         ################################
@@ -522,6 +571,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geo_edit_toolbar.setObjectName('GeoEditor_TB')
         self.addToolBar(self.geo_edit_toolbar)
 
+        self.grb_edit_toolbar = QtWidgets.QToolBar(_('Gerber Editor Toolbar'))
+        self.grb_edit_toolbar.setObjectName('GrbEditor_TB')
+        self.addToolBar(self.grb_edit_toolbar)
+
         self.snap_toolbar = QtWidgets.QToolBar(_('Grid Toolbar'))
         self.snap_toolbar.setObjectName('Snap_TB')
         self.addToolBar(self.snap_toolbar)
@@ -529,9 +582,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         settings = QSettings("Open Source", "FlatCAM")
         if settings.contains("layout"):
             layout = settings.value('layout', type=str)
-            if layout == 'Standard':
+            if layout == 'standard':
                 pass
-            elif layout == 'Compact':
+            elif layout == 'compact':
                 self.removeToolBar(self.snap_toolbar)
                 self.snap_toolbar.setMaximumHeight(30)
                 self.splitter_left.addWidget(self.snap_toolbar)
@@ -546,6 +599,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
 
         ### Edit Toolbar ###
         self.newgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_geo32_bis.png'), _("New Blank Geometry"))
+        self.newgrb_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_geo32.png'), _("New Blank Gerber"))
         self.newexc_btn = self.toolbargeo.addAction(QtGui.QIcon('share/new_exc32.png'), _("New Blank Excellon"))
         self.toolbargeo.addSeparator()
         self.editgeo_btn = self.toolbargeo.addAction(QtGui.QIcon('share/edit32.png'), _("Editor"))
@@ -587,12 +641,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.select_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
         self.add_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/plus16.png'), _('Add Drill Hole'))
         self.add_drill_array_btn = self.exc_edit_toolbar.addAction(
-            QtGui.QIcon('share/addarray16.png'), 'Add Drill Hole Array')
+            QtGui.QIcon('share/addarray16.png'), _('Add Drill Hole Array'))
         self.resize_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/resize16.png'), _('Resize Drill'))
         self.exc_edit_toolbar.addSeparator()
 
         self.copy_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _('Copy Drill'))
-        self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'), _("Delete Drill"))
+        self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'), _("Delete Drill"))
 
         self.exc_edit_toolbar.addSeparator()
         self.move_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move Drill"))
@@ -623,13 +677,32 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geo_cutpath_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/cutpath32.png'), _('Cut Path'))
         self.geo_copy_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy Shape(s)"))
 
-        self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'),
+        self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
                                                               _("Delete Shape '-'"))
         self.geo_transform_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
                                                                  _("Transformations"))
         self.geo_edit_toolbar.addSeparator()
         self.geo_move_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move Objects "))
 
+        ### Gerber Editor Toolbar ###
+        self.grb_select_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
+        self.grb_add_pad_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/aperture32.png'), _("Add Pad"))
+        self.add_pad_ar_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/padarray32.png'), _('Add Pad Array'))
+        self.grb_add_track_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/track32.png'), _("Add Track"))
+        self.grb_add_region_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), _("Add Region"))
+        self.grb_edit_toolbar.addSeparator()
+
+        self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer'))
+        self.aperture_scale_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/scale32.png'), _('Scale'))
+        self.grb_edit_toolbar.addSeparator()
+        self.aperture_copy_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy"))
+        self.aperture_delete_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
+                                                                   _("Delete"))
+        self.grb_transform_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
+                                                                 _("Transformations"))
+        self.grb_edit_toolbar.addSeparator()
+        self.aperture_move_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
+
         ### Snap Toolbar ###
         # Snap GRID toolbar is always active to facilitate usage of measurements done on GRID
         # self.addToolBar(self.snap_toolbar)
@@ -1340,6 +1413,81 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         <td>&nbsp;Save Object and Exit Editor</td>
                     </tr>
                 </tbody>
+            </table>
+            <br>
+            <br>
+            <strong><span style="color:#00ff00">GERBER EDITOR</span></strong><br>
+            <table border="0" cellpadding="0" cellspacing="0" style="width:283px">
+                <tbody>
+                    <tr height="20">
+                        <td height="20" width="89"><strong>A</strong></td>
+                        <td width="194">&nbsp;Add Pad Array</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>B</strong></td>
+                        <td>&nbsp;Buffer</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>C</strong></td>
+                        <td>&nbsp;Copy</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>J</strong></td>
+                        <td>&nbsp;Jump to Location (x, y)</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>M</strong></td>
+                        <td>&nbsp;Move</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>N</strong></td>
+                        <td>&nbsp;Add Region</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>P</strong></td>
+                        <td>&nbsp;Add Pad</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>S</strong></td>
+                        <td>&nbsp;Scale</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>T</strong></td>
+                        <td>&nbsp;Add Track</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20">&nbsp;</td>
+                        <td>&nbsp;</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>Del</strong></td>
+                        <td>&nbsp;Delete</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>Del</strong></td>
+                        <td>&nbsp;Alternate: Delete Apertures</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20">&nbsp;</td>
+                        <td>&nbsp;</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>ESC</strong></td>
+                        <td>&nbsp;Abort and return to Select</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>CTRL+S</strong></td>
+                        <td>&nbsp;Save Object and Exit Editor</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20">&nbsp;</td>
+                        <td>&nbsp;</td>
+                    </tr>
+                    <tr height="20">
+                        <td height="20"><strong>ALT+R</strong></td>
+                        <td>&nbsp;Transformation Tool</td>
+                    </tr>
+                </tbody>
             </table>
                     '''
         )
@@ -1379,6 +1527,16 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.g_editor_cmenu.addSeparator()
         self.draw_move = self.g_editor_cmenu.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
 
+        self.grb_editor_cmenu = self.popMenu.addMenu(QtGui.QIcon('share/draw32.png'), _("Gerber Editor"))
+        self.grb_draw_pad = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/aperture32.png'), _("Pad"))
+        self.grb_draw_pad_array = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/padarray32.png'), _("Pad Array"))
+        self.grb_draw_track = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/track32.png'), _("Track"))
+        self.grb_draw_region = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/polygon32.png'), _("Region"))
+        self.grb_editor_cmenu.addSeparator()
+        self.grb_copy = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/copy.png'), _("Copy"))
+        self.grb_delete = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/trash32.png'), _("Delete"))
+        self.grb_move = self.grb_editor_cmenu.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
+
         self.e_editor_cmenu = self.popMenu.addMenu(QtGui.QIcon('share/drill32.png'), _("Exc Editor"))
         self.drill = self.e_editor_cmenu.addAction(QtGui.QIcon('share/drill32.png'), _("Add Drill"))
         self.drill_array = self.e_editor_cmenu.addAction(QtGui.QIcon('share/addarray32.png'), _("Add Drill Array"))
@@ -1388,7 +1546,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.popmenu_copy = self.popMenu.addAction(QtGui.QIcon('share/copy32.png'), _("Copy"))
         self.popmenu_delete = self.popMenu.addAction(QtGui.QIcon('share/delete32.png'), _("Delete"))
         self.popmenu_edit = self.popMenu.addAction(QtGui.QIcon('share/edit32.png'), _("Edit"))
-        self.popmenu_save = self.popMenu.addAction(QtGui.QIcon('share/floppy32.png'), _("Save && Close Edit"))
+        self.popmenu_save = self.popMenu.addAction(QtGui.QIcon('share/floppy32.png'), _("Close Editor"))
         self.popmenu_save.setVisible(False)
         self.popMenu.addSeparator()
 
@@ -1404,7 +1562,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.cncjob_tab_layout.setContentsMargins(2, 2, 2, 2)
         self.cncjob_tab.setLayout(self.cncjob_tab_layout)
 
-        self.code_editor = QtWidgets.QTextEdit()
+        self.code_editor = FCTextAreaExtended()
         stylesheet = """
                         QTextEdit { selection-background-color:yellow;
                                     selection-color:black;
@@ -1523,6 +1681,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.grid_snap_btn.trigger()
 
         self.g_editor_cmenu.setEnabled(False)
+        self.grb_editor_cmenu.setEnabled(False)
         self.e_editor_cmenu.setEnabled(False)
 
         self.general_defaults_form = GeneralPreferencesUI()
@@ -1548,6 +1707,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             self.restoreState(saved_gui_state)
             log.debug("FlatCAMGUI.__init__() --> UI state restored.")
 
+        settings = QSettings("Open Source", "FlatCAM")
         if settings.contains("layout"):
             layout = settings.value('layout', type=str)
             if layout == 'standard':
@@ -1555,24 +1715,36 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 self.exc_edit_toolbar.setDisabled(True)
                 self.geo_edit_toolbar.setVisible(False)
                 self.geo_edit_toolbar.setDisabled(True)
+                self.grb_edit_toolbar.setVisible(False)
+                self.grb_edit_toolbar.setDisabled(True)
 
                 self.corner_snap_btn.setVisible(False)
                 self.snap_magnet.setVisible(False)
-            elif layout == 'Compact':
+            elif layout == 'compact':
                 self.exc_edit_toolbar.setDisabled(True)
                 self.geo_edit_toolbar.setDisabled(True)
+                self.grb_edit_toolbar.setDisabled(True)
+
                 self.snap_magnet.setVisible(True)
                 self.corner_snap_btn.setVisible(True)
                 self.snap_magnet.setDisabled(True)
                 self.corner_snap_btn.setDisabled(True)
+            log.debug("FlatCAMGUI.__init__() --> UI layout restored from QSettings.")
         else:
             self.exc_edit_toolbar.setVisible(False)
             self.exc_edit_toolbar.setDisabled(True)
             self.geo_edit_toolbar.setVisible(False)
             self.geo_edit_toolbar.setDisabled(True)
+            self.grb_edit_toolbar.setVisible(False)
+            self.grb_edit_toolbar.setDisabled(True)
 
             self.corner_snap_btn.setVisible(False)
             self.snap_magnet.setVisible(False)
+            settings.setValue('layout', "standard")
+
+            # This will write the setting to the platform specific storage.
+            del settings
+            log.debug("FlatCAMGUI.__init__() --> UI layout restored from defaults. QSettings set to 'standard'")
 
     def eventFilter(self, obj, event):
         if self.general_defaults_form.general_app_group.toggle_tooltips_cb.get_value() is False:
@@ -1643,7 +1815,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.exc_edit_toolbar.addSeparator()
 
         self.copy_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _('Copy Drill'))
-        self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'),
+        self.delete_drill_btn = self.exc_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
                                                                 _("Delete Drill"))
 
         self.exc_edit_toolbar.addSeparator()
@@ -1676,7 +1848,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geo_edit_toolbar.addSeparator()
         self.geo_cutpath_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/cutpath32.png'), _('Cut Path'))
         self.geo_copy_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy Objects"))
-        self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/deleteshape32.png'),
+        self.geo_delete_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
                                                               _("Delete Shape"))
         self.geo_transform_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
                                                                  _("Transformations"))
@@ -1684,6 +1856,25 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
         self.geo_edit_toolbar.addSeparator()
         self.geo_move_btn = self.geo_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move Objects"))
 
+        ### Gerber Editor Toolbar ###
+        self.grb_select_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/pointer32.png'), _("Select"))
+        self.grb_add_pad_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/aperture32.png'), _("Add Pad"))
+        self.add_pad_ar_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/padarray32.png'), _('Add Pad Array'))
+        self.grb_add_track_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/track32.png'), _("Add Track"))
+        self.grb_add_region_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/polygon32.png'), _("Add Region"))
+        self.grb_edit_toolbar.addSeparator()
+
+        self.aperture_buffer_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/buffer16-2.png'), _('Buffer'))
+        self.aperture_scale_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/scale32.png'), _('Scale'))
+        self.grb_edit_toolbar.addSeparator()
+        self.aperture_copy_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/copy32.png'), _("Copy"))
+        self.aperture_delete_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/trash32.png'),
+                                                                   _("Delete"))
+        self.grb_transform_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/transform.png'),
+                                                                 _("Transformations"))
+        self.grb_edit_toolbar.addSeparator()
+        self.aperture_move_btn = self.grb_edit_toolbar.addAction(QtGui.QIcon('share/move32.png'), _("Move"))
+
         ### Snap Toolbar ###
         # Snap GRID toolbar is always active to facilitate usage of measurements done on GRID
         # self.addToolBar(self.snap_toolbar)
@@ -1729,14 +1920,18 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 self.exc_edit_toolbar.setDisabled(True)
                 self.geo_edit_toolbar.setVisible(False)
                 self.geo_edit_toolbar.setDisabled(True)
+                self.grb_edit_toolbar.setVisible(False)
+                self.grb_edit_toolbar.setDisabled(True)
 
                 self.corner_snap_btn.setVisible(False)
                 self.snap_magnet.setVisible(False)
-            elif layout == 'Compact':
+            elif layout == 'compact':
                 self.exc_edit_toolbar.setVisible(True)
                 self.exc_edit_toolbar.setDisabled(True)
                 self.geo_edit_toolbar.setVisible(True)
                 self.geo_edit_toolbar.setDisabled(True)
+                self.grb_edit_toolbar.setVisible(False)
+                self.grb_edit_toolbar.setDisabled(True)
 
                 self.corner_snap_btn.setVisible(True)
                 self.snap_magnet.setVisible(True)
@@ -1948,6 +2143,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 # Escape = Deselect All
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
                     self.app.on_deselect_all()
+
+                    # if in full screen, exit to normal view
+                    self.showNormal()
+                    self.app.restore_toolbar_view()
+                    self.splitter_left.setVisible(True)
+                    self.app.toggle_fscreen = False
+
                     # try to disconnect the slot from Set Origin
                     try:
                         self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_set_zero_click)
@@ -1961,6 +2163,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         select.ui.plot_cb.toggle()
                     self.app.delete_selection_shape()
 
+                # New Geometry
+                if key == QtCore.Qt.Key_B:
+                    self.app.new_gerber_object()
+
                 # Copy Object Name
                 if key == QtCore.Qt.Key_E:
                     self.app.object2editor()
@@ -2111,7 +2317,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
 
                             if self.app.geo_editor.active_tool.complete:
                                 self.app.geo_editor.on_shape_complete()
-                                self.app.inform.emit(_("[success]Done."))
+                                self.app.inform.emit(_("[success] Done."))
                             # automatically make the selection tool active after completing current action
                             self.app.geo_editor.select_tool('select')
                             return
@@ -2123,7 +2329,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
 
                             if self.app.geo_editor.active_tool.complete:
                                 self.app.geo_editor.on_shape_complete()
-                                self.app.inform.emit(_("[success]Done."))
+                                self.app.inform.emit(_("[success] Done."))
                             # automatically make the selection tool active after completing current action
                             self.app.geo_editor.select_tool('select')
 
@@ -2131,7 +2337,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
                     # TODO: ...?
                     # self.on_tool_select("select")
-                    self.app.inform.emit(_("[WARNING_NOTCL]Cancelled."))
+                    self.app.inform.emit(_("[WARNING_NOTCL] Cancelled."))
 
                     self.app.geo_editor.delete_utility_geometry()
 
@@ -2150,7 +2356,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                     self.app.geo_editor.delete_selected()
                     self.app.geo_editor.replot()
 
-                # Move
+                # Rotate
                 if key == QtCore.Qt.Key_Space or key == 'Space':
                     self.app.geo_editor.transform_tool.on_rotate_key()
 
@@ -2304,6 +2510,208 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 # Show Shortcut list
                 if key == 'F3':
                     self.app.on_shortcut_list()
+        elif self.app.call_source == 'grb_editor':
+            if modifiers == QtCore.Qt.ControlModifier:
+                # save (update) the current geometry and return to the App
+                if key == QtCore.Qt.Key_S or key == 'S':
+                    self.app.editor2object()
+                    return
+
+                # toggle the measurement tool
+                if key == QtCore.Qt.Key_M or key == 'M':
+                    self.app.measurement_tool.run()
+                    return
+
+            elif modifiers == QtCore.Qt.ShiftModifier:
+                pass
+            elif modifiers == QtCore.Qt.AltModifier:
+                # Transformation Tool
+                if key == QtCore.Qt.Key_R or key == 'R':
+                    self.app.grb_editor.on_transform()
+                    return
+
+            elif modifiers == QtCore.Qt.NoModifier:
+                # Abort the current action
+                if key == QtCore.Qt.Key_Escape or key == 'Escape':
+                    # self.on_tool_select("select")
+                    self.app.inform.emit(_("[WARNING_NOTCL] Cancelled."))
+
+                    self.app.grb_editor.delete_utility_geometry()
+
+                    # self.app.grb_editor.plot_all()
+                    self.app.grb_editor.active_tool.clean_up()
+                    self.app.grb_editor.select_tool('select')
+                    return
+
+                # Delete selected object if delete key event comes out of canvas
+                if key == 'Delete':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    if self.app.grb_editor.selected:
+                        self.app.grb_editor.delete_selected()
+                        self.app.grb_editor.plot_all()
+                    else:
+                        self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to delete."))
+                    return
+
+                # Delete aperture in apertures table if delete key event comes from the Selected Tab
+                if key == QtCore.Qt.Key_Delete:
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.grb_editor.on_aperture_delete()
+                    return
+
+                if key == QtCore.Qt.Key_Minus or key == '-':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.plotcanvas.zoom(1 / self.app.defaults['zoom_ratio'],
+                                             [self.app.grb_editor.snap_x, self.app.grb_editor.snap_y])
+                    return
+
+                if key == QtCore.Qt.Key_Equal or key == '=':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.plotcanvas.zoom(self.app.defaults['zoom_ratio'],
+                                             [self.app.grb_editor.snap_x, self.app.grb_editor.snap_y])
+                    return
+
+                # toggle display of Notebook area
+                if key == QtCore.Qt.Key_QuoteLeft or key == '`':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.on_toggle_notebook()
+                    return
+
+                # Rotate
+                if key == QtCore.Qt.Key_Space or key == 'Space':
+                    self.app.grb_editor.transform_tool.on_rotate_key()
+
+                # Switch to Project Tab
+                if key == QtCore.Qt.Key_1 or key == '1':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.on_select_tab('project')
+                    return
+
+                # Switch to Selected Tab
+                if key == QtCore.Qt.Key_2 or key == '2':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.on_select_tab('selected')
+                    return
+
+                # Switch to Tool Tab
+                if key == QtCore.Qt.Key_3 or key == '3':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.on_select_tab('tool')
+                    return
+
+                # Add Array of pads
+                if key == QtCore.Qt.Key_A or key == 'A':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.inform.emit("Click on target point.")
+                    self.app.ui.add_pad_ar_btn.setChecked(True)
+
+                    self.app.grb_editor.x = self.app.mouse[0]
+                    self.app.grb_editor.y = self.app.mouse[1]
+
+                    self.app.grb_editor.select_tool('array')
+                    return
+
+                # Scale Tool
+                if key == QtCore.Qt.Key_B or key == 'B':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.grb_editor.select_tool('buffer')
+                    return
+
+                # Copy
+                if key == QtCore.Qt.Key_C or key == 'C':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    if self.app.grb_editor.selected:
+                        self.app.inform.emit(_("Click on target point."))
+                        self.app.ui.aperture_copy_btn.setChecked(True)
+                        self.app.grb_editor.on_tool_select('copy')
+                        self.app.grb_editor.active_tool.set_origin(
+                            (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y))
+                    else:
+                        self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to copy."))
+                    return
+
+                # Grid Snap
+                if key == QtCore.Qt.Key_G or key == 'G':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    # make sure that the cursor shape is enabled/disabled, too
+                    if self.app.grb_editor.options['grid_snap'] is True:
+                        self.app.app_cursor.enabled = False
+                    else:
+                        self.app.app_cursor.enabled = True
+                    self.app.ui.grid_snap_btn.trigger()
+                    return
+
+                # Jump to coords
+                if key == QtCore.Qt.Key_J or key == 'J':
+                    self.app.on_jump_to()
+
+                # Corner Snap
+                if key == QtCore.Qt.Key_K or key == 'K':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.ui.corner_snap_btn.trigger()
+                    return
+
+                # Move
+                if key == QtCore.Qt.Key_M or key == 'M':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    if self.app.grb_editor.selected:
+                        self.app.inform.emit(_("Click on target point."))
+                        self.app.ui.aperture_move_btn.setChecked(True)
+                        self.app.grb_editor.on_tool_select('move')
+                        self.app.grb_editor.active_tool.set_origin(
+                            (self.app.grb_editor.snap_x, self.app.grb_editor.snap_y))
+                    else:
+                        self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to move."))
+                    return
+
+                # Add Region Tool
+                if key == QtCore.Qt.Key_N or key == 'N':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.grb_editor.select_tool('region')
+                    return
+
+                # Add Pad Tool
+                if key == QtCore.Qt.Key_P or key == 'P':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.inform.emit(_("Click on target point."))
+                    self.app.ui.add_pad_ar_btn.setChecked(True)
+
+                    self.app.grb_editor.x = self.app.mouse[0]
+                    self.app.grb_editor.y = self.app.mouse[1]
+
+                    self.app.grb_editor.select_tool('pad')
+                    return
+
+                # Scale Tool
+                if key == QtCore.Qt.Key_S or key == 'S':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.grb_editor.select_tool('scale')
+                    return
+
+                # Add Track
+                if key == QtCore.Qt.Key_T or key == 'T':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    ## Current application units in Upper Case
+                    self.app.grb_editor.select_tool('track')
+                    return
+
+                # Zoom Fit
+                if key == QtCore.Qt.Key_V or key == 'V':
+                    self.app.grb_editor.launched_from_shortcuts = True
+                    self.app.on_zoom_fit(None)
+                    return
+
+                # Propagate to tool
+                response = None
+                if self.app.grb_editor.active_tool is not None:
+                    response = self.app.grb_editor.active_tool.on_key(key=key)
+                if response is not None:
+                    self.app.inform.emit(response)
+
+                # Show Shortcut list
+                if key == QtCore.Qt.Key_F3 or key == 'F3':
+                    self.app.on_shortcut_list()
+                    return
         elif self.app.call_source == 'exc_editor':
             if modifiers == QtCore.Qt.ControlModifier:
                 # save (update) the current geometry and return to the App
@@ -2325,7 +2733,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if key == QtCore.Qt.Key_Escape or key == 'Escape':
                     # TODO: ...?
                     # self.on_tool_select("select")
-                    self.app.inform.emit(_("[WARNING_NOTCL]Cancelled."))
+                    self.app.inform.emit(_("[WARNING_NOTCL] Cancelled."))
 
                     self.app.exc_editor.delete_utility_geometry()
 
@@ -2342,7 +2750,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         self.app.exc_editor.delete_selected()
                         self.app.exc_editor.replot()
                     else:
-                        self.app.inform.emit(_("[WARNING_NOTCL]Cancelled. Nothing selected to delete."))
+                        self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to delete."))
                     return
 
                 # Delete tools in tools table if delete key event comes from the Selected Tab
@@ -2409,7 +2817,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         self.app.exc_editor.active_tool.set_origin(
                             (self.app.exc_editor.snap_x, self.app.exc_editor.snap_y))
                     else:
-                        self.app.inform.emit(_("[WARNING_NOTCL]Cancelled. Nothing selected to copy."))
+                        self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to copy."))
                     return
 
                 # Add Drill Hole Tool
@@ -2455,7 +2863,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                         self.app.exc_editor.active_tool.set_origin(
                             (self.app.exc_editor.snap_x, self.app.exc_editor.snap_y))
                     else:
-                        self.app.inform.emit(_("[WARNING_NOTCL]Cancelled. Nothing selected to move."))
+                        self.app.inform.emit(_("[WARNING_NOTCL] Cancelled. Nothing selected to move."))
                     return
 
                 # Resize Tool
@@ -2478,7 +2886,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                     if ok:
                         self.app.exc_editor.on_tool_add(tooldia=val)
                         self.app.inform.emit(
-                            _("[success]Added new tool with dia: {dia} {units}").format(dia='%.4f' % float(val), units=str(self.units)))
+                            _("[success] Added new tool with dia: {dia} {units}").format(dia='%.4f' % float(val), units=str(self.units)))
                     else:
                         self.app.inform.emit(
                             _("[WARNING_NOTCL] Adding Tool cancelled ..."))
@@ -2524,35 +2932,37 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
                 if self.filename == "":
                     self.app.inform.emit("Open cancelled.")
                 else:
-                    if self.filename.lower().rpartition('.')[-1] in self.app.grb_list:
+                    extension = self.filename.lower().rpartition('.')[-1]
+
+                    if extension in self.app.grb_list:
                         self.app.worker_task.emit({'fcn': self.app.open_gerber,
                                                    'params': [self.filename]})
                     else:
                         event.ignore()
 
-                    if self.filename.lower().rpartition('.')[-1] in self.app.exc_list:
+                    if extension in self.app.exc_list:
                         self.app.worker_task.emit({'fcn': self.app.open_excellon,
                                                    'params': [self.filename]})
                     else:
                         event.ignore()
 
-                    if self.filename.lower().rpartition('.')[-1] in self.app.gcode_list:
+                    if extension in self.app.gcode_list:
                         self.app.worker_task.emit({'fcn': self.app.open_gcode,
                                                    'params': [self.filename]})
                     else:
                         event.ignore()
 
-                    if self.filename.lower().rpartition('.')[-1] in self.app.svg_list:
+                    if extension in self.app.svg_list:
                         object_type = 'geometry'
                         self.app.worker_task.emit({'fcn': self.app.import_svg,
                                                    'params': [self.filename, object_type, None]})
 
-                    if self.filename.lower().rpartition('.')[-1] in self.app.dxf_list:
+                    if extension in self.app.dxf_list:
                         object_type = 'geometry'
                         self.app.worker_task.emit({'fcn': self.app.import_dxf,
                                                    'params': [self.filename, object_type, None]})
 
-                    if self.filename.lower().rpartition('.')[-1] in self.app.prj_list:
+                    if extension in self.app.prj_list:
                         # self.app.open_project() is not Thread Safe
                         self.app.open_project(self.filename)
                     else:
@@ -2561,16 +2971,17 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             event.ignore()
 
     def closeEvent(self, event):
-        grect = self.geometry()
-
-        # self.splitter.sizes()[0] is actually the size of the "notebook"
-        if not self.isMaximized():
-            self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height(), self.splitter.sizes()[0])
+        if self.app.save_in_progress:
+            self.app.inform.emit(_("[WARNING_NOTCL] Application is saving the project. Please wait ..."))
+        else:
+            grect = self.geometry()
 
-        self.final_save.emit()
+            # self.splitter.sizes()[0] is actually the size of the "notebook"
+            if not self.isMaximized():
+                self.geom_update.emit(grect.x(), grect.y(), grect.width(), grect.height(), self.splitter.sizes()[0])
 
-        if self.app.should_we_quit is False:
-            event.ignore()
+            self.final_save.emit()
+        event.ignore()
 
 
 class GeneralPreferencesUI(QtWidgets.QWidget):
@@ -3034,8 +3445,8 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
         )
         self.layout_combo = FCComboBox()
         # don't translate the QCombo items as they are used in QSettings and identified by name
-        self.layout_combo.addItem("Standard")
-        self.layout_combo.addItem("Compact")
+        self.layout_combo.addItem("standard")
+        self.layout_combo.addItem("compact")
 
         # Set the current index for layout_combo
         settings = QSettings("Open Source", "FlatCAM")
@@ -3124,24 +3535,24 @@ class GeneralGUISetGroupUI(OptionsGroupUI):
 
     def handle_clear(self):
         msgbox = QtWidgets.QMessageBox()
-        # msgbox.setText("<B>Save changes ...</B>")
         msgbox.setText(_("Are you sure you want to delete the GUI Settings? "
                        "\n")
                        )
         msgbox.setWindowTitle(_("Clear GUI Settings"))
         msgbox.setWindowIcon(QtGui.QIcon('share/trash32.png'))
-        msgbox.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
-        msgbox.setDefaultButton(QtWidgets.QMessageBox.No)
+        bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
+        bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
 
-        response = msgbox.exec_()
+        msgbox.setDefaultButton(bt_no)
+        msgbox.exec_()
+        response = msgbox.clickedButton()
 
-        if response == QtWidgets.QMessageBox.Yes:
+        if response == bt_yes:
             settings = QSettings("Open Source", "FlatCAM")
             for key in settings.allKeys():
                 settings.remove(key)
             # This will write the setting to the platform specific storage.
             del settings
-            self.app.inform.emit(_("[success] GUI settings deleted ..."))
 
 
 class GeneralAppPrefGroupUI(OptionsGroupUI):
@@ -3267,6 +3678,25 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
            _( "Check this box if you want to have toolTips displayed\n"
             "when hovering with mouse over items throughout the App.")
         )
+        self.worker_number_label = QtWidgets.QLabel(_('Workers number:'))
+        self.worker_number_label.setToolTip(
+            _("The number of Qthreads made available to the App.\n"
+              "A bigger number may finish the jobs more quickly but\n"
+              "depending on your computer speed, may make the App\n"
+              "unresponsive. Can have a value between 2 and 16.\n"
+              "Default value is 2.\n"
+              "After change, it will be applied at next App start.")
+        )
+        self.worker_number_sb = FCSpinner()
+        self.worker_number_sb.setToolTip(
+            _("The number of Qthreads made available to the App.\n"
+              "A bigger number may finish the jobs more quickly but\n"
+              "depending on your computer speed, may make the App\n"
+              "unresponsive. Can have a value between 2 and 16.\n"
+              "Default value is 2.\n"
+              "After change, it will be applied at next App start.")
+        )
+        self.worker_number_sb.set_range(2, 16)
 
         # Just to add empty rows
         self.spacelabel = QtWidgets.QLabel('')
@@ -3287,6 +3717,8 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
         self.form_box.addRow(self.project_startup_label, self.project_startup_cb)
         self.form_box.addRow(self.project_autohide_label, self.project_autohide_cb)
         self.form_box.addRow(self.toggle_tooltips_label, self.toggle_tooltips_cb)
+        self.form_box.addRow(self.worker_number_label, self.worker_number_sb)
+
         self.form_box.addRow(self.spacelabel, self.spacelabel)
 
         # Add the QFormLayout that holds the Application general defaults
@@ -4787,7 +5219,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI):
         self.ncc_tool_dia_entry = FCEntry()
         grid0.addWidget(self.ncc_tool_dia_entry, 0, 1)
 
-        nccoverlabel = QtWidgets.QLabel(_('Overlap:'))
+        nccoverlabel = QtWidgets.QLabel(_('Overlap Rate:'))
         nccoverlabel.setToolTip(
            _( "How much (fraction) of the tool width to overlap each tool pass.\n"
             "Example:\n"
@@ -4929,6 +5361,15 @@ class ToolsCutoutPrefGroupUI(OptionsGroupUI):
             self.gaps_combo.addItem(it)
             self.gaps_combo.setStyleSheet('background-color: rgb(255,255,255)')
 
+        # Surrounding convex box shape
+        self.convex_box = FCCheckBox()
+        self.convex_box_label = QtWidgets.QLabel(_("Convex Sh.:"))
+        self.convex_box_label.setToolTip(
+            _("Create a convex shape surrounding the entire PCB.")
+        )
+        grid0.addWidget(self.convex_box_label, 4, 0)
+        grid0.addWidget(self.convex_box, 4, 1)
+
         self.layout.addStretch()
 
 
@@ -5023,7 +5464,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.painttooldia_entry, 0, 1)
 
         # Overlap
-        ovlabel = QtWidgets.QLabel(_('Overlap:'))
+        ovlabel = QtWidgets.QLabel(_('Overlap Rate:'))
         ovlabel.setToolTip(
             _("How much (fraction) of the tool\n"
             "width to overlap each tool pass.")

+ 326 - 99
flatcamGUI/GUIElements.py

@@ -490,6 +490,106 @@ class FCTextAreaRich(QtWidgets.QTextEdit):
         return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
 
 
+class FCTextAreaExtended(QtWidgets.QTextEdit):
+    def __init__(self, parent=None):
+        super(FCTextAreaExtended, self).__init__(parent)
+
+        self.completer = MyCompleter()
+
+        self.model = QtCore.QStringListModel()
+        self.completer.setModel(self.model)
+        self.set_model_data(keyword_list=[])
+        self.completer.insertText.connect(self.insertCompletion)
+
+        self.completer_enable = False
+
+    def set_model_data(self, keyword_list):
+        self.model.setStringList(keyword_list)
+
+    def insertCompletion(self, completion):
+        tc = self.textCursor()
+        extra = (len(completion) - len(self.completer.completionPrefix()))
+
+        # don't insert if the word is finished but add a space instead
+        if extra == 0:
+            tc.insertText(' ')
+            self.completer.popup().hide()
+            return
+
+        tc.movePosition(QTextCursor.Left)
+        tc.movePosition(QTextCursor.EndOfWord)
+        tc.insertText(completion[-extra:])
+        # add a space after inserting the word
+        tc.insertText(' ')
+        self.setTextCursor(tc)
+        self.completer.popup().hide()
+
+    def focusInEvent(self, event):
+        if self.completer:
+            self.completer.setWidget(self)
+        QTextEdit.focusInEvent(self, event)
+
+    def set_value(self, val):
+        self.setText(val)
+
+    def get_value(self):
+        self.toPlainText()
+
+    def insertFromMimeData(self, data):
+        """
+        Reimplemented such that when SHIFT is pressed and doing click Paste in the contextual menu, the '\' symbol
+        is replaced with the '/' symbol. That's because of the difference in path separators in Windows and TCL
+        :param data:
+        :return:
+        """
+        modifier = QtWidgets.QApplication.keyboardModifiers()
+        if modifier == Qt.ShiftModifier:
+            text = data.text()
+            text = text.replace('\\', '/')
+            self.insertPlainText(text)
+        else:
+            self.insertPlainText(data.text())
+
+    def keyPressEvent(self, event):
+        """
+        Reimplemented so the CTRL + SHIFT + V shortcut key combo will paste the text but replacing '\' with '/'
+        :param event:
+        :return:
+        """
+        key = event.key()
+        modifier = QtWidgets.QApplication.keyboardModifiers()
+
+        if modifier & Qt.ControlModifier and modifier & Qt.ShiftModifier:
+            if key == QtCore.Qt.Key_V:
+                clipboard = QtWidgets.QApplication.clipboard()
+                clip_text = clipboard.text()
+                clip_text = clip_text.replace('\\', '/')
+                self.insertPlainText(clip_text)
+
+        tc = self.textCursor()
+        if (key == Qt.Key_Tab or key == Qt.Key_Enter or key == Qt.Key_Return) and self.completer.popup().isVisible():
+            self.completer.insertText.emit(self.completer.getSelected())
+            self.completer.setCompletionMode(QCompleter.PopupCompletion)
+            return
+        else:
+            super(FCTextAreaExtended, self).keyPressEvent(event)
+
+        if self.completer_enable:
+            tc.select(QTextCursor.WordUnderCursor)
+            cr = self.cursorRect()
+
+            if len(tc.selectedText()) > 0:
+                self.completer.setCompletionPrefix(tc.selectedText())
+                popup = self.completer.popup()
+                popup.setCurrentIndex(self.completer.completionModel().index(0, 0))
+
+                cr.setWidth(self.completer.popup().sizeHintForColumn(0)
+                            + self.completer.popup().verticalScrollBar().sizeHint().width())
+                self.completer.complete(cr)
+            else:
+                self.completer.popup().hide()
+
+
 class FCComboBox(QtWidgets.QComboBox):
 
     def __init__(self, parent=None, callback=None):
@@ -595,9 +695,12 @@ class FCTab(QtWidgets.QTabWidget):
 
 
 class FCDetachableTab(QtWidgets.QTabWidget):
-    # From here: https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
-    def __init__(self, protect=None, protect_by_name=None, parent=None):
+    """
+    From here:
+    https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget
+    """
 
+    def __init__(self, protect=None, protect_by_name=None, parent=None):
         super().__init__()
 
         self.tabBar = self.FCTabBar(self)
@@ -639,25 +742,38 @@ class FCDetachableTab(QtWidgets.QTabWidget):
         self.removeTab(currentIndex)
 
     def closeTab(self, currentIndex):
+        """
+        Slot connected to the tabCloseRequested signal
+
+        :param currentIndex:
+        :return:
+        """
         self.removeTab(currentIndex)
 
     def protectTab(self, currentIndex):
         # self.FCTabBar().setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
         self.tabBar.setTabButton(currentIndex, QtWidgets.QTabBar.RightSide, None)
 
-    ##
-    #  The default movable functionality of QTabWidget must remain disabled
-    #  so as not to conflict with the added features
     def setMovable(self, movable):
+        """
+        The default movable functionality of QTabWidget must remain disabled
+        so as not to conflict with the added features
+
+        :param movable:
+        :return:
+        """
         pass
 
-    ##
-    #  Move a tab from one position (index) to another
-    #
-    #  @param    fromIndex    the original index location of the tab
-    #  @param    toIndex      the new index location of the tab
     @pyqtSlot(int, int)
     def moveTab(self, fromIndex, toIndex):
+        """
+        Move a tab from one position (index) to another
+
+        :param fromIndex:   the original index location of the tab
+        :param toIndex:     the new index location of the tab
+        :return:
+        """
+
         widget = self.widget(fromIndex)
         icon = self.tabIcon(fromIndex)
         text = self.tabText(fromIndex)
@@ -666,15 +782,16 @@ class FCDetachableTab(QtWidgets.QTabWidget):
         self.insertTab(toIndex, widget, icon, text)
         self.setCurrentIndex(toIndex)
 
-    ##
-    #  Detach the tab by removing it's contents and placing them in
-    #  a DetachedTab window
-    #
-    #  @param    index    the index location of the tab to be detached
-    #  @param    point    the screen position for creating the new DetachedTab window
     @pyqtSlot(int, QtCore.QPoint)
     def detachTab(self, index, point):
+        """
+        Detach the tab by removing it's contents and placing them in
+        a DetachedTab window
 
+        :param index:   the index location of the tab to be detached
+        :param point:   the screen position for creating the new DetachedTab window
+        :return:
+        """
         self.old_index = index
 
         # Get the tab content and add name FlatCAM to the tab so we know on which app is this tab linked
@@ -699,20 +816,20 @@ class FCDetachableTab(QtWidgets.QTabWidget):
         detachedTab.move(point)
         detachedTab.show()
 
-
         # Create a reference to maintain access to the detached tab
         self.detachedTabs[name] = detachedTab
 
-
-    ##
-    #  Re-attach the tab by removing the content from the DetachedTab window,
-    #  closing it, and placing the content back into the DetachableTabWidget
-    #
-    #  @param    contentWidget    the content widget from the DetachedTab window
-    #  @param    name             the name of the detached tab
-    #  @param    icon             the window icon for the detached tab
-    #  @param    insertAt         insert the re-attached tab at the given index
     def attachTab(self, contentWidget, name, icon, insertAt=None):
+        """
+        Re-attach the tab by removing the content from the DetachedTab window,
+        closing it, and placing the content back into the DetachableTabWidget
+
+        :param contentWidget:   the content widget from the DetachedTab window
+        :param name:            the name of the detached tab
+        :param icon:            the window icon for the detached tab
+        :param insertAt:        insert the re-attached tab at the given index
+        :return:
+        """
 
         # Make the content widget a child of this widget
         contentWidget.setParent(self)
@@ -773,11 +890,13 @@ class FCDetachableTab(QtWidgets.QTabWidget):
             if index > -1:
                 self.setCurrentIndex(insert_index) if self.use_old_index else self.setCurrentIndex(index)
 
-    ##
-    #  Remove the tab with the given name, even if it is detached
-    #
-    #  @param    name    the name of the tab to be removed
     def removeTabByName(self, name):
+        """
+        Remove the tab with the given name, even if it is detached
+
+        :param name: the name of the tab to be removed
+        :return:
+        """
 
         # Remove the tab if it is attached
         attached = False
@@ -798,17 +917,18 @@ class FCDetachableTab(QtWidgets.QTabWidget):
                     del self.detachedTabs[key]
                     break
 
-
-    ##
-    #  Handle dropping of a detached tab inside the DetachableTabWidget
-    #
-    #  @param    name     the name of the detached tab
-    #  @param    index    the index of an existing tab (if the tab bar
-    #                     determined that the drop occurred on an
-    #                     existing tab)
-    #  @param    dropPos  the mouse cursor position when the drop occurred
     @QtCore.pyqtSlot(str, int, QtCore.QPoint)
     def detachedTabDrop(self, name, index, dropPos):
+        """
+        Handle dropping of a detached tab inside the DetachableTabWidget
+
+        :param name:        the name of the detached tab
+        :param index:       the index of an existing tab (if the tab bar
+    #                       determined that the drop occurred on an
+    #                       existing tab)
+        :param dropPos:     the mouse cursor position when the drop occurred
+        :return:
+        """
 
         # If the drop occurred on an existing tab, insert the detached
         # tab at the existing tab's location
@@ -848,10 +968,12 @@ class FCDetachableTab(QtWidgets.QTabWidget):
                     # automatically
                     self.detachedTabs[name].close()
 
-
-    ##
-    #  Close all tabs that are currently detached.
     def closeDetachedTabs(self):
+        """
+        Close all tabs that are currently detached.
+
+        :return:
+        """
         listOfDetachedTabs = []
 
         for key in self.detachedTabs:
@@ -860,11 +982,12 @@ class FCDetachableTab(QtWidgets.QTabWidget):
         for detachedTab in listOfDetachedTabs:
             detachedTab.close()
 
-
-    ##
-    #  When a tab is detached, the contents are placed into this QMainWindow.  The tab
-    #  can be re-attached by closing the dialog or by dragging the window into the tab bar
     class FCDetachedTab(QtWidgets.QMainWindow):
+        """
+        When a tab is detached, the contents are placed into this QMainWindow.  The tab
+        can be re-attached by closing the dialog or by dragging the window into the tab bar
+        """
+
         onCloseSignal = pyqtSignal(QtWidgets.QWidget, str, QtGui.QIcon)
         onDropSignal = pyqtSignal(str, QtCore.QPoint)
 
@@ -882,42 +1005,46 @@ class FCDetachableTab(QtWidgets.QTabWidget):
             self.installEventFilter(self.windowDropFilter)
             self.windowDropFilter.onDropSignal.connect(self.windowDropSlot)
 
-
-        ##
-        #  Handle a window drop event
-        #
-        #  @param    dropPos    the mouse cursor position of the drop
         @QtCore.pyqtSlot(QtCore.QPoint)
         def windowDropSlot(self, dropPos):
-            self.onDropSignal.emit(self.objectName(), dropPos)
+            """
+            Handle a window drop event
 
+            :param dropPos:     the mouse cursor position of the drop
+            :return:
+            """
+            self.onDropSignal.emit(self.objectName(), dropPos)
 
-        ##
-        #  If the window is closed, emit the onCloseSignal and give the
-        #  content widget back to the DetachableTabWidget
-        #
-        #  @param    event    a close event
         def closeEvent(self, event):
-            self.onCloseSignal.emit(self.contentWidget, self.objectName(), self.windowIcon())
+            """
+            If the window is closed, emit the onCloseSignal and give the
+            content widget back to the DetachableTabWidget
 
+            :param event:    a close event
+            :return:
+            """
+            self.onCloseSignal.emit(self.contentWidget, self.objectName(), self.windowIcon())
 
-        ##
-        #  An event filter class to detect a QMainWindow drop event
         class WindowDropFilter(QtCore.QObject):
+            """
+            An event filter class to detect a QMainWindow drop event
+            """
+
             onDropSignal = pyqtSignal(QtCore.QPoint)
 
             def __init__(self):
                 QtCore.QObject.__init__(self)
                 self.lastEvent = None
 
-
-            ##
-            #  Detect a QMainWindow drop event by looking for a NonClientAreaMouseMove (173)
-            #  event that immediately follows a Move event
-            #
-            #  @param    obj    the object that generated the event
-            #  @param    event  the current event
             def eventFilter(self, obj, event):
+                """
+                Detect a QMainWindow drop event by looking for a NonClientAreaMouseMove (173)
+                event that immediately follows a Move event
+
+                :param obj:     the object that generated the event
+                :param event:   the current event
+                :return:
+                """
 
                 # If a NonClientAreaMouseMove (173) event immediately follows a Move event...
                 if self.lastEvent == QtCore.QEvent.Move and event.type() == 173:
@@ -951,19 +1078,24 @@ class FCDetachableTab(QtWidgets.QTabWidget):
             self.mouseCursor = QtGui.QCursor()
             self.dragInitiated = False
 
-
-        #  Send the onDetachTabSignal when a tab is double clicked
-        #
-        #  @param    event    a mouse double click event
         def mouseDoubleClickEvent(self, event):
+            """
+            Send the onDetachTabSignal when a tab is double clicked
+
+            :param event:   a mouse double click event
+            :return:
+            """
+
             event.accept()
             self.onDetachTabSignal.emit(self.tabAt(event.pos()), self.mouseCursor.pos())
 
-
-        #  Set the starting position for a drag event when the mouse button is pressed
-        #
-        #  @param    event    a mouse press event
         def mousePressEvent(self, event):
+            """
+            Set the starting position for a drag event when the mouse button is pressed
+
+            :param event:   a mouse press event
+            :return:
+            """
             if event.button() == QtCore.Qt.LeftButton:
                 self.dragStartPos = event.pos()
 
@@ -974,14 +1106,15 @@ class FCDetachableTab(QtWidgets.QTabWidget):
 
             QtWidgets.QTabBar.mousePressEvent(self, event)
 
-
-        #  Determine if the current movement is a drag.  If it is, convert it into a QDrag.  If the
-        #  drag ends inside the tab bar, emit an onMoveTabSignal.  If the drag ends outside the tab
-        #  bar, emit an onDetachTabSignal.
-        #
-        #  @param    event    a mouse move event
         def mouseMoveEvent(self, event):
-
+            """
+            Determine if the current movement is a drag.  If it is, convert it into a QDrag.  If the
+            drag ends inside the tab bar, emit an onMoveTabSignal.  If the drag ends outside the tab
+            bar, emit an onDetachTabSignal.
+
+            :param event:   a mouse move event
+            :return:
+            """
             # Determine if the current movement is detected as a drag
             if not self.dragStartPos.isNull() and ((event.pos() - self.dragStartPos).manhattanLength() < QtWidgets.QApplication.startDragDistance()):
                 self.dragInitiated = True
@@ -1038,29 +1171,40 @@ class FCDetachableTab(QtWidgets.QTabWidget):
             else:
                 QtWidgets.QTabBar.mouseMoveEvent(self, event)
 
-        #  Determine if the drag has entered a tab position from another tab position
-        #
-        #  @param    event    a drag enter event
         def dragEnterEvent(self, event):
+            """
+            Determine if the drag has entered a tab position from another tab position
+
+            :param event:   a drag enter event
+            :return:
+            """
             mimeData = event.mimeData()
             # formats = mcd imeData.formats()
 
-        # if formats.contains('action') and mimeData.data('action') == 'application/tab-detach':
-        # event.acceptProposedAction()
+            # if formats.contains('action') and mimeData.data('action') == 'application/tab-detach':
+            # event.acceptProposedAction()
 
             QtWidgets.QTabBar.dragMoveEvent(self, event)
 
-        #  Get the position of the end of the drag
-        #
-        #  @param    event    a drop event
         def dropEvent(self, event):
+            """
+            Get the position of the end of the drag
+
+            :param event:    a drop event
+            :return:
+            """
             self.dragDropedPos = event.pos()
             QtWidgets.QTabBar.dropEvent(self, event)
 
-        #  Determine if the detached tab drop event occurred on an existing tab,
-        #  then send the event to the DetachableTabWidget
         def detachedTabDrop(self, name, dropPos):
-
+            """
+            Determine if the detached tab drop event occurred on an existing tab,
+            then send the event to the DetachableTabWidget
+
+            :param name:
+            :param dropPos:
+            :return:
+            """
             tabDropPos = self.mapFromGlobal(dropPos)
 
             index = self.tabAt(tabDropPos)
@@ -1192,9 +1336,64 @@ class FCTable(QtWidgets.QTableWidget):
         self.addAction(action)
         action.triggered.connect(call_function)
 
+
+class SpinBoxDelegate(QtWidgets.QItemDelegate):
+
+    def __init__(self, units):
+        super(SpinBoxDelegate, self).__init__()
+        self.units = units
+        self.current_value = None
+
+    def createEditor(self, parent, option, index):
+        editor = QtWidgets.QDoubleSpinBox(parent)
+        editor.setMinimum(-999.9999)
+        editor.setMaximum(999.9999)
+
+        if self.units == 'MM':
+            editor.setDecimals(2)
+        else:
+            editor.setDecimals(3)
+
+        return editor
+
+    def setEditorData(self, spinBox, index):
+        try:
+            value = float(index.model().data(index, Qt.EditRole))
+        except ValueError:
+            value = self.current_value
+            # return
+
+        spinBox.setValue(value)
+
+    def setModelData(self, spinBox, model, index):
+        spinBox.interpretText()
+        value = spinBox.value()
+        self.current_value = value
+
+        model.setData(index, value, Qt.EditRole)
+
+    def updateEditorGeometry(self, editor, option, index):
+        editor.setGeometry(option.rect)
+
+    def setDecimals(self, spinbox, digits):
+        spinbox.setDecimals(digits)
+
+
 class FCSpinner(QtWidgets.QSpinBox):
     def __init__(self, parent=None):
         super(FCSpinner, self).__init__(parent)
+        self.readyToEdit = True
+
+    def mousePressEvent(self, e, parent=None):
+        super(FCSpinner, self).mousePressEvent(e)  # required to deselect on 2e click
+        if self.readyToEdit:
+            self.lineEdit().selectAll()
+            self.readyToEdit = False
+
+    def focusOutEvent(self, e):
+        super(FCSpinner, self).focusOutEvent(e)  # required to remove cursor on focusOut
+        self.lineEdit().deselect()
+        self.readyToEdit = True
 
     def get_value(self):
         return str(self.value())
@@ -1207,6 +1406,9 @@ class FCSpinner(QtWidgets.QSpinBox):
             return
         self.setValue(k)
 
+    def set_range(self, min_val, max_val):
+        self.setRange(min_val, max_val)
+
     # def sizeHint(self):
     #     default_hint_size = super(FCSpinner, self).sizeHint()
     #     return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
@@ -1243,7 +1445,7 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
         self.setDecimals(val)
 
     def set_range(self, min_val, max_val):
-        self.setRange(self, min_val, max_val)
+        self.setRange(min_val, max_val)
 
 
 class Dialog_box(QtWidgets.QWidget):
@@ -1261,7 +1463,20 @@ class Dialog_box(QtWidgets.QWidget):
         dialog_box.setFixedWidth(290)
         self.setWindowIcon(icon)
 
-        self.location, self.ok = dialog_box.getText(self, title, label)
+        self.location, self.ok = dialog_box.getText(self, title, label, text="0, 0")
+        self.readyToEdit = True
+
+    def mousePressEvent(self, e, parent=None):
+        super(Dialog_box, self).mousePressEvent(e)  # required to deselect on 2e click
+        if self.readyToEdit:
+            self.lineEdit().selectAll()
+            self.readyToEdit = False
+
+    def focusOutEvent(self, e):
+        super(Dialog_box, self).focusOutEvent(e)  # required to remove cursor on focusOut
+        self.lineEdit().deselect()
+        self.readyToEdit = True
+
 
 
 class _BrowserTextEdit(QTextEdit):
@@ -1318,9 +1533,18 @@ class _ExpandableTextEdit(QTextEdit):
     def insertCompletion(self, completion):
         tc = self.textCursor()
         extra = (len(completion) - len(self.completer.completionPrefix()))
+
+        # don't insert if the word is finished but add a space instead
+        if extra == 0:
+            tc.insertText(' ')
+            self.completer.popup().hide()
+            return
+
         tc.movePosition(QTextCursor.Left)
         tc.movePosition(QTextCursor.EndOfWord)
         tc.insertText(completion[-extra:])
+        # add a space after inserting the word
+        tc.insertText(' ')
         self.setTextCursor(tc)
         self.completer.popup().hide()
 
@@ -1333,6 +1557,13 @@ class _ExpandableTextEdit(QTextEdit):
         """
         Catch keyboard events. Process Enter, Up, Down
         """
+
+        key = event.key()
+        if (key == Qt.Key_Tab or key == Qt.Key_Return or key == Qt.Key_Enter) and self.completer.popup().isVisible():
+            self.completer.insertText.emit(self.completer.getSelected())
+            self.completer.setCompletionMode(QCompleter.PopupCompletion)
+            return
+
         if event.matches(QKeySequence.InsertParagraphSeparator):
             text = self.toPlainText()
             if self._termWidget.is_command_complete(text):
@@ -1363,10 +1594,6 @@ class _ExpandableTextEdit(QTextEdit):
             return self._termWidget.browser().keyPressEvent(event)
 
         tc = self.textCursor()
-        if event.key() == Qt.Key_Tab and self.completer.popup().isVisible():
-            self.completer.insertText.emit(self.completer.getSelected())
-            self.completer.setCompletionMode(QCompleter.PopupCompletion)
-            return
 
         QTextEdit.keyPressEvent(self, event)
         tc.select(QTextCursor.WordUnderCursor)

+ 7 - 89
flatcamGUI/ObjectUI.py

@@ -101,7 +101,7 @@ class ObjectUI(QtWidgets.QWidget):
         self.scale_button.setToolTip(
             _("Perform scaling operation.")
         )
-        self.scale_button.setFixedWidth(50)
+        self.scale_button.setFixedWidth(70)
         self.scale_grid.addWidget(self.scale_button, 0, 2)
 
         #### Offset ####
@@ -128,7 +128,7 @@ class ObjectUI(QtWidgets.QWidget):
         self.offset_button.setToolTip(
             _("Perform the offset operation.")
         )
-        self.offset_button.setFixedWidth(50)
+        self.offset_button.setFixedWidth(70)
         self.offset_grid.addWidget(self.offset_button, 0, 2)
 
         layout.addStretch()
@@ -244,79 +244,8 @@ class GerberObjectUI(ObjectUI):
             _("Mark the aperture instances on canvas."))
         # self.apertures_table.setColumnHidden(5, True)
 
-        #### Aperture Scale ####
-        self.transform_aperture_grid = QtWidgets.QGridLayout()
-        self.custom_box.addLayout(self.transform_aperture_grid)
-
-        # Scale Aperture Factor
-        self.scale_aperture_label = QtWidgets.QLabel(_('Scale Factor:'))
-        self.scale_aperture_label.setToolTip(
-            _("Change the size of the selected apertures.\n"
-            "Factor by which to multiply\n"
-            "geometric features of this object.")
-        )
-        self.scale_aperture_label.setFixedWidth(90)
-        self.transform_aperture_grid.addWidget(self.scale_aperture_label, 0, 0)
-
-        self.scale_aperture_entry = FloatEntry2()
-        self.transform_aperture_grid.addWidget(self.scale_aperture_entry, 0, 1)
-
-        # Scale Button
-        self.scale_aperture_button = QtWidgets.QPushButton(_('Scale'))
-        self.scale_aperture_button.setToolTip(
-            _("Perform scaling operation on the selected apertures.")
-        )
-        self.scale_aperture_button.setFixedWidth(50)
-        self.transform_aperture_grid.addWidget(self.scale_aperture_button, 0, 2)
-
-        # Buffer Aperture Factor
-        self.buffer_aperture_label = QtWidgets.QLabel(_('Buffer Factor:'))
-        self.buffer_aperture_label.setToolTip(
-            _("Change the size of the selected apertures.\n"
-            "Factor by which to expand/shrink\n"
-            "geometric features of this object.")
-        )
-        self.buffer_aperture_label.setFixedWidth(90)
-        self.transform_aperture_grid.addWidget(self.buffer_aperture_label, 1, 0)
-
-        self.buffer_aperture_entry = FloatEntry2()
-        self.transform_aperture_grid.addWidget(self.buffer_aperture_entry, 1, 1)
-
-        # Buffer Button
-        self.buffer_aperture_button = QtWidgets.QPushButton(_('Buffer'))
-        self.buffer_aperture_button.setToolTip(
-            _("Perform buffer operation on the selected apertures.")
-        )
-        self.buffer_aperture_button.setFixedWidth(50)
-        self.transform_aperture_grid.addWidget(self.buffer_aperture_button, 1, 2)
-
-        new_hlay = QtWidgets.QHBoxLayout()
-        self.custom_box.addLayout(new_hlay)
-
-        self.new_grb_label = QtWidgets.QLabel(_("<b>Generate new Gerber Object:</b>"))
-        self.new_grb_label.setToolTip(
-            _("Will generate a new Gerber object from the changed apertures.")
-        )
-        new_hlay.addWidget(self.new_grb_label)
-
-        new_hlay.addStretch()
-
-        self.new_grb_button = FCButton(_('Go'))
-        self.new_grb_button.setToolTip(
-            _("Will generate a new Gerber object from the changed apertures.\n"
-            "This new object can then be isolated etc."))
-        self.new_grb_button.setFixedWidth(50)
-        new_hlay.addWidget(self.new_grb_button)
-
         # start with apertures table hidden
         self.apertures_table.setVisible(False)
-        self.scale_aperture_label.setVisible(False)
-        self.scale_aperture_entry.setVisible(False)
-        self.scale_aperture_button.setVisible(False)
-
-        self.buffer_aperture_label.setVisible(False)
-        self.buffer_aperture_entry.setVisible(False)
-        self.buffer_aperture_button.setVisible(False)
 
         # Isolation Routing
         self.isolation_routing_label = QtWidgets.QLabel(_("<b>Isolation Routing:</b>"))
@@ -1601,13 +1530,6 @@ class CNCObjectUI(ObjectUI):
                 "a Custom Toolchange GCode (macro)."
             )
         )
-        cnclay.addWidget(self.toolchange_cb)
-
-        self.toolch_ois = OptionalInputSection(self.toolchange_cb, [self.toolchangelabel, self.toolchange_text])
-        cnclay.addStretch()
-
-        cnclay1 = QtWidgets.QHBoxLayout()
-        self.cnc_box.addLayout(cnclay1)
 
         # Variable list
         self.tc_variable_combo = FCComboBox()
@@ -1618,7 +1540,6 @@ class CNCObjectUI(ObjectUI):
                 "They have to be surrounded by the '%' symbol"
             )
         )
-        cnclay1.addWidget(self.tc_variable_combo)
 
         # Populate the Combo Box
         variables = [_('Parameters'), 'tool', 'tooldia', 't_drills', 'x_toolchange', 'y_toolchange', 'z_toolchange',
@@ -1638,15 +1559,12 @@ class CNCObjectUI(ObjectUI):
         self.tc_variable_combo.setItemData(11, _("dwelltime = time to dwell to allow the spindle to reach it's set RPM"),
                                            Qt.ToolTipRole)
 
-        cnclay1.addStretch()
+        cnclay.addWidget(self.toolchange_cb)
+        cnclay.addStretch()
+        cnclay.addWidget(self.tc_variable_combo)
 
-        # Insert Variable into the Toolchange G-Code Text Box
-        # self.tc_insert_buton = FCButton("Insert")
-        # self.tc_insert_buton.setToolTip(
-        #     "Insert the variable in the GCode Box\n"
-        #     "surrounded by the '%' symbol."
-        # )
-        # cnclay1.addWidget(self.tc_insert_buton)
+        self.toolch_ois = OptionalInputSection(self.toolchange_cb,
+                                               [self.toolchangelabel, self.toolchange_text, self.tc_variable_combo])
 
         h_lay = QtWidgets.QHBoxLayout()
         h_lay.setAlignment(QtCore.Qt.AlignVCenter)

+ 5 - 2
flatcamGUI/PlotCanvas.py

@@ -148,8 +148,11 @@ class PlotCanvas(QtCore.QObject):
     def vis_connect(self, event_name, callback):
         return getattr(self.vispy_canvas.events, event_name).connect(callback)
 
-    def vis_disconnect(self, event_name, callback):
-        getattr(self.vispy_canvas.events, event_name).disconnect(callback)
+    def vis_disconnect(self, event_name, callback=None):
+        if callback is None:
+            getattr(self.vispy_canvas.events, event_name).disconnect()
+        else:
+            getattr(self.vispy_canvas.events, event_name).disconnect(callback)
 
     def zoom(self, factor, center=None):
         """

+ 5 - 2
flatcamGUI/VisPyVisuals.py

@@ -437,8 +437,11 @@ class TextGroup(object):
         :param value: bool
         """
         self._visible = value
-        self._collection.data[self._index]['visible'] = value
-
+        try:
+            self._collection.data[self._index]['visible'] = value
+        except KeyError:
+            print("VisPyVisuals.TextGroup.visible --> KeyError")
+            pass
         self._collection.redraw()
 
 

+ 21 - 25
flatcamParsers/ParseSVG.py

@@ -56,9 +56,9 @@ def svgparselength(lengthstr):
 def path2shapely(path, object_type, res=1.0):
     """
     Converts an svg.path.Path into a Shapely
-    LinearRing or LinearString.
+    Polygon or LinearString.
 
-    :rtype : LinearRing
+    :rtype : Polygon
     :rtype : LineString
     :param path: svg.path.Path instance
     :param res: Resolution (minimum step along path)
@@ -68,6 +68,7 @@ def path2shapely(path, object_type, res=1.0):
     points = []
     geometry = []
     geo_element = None
+    rings = []
 
     for component in path:
 
@@ -109,35 +110,30 @@ def path2shapely(path, object_type, res=1.0):
 
         # Move
         if isinstance(component, Move):
-            if object_type == 'geometry':
-                geo_element = LineString(points)
-            elif object_type == 'gerber':
-                # Didn't worked out using Polygon because if there is a large outline it will envelope everything
-                # and create issues with intersections. I will let the parameter obj_type present though
-                # geo_element = Polygon(points)
-                geo_element = LineString(points)
-            else:
-                log.error("[ERROR]: Not a valid target object.")
             if not points:
                 continue
             else:
-                geometry.append(geo_element)
+                rings.append(points)
                 points = []
             continue
         log.warning("I don't know what this is: %s" % str(component))
         continue
 
-    # if there are still points in points then add them as a LineString
+    # if there are still points in points then add them to the last ring
+
     if points:
-        geo_element = LineString(points)
+        rings.append(points)
+    if len(rings) > 0:
+        if len(rings) == 1:
+            # Polygons are closed and require more than 2 points
+            if Point(rings[0][0]).almost_equals(Point(rings[0][-1])) and len(rings[0]) > 2:
+                geo_element = Polygon(rings[0])
+            else:
+                geo_element = LineString(rings[0])
+        else:
+            geo_element = Polygon(rings[0], rings[1:])
         geometry.append(geo_element)
-        points = []
 
-    # if path.closed:
-    #     return Polygon(points).buffer(0)
-    #     # return LinearRing(points)
-    # else:
-    #     return LineString(points)
     return geometry
 
 def svgrect2shapely(rect, n_points=32):
@@ -362,7 +358,7 @@ def getsvggeo(node, object_type):
                 if tr[0] == 'translate':
                     geo = [translate(geoi, tr[1], tr[2]) for geoi in geo]
                 elif tr[0] == 'scale':
-                    geo = [scale(geoi, tr[0], tr[1], origin=(0, 0))
+                    geo = [scale(geoi, tr[1], tr[2], origin=(0, 0))
                            for geoi in geo]
                 elif tr[0] == 'rotate':
                     geo = [rotate(geoi, tr[1], origin=(tr[2], tr[3]))
@@ -459,7 +455,7 @@ def getsvgtext(node, object_type, units='MM'):
                 if tr[0] == 'translate':
                     geo = [translate(geoi, tr[1], tr[2]) for geoi in geo]
                 elif tr[0] == 'scale':
-                    geo = [scale(geoi, tr[0], tr[1], origin=(0, 0))
+                    geo = [scale(geoi, tr[1], tr[2], origin=(0, 0))
                            for geoi in geo]
                 elif tr[0] == 'rotate':
                     geo = [rotate(geoi, tr[1], origin=(tr[2], tr[3]))
@@ -592,7 +588,7 @@ def parse_svg_transform(trstr):
             trlist.append([
                 'translate',
                 float(match.group(1)),
-                float(match.group(2)) if match.group else 0.0
+                float(match.group(2)) if (match.group(2) is not None) else 0.0
             ])
             trstr = trstr[len(match.group(0)):].strip(' ')
             continue
@@ -600,9 +596,9 @@ def parse_svg_transform(trstr):
         match = re.search(r'^' + scale_re_str, trstr)
         if match:
             trlist.append([
-                'translate',
+                'scale',
                 float(match.group(1)),
-                float(match.group(2)) if not None else float(match.group(1))
+                float(match.group(2)) if (match.group(2) is not None) else float(match.group(1))
             ])
             trstr = trstr[len(match.group(0)):].strip(' ')
             continue

+ 9 - 9
flatcamTools/ToolCalculators.py

@@ -304,7 +304,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -315,7 +315,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         half_tip_angle /= 2
@@ -327,7 +327,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -342,7 +342,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         self.inch_entry.set_value('%.6f' % (mm_val / 25.4))
@@ -355,7 +355,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         self.mm_entry.set_value('%.6f' % (inch_val * 25.4))
@@ -369,7 +369,7 @@ class ToolCalculator(FlatCAMTool):
             try:
                 length = float(self.pcblength_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -380,7 +380,7 @@ class ToolCalculator(FlatCAMTool):
             try:
                 width = float(self.pcbwidth_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -391,7 +391,7 @@ class ToolCalculator(FlatCAMTool):
             try:
                 density = float(self.cdensity_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -402,7 +402,7 @@ class ToolCalculator(FlatCAMTool):
             try:
                 copper = float(self.growth_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 

+ 44 - 24
flatcamTools/ToolCutOut.py

@@ -110,6 +110,14 @@ class CutOut(FlatCAMTool):
         # 2tb   - 2*top + 2*bottom
         # 8     - 2*left + 2*right +2*top + 2*bottom
 
+        # Surrounding convex box shape
+        self.convex_box = FCCheckBox()
+        self.convex_box_label = QtWidgets.QLabel(_("Convex Sh.:"))
+        self.convex_box_label.setToolTip(
+            _("Create a convex shape surrounding the entire PCB.")
+        )
+        form_layout.addRow(self.convex_box_label, self.convex_box)
+
         ## Title2
         title_param_label = QtWidgets.QLabel("<font size=4><b>%s</b></font>" % _('A. Automatic Bridge Gaps'))
         title_param_label.setToolTip(
@@ -310,7 +318,8 @@ class CutOut(FlatCAMTool):
         self.dia.set_value(float(self.app.defaults["tools_cutouttooldia"]))
         self.margin.set_value(float(self.app.defaults["tools_cutoutmargin"]))
         self.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"]))
-        self.gaps.set_value(4)
+        self.gaps.set_value(self.app.defaults["tools_gaps_ff"])
+        self.convex_box.set_value(self.app.defaults['tools_cutout_convexshape'])
 
         self.gapFinished.connect(self.on_gap_finished)
 
@@ -326,11 +335,11 @@ class CutOut(FlatCAMTool):
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % name)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % 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] There is no object selected for Cutout.\nSelect one and try again."))
             return
 
         try:
@@ -346,8 +355,8 @@ class CutOut(FlatCAMTool):
 
 
         if 0 in {dia}:
-            self.app.inform.emit(_("[WARNING_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
-            return "Tool Diameter is zero value. Change it to a positive integer."
+            self.app.inform.emit(_("[WARNING_NOTCL] 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:
             margin = float(self.margin.get_value())
@@ -388,6 +397,8 @@ class CutOut(FlatCAMTool):
                                  "and after that perform Cutout."))
             return
 
+        convex_box = self.convex_box.get_value()
+
         # Get min and max data for each object as we just cut rectangles across X or Y
         xmin, ymin, xmax, ymax = cutout_obj.bounds()
         px = 0.5 * (xmin + xmax) + margin
@@ -402,8 +413,12 @@ class CutOut(FlatCAMTool):
             cutout_obj.options["name"] += "_cutout"
         else:
             def geo_init(geo_obj, app_obj):
-                geo = cutout_obj.solid_geometry.convex_hull
-                geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
+                if convex_box:
+                    geo = cutout_obj.solid_geometry.convex_hull
+                    geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
+                else:
+                    geo = cutout_obj.solid_geometry
+                    geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2)).exterior
 
             outname = cutout_obj.options["name"] + "_cutout"
             self.app.new_object('geometry', outname, geo_init)
@@ -465,11 +480,11 @@ class CutOut(FlatCAMTool):
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % name)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % 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] Object not found: %s") % cutout_obj)
 
         try:
             dia = float(self.dia.get_value())
@@ -483,8 +498,8 @@ class CutOut(FlatCAMTool):
                 return
 
         if 0 in {dia}:
-            self.app.inform.emit(_("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
-            return "Tool Diameter is zero value. Change it to a positive integer."
+            self.app.inform.emit(_("[ERROR_NOTCL] 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:
             margin = float(self.margin.get_value())
@@ -603,8 +618,8 @@ class CutOut(FlatCAMTool):
                 return
 
         if 0 in {self.cutting_dia}:
-            self.app.inform.emit(_("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
-            return "Tool Diameter is zero value. Change it to a positive integer."
+            self.app.inform.emit(_("[ERROR_NOTCL] 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:
             self.cutting_gapsize = float(self.gapsize.get_value())
@@ -652,11 +667,11 @@ class CutOut(FlatCAMTool):
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve Geoemtry object: %s") % name)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Geometry object: %s") % name)
             return "Could not retrieve object: %s" % name
 
         if cutout_obj is None:
-            self.app.inform.emit(_("[ERROR_NOTCL]Geometry object for manual cutout not found: %s") % cutout_obj)
+            self.app.inform.emit(_("[ERROR_NOTCL] Geometry object for manual cutout not found: %s") % cutout_obj)
             return
 
         # use the snapped position as reference
@@ -683,16 +698,16 @@ class CutOut(FlatCAMTool):
         try:
             cutout_obj = self.app.collection.get_by_name(str(name))
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve Gerber object: %s") % name)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve Gerber object: %s") % 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] 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] The selected object has to be of Gerber type.\n"
                                  "Select a Gerber file and try again."))
             return
 
@@ -708,8 +723,8 @@ class CutOut(FlatCAMTool):
                 return
 
         if 0 in {dia}:
-            self.app.inform.emit(_("[ERROR_NOTCL]Tool Diameter is zero value. Change it to a positive integer."))
-            return "Tool Diameter is zero value. Change it to a positive integer."
+            self.app.inform.emit(_("[ERROR_NOTCL] 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:
             margin = float(self.margin.get_value())
@@ -722,16 +737,21 @@ class CutOut(FlatCAMTool):
                                      "Add it and retry."))
                 return
 
+        convex_box = self.convex_box.get_value()
+
         def geo_init(geo_obj, app_obj):
-            geo = cutout_obj.solid_geometry.convex_hull
-            geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
+            if convex_box:
+                geo = cutout_obj.solid_geometry.convex_hull
+                geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2))
+            else:
+                geo = cutout_obj.solid_geometry
+                geo_obj.solid_geometry = geo.buffer(margin + abs(dia / 2)).exterior
 
         outname = cutout_obj.options["name"] + "_cutout"
         self.app.new_object('geometry', outname, geo_init)
 
     def cutting_geo(self, pos):
-        self.cutting_gapsize = self.cutting_gapsize / 2 + (self.cutting_dia / 2)
-        offset = self.cutting_gapsize / 2
+        offset = self.cutting_dia / 2 + self.cutting_gapsize / 2
 
         # cutting area definition
         orig_x = pos[0]

+ 2 - 2
flatcamTools/ToolDblSided.py

@@ -190,7 +190,7 @@ class DblSidedTool(FlatCAMTool):
 
 
         ## Alignment holes
-        self.ah_label = QtWidgets.QLabel("<b%s</b>" % _('Alignment Drill Coordinates:'))
+        self.ah_label = QtWidgets.QLabel("<b>%s</b>" % _('Alignment Drill Coordinates:'))
         self.ah_label.setToolTip(
            _( "Alignment holes (x1, y1), (x2, y2), ... "
             "on one side of the mirror axis. For each set of (x, y) coordinates\n"
@@ -365,7 +365,7 @@ class DblSidedTool(FlatCAMTool):
                 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] No value or wrong format in Drill Dia entry. Add it and retry."))
             return
         tools = {"1": {"C": dia}}
 

+ 4 - 4
flatcamTools/ToolFilm.py

@@ -238,14 +238,14 @@ class Film(FlatCAMTool):
             try:
                 border = float(self.boundary_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] 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, "
+            self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                  "use a number."))
             return
 
@@ -266,7 +266,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] Export SVG positive cancelled."))
                 return
             else:
                 self.app.export_svg_black(name, boxname, filename, scale_factor=scale_stroke_width)
@@ -282,7 +282,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] Export SVG negative cancelled."))
                 return
             else:
                 self.app.export_svg_negative(name, boxname, filename, border, scale_factor=scale_stroke_width)

+ 127 - 186
flatcamTools/ToolMeasurement.py

@@ -28,6 +28,8 @@ class Measurement(FlatCAMTool):
     def __init__(self, app):
         FlatCAMTool.__init__(self, app)
 
+        self.app = app
+        self.canvas = self.app.plotcanvas
         self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
         ## Title
@@ -38,11 +40,11 @@ class Measurement(FlatCAMTool):
         form_layout = QtWidgets.QFormLayout()
         self.layout.addLayout(form_layout)
 
-        form_layout_child_1 = QtWidgets.QFormLayout()
-        form_layout_child_1_1 = QtWidgets.QFormLayout()
-        form_layout_child_1_2 = QtWidgets.QFormLayout()
-        form_layout_child_2 = QtWidgets.QFormLayout()
-        form_layout_child_3 = QtWidgets.QFormLayout()
+
+        self.units_label = QtWidgets.QLabel(_("Units:"))
+        self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
+        self.units_value = QtWidgets.QLabel("%s" % str({'mm': "METRIC (mm)", 'in': "INCH (in)"}[self.units]))
+        self.units_value.setDisabled(True)
 
         self.start_label = QtWidgets.QLabel("<b>%s</b> %s:" % (_('Start'), _('Coords')))
         self.start_label.setToolTip(_("This is measuring Start point coordinates."))
@@ -59,84 +61,38 @@ class Measurement(FlatCAMTool):
         self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))
         self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance."))
 
-        self.units_entry_1 = FCEntry()
-        self.units_entry_1.setToolTip(_("Those are the units in which the distance is measured."))
-        self.units_entry_1.setDisabled(True)
-        self.units_entry_1.setFocusPolicy(QtCore.Qt.NoFocus)
-        self.units_entry_1.setFrame(False)
-        self.units_entry_1.setFixedWidth(30)
-
-        self.units_entry_2 = FCEntry()
-        self.units_entry_2.setToolTip(_("Those are the units in which the distance is measured."))
-        self.units_entry_2.setDisabled(True)
-        self.units_entry_2.setFocusPolicy(QtCore.Qt.NoFocus)
-        self.units_entry_2.setFrame(False)
-        self.units_entry_2.setFixedWidth(30)
-
-        self.units_entry_3 = FCEntry()
-        self.units_entry_3.setToolTip(_("Those are the units in which the distance is measured."))
-        self.units_entry_3.setDisabled(True)
-        self.units_entry_3.setFocusPolicy(QtCore.Qt.NoFocus)
-        self.units_entry_3.setFrame(False)
-        self.units_entry_3.setFixedWidth(30)
-
-        self.units_entry_4 = FCEntry()
-        self.units_entry_4.setToolTip(_("Those are the units in which the distance is measured."))
-        self.units_entry_4.setDisabled(True)
-        self.units_entry_4.setFocusPolicy(QtCore.Qt.NoFocus)
-        self.units_entry_4.setFrame(False)
-        self.units_entry_4.setFixedWidth(30)
-
-        self.units_entry_5 = FCEntry()
-        self.units_entry_5.setToolTip(_("Those are the units in which the distance is measured."))
-        self.units_entry_5.setDisabled(True)
-        self.units_entry_5.setFocusPolicy(QtCore.Qt.NoFocus)
-        self.units_entry_5.setFrame(False)
-        self.units_entry_5.setFixedWidth(30)
-
         self.start_entry = FCEntry()
         self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         self.start_entry.setToolTip(_("This is measuring Start point coordinates."))
-        self.start_entry.setFixedWidth(100)
 
         self.stop_entry = FCEntry()
         self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         self.stop_entry.setToolTip(_("This is the measuring Stop point coordinates."))
-        self.stop_entry.setFixedWidth(100)
 
         self.distance_x_entry = FCEntry()
         self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis."))
-        self.distance_x_entry.setFixedWidth(100)
 
 
         self.distance_y_entry = FCEntry()
         self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis."))
-        self.distance_y_entry.setFixedWidth(100)
 
 
         self.total_distance_entry = FCEntry()
         self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
         self.total_distance_entry.setToolTip(_("This is the point to point Euclidian distance."))
-        self.total_distance_entry.setFixedWidth(100)
 
         self.measure_btn = QtWidgets.QPushButton(_("Measure"))
-        self.measure_btn.setFixedWidth(70)
+        # self.measure_btn.setFixedWidth(70)
         self.layout.addWidget(self.measure_btn)
 
-
-        form_layout_child_1.addRow(self.start_entry, self.units_entry_1)
-        form_layout_child_1_1.addRow(self.stop_entry, self.units_entry_2)
-        form_layout_child_1_2.addRow(self.distance_x_entry, self.units_entry_3)
-        form_layout_child_2.addRow(self.distance_y_entry, self.units_entry_4)
-        form_layout_child_3.addRow(self.total_distance_entry, self.units_entry_5)
-
-        form_layout.addRow(self.start_label, form_layout_child_1)
-        form_layout.addRow(self.stop_label, form_layout_child_1_1)
-        form_layout.addRow(self.distance_x_label, form_layout_child_1_2)
-        form_layout.addRow(self.distance_y_label, form_layout_child_2)
-        form_layout.addRow(self.total_distance_label, form_layout_child_3)
+        form_layout.addRow(self.units_label, self.units_value)
+        form_layout.addRow(self.start_label, self.start_entry)
+        form_layout.addRow(self.stop_label, self.stop_entry)
+        form_layout.addRow(self.distance_x_label, self.distance_x_entry)
+        form_layout.addRow(self.distance_y_label, self.distance_y_entry)
+        form_layout.addRow(self.total_distance_label, self.total_distance_entry)
 
         # initial view of the layout
         self.start_entry.set_value('(0, 0)')
@@ -144,12 +100,6 @@ class Measurement(FlatCAMTool):
         self.distance_x_entry.set_value('0')
         self.distance_y_entry.set_value('0')
         self.total_distance_entry.set_value('0')
-        self.units_entry_1.set_value(str(self.units))
-        self.units_entry_2.set_value(str(self.units))
-        self.units_entry_3.set_value(str(self.units))
-        self.units_entry_4.set_value(str(self.units))
-        self.units_entry_5.set_value(str(self.units))
-
 
         self.layout.addStretch()
 
@@ -160,12 +110,12 @@ class Measurement(FlatCAMTool):
 
         # the default state is disabled for the Move command
         # self.setVisible(False)
-        self.active = False
+        self.active = 0
 
         # VisPy visuals
         self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene, layers=1)
 
-        self.measure_btn.clicked.connect(self.toggle_f)
+        self.measure_btn.clicked.connect(lambda: self.on_measure(activate=True))
 
     def run(self, toggle=False):
         self.app.report_usage("ToolMeasurement()")
@@ -173,14 +123,13 @@ class Measurement(FlatCAMTool):
         if self.app.tool_tab_locked is True:
             return
 
+        self.app.ui.notebook.setTabText(2, _("Meas. Tool"))
+
         # if the splitter is hidden, display it, else hide it but only if the current widget is the same
         if self.app.ui.splitter.sizes()[0] == 0:
             self.app.ui.splitter.setSizes([1, 1])
 
-        self.toggle_f()
-
-        self.set_tool_ui()
-        self.app.ui.notebook.setTabText(2, _("Meas. Tool"))
+        self.on_measure(activate=True)
 
     def install(self, icon=None, separator=None, **kwargs):
         FlatCAMTool.install(self, icon, separator, shortcut='CTRL+M', **kwargs)
@@ -195,90 +144,91 @@ class Measurement(FlatCAMTool):
         # Switch notebook to tool page
         self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
         self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
-        self.show()
 
-    def toggle_f(self):
-        # the self.active var is doing the 'toggle'
-        if self.active is True:
+        self.app.command_active = "Measurement"
+
+        # initial view of the layout
+        self.start_entry.set_value('(0, 0)')
+        self.stop_entry.set_value('(0, 0)')
+
+        self.distance_x_entry.set_value('0')
+        self.distance_y_entry.set_value('0')
+        self.total_distance_entry.set_value('0')
+
+    def activate(self):
+        # we disconnect the mouse/key handlers from wherever the measurement tool was called
+        self.canvas.vis_disconnect('key_press')
+        self.canvas.vis_disconnect('mouse_move')
+        self.canvas.vis_disconnect('mouse_press')
+        self.canvas.vis_disconnect('mouse_release')
+        self.canvas.vis_disconnect('key_release')
+
+        # we can safely connect the app mouse events to the measurement tool
+        self.canvas.vis_connect('mouse_move', self.on_mouse_move_meas)
+        self.canvas.vis_connect('mouse_release', self.on_mouse_click)
+        self.canvas.vis_connect('key_release', self.on_key_release_meas)
+
+        self.set_tool_ui()
+
+    def deactivate(self):
+        # disconnect the mouse/key events from functions of measurement tool
+        self.canvas.vis_disconnect('mouse_move', self.on_mouse_move_meas)
+        self.canvas.vis_disconnect('mouse_release', self.on_mouse_click)
+        self.canvas.vis_disconnect('key_release', self.on_key_release_meas)
+
+        # reconnect the mouse/key events to the functions from where the tool was called
+        self.canvas.vis_connect('key_press', self.app.ui.keyPressEvent)
+
+        if self.app.call_source == 'app':
+            self.canvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
+            self.canvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
+            self.canvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+        elif self.app.call_source == 'geo_editor':
+            self.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move)
+            self.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click)
+            # self.canvas.vis_connect('key_press', self.app.geo_editor.on_canvas_key)
+            self.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release)
+        elif self.app.call_source == 'exc_editor':
+            self.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move)
+            self.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click)
+            # self.canvas.vis_connect('key_press', self.app.exc_editor.on_canvas_key)
+            self.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release)
+        elif self.app.call_source == 'grb_editor':
+            self.canvas.vis_connect('mouse_move', self.app.grb_editor.on_canvas_move)
+            self.canvas.vis_connect('mouse_press', self.app.grb_editor.on_canvas_click)
+            # self.canvas.vis_connect('key_press', self.app.grb_editor.on_canvas_key)
+            self.canvas.vis_connect('mouse_release', self.app.grb_editor.on_canvas_click_release)
+
+        self.app.ui.notebook.setTabText(2, _("Tools"))
+        self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
+
+    def on_measure(self, signal=None, activate=None):
+        log.debug("Measurement.on_measure()")
+        if activate is False or activate is None:
             # DISABLE the Measuring TOOL
-            self.active = False
-
-            # disconnect the mouse/key events from functions of measurement tool
-            self.app.plotcanvas.vis_disconnect('mouse_move', self.on_mouse_move_meas)
-            self.app.plotcanvas.vis_disconnect('mouse_press', self.on_click_meas)
-
-            # reconnect the mouse/key events to the functions from where the tool was called
-            if self.app.call_source == 'app':
-                self.app.plotcanvas.vis_connect('mouse_move', self.app.on_mouse_move_over_plot)
-                self.app.plotcanvas.vis_connect('mouse_press', self.app.on_mouse_click_over_plot)
-                self.app.plotcanvas.vis_connect('key_press', self.app.ui.keyPressEvent)
-                self.app.plotcanvas.vis_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-            elif self.app.call_source == 'geo_editor':
-                self.app.geo_editor.canvas.vis_connect('mouse_move', self.app.geo_editor.on_canvas_move)
-                self.app.geo_editor.canvas.vis_connect('mouse_press', self.app.geo_editor.on_canvas_click)
-                self.app.geo_editor.canvas.vis_connect('key_press', self.app.geo_editor.on_canvas_key)
-                self.app.geo_editor.canvas.vis_connect('mouse_release', self.app.geo_editor.on_canvas_click_release)
-            elif self.app.call_source == 'exc_editor':
-                self.app.exc_editor.canvas.vis_connect('mouse_move', self.app.exc_editor.on_canvas_move)
-                self.app.exc_editor.canvas.vis_connect('mouse_press', self.app.exc_editor.on_canvas_click)
-                self.app.exc_editor.canvas.vis_connect('key_press', self.app.exc_editor.on_canvas_key)
-                self.app.exc_editor.canvas.vis_connect('mouse_release', self.app.exc_editor.on_canvas_click_release)
-
-            self.app.call_source = 'measurement'
-            self.clicked_meas = 0
+            self.deactivate()
+
             self.app.command_active = None
+
             # delete the measuring line
             self.delete_shape()
-            return
-        else:
-            # ENABLE the Measuring TOOL
-            self.active = True
-            self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
 
-            # we disconnect the mouse/key handlers from wherever the measurement tool was called
-            if self.app.call_source == 'app':
-                self.app.plotcanvas.vis_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
-                self.app.plotcanvas.vis_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
-                self.app.plotcanvas.vis_disconnect('key_press', self.app.ui.keyPressEvent)
-                self.app.plotcanvas.vis_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
-            elif self.app.call_source == 'geo_editor':
-                self.app.geo_editor.canvas.vis_disconnect('mouse_move', self.app.geo_editor.on_canvas_move)
-                self.app.geo_editor.canvas.vis_disconnect('mouse_press', self.app.geo_editor.on_canvas_click)
-                self.app.geo_editor.canvas.vis_disconnect('key_press', self.app.geo_editor.on_canvas_key)
-                self.app.geo_editor.canvas.vis_disconnect('mouse_release', self.app.geo_editor.on_canvas_click_release)
-            elif self.app.call_source == 'exc_editor':
-                self.app.exc_editor.canvas.vis_disconnect('mouse_move', self.app.exc_editor.on_canvas_move)
-                self.app.exc_editor.canvas.vis_disconnect('mouse_press', self.app.exc_editor.on_canvas_click)
-                self.app.exc_editor.canvas.vis_disconnect('key_press', self.app.exc_editor.on_canvas_key)
-                self.app.exc_editor.canvas.vis_disconnect('mouse_release', self.app.exc_editor.on_canvas_click_release)
-
-            # we can safely connect the app mouse events to the measurement tool
-            self.app.plotcanvas.vis_connect('mouse_move', self.on_mouse_move_meas)
-            self.app.plotcanvas.vis_connect('mouse_press', self.on_click_meas)
-            self.app.plotcanvas.vis_connect('key_release', self.on_key_release_meas)
-
-            self.app.command_active = "Measurement"
-
-            # initial view of the layout
-            self.start_entry.set_value('(0, 0)')
-            self.stop_entry.set_value('(0, 0)')
-
-            self.distance_x_entry.set_value('0')
-            self.distance_y_entry.set_value('0')
-            self.total_distance_entry.set_value('0')
-
-            self.units_entry_1.set_value(str(self.units))
-            self.units_entry_2.set_value(str(self.units))
-            self.units_entry_3.set_value(str(self.units))
-            self.units_entry_4.set_value(str(self.units))
-            self.units_entry_5.set_value(str(self.units))
+            log.debug("Measurement Tool --> exit tool")
+        elif activate is True:
+            # ENABLE the Measuring TOOL
+            self.clicked_meas = 0
 
             self.app.inform.emit(_("MEASURING: Click on the Start point ..."))
+            self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower()
+
+            self.activate()
+            log.debug("Measurement Tool --> tool initialized")
 
     def on_key_release_meas(self, event):
         if event.key == 'escape':
             # abort the measurement action
-            self.toggle()
+            self.on_measure(activate=False)
+            self.app.inform.emit(_("Measurement Tool exit..."))
             return
 
         if event.key == 'G':
@@ -286,63 +236,57 @@ class Measurement(FlatCAMTool):
             self.app.ui.grid_snap_btn.trigger()
             return
 
-    def on_click_meas(self, event):
-        # mouse click will be accepted only if the left button is clicked
-        # this is necessary because right mouse click and middle mouse click
+    def on_mouse_click(self, event):
+        # mouse click releases will be accepted only if the left button is clicked
+        # this is necessary because right mouse click or middle mouse click
         # are used for panning on the canvas
 
         if event.button == 1:
+            pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
+
             if self.clicked_meas == 0:
-                pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
+                self.clicked_meas = 1
 
                 # if GRID is active we need to get the snapped positions
                 if self.app.grid_status() == True:
                     pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
                 else:
                     pos = pos_canvas[0], pos_canvas[1]
+
                 self.point1 = pos
                 self.start_entry.set_value("(%.4f, %.4f)" % pos)
                 self.app.inform.emit(_("MEASURING: Click on the Destination point ..."))
 
-            if self.clicked_meas == 1:
-                try:
-                    pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
-
-                    # delete the selection bounding box
-                    self.delete_shape()
-
-                    # if GRID is active we need to get the snapped positions
-                    if self.app.grid_status() == True:
-                        pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
-                    else:
-                        pos = pos_canvas[0], pos_canvas[1]
+            else:
+                # delete the selection bounding box
+                self.delete_shape()
 
-                    dx = pos[0] - self.point1[0]
-                    dy = pos[1] - self.point1[1]
-                    d = sqrt(dx**2 + dy**2)
-
-                    self.stop_entry.set_value("(%.4f, %.4f)" % pos)
+                # if GRID is active we need to get the snapped positions
+                if self.app.grid_status() is True:
+                    pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
+                else:
+                    pos = pos_canvas[0], pos_canvas[1]
 
-                    self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format(
-                        d_x='%4f' % abs(dx), d_y='%4f' % abs(dy), d_z='%4f' % abs(d)))
+                dx = pos[0] - self.point1[0]
+                dy = pos[1] - self.point1[1]
+                d = sqrt(dx ** 2 + dy ** 2)
 
-                    self.distance_x_entry.set_value('%.4f' % abs(dx))
-                    self.distance_y_entry.set_value('%.4f' % abs(dy))
-                    self.total_distance_entry.set_value('%.4f' % abs(d))
+                self.stop_entry.set_value("(%.4f, %.4f)" % pos)
 
-                    self.clicked_meas = 0
-                    self.toggle_f()
+                self.app.inform.emit(_("MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}").format(
+                    d_x='%4f' % abs(dx), d_y='%4f' % abs(dy), d_z='%4f' % abs(d)))
 
-                    # delete the measuring line
-                    self.delete_shape()
-                    return
-                except TypeError:
-                    pass
+                self.distance_x_entry.set_value('%.4f' % abs(dx))
+                self.distance_y_entry.set_value('%.4f' % abs(dy))
+                self.total_distance_entry.set_value('%.4f' % abs(d))
 
-            self.clicked_meas = 1
+                # TODO: I don't understand why I have to do it twice ... but without it the mouse handlers are
+                # TODO: are left disconnected ...
+                self.on_measure(activate=False)
+                self.deactivate()
 
     def on_mouse_move_meas(self, event):
-        pos_canvas = self.app.plotcanvas.vispy_canvas.translate_coords(event.pos)
+        pos_canvas = self.canvas.vispy_canvas.translate_coords(event.pos)
 
         # if GRID is active we need to get the snapped positions
         if self.app.grid_status() == True:
@@ -356,21 +300,18 @@ class Measurement(FlatCAMTool):
 
         self.point2 = (pos[0], pos[1])
 
+        # update utility geometry
         if self.clicked_meas == 1:
-            self.update_meas_shape([self.point2, self.point1])
-
-    def update_meas_shape(self, pos):
-        self.delete_shape()
-        self.draw_shape(pos)
+            # first delete old shape
+            self.delete_shape()
+            # second draw the new shape of the utility geometry
+            self.meas_line = LineString([self.point2, self.point1])
+            self.sel_shapes.add(self.meas_line, color='black', update=True, layer=0, tolerance=None)
 
     def delete_shape(self):
         self.sel_shapes.clear()
         self.sel_shapes.redraw()
 
-    def draw_shape(self, coords):
-        self.meas_line = LineString(coords)
-        self.sel_shapes.add(self.meas_line, color='black', update=True, layer=0, tolerance=None)
-
     def set_meas_units(self, units):
         self.meas.units_label.setText("[" + self.app.options["units"].lower() + "]")
 

+ 3 - 3
flatcamTools/ToolMove.py

@@ -161,7 +161,7 @@ class ToolMove(FlatCAMTool):
                         proc.done()
                         # delete the selection bounding box
                         self.delete_shape()
-                        self.app.inform.emit(_('[success]%s object was moved ...') %
+                        self.app.inform.emit(_('[success] %s object was moved ...') %
                                              str(sel_obj.kind).capitalize())
 
                     self.app.worker_task.emit({'fcn': job_move, 'params': [self]})
@@ -199,7 +199,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] Move action cancelled."))
             self.toggle()
         return
 
@@ -211,7 +211,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] Object(s) not selected"))
             self.toggle()
         else:
             # if we have an object selected then we can safely activate the mouse events

+ 15 - 9
flatcamTools/ToolNonCopperClear.py

@@ -13,6 +13,7 @@ import time
 
 import gettext
 import FlatCAMTranslation as fcTranslate
+from shapely.geometry import base
 
 fcTranslate.apply_language('strings')
 import builtins
@@ -161,7 +162,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
         e_lab_1 = QtWidgets.QLabel('')
         grid3.addWidget(e_lab_1, 0, 0)
 
-        nccoverlabel = QtWidgets.QLabel(_('Overlap:'))
+        nccoverlabel = QtWidgets.QLabel(_('Overlap Rate:'))
         nccoverlabel.setToolTip(
             _("How much (fraction) of the tool width to overlap each tool pass.\n"
             "Example:\n"
@@ -475,7 +476,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                 try:
                     tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                          "use a number."))
                     return
             if tool_dia is None:
@@ -508,7 +509,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
 
         if float('%.4f' % tool_dia) in tool_dias:
             if muted is None:
-                self.app.inform.emit(_("[WARNING_NOTCL]Adding tool cancelled. Tool already in Tool Table."))
+                self.app.inform.emit(_("[WARNING_NOTCL] Adding tool cancelled. Tool already in Tool Table."))
             self.tools_table.itemChanged.connect(self.on_tool_edit)
             return
         else:
@@ -605,7 +606,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
                     self.ncc_tools.pop(t, None)
 
         except AttributeError:
-            self.app.inform.emit(_("[WARNING_NOTCL]Delete failed. Select a tool to delete."))
+            self.app.inform.emit(_("[WARNING_NOTCL] Delete failed. Select a tool to delete."))
             return
         except Exception as e:
             log.debug(str(e))
@@ -622,11 +623,16 @@ class NonCopperClear(FlatCAMTool, Gerber):
             try:
                 over = float(self.ncc_overlap_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         over = over if over else self.app.defaults["tools_nccoverlap"]
 
+        if over >= 1 or over < 0:
+            self.app.inform.emit(_("[ERROR_NOTCL] Overlap value must be between "
+                                  "0 (inclusive) and 1 (exclusive), "))
+            return
+
         try:
             margin = float(self.ncc_margin_entry.get_value())
         except ValueError:
@@ -634,7 +640,7 @@ class NonCopperClear(FlatCAMTool, Gerber):
             try:
                 margin = float(self.ncc_margin_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         margin = margin if margin else self.app.defaults["tools_nccmargin"]
@@ -656,14 +662,14 @@ class NonCopperClear(FlatCAMTool, Gerber):
         try:
             self.ncc_obj = self.app.collection.get_by_name(self.obj_name)
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % self.obj_name)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
             return "Could not retrieve object: %s" % self.obj_name
 
         # Prepare non-copper polygons
         try:
-            bounding_box = self.ncc_obj.solid_geometry.envelope.buffer(distance=margin, join_style=JOIN_STYLE.mitre)
+            bounding_box = self.ncc_obj.solid_geometry.envelope.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
         except AttributeError:
-            self.app.inform.emit(_("[ERROR_NOTCL]No Gerber file available."))
+            self.app.inform.emit(_("[ERROR_NOTCL] No Gerber file available."))
             return
 
         # calculate the empty area by subtracting the solid_geometry from the object bounding box geometry

+ 23 - 16
flatcamTools/ToolPaint.py

@@ -157,7 +157,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.tools_box.addLayout(grid3)
 
         # Overlap
-        ovlabel = QtWidgets.QLabel(_('Overlap:'))
+        ovlabel = QtWidgets.QLabel(_('Overlap Rate:'))
         ovlabel.setToolTip(
             _("How much (fraction) of the tool width to overlap each tool pass.\n"
             "Example:\n"
@@ -534,7 +534,7 @@ class ToolPaint(FlatCAMTool, Gerber):
                 try:
                     tool_dia = float(self.addtool_entry.get_value().replace(',', '.'))
                 except ValueError:
-                    self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                          "use a number."))
                     return
 
@@ -564,7 +564,7 @@ class ToolPaint(FlatCAMTool, Gerber):
 
         if float('%.4f' % tool_dia) in tool_dias:
             if muted is None:
-                self.app.inform.emit(_("[WARNING_NOTCL]Adding tool cancelled. Tool already in Tool Table."))
+                self.app.inform.emit(_("[WARNING_NOTCL] Adding tool cancelled. Tool already in Tool Table."))
             self.tools_table.itemChanged.connect(self.on_tool_edit)
             return
         else:
@@ -604,7 +604,7 @@ class ToolPaint(FlatCAMTool, Gerber):
                 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, "
+                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                          "use a number."))
                     return
             tooluid = int(self.tools_table.item(row, 3).text())
@@ -656,7 +656,7 @@ class ToolPaint(FlatCAMTool, Gerber):
     #                         print("COPIED", self.paint_tools[td])
     #                     self.build_ui()
     #                 except AttributeError:
-    #                     self.app.inform.emit("[WARNING_NOTCL]Failed. Select a tool to copy.")
+    #                     self.app.inform.emit("[WARNING_NOTCL] Failed. Select a tool to copy.")
     #                     self.build_ui()
     #                     return
     #                 except Exception as e:
@@ -664,7 +664,7 @@ class ToolPaint(FlatCAMTool, Gerber):
     #             # deselect the table
     #             # self.ui.geo_tools_table.clearSelection()
     #         else:
-    #             self.app.inform.emit("[WARNING_NOTCL]Failed. Select a tool to copy.")
+    #             self.app.inform.emit("[WARNING_NOTCL] Failed. Select a tool to copy.")
     #             self.build_ui()
     #             return
     #     else:
@@ -720,7 +720,7 @@ class ToolPaint(FlatCAMTool, Gerber):
                     self.paint_tools.pop(t, None)
 
         except AttributeError:
-            self.app.inform.emit(_("[WARNING_NOTCL]Delete failed. Select a tool to delete."))
+            self.app.inform.emit(_("[WARNING_NOTCL] Delete failed. Select a tool to delete."))
             return
         except Exception as e:
             log.debug(str(e))
@@ -730,9 +730,8 @@ class ToolPaint(FlatCAMTool, Gerber):
 
     def on_paint_button_click(self):
         self.app.report_usage(_("geometry_on_paint_button"))
-        self.app.call_source = 'paint'
+        # self.app.call_source = 'paint'
 
-        self.app.inform.emit(_("[WARNING_NOTCL]Click inside the desired polygon."))
         try:
             overlap = float(self.paintoverlap_entry.get_value())
         except ValueError:
@@ -740,10 +739,17 @@ class ToolPaint(FlatCAMTool, Gerber):
             try:
                 overlap = float(self.paintoverlap_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
+        if overlap >= 1 or overlap < 0:
+            self.app.inform.emit(_("[ERROR_NOTCL] Overlap value must be between "
+                                  "0 (inclusive) and 1 (exclusive), "))
+            return
+
+        self.app.inform.emit(_("[WARNING_NOTCL] Click inside the desired polygon."))
+
         connect = self.pathconnect_cb.get_value()
         contour = self.paintcontour_cb.get_value()
         select_method = self.selectmethod_combo.get_value()
@@ -754,11 +760,11 @@ class ToolPaint(FlatCAMTool, Gerber):
         try:
             self.paint_obj = self.app.collection.get_by_name(str(self.obj_name))
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % self.obj_name)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % self.obj_name)
             return
 
         if self.paint_obj is None:
-            self.app.inform.emit(_("[ERROR_NOTCL]Object not found: %s") % self.paint_obj)
+            self.app.inform.emit(_("[ERROR_NOTCL] Object not found: %s") % self.paint_obj)
             return
 
         # test if the Geometry Object is multigeo and return Fail if True because
@@ -777,7 +783,7 @@ class ToolPaint(FlatCAMTool, Gerber):
                                 contour=contour)
 
         if select_method == "single":
-            self.app.inform.emit(_("[WARNING_NOTCL]Click inside the desired polygon."))
+            self.app.inform.emit(_("[WARNING_NOTCL] Click inside the desired polygon."))
 
             # use the first tool in the tool table; get the diameter
             tooldia = float('%.4f' % float(self.tools_table.item(0, 1).text()))
@@ -830,7 +836,7 @@ class ToolPaint(FlatCAMTool, Gerber):
             try:
                 paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -985,7 +991,7 @@ class ToolPaint(FlatCAMTool, Gerber):
             try:
                 paint_margin = float(self.paintmargin_entry.get_value().replace(',', '.'))
             except ValueError:
-                self.app.inform.emit(_("[ERROR_NOTCL]Wrong value format entered, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -1068,8 +1074,9 @@ class ToolPaint(FlatCAMTool, Gerber):
 
                 for geo in recurse(obj.solid_geometry):
                     try:
+                        #Polygons are the only really paintable geometries, lines in theory have no area to be painted
                         if not isinstance(geo, Polygon):
-                            geo = Polygon(geo)
+                            continue
                         poly_buf = geo.buffer(-paint_margin)
 
                         if paint_method == "seed":

+ 12 - 12
flatcamTools/ToolPanelize.py

@@ -290,13 +290,13 @@ class Panelize(FlatCAMTool):
         try:
             obj = self.app.collection.get_by_name(str(name))
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % name)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % 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] Object not found: %s") % panel_obj)
             return "Object not found: %s" % panel_obj
 
         boxname = self.box_combo.currentText()
@@ -304,7 +304,7 @@ class Panelize(FlatCAMTool):
         try:
             box = self.app.collection.get_by_name(boxname)
         except:
-            self.app.inform.emit(_("[ERROR_NOTCL]Could not retrieve object: %s") % boxname)
+            self.app.inform.emit(_("[ERROR_NOTCL] Could not retrieve object: %s") % boxname)
             return "Could not retrieve object: %s" % boxname
 
         if box is None:
@@ -320,7 +320,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         spacing_columns = spacing_columns if spacing_columns is not None else 0
@@ -332,7 +332,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         spacing_rows = spacing_rows if spacing_rows is not None else 0
@@ -345,7 +345,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         rows = rows if rows is not None else 1
@@ -358,7 +358,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
         columns = columns if columns is not None else 1
@@ -370,7 +370,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -381,7 +381,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                      "use a number."))
                 return
 
@@ -389,7 +389,7 @@ class Panelize(FlatCAMTool):
 
 
         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] 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()
@@ -517,7 +517,7 @@ 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] Panel done..."))
         else:
             self.constrain_flag = False
             self.app.inform.emit(_("[WARNING] Too big for the constrain area. Final panel has {col} columns and {row} rows").format(
@@ -528,7 +528,7 @@ class Panelize(FlatCAMTool):
         def job_thread(app_obj):
             try:
                 panelize_2()
-                self.app.inform.emit(_("[success]Panel created successfully."))
+                self.app.inform.emit(_("[success] Panel created successfully."))
             except Exception as e:
                 proc.done()
                 log.debug(str(e))

+ 6 - 0
flatcamTools/ToolProperties.py

@@ -178,6 +178,12 @@ class Properties(FlatCAMTool):
                 if obj.apertures[ap]['solid_geometry']:
                     elems = len(obj.apertures[ap]['solid_geometry'])
                     temp_ap['solid_geometry'] = '%s Polygons' % str(elems)
+                try:
+                    if obj.apertures[ap]['follow_geometry']:
+                        elems = len(obj.apertures[ap]['follow_geometry'])
+                        temp_ap['follow_geometry'] = '%s Polygons' % str(elems)
+                except KeyError:
+                    pass
                 self.addChild(apertures, [str(ap), str(temp_ap)], True)
         elif obj.kind.lower() == 'excellon':
             for tool, value in obj.tools.items():

+ 2 - 2
flatcamTools/ToolSolderPaste.py

@@ -752,7 +752,7 @@ 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, "
+                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                          "use a number."))
                     return
             if tool_dia is None:
@@ -823,7 +823,7 @@ 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, "
+                    self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered, "
                                          "use a number."))
                     return
 

+ 12 - 12
flatcamTools/ToolTransform.py

@@ -465,7 +465,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Rotate, "
                                      "use a number."))
                 return
         self.app.worker_task.emit({'fcn': self.on_rotate_action,
@@ -499,7 +499,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew X, "
                                      "use a number."))
                 return
 
@@ -517,7 +517,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Skew Y, "
                                      "use a number."))
                 return
 
@@ -535,7 +535,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale X, "
                                      "use a number."))
                 return
 
@@ -569,7 +569,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Scale Y, "
                                      "use a number."))
                 return
 
@@ -598,7 +598,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset X, "
                                      "use a number."))
                 return
 
@@ -616,7 +616,7 @@ 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, "
+                self.app.inform.emit(_("[ERROR_NOTCL] Wrong value format entered for Offset Y, "
                                      "use a number."))
                 return
 
@@ -671,7 +671,7 @@ class ToolTransform(FlatCAMTool):
                         # add information to the object that it was changed and how much
                         sel_obj.options['rotate'] = num
 
-                    self.app.inform.emit(_('[success]Rotate done ...'))
+                    self.app.inform.emit(_('[success] Rotate done ...'))
                     self.app.progress.emit(100)
 
                 except Exception as e:
@@ -732,7 +732,7 @@ class ToolTransform(FlatCAMTool):
                                 else:
                                     obj.options['mirror_y'] = True
                                 obj.plot()
-                                self.app.inform.emit(_('[success]Flip on the Y axis done ...'))
+                                self.app.inform.emit(_('[success] Flip on the Y axis done ...'))
                             elif axis is 'Y':
                                 obj.mirror('Y', (px, py))
                                 # add information to the object that it was changed and how much
@@ -742,7 +742,7 @@ class ToolTransform(FlatCAMTool):
                                 else:
                                     obj.options['mirror_x'] = True
                                 obj.plot()
-                                self.app.inform.emit(_('[success]Flip on the X axis done ...'))
+                                self.app.inform.emit(_('[success] Flip on the X axis done ...'))
                             self.app.object_changed.emit(obj)
                     self.app.progress.emit(100)
 
@@ -790,7 +790,7 @@ class ToolTransform(FlatCAMTool):
                                 obj.options['skew_y'] = num
                             obj.plot()
                             self.app.object_changed.emit(obj)
-                    self.app.inform.emit(_('[success]Skew on the %s axis done ...') % str(axis))
+                    self.app.inform.emit(_('[success] Skew on the %s axis done ...') % str(axis))
                     self.app.progress.emit(100)
 
                 except Exception as e:
@@ -891,7 +891,7 @@ class ToolTransform(FlatCAMTool):
                                 obj.options['offset_y'] = num
                             obj.plot()
                             self.app.object_changed.emit(obj)
-                    self.app.inform.emit(_('[success]Offset on the %s axis done ...') % str(axis))
+                    self.app.inform.emit(_('[success] Offset on the %s axis done ...') % str(axis))
                     self.app.progress.emit(100)
 
                 except Exception as e:

BIN
locale/de/LC_MESSAGES/strings.mo


+ 11783 - 0
locale/de/LC_MESSAGES/strings.po

@@ -0,0 +1,11783 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: 2019-04-13 01:11+0300\n"
+"PO-Revision-Date: 2019-04-13 16:52+0300\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: de\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.1\n"
+"X-Poedit-Basepath: ../../..\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-SearchPath-0: .\n"
+"X-Poedit-SearchPathExcluded-0: build\n"
+"X-Poedit-SearchPathExcluded-1: doc\n"
+"X-Poedit-SearchPathExcluded-2: tests\n"
+
+#: FlatCAMApp.py:857
+msgid "[ERROR] Could not find the Language files. The App strings are missing."
+msgstr ""
+"[ERROR] Die Sprachdateien konnten nicht gefunden werden. Die App-"
+"Zeichenfolgen fehlen."
+
+#: FlatCAMApp.py:1888 ObjectCollection.py:80 flatcamTools/ToolImage.py:213
+msgid "Open cancelled."
+msgstr "Geöffnet storniert."
+
+#: FlatCAMApp.py:1902
+msgid "Open Config file failed."
+msgstr "Open Config-Datei ist fehlgeschlagen."
+
+#: FlatCAMApp.py:1916
+msgid "Open Script file failed."
+msgstr "Open Script-Datei ist fehlgeschlagen."
+
+#: FlatCAMApp.py:2098
+msgid "[WARNING_NOTCL] Select a Geometry or Excellon Object to edit."
+msgstr ""
+"[WARNING_NOTCL] Wählen Sie ein Geometrie- oder Excellon-Objekt zum "
+"Bearbeiten aus."
+
+#: FlatCAMApp.py:2108
+msgid ""
+"[WARNING_NOTCL] Simultanoeus editing of tools geometry in a MultiGeo "
+"Geometry is not possible.\n"
+" Edit only one geometry at a time."
+msgstr ""
+"[WARNING_NOTCL] Die gleichzeitige Bearbeitung der Werkzeuggeometrie in einer "
+"Multi-Geo-Geometrie ist nicht möglich. Bearbeiten Sie jeweils nur eine "
+"Geometrie."
+
+#: FlatCAMApp.py:2144
+msgid "[WARNING_NOTCL] Editor is activated ..."
+msgstr "[WARNING_NOTCL] Editor ist aktiviert ..."
+
+#: FlatCAMApp.py:2171
+msgid "Do you want to save the edited object?"
+msgstr "Möchten Sie das bearbeitete Objekt speichern?"
+
+#: FlatCAMApp.py:2172 flatcamGUI/FlatCAMGUI.py:1549
+msgid "Close Editor"
+msgstr "Editor schließen"
+
+#: FlatCAMApp.py:2175 FlatCAMApp.py:3255 FlatCAMApp.py:5564
+#: FlatCAMTranslation.py:89 flatcamGUI/FlatCAMGUI.py:3542
+msgid "Yes"
+msgstr "Ja"
+
+#: FlatCAMApp.py:2176 FlatCAMApp.py:3256 FlatCAMApp.py:5565
+#: FlatCAMTranslation.py:90 flatcamGUI/FlatCAMGUI.py:3543
+msgid "No"
+msgstr "Nein"
+
+#: FlatCAMApp.py:2177 FlatCAMApp.py:3257 FlatCAMApp.py:3589 FlatCAMApp.py:5566
+msgid "Cancel"
+msgstr "Kündigen"
+
+#: FlatCAMApp.py:2199 FlatCAMApp.py:2224
+msgid "[WARNING] Object empty after edit."
+msgstr "[WARNING] Das Objekt ist nach der Bearbeitung leer."
+
+#: FlatCAMApp.py:2233 FlatCAMApp.py:2245 FlatCAMApp.py:2257
+msgid "[WARNING_NOTCL] Select a Gerber, Geometry or Excellon Object to update."
+msgstr ""
+"[WARNING_NOTCL] Wählen Sie ein Gerber-, Geometrie- oder Excellon-Objekt zum "
+"Aktualisieren aus."
+
+#: FlatCAMApp.py:2236
+#, python-format
+msgid "[selected] %s is updated, returning to App..."
+msgstr "[selected] %s wird aktualisiert und kehrt zur App zurück ..."
+
+#: FlatCAMApp.py:2593
+msgid "[ERROR] Could not load defaults file."
+msgstr "[ERROR] Standarddatei konnte nicht geladen werden."
+
+#: FlatCAMApp.py:2605
+msgid "[ERROR] Failed to parse defaults file."
+msgstr "[ERROR] Fehler beim Parsen der Standarddatei."
+
+#: FlatCAMApp.py:2626 FlatCAMApp.py:2629
+msgid "Import FlatCAM Preferences"
+msgstr "FlatCAM-Voreinstellungen importieren"
+
+#: FlatCAMApp.py:2634
+msgid "[WARNING_NOTCL] FlatCAM preferences import cancelled."
+msgstr "[WARNING_NOTCL] Import der FlatCAM-Einstellungen wurde abgebrochen."
+
+#: FlatCAMApp.py:2642 FlatCAMApp.py:2689 FlatCAMApp.py:3134
+msgid "[ERROR_NOTCL] Could not load defaults file."
+msgstr "[ERROR_NOTCL] Standarddatei konnte nicht geladen werden."
+
+#: FlatCAMApp.py:2650 FlatCAMApp.py:3143
+msgid "[ERROR_NOTCL] Failed to parse defaults file."
+msgstr "[ERROR_NOTCL] Fehler beim Parsen der Standarddatei."
+
+#: FlatCAMApp.py:2653
+#, python-format
+msgid "[success] Imported Defaults from %s"
+msgstr "[success] Importierte Standardwerte aus %s"
+
+#: FlatCAMApp.py:2663 FlatCAMApp.py:2667
+msgid "Export FlatCAM Preferences"
+msgstr "FlatCAM-Voreinstellungen exportieren"
+
+#: FlatCAMApp.py:2673
+msgid "[WARNING_NOTCL] FlatCAM preferences export cancelled."
+msgstr "[WARNING_NOTCL] Export der FlatCAM-Einstellungen wurde abgebrochen."
+
+#: FlatCAMApp.py:2708 FlatCAMApp.py:3188
+msgid "[ERROR_NOTCL] Failed to write defaults to file."
+msgstr "[ERROR_NOTCL] Fehler beim Schreiben der Standardwerte in die Datei."
+
+#: FlatCAMApp.py:2760
+msgid "[ERROR_NOTCL] Failed to open recent files file for writing."
+msgstr ""
+"[ERROR_NOTCL] Fehler beim Öffnen der zuletzt geöffneten Datei zum Schreiben."
+
+#: FlatCAMApp.py:2845 camlib.py:4430
+msgid "[ERROR_NOTCL] An internal error has ocurred. See shell.\n"
+msgstr "[ERROR_NOTCL] Ein interner Fehler ist aufgetreten. Siehe Shell.\n"
+
+#: FlatCAMApp.py:2846
+#, python-brace-format
+msgid ""
+"Object ({kind}) failed because: {error} \n"
+"\n"
+msgstr ""
+"Objekt ({kind}) gescheitert weil: {error} \n"
+"\n"
+
+#: FlatCAMApp.py:2866
+msgid "Converting units to "
+msgstr "Einheiten in umrechnen "
+
+#: FlatCAMApp.py:2936 FlatCAMApp.py:2939 FlatCAMApp.py:2942 FlatCAMApp.py:2945
+#, python-brace-format
+msgid ""
+"[selected] {kind} created/selected: <span style=\"color:{color};\">{name}</"
+"span>"
+msgstr ""
+"[selected]{kind} erstellt / ausgewählt: <span style=\"color:{color};\">{name}"
+"</span>"
+
+#: FlatCAMApp.py:3039
+#, python-brace-format
+msgid ""
+"<font size=8><B>FlatCAM</B></font><BR>Version {version} {beta} ({date}) - "
+"{arch} <BR><BR>2D Computer-Aided Printed Circuit Board<BR>Manufacturing."
+"<BR><BR>(c) 2014-2019 <B>Juan Pablo Caram</B><BR><BR><B> Main Contributors:</"
+"B><BR>Denis Hayrullin<BR>Kamil Sopko<BR>Marius Stanciu<BR>Matthieu "
+"Berthomé<BR>and many others found <a href = \"https://bitbucket.org/jpcgt/"
+"flatcam/pull-requests/?state=MERGED\">here.</a><BR><BR>Development is done "
+"<a href = \"https://bitbucket.org/jpcgt/flatcam/src/Beta/\">here.</"
+"a><BR>DOWNLOAD area <a href = \"https://bitbucket.org/jpcgt/flatcam/"
+"downloads/\">here.</a><BR>"
+msgstr ""
+"<font size=8><B>FlatCAM</B></font><BR>Ausführung {version} {beta} ({date}) - "
+"{arch} <BR><BR>Computerunterstützte 2D-Leiterplatte<BR>13/5000\n"
+"Herstellung.<BR><BR>(c) 2014-2019 <B>Juan Pablo Caram</B><BR><BR><B> "
+"Hauptakteure:</B><BR>Denis Hayrullin<BR>Kamil Sopko<BR>Marius "
+"Stanciu<BR>Matthieu Berthomé<BR>und viele andere gefunden <a href = "
+"\"https://bitbucket.org/jpcgt/flatcam/pull-requests/?state=MERGED\">hier.</"
+"a><BR><BR>Die Entwicklung ist abgeschlossen <a href = \"https://bitbucket."
+"org/jpcgt/flatcam/src/Beta/\">hier.</a><BR>DOWNLOAD-Bereich <a href = "
+"\"https://bitbucket.org/jpcgt/flatcam/downloads/\">hier.</a><BR>"
+
+#: FlatCAMApp.py:3192
+msgid "[success] Defaults saved."
+msgstr "[success] Standardeinstellungen gespeichert."
+
+#: FlatCAMApp.py:3213
+msgid "[ERROR_NOTCL] Could not load factory defaults file."
+msgstr "[ERROR_NOTCL] Factory-Standarddatei konnte nicht geladen werden."
+
+#: FlatCAMApp.py:3222
+msgid "[ERROR_NOTCL] Failed to parse factory defaults file."
+msgstr "[ERROR_NOTCL] Fehler beim Parsen der Werksvorgaben-Datei."
+
+#: FlatCAMApp.py:3236
+msgid "[ERROR_NOTCL] Failed to write factory defaults to file."
+msgstr ""
+"[ERROR_NOTCL] Fehler beim Schreiben der Werkseinstellungen in die Datei."
+
+#: FlatCAMApp.py:3240
+msgid "Factory defaults saved."
+msgstr "Werkseinstellungen gespeichert."
+
+#: FlatCAMApp.py:3245 flatcamGUI/FlatCAMGUI.py:2974
+msgid "[WARNING_NOTCL] Application is saving the project. Please wait ..."
+msgstr "[WARNING_NOTCL] Anwendung speichert das Projekt. Warten Sie mal ..."
+
+#: FlatCAMApp.py:3250
+msgid ""
+"There are files/objects modified in FlatCAM. \n"
+"Do you want to Save the project?"
+msgstr ""
+"In FlatCAM wurden Dateien / Objekte geändert.\n"
+"Möchten Sie das Projekt speichern?"
+
+#: FlatCAMApp.py:3253 FlatCAMApp.py:5562
+msgid "Save changes"
+msgstr "Änderungen speichern"
+
+#: FlatCAMApp.py:3320
+msgid ""
+"[ERROR] Failed join. The Geometry objects are of different types.\n"
+"At least one is MultiGeo type and the other is SingleGeo type. A possibility "
+"is to convert from one to another and retry joining \n"
+"but in the case of converting from MultiGeo to SingleGeo, informations may "
+"be lost and the result may not be what was expected. \n"
+"Check the generated GCODE."
+msgstr ""
+"[ERROR]Fehlgeschlagen beitreten. Die Geometrieobjekte sind unterschiedlich.\n"
+"Mindestens einer ist vom Typ MultiGeo und der andere vom Typ SingleGeo. \n"
+"Eine Möglichkeit besteht darin, von einem zum anderen zu konvertieren und "
+"erneut zu verbinden\n"
+"Bei einer Konvertierung von MultiGeo in SingleGeo können jedoch "
+"Informationen verloren gehen \n"
+"und das Ergebnis entspricht möglicherweise nicht dem, was erwartet wurde.\n"
+"Überprüfen Sie den generierten GCODE."
+
+#: FlatCAMApp.py:3361
+msgid "[ERROR_NOTCL] Failed. Excellon joining works only on Excellon objects."
+msgstr ""
+"[ERROR_NOTCL] Gescheitert. Die Verbindung von Excellon funktioniert nur bei "
+"Excellon-Objekten."
+
+#: FlatCAMApp.py:3383
+msgid "[ERROR_NOTCL] Failed. Gerber joining works only on Gerber objects."
+msgstr ""
+"[ERROR_NOTCL] Gescheitert. Das Gerber-Verbinden funktioniert nur bei Gerber-"
+"Objekten."
+
+#: FlatCAMApp.py:3398 FlatCAMApp.py:3423
+msgid "[ERROR_NOTCL] Failed. Select a Geometry Object and try again."
+msgstr ""
+"[ERROR_NOTCL] Gescheitert. Wählen Sie ein Geometrieobjekt aus und versuchen "
+"Sie es erneut."
+
+#: FlatCAMApp.py:3402 FlatCAMApp.py:3427
+#, python-format
+msgid "[ERROR_NOTCL] Expected a FlatCAMGeometry, got %s"
+msgstr "[ERROR_NOTCL] Erwartete eine FlatCAMGeometry, bekam % s"
+
+#: FlatCAMApp.py:3415
+msgid "[success] A Geometry object was converted to MultiGeo type."
+msgstr "[success] Ein Geometrieobjekt wurde in den MultiGeo-Typ konvertiert."
+
+#: FlatCAMApp.py:3441
+msgid "[success] A Geometry object was converted to SingleGeo type."
+msgstr "[success] Ein Geometrieobjekt wurde in den SingleGeo-Typ konvertiert."
+
+#: FlatCAMApp.py:3588 FlatCAMApp.py:4353 FlatCAMApp.py:5829 FlatCAMApp.py:5840
+#: FlatCAMApp.py:6026 FlatCAMApp.py:6036
+msgid "Ok"
+msgstr "Ok"
+
+#: FlatCAMApp.py:3629
+#, python-format
+msgid "[success] Converted units to %s"
+msgstr "[success] Einheiten in umgerechnet %s"
+
+#: FlatCAMApp.py:3640
+msgid "[WARNING_NOTCL] Units conversion cancelled."
+msgstr "[WARNING_NOTCL] Einheitenumrechnung abgebrochen."
+
+#: FlatCAMApp.py:4222
+msgid "Open file"
+msgstr "Datei öffnen"
+
+#: FlatCAMApp.py:4253 FlatCAMApp.py:4258
+msgid "Export G-Code ..."
+msgstr "G-Code exportieren ..."
+
+#: FlatCAMApp.py:4261
+msgid "[WARNING_NOTCL] Export Code cancelled."
+msgstr "[WARNING_NOTCL] Exportcode wurde abgebrochen."
+
+#: FlatCAMApp.py:4271
+msgid "[WARNING] No such file or directory"
+msgstr "[WARNING] Keine solche Datei oder Ordner"
+
+#: FlatCAMApp.py:4278
+#, python-format
+msgid "Saved to: %s"
+msgstr "Gespeichert in: %s"
+
+#: FlatCAMApp.py:4341 FlatCAMApp.py:4374 FlatCAMApp.py:4385 FlatCAMApp.py:4396
+#: flatcamTools/ToolNonCopperClear.py:488 flatcamTools/ToolSolderPaste.py:764
+msgid ""
+"[WARNING_NOTCL] Please enter a tool diameter with non-zero value, in Float "
+"format."
+msgstr ""
+"[WARNING_NOTCL] Bitte geben Sie einen Werkzeugdurchmesser mit einem Wert "
+"ungleich Null im Float-Format ein."
+
+#: FlatCAMApp.py:4346 FlatCAMApp.py:4379 FlatCAMApp.py:4390 FlatCAMApp.py:4401
+#: flatcamGUI/FlatCAMGUI.py:2891
+msgid "[WARNING_NOTCL] Adding Tool cancelled ..."
+msgstr "[WARNING_NOTCL] Addierwerkzeug abgebrochen ..."
+
+#: FlatCAMApp.py:4349
+msgid ""
+"Adding Tool works only when Advanced is checked.\n"
+"Go to Preferences -> General - Show Advanced Options."
+msgstr ""
+"Das Hinzufügen eines Tools funktioniert nur, wenn \"Erweitert\" aktiviert "
+"ist.\n"
+"Gehen Sie zu Einstellungen -> Allgemein - Erweiterte Optionen anzeigen."
+
+#: FlatCAMApp.py:4455
+msgid "Object(s) deleted ..."
+msgstr "Objekt (e) gelöscht ..."
+
+#: FlatCAMApp.py:4459
+msgid "Failed. No object(s) selected..."
+msgstr "Gescheitert. Kein Objekt ausgewählt ..."
+
+#: FlatCAMApp.py:4461
+msgid "Save the work in Editor and try again ..."
+msgstr "Speichern Sie die Arbeit im Editor und versuchen Sie es erneut ..."
+
+#: FlatCAMApp.py:4474
+msgid "Click to set the origin ..."
+msgstr "Klicken Sie hier, um den Ursprung festzulegen ..."
+
+#: FlatCAMApp.py:4487
+msgid "Jump to ..."
+msgstr "Springen zu ..."
+
+#: FlatCAMApp.py:4488
+msgid "Enter the coordinates in format X,Y:"
+msgstr "Geben Sie die Koordinaten im Format X, Y ein:"
+
+#: FlatCAMApp.py:4495
+msgid "Wrong coordinates. Enter coordinates in format: X,Y"
+msgstr "Falsche Koordinaten. Koordinaten im Format eingeben: X, Y"
+
+#: FlatCAMApp.py:4513
+msgid "Done."
+msgstr "Gemacht."
+
+#: FlatCAMApp.py:4672
+msgid "[success] Origin set ..."
+msgstr "[success] Ursprung gesetzt ..."
+
+#: FlatCAMApp.py:4690
+msgid "Preferences"
+msgstr "Einstellungen"
+
+#: FlatCAMApp.py:4710
+msgid "[WARNING_NOTCL] No object selected to Flip on Y axis."
+msgstr "[WARNING_NOTCL] Kein Objekt ausgewählt, um auf der Y-Achse zu kippen."
+
+#: FlatCAMApp.py:4735
+msgid "[success] Flip on Y axis done."
+msgstr "[success] Y-Achse umdrehen fertig."
+
+#: FlatCAMApp.py:4737 FlatCAMApp.py:4777
+#: flatcamEditors/FlatCAMGeoEditor.py:1353
+#: flatcamEditors/FlatCAMGrbEditor.py:3522 flatcamTools/ToolTransform.py:750
+#, python-format
+msgid "[ERROR_NOTCL] Due of %s, Flip action was not executed."
+msgstr "[ERROR_NOTCL] Aufgrund von %s wurde die Flip-Aktion nicht ausgeführt."
+
+#: FlatCAMApp.py:4750
+msgid "[WARNING_NOTCL] No object selected to Flip on X axis."
+msgstr "[WARNING_NOTCL] Kein Objekt ausgewählt, um auf der X-Achse zu kippen."
+
+#: FlatCAMApp.py:4775
+msgid "[success] Flip on X axis done."
+msgstr "[success] Dreh auf der X-Achse fertig."
+
+#: FlatCAMApp.py:4790
+msgid "[WARNING_NOTCL] No object selected to Rotate."
+msgstr "[WARNING_NOTCL] Kein Objekt zum Drehen ausgewählt."
+
+#: FlatCAMApp.py:4793 FlatCAMApp.py:4838 FlatCAMApp.py:4869
+msgid "Transform"
+msgstr "Verwandeln"
+
+#: FlatCAMApp.py:4793 FlatCAMApp.py:4838 FlatCAMApp.py:4869
+msgid "Enter the Angle value:"
+msgstr "Geben Sie den Winkelwert ein:"
+
+#: FlatCAMApp.py:4823
+msgid "[success] Rotation done."
+msgstr "[success] Rotation erfolgt."
+
+#: FlatCAMApp.py:4825 flatcamEditors/FlatCAMGeoEditor.py:1296
+#: flatcamEditors/FlatCAMGrbEditor.py:3465 flatcamTools/ToolTransform.py:678
+#, python-format
+msgid "[ERROR_NOTCL] Due of %s, rotation movement was not executed."
+msgstr ""
+"[ERROR_NOTCL] Aufgrund von %s wurde keine Rotationsbewegung ausgeführt."
+
+#: FlatCAMApp.py:4836
+msgid "[WARNING_NOTCL] No object selected to Skew/Shear on X axis."
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt für Neigung / Scherung auf der X-Achse "
+"ausgewählt."
+
+#: FlatCAMApp.py:4857
+msgid "[success] Skew on X axis done."
+msgstr "[success] Neigung auf der X-Achse fertig."
+
+#: FlatCAMApp.py:4867
+msgid "[WARNING_NOTCL] No object selected to Skew/Shear on Y axis."
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt für Neigung / Scherung auf der Y-Achse "
+"ausgewählt."
+
+#: FlatCAMApp.py:4888
+msgid "[success] Skew on Y axis done."
+msgstr "[success] Neigung auf der Y-Achse fertig."
+
+#: FlatCAMApp.py:4984 FlatCAMApp.py:5011
+msgid ""
+"[WARNING_NOTCL] Please enter a grid value with non-zero value, in Float "
+"format."
+msgstr ""
+"[WARNING_NOTCL] Bitte geben Sie im Float-Format einen Rasterwert mit einem "
+"Wert ungleich Null ein."
+
+#: FlatCAMApp.py:4990
+msgid "[success] New Grid added ..."
+msgstr "[success] Neues Netz hinzugefügt ..."
+
+#: FlatCAMApp.py:4993
+msgid "[WARNING_NOTCL] Grid already exists ..."
+msgstr "[WARNING_NOTCL] Netz existiert bereits ..."
+
+#: FlatCAMApp.py:4996
+msgid "[WARNING_NOTCL] Adding New Grid cancelled ..."
+msgstr "[WARNING_NOTCL] Neues Netz wurde abgebrochen ..."
+
+#: FlatCAMApp.py:5018
+msgid "[ERROR_NOTCL] Grid Value does not exist ..."
+msgstr "[ERROR_NOTCL] Rasterwert existiert nicht ..."
+
+#: FlatCAMApp.py:5021
+msgid "[success] Grid Value deleted ..."
+msgstr "[success] Rasterwert gelöscht ..."
+
+#: FlatCAMApp.py:5024
+msgid "[WARNING_NOTCL] Delete Grid value cancelled ..."
+msgstr "[WARNING_NOTCL] Rasterwert löschen abgebrochen ..."
+
+#: FlatCAMApp.py:5063
+msgid "[WARNING_NOTCL] No object selected to copy it's name"
+msgstr "[WARNING_NOTCL] Kein Objekt zum Kopieren des Namens ausgewählt"
+
+#: FlatCAMApp.py:5067
+msgid "Name copied on clipboard ..."
+msgstr "Name in Zwischenablage kopiert ..."
+
+#: FlatCAMApp.py:5362 FlatCAMApp.py:5365 FlatCAMApp.py:5368 FlatCAMApp.py:5371
+#: FlatCAMApp.py:5385 FlatCAMApp.py:5388 FlatCAMApp.py:5391 FlatCAMApp.py:5394
+#: FlatCAMApp.py:5433 FlatCAMApp.py:5436 FlatCAMApp.py:5439 FlatCAMApp.py:5442
+#: ObjectCollection.py:711 ObjectCollection.py:714 ObjectCollection.py:717
+#: ObjectCollection.py:720
+#, python-brace-format
+msgid "[selected]<span style=\"color:{color};\">{name}</span> selected"
+msgstr "[selected]<span style=\"color:{color};\">{name}</span> ausgewählt"
+
+#: FlatCAMApp.py:5559
+msgid ""
+"There are files/objects opened in FlatCAM.\n"
+"Creating a New project will delete them.\n"
+"Do you want to Save the project?"
+msgstr ""
+"In FlatCAM sind Dateien / Objekte geöffnet.\n"
+"Wenn Sie ein neues Projekt erstellen, werden diese gelöscht.\n"
+"Möchten Sie das Projekt speichern?"
+
+#: FlatCAMApp.py:5580
+msgid "[success] New Project created..."
+msgstr "[success] Neues Projekt erstellt ..."
+
+#: FlatCAMApp.py:5688 FlatCAMApp.py:5691 flatcamGUI/FlatCAMGUI.py:594
+#: flatcamGUI/FlatCAMGUI.py:1762
+msgid "Open Gerber"
+msgstr "Gerber öffnen"
+
+#: FlatCAMApp.py:5696
+msgid "[WARNING_NOTCL] Open Gerber cancelled."
+msgstr "[WARNING_NOTCL] Offener Gerber abgebrochen."
+
+#: FlatCAMApp.py:5717 FlatCAMApp.py:5720 flatcamGUI/FlatCAMGUI.py:595
+#: flatcamGUI/FlatCAMGUI.py:1763
+msgid "Open Excellon"
+msgstr "Excellon öffnen"
+
+#: FlatCAMApp.py:5725
+msgid "[WARNING_NOTCL] Open Excellon cancelled."
+msgstr "[WARNING_NOTCL] Offener Excellon abgebrochen."
+
+#: FlatCAMApp.py:5747 FlatCAMApp.py:5750
+msgid "Open G-Code"
+msgstr "G-Code öffnen"
+
+#: FlatCAMApp.py:5755
+msgid "[WARNING_NOTCL] Open G-Code cancelled."
+msgstr "[WARNING_NOTCL] Geöffneter G-Code wurde abgebrochen."
+
+#: FlatCAMApp.py:5773 FlatCAMApp.py:5776
+msgid "Open Project"
+msgstr "Offenes Projekt"
+
+#: FlatCAMApp.py:5784
+msgid "[WARNING_NOTCL] Open Project cancelled."
+msgstr "[WARNING_NOTCL] Projekt abbrechen abgebrochen."
+
+#: FlatCAMApp.py:5803 FlatCAMApp.py:5806
+msgid "Open Configuration File"
+msgstr "Offene Einstellungsdatei"
+
+#: FlatCAMApp.py:5810
+msgid "[WARNING_NOTCL Open Config cancelled."
+msgstr "[WARNING_NOTCL] Open Config abgesagt."
+
+#: FlatCAMApp.py:5825 FlatCAMApp.py:6022 FlatCAMApp.py:8105 FlatCAMApp.py:8125
+#: FlatCAMApp.py:8146 FlatCAMApp.py:8168
+msgid "[WARNING_NOTCL] No object selected."
+msgstr "[WARNING_NOTCL] Kein Objekt ausgewählt"
+
+#: FlatCAMApp.py:5826 FlatCAMApp.py:6023
+msgid "Please Select a Geometry object to export"
+msgstr "Bitte wählen Sie ein Geometrieobjekt zum Exportieren aus"
+
+#: FlatCAMApp.py:5837
+msgid "[ERROR_NOTCL] Only Geometry, Gerber and CNCJob objects can be used."
+msgstr ""
+"[ERROR_NOTCL] Es können nur Geometrie-, Gerber- und CNCJob-Objekte verwendet "
+"werden."
+
+#: FlatCAMApp.py:5850 FlatCAMApp.py:5854
+msgid "Export SVG"
+msgstr "SVG exportieren"
+
+#: FlatCAMApp.py:5859
+msgid "[WARNING_NOTCL] Export SVG cancelled."
+msgstr "[WARNING_NOTCL] Export SVG abgebrochen."
+
+#: FlatCAMApp.py:5873
+msgid "[[WARNING_NOTCL]] Data must be a 3D array with last dimension 3 or 4"
+msgstr ""
+"[WARNING_NOTCL] Daten müssen ein 3D-Array mit der letzten Dimension 3 oder 4 "
+"sein"
+
+#: FlatCAMApp.py:5879 FlatCAMApp.py:5883
+msgid "Export PNG Image"
+msgstr "PNG-Bild exportieren"
+
+#: FlatCAMApp.py:5888
+msgid "Export PNG cancelled."
+msgstr "Export PNG abgebrochen."
+
+#: FlatCAMApp.py:5905
+msgid ""
+"[WARNING_NOTCL] No object selected. Please select an Gerber object to export."
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt. Bitte wählen Sie ein Gerber-Objekt "
+"aus, das Sie exportieren möchten."
+
+#: FlatCAMApp.py:5910
+msgid ""
+"[ERROR_NOTCL] Failed. Only Gerber objects can be saved as Gerber files..."
+msgstr ""
+"[ERROR_NOTCL] Fehlgeschlagen. Nur Gerber-Objekte können als Gerber-Dateien "
+"gespeichert werden ..."
+
+#: FlatCAMApp.py:5922
+msgid "Save Gerber source file"
+msgstr "Gerber-Quelldatei speichern"
+
+#: FlatCAMApp.py:5927
+msgid "[WARNING_NOTCL] Save Gerber source file cancelled."
+msgstr "[WARNING_NOTCL] Gerber Quelldatei speichern abgebrochen."
+
+#: FlatCAMApp.py:5944
+msgid ""
+"[WARNING_NOTCL] No object selected. Please select an Excellon object to "
+"export."
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt Bitte wählen Sie ein Excellon-Objekt "
+"zum Exportieren aus."
+
+#: FlatCAMApp.py:5949 FlatCAMApp.py:5988
+msgid ""
+"[ERROR_NOTCL] Failed. Only Excellon objects can be saved as Excellon files..."
+msgstr ""
+"[ERROR_NOTCL] Fehlgeschlagen. Nur Excellon-Objekte können als Excellon-"
+"Dateien gespeichert werden ..."
+
+#: FlatCAMApp.py:5957 FlatCAMApp.py:5961
+msgid "Save Excellon source file"
+msgstr "Speichern Sie die Excellon-Quelldatei"
+
+#: FlatCAMApp.py:5966
+msgid "[WARNING_NOTCL] Saving Excellon source file cancelled."
+msgstr "[WARNING_NOTCL] Speichern der Excellon-Quelldatei abgebrochen."
+
+#: FlatCAMApp.py:5983
+msgid ""
+"[WARNING_NOTCL] No object selected. Please Select an Excellon object to "
+"export."
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt. Bitte wählen Sie ein Excellon-Objekt "
+"aus, das Sie exportieren möchten."
+
+#: FlatCAMApp.py:5996 FlatCAMApp.py:6000
+msgid "Export Excellon"
+msgstr "Excellon exportieren"
+
+#: FlatCAMApp.py:6005
+msgid "[WARNING_NOTCL] Export Excellon cancelled."
+msgstr "[WARNING_NOTCL] Export Excellon wurde abgebrochen."
+
+#: FlatCAMApp.py:6033
+msgid "[ERROR_NOTCL] Only Geometry objects can be used."
+msgstr "[ERROR_NOTCL] Es können nur Geometrieobjekte verwendet werden."
+
+#: FlatCAMApp.py:6047 FlatCAMApp.py:6051
+msgid "Export DXF"
+msgstr "DXF exportieren"
+
+#: FlatCAMApp.py:6056
+msgid "[WARNING_NOTCL] Export DXF cancelled."
+msgstr "[WARNING_NOTCL] Export DXF wurde abgebrochen."
+
+#: FlatCAMApp.py:6074 FlatCAMApp.py:6077
+msgid "Import SVG"
+msgstr "SVG importieren"
+
+#: FlatCAMApp.py:6085
+msgid "[WARNING_NOTCL] Open SVG cancelled."
+msgstr "[WARNING_NOTCL] Open SVG abgebrochen."
+
+#: FlatCAMApp.py:6104 FlatCAMApp.py:6107
+msgid "Import DXF"
+msgstr "Importieren Sie DXF"
+
+#: FlatCAMApp.py:6115
+msgid "[WARNING_NOTCL] Open DXF cancelled."
+msgstr "[WARNING_NOTCL] Open DXF cancelled."
+
+#: FlatCAMApp.py:6133
+#, python-format
+msgid "%s"
+msgstr "%s"
+
+#: FlatCAMApp.py:6153
+msgid ""
+"[WARNING_NOTCL] Select an Gerber or Excellon file to view it's source file."
+msgstr ""
+"[WARNING_NOTCL] Wählen Sie eine Gerber- oder Excellon-Datei aus, um die "
+"Quelldatei anzuzeigen."
+
+#: FlatCAMApp.py:6160
+msgid ""
+"[WARNING_NOTCL] There is no selected object for which to see it's source "
+"file code."
+msgstr ""
+"[WARNING_NOTCL] Es gibt kein ausgewähltes Objekt, für das man seinen "
+"Quelldateien sehen kann."
+
+#: FlatCAMApp.py:6168
+msgid "Source Editor"
+msgstr "Quelleditor"
+
+#: FlatCAMApp.py:6178
+#, python-format
+msgid "[ERROR]App.on_view_source() -->%s"
+msgstr "[ERROR]App.on_view_source() -->%s"
+
+#: FlatCAMApp.py:6190 FlatCAMApp.py:7211 FlatCAMObj.py:5257
+msgid "Code Editor"
+msgstr "Code-Editor"
+
+#: FlatCAMApp.py:6202
+msgid "Script Editor"
+msgstr "Script Editor"
+
+#: FlatCAMApp.py:6205
+msgid ""
+"#\n"
+"# CREATE A NEW FLATCAM TCL SCRIPT\n"
+"# TCL Tutorial here: https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial."
+"html\n"
+"#\n"
+"\n"
+"# FlatCAM commands list:\n"
+"# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, "
+"AlignDrillGrid, ClearShell, Cncjob,\n"
+"# Cutout, Delete, Drillcncjob, ExportGcode, ExportSVG, Exteriors, GeoCutout, "
+"GeoUnion, GetNames, GetSys,\n"
+"# ImportSvg, Interiors, Isolate, Follow, JoinExcellon, JoinGeometry, "
+"ListSys, MillHoles, Mirror, New,\n"
+"# NewGeometry, Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject, "
+"Options, Paint, Panelize,\n"
+"# Plot, SaveProject, SaveSys, Scale, SetActive, SetSys, Skew, SubtractPoly,"
+"SubtractRectangle, Version,\n"
+"# WriteGCode\n"
+"#\n"
+"\n"
+msgstr ""
+"#\n"
+"# ERSTELLE EINEN NEUEN FLATCAM-TCL-SCRIPT\n"
+"# TCL Tutorial hier: https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial."
+"html\n"
+"#\n"
+"\n"
+"# Liste der FlatCAM-Befehle:\n"
+"# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, "
+"AlignDrillGrid, ClearShell, Cncjob,\n"
+"# Cutout, Delete, Drillcncjob, ExportGcode, ExportSVG, Exteriors, GeoCutout, "
+"GeoUnion, GetNames, GetSys,\n"
+"# ImportSvg, Interiors, Isolate, Follow, JoinExcellon, JoinGeometry, "
+"ListSys, MillHoles, Mirror, New,\n"
+"# NewGeometry, Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject, "
+"Options, Paint, Panelize,\n"
+"# Plot, SaveProject, SaveSys, Scale, SetActive, SetSys, Skew, SubtractPoly,"
+"SubtractRectangle, Version,\n"
+"# WriteGCode\n"
+"#\n"
+"\n"
+
+#: FlatCAMApp.py:6228 FlatCAMApp.py:6231
+msgid "Open TCL script"
+msgstr "Öffnen Sie das TCL-Skript"
+
+#: FlatCAMApp.py:6239
+msgid "[WARNING_NOTCL] Open TCL script cancelled."
+msgstr "[WARNING_NOTCL] Open TCL-Skript wurde abgebrochen."
+
+#: FlatCAMApp.py:6251
+#, python-format
+msgid "[ERROR]App.on_fileopenscript() -->%s"
+msgstr "[ERROR]App.on_fileopenscript() -->%s"
+
+#: FlatCAMApp.py:6277 FlatCAMApp.py:6280
+msgid "Run TCL script"
+msgstr "Führen Sie das TCL-Skript aus"
+
+#: FlatCAMApp.py:6288
+msgid "[WARNING_NOTCL] Run TCL script cancelled."
+msgstr "[WARNING_NOTCL] Das TCL-Skript wird abgebrochen."
+
+#: FlatCAMApp.py:6334 FlatCAMApp.py:6338
+msgid "Save Project As ..."
+msgstr "Projekt speichern als ..."
+
+#: FlatCAMApp.py:6335
+#, python-brace-format
+msgid "{l_save}/Project_{date}"
+msgstr "{l_save}/Projekt_{date}"
+
+#: FlatCAMApp.py:6343
+msgid "[WARNING_NOTCL] Save Project cancelled."
+msgstr "[WARNING_NOTCL] Projekt speichern abgebrochen"
+
+#: FlatCAMApp.py:6388
+msgid "Exporting SVG"
+msgstr "SVG exportieren"
+
+#: FlatCAMApp.py:6421 FlatCAMApp.py:6526 FlatCAMApp.py:6640
+#, python-format
+msgid "[success] SVG file exported to %s"
+msgstr "[success] SVG-Datei in exportiert %s"
+
+#: FlatCAMApp.py:6452 FlatCAMApp.py:6572
+#, python-format
+msgid "[WARNING_NOTCL] No object Box. Using instead %s"
+msgstr "[WARNING_NOTCL] Kein Objektfeld. Stattdessen verwenden %s"
+
+#: FlatCAMApp.py:6529 FlatCAMApp.py:6643
+msgid "Generating Film ... Please wait."
+msgstr "Film wird erstellt ... Bitte warten Sie."
+
+#: FlatCAMApp.py:6790
+#, python-format
+msgid "[success] Excellon file exported to %s"
+msgstr "[success] Excellon-Datei nach exportiert %s"
+
+#: FlatCAMApp.py:6797
+msgid "Exporting Excellon"
+msgstr "Excellon exportieren"
+
+#: FlatCAMApp.py:6802 FlatCAMApp.py:6809
+msgid "[ERROR_NOTCL] Could not export Excellon file."
+msgstr "[ERROR_NOTCL] Excellon-Datei konnte nicht exportiert werden."
+
+#: FlatCAMApp.py:6848
+#, python-format
+msgid "[success] DXF file exported to %s"
+msgstr "[success] DXF-Datei in exportiert %s"
+
+#: FlatCAMApp.py:6854
+msgid "Exporting DXF"
+msgstr "DXF exportieren"
+
+#: FlatCAMApp.py:6859 FlatCAMApp.py:6866
+msgid "[[WARNING_NOTCL]] Could not export DXF file."
+msgstr "[WARNING_NOTCL] DXF-Datei konnte nicht exportiert werden."
+
+#: FlatCAMApp.py:6886 FlatCAMApp.py:6928 FlatCAMApp.py:6969
+msgid ""
+"[ERROR_NOTCL] Not supported type is picked as parameter. Only Geometry and "
+"Gerber are supported"
+msgstr ""
+"[ERROR_NOTCL] Nicht unterstützte Art wird als Parameter ausgewählt. Nur "
+"Geometrie und Gerber werden unterstützt"
+
+#: FlatCAMApp.py:6896
+msgid "Importing SVG"
+msgstr "SVG importieren"
+
+#: FlatCAMApp.py:6907 FlatCAMApp.py:6949 FlatCAMApp.py:6989 FlatCAMApp.py:7065
+#: FlatCAMApp.py:7132 FlatCAMApp.py:7197
+#, python-format
+msgid "[success] Opened: %s"
+msgstr "[success] Geöffnet: %s"
+
+#: FlatCAMApp.py:6938
+msgid "Importing DXF"
+msgstr "DXF importieren"
+
+#: FlatCAMApp.py:6977
+msgid "Importing Image"
+msgstr "Bild importieren"
+
+#: FlatCAMApp.py:7018 FlatCAMApp.py:7020
+#, python-format
+msgid "[ERROR_NOTCL] Failed to open file: %s"
+msgstr "[ERROR_NOTCL] Datei konnte nicht geöffnet werden: %s"
+
+#: FlatCAMApp.py:7023
+#, python-brace-format
+msgid "[ERROR_NOTCL] Failed to parse file: {name}. {error}"
+msgstr "[ERROR_NOTCL] Fehler beim Parsen der Datei: {name}. {error}"
+
+#: FlatCAMApp.py:7029 FlatCAMObj.py:3961
+#: flatcamEditors/FlatCAMExcEditor.py:1927
+#: flatcamEditors/FlatCAMGrbEditor.py:2061
+msgid "[ERROR] An internal error has ocurred. See shell.\n"
+msgstr "[ERROR] Ein interner Fehler ist aufgetreten. Siehe Shell.\n"
+
+#: FlatCAMApp.py:7038
+msgid ""
+"[ERROR_NOTCL] Object is not Gerber file or empty. Aborting object creation."
+msgstr ""
+"[ERROR_NOTCL] Objekt ist keine Gerber-Datei oder leer. Abbruch der "
+"Objekterstellung"
+
+#: FlatCAMApp.py:7046
+msgid "Opening Gerber"
+msgstr "Gerber öffnen"
+
+#: FlatCAMApp.py:7056
+msgid "[ERROR_NOTCL] Open Gerber failed. Probable not a Gerber file."
+msgstr ""
+"[ERROR_NOTCL] Gerber öffnen ist fehlgeschlagen. Wahrscheinlich keine Gerber-"
+"Datei."
+
+#: FlatCAMApp.py:7091
+msgid "[ERROR_NOTCL] This is not Excellon file."
+msgstr "[ERROR_NOTCL] Dies ist keine Excellon-Datei."
+
+#: FlatCAMApp.py:7094
+#, python-format
+msgid "[ERROR_NOTCL] Cannot open file: %s"
+msgstr "[ERROR_NOTCL] Kann Datei nicht öffnen: %s"
+
+#: FlatCAMApp.py:7099
+msgid "[ERROR_NOTCL] An internal error has occurred. See shell.\n"
+msgstr "[ERROR_NOTCL] Ein interner Fehler ist aufgetreten. Siehe Shell.\n"
+
+#: FlatCAMApp.py:7115
+#, python-format
+msgid "[ERROR_NOTCL] No geometry found in file: %s"
+msgstr "[ERROR_NOTCL] Keine Geometrie in der Datei gefunden: %s"
+
+#: FlatCAMApp.py:7118
+msgid "Opening Excellon."
+msgstr "Eröffnung Excellon."
+
+#: FlatCAMApp.py:7125
+msgid "[ERROR_NOTCL] Open Excellon file failed. Probable not an Excellon file."
+msgstr ""
+"[ERROR_NOTCL] Die Excellon-Datei konnte nicht geöffnet werden. "
+"Wahrscheinlich keine Excellon-Datei."
+
+#: FlatCAMApp.py:7164
+#, python-format
+msgid "[ERROR_NOTCL] Failed to open %s"
+msgstr "[ERROR_NOTCL] Gescheitert zu öffnen %s"
+
+#: FlatCAMApp.py:7174
+msgid "[ERROR_NOTCL] This is not GCODE"
+msgstr "[ERROR_NOTCL] Dies ist kein GCODE"
+
+#: FlatCAMApp.py:7180
+msgid "Opening G-Code."
+msgstr "G-Code öffnen."
+
+#: FlatCAMApp.py:7188
+msgid ""
+"[ERROR_NOTCL] Failed to create CNCJob Object. Probable not a GCode file.\n"
+" Attempting to create a FlatCAM CNCJob Object from G-Code file failed during "
+"processing"
+msgstr ""
+"[ERROR_NOTCL] CNCJob-Objekt konnte nicht erstellt werden. Wahrscheinlich "
+"keine GCode-Datei.\n"
+"Der Versuch, ein FlatCAM-CNCJob-Objekt aus einer G-Code-Datei zu erstellen, "
+"ist während der Verarbeitung fehlgeschlagen"
+
+#: FlatCAMApp.py:7228
+#, python-format
+msgid "[ERROR_NOTCL] Failed to open config file: %s"
+msgstr "[ERROR_NOTCL] Fehler beim Öffnen der Konfigurationsdatei: %s"
+
+#: FlatCAMApp.py:7253 FlatCAMApp.py:7269
+#, python-format
+msgid "[ERROR_NOTCL] Failed to open project file: %s"
+msgstr "[ERROR_NOTCL] Projektdatei konnte nicht geöffnet werden: %s"
+
+#: FlatCAMApp.py:7295
+#, python-format
+msgid "[success] Project loaded from: %s"
+msgstr "[success] Projekt geladen von: %s"
+
+#: FlatCAMApp.py:7425
+msgid "Available commands:\n"
+msgstr "Verfügbare Befehle:\n"
+
+#: FlatCAMApp.py:7427
+msgid ""
+"\n"
+"\n"
+"Type help <command_name> for usage.\n"
+" Example: help open_gerber"
+msgstr ""
+"\n"
+"\n"
+"Geben Sie help <Befehlsname> für die Verwendung ein.\n"
+"Beispiel: help open_gerber"
+
+#: FlatCAMApp.py:7575
+msgid "Shows list of commands."
+msgstr "Zeigt eine Liste von Befehlen an."
+
+#: FlatCAMApp.py:7628
+msgid "[ERROR_NOTCL] Failed to load recent item list."
+msgstr "[ERROR_NOTCL] Fehler beim Laden der letzten Elementliste."
+
+#: FlatCAMApp.py:7635
+msgid "[ERROR_NOTCL] Failed to parse recent item list."
+msgstr ""
+"[ERROR_NOTCL] Liste der letzten Artikel konnte nicht analysiert werden."
+
+#: FlatCAMApp.py:7696 flatcamGUI/FlatCAMGUI.py:929
+msgid "<b>Shortcut Key List</b>"
+msgstr "<b> Liste der Tastenkombinationen </b>"
+
+#: FlatCAMApp.py:7703
+msgid ""
+"\n"
+"<p><span style=\"font-size:14px\"><strong>Selected Tab - Choose an Item from "
+"Project Tab</strong></span></p>\n"
+"\n"
+"<p><span style=\"font-size:10px\"><strong>Details</strong>:<br />\n"
+"The normal flow when working in FlatCAM is the following:</span></p>\n"
+"\n"
+"<ol>\n"
+"\t<li><span style=\"font-size:10px\">Loat/Import a Gerber, Excellon, Gcode, "
+"DXF, Raster Image or SVG file into FlatCAM using either the menu&#39;s, "
+"toolbars, key shortcuts or even dragging and dropping the files on the GUI."
+"<br />\n"
+"\t<br />\n"
+"\tYou can also load a <strong>FlatCAM project</strong> by double clicking on "
+"the project file, drag &amp; drop of the file into the FLATCAM GUI or "
+"through the menu/toolbar links offered within the app.</span><br />\n"
+"\t&nbsp;</li>\n"
+"\t<li><span style=\"font-size:10px\">Once an object is available in the "
+"Project Tab, by selecting it and then focusing on <strong>SELECTED TAB </"
+"strong>(more simpler is to double click the object name in the Project Tab), "
+"<strong>SELECTED TAB </strong>will be updated with the object properties "
+"according to it&#39;s kind: Gerber, Excellon, Geometry or CNCJob object.<br /"
+">\n"
+"\t<br />\n"
+"\tIf the selection of the object is done on the canvas by single click "
+"instead, and the <strong>SELECTED TAB</strong> is in focus, again the object "
+"properties will be displayed into the Selected Tab. Alternatively, double "
+"clicking on the object on the canvas will bring the <strong>SELECTED TAB</"
+"strong> and populate it even if it was out of focus.<br />\n"
+"\t<br />\n"
+"\tYou can change the parameters in this screen and the flow direction is "
+"like this:<br />\n"
+"\t<br />\n"
+"\t<strong>Gerber/Excellon Object</strong> -&gt; Change Param -&gt; Generate "
+"Geometry -&gt;<strong> Geometry Object </strong>-&gt; Add tools (change "
+"param in Selected Tab) -&gt; Generate CNCJob -&gt;<strong> CNCJob Object </"
+"strong>-&gt; Verify GCode (through Edit CNC Code) and/or append/prepend to "
+"GCode (again, done in <strong>SELECTED TAB)&nbsp;</strong>-&gt; Save GCode</"
+"span></li>\n"
+"</ol>\n"
+"\n"
+"<p><span style=\"font-size:10px\">A list of key shortcuts is available "
+"through an menu entry in <strong>Help -&gt; Shortcuts List</strong>&nbsp;or "
+"through it&#39;s own key shortcut: <strng>F3</strong>.</span></p>\n"
+"\n"
+"        "
+msgstr ""
+"\n"
+"<p><span style=\"font-size:14px\"><strong> Ausgewählte Registerkarte - "
+"Wählen Sie ein Element aus der Registerkarte \"Projekt\" aus </strong></"
+"span></p>\n"
+"\n"
+"<p><span style=\"font-size: 10px\"> <strong> Details </strong>: <br />\n"
+"Der normale Fluss beim Arbeiten in FlatCAM ist folgender: </span> </p>\n"
+"\n"
+"<ol>\n"
+"<li><span style=\"font-size: 10px\"> Laden Sie eine Gerber, Excellon, Gcode, "
+"DXF, Rasterbild oder SVG-Datei in FlatCAM, indem Sie entweder die Menü, "
+"Symbolleisten, Tastenkombinationen oder sogar die Dateien ziehen und ablegen "
+"auf der GUI. <br />\n"
+"<br />\n"
+"Sie können ein <strong> FlatCAM-Projekt </strong> auch laden, indem Sie auf "
+"die Projektdatei doppelklicken und &amp; ziehen. Legen Sie die Datei in die "
+"FLATCAM-GUI oder über die in der App angebotenen Menü- / Symbolleisten-Links "
+"ab. </span><br />\n"
+"&nbsp; </ li>\n"
+"<li><span style=\"font-size: 10px\"> Sobald ein Objekt auf der Registerkarte "
+"\"Projekt\" verfügbar ist, wählen Sie es aus und fokussieren Sie dann auf "
+"<strong> Ausgewählte Registerkarte </strong> (einfacher ist das "
+"Doppelklicken auf das Objekt) Name in der Registerkarte \"Projekt\"), "
+"<strong>Ausgewählte Registerkarte </strong> wird mit den Objekteigenschaften "
+"entsprechend seiner Art aktualisiert: Gerber, Excellon, Geometry oder CNCJob-"
+"Objekt. <br />\n"
+"<br />\n"
+"Wenn die Auswahl des Objekts stattdessen mit einem einzigen Klick auf der "
+"Leinwand erfolgt und die <strong> Ausgewählte Registerkarte </strong> im "
+"Fokus ist, werden die Objekteigenschaften wieder auf der Registerkarte "
+"\"Ausgewählt\" angezeigt. Alternativ können Sie durch Doppelklicken auf das "
+"Objekt auf der Leinwand das <strong> Ausgewählte Registerkarte</strong> "
+"aufrufen und es auch dann ausfüllen, wenn es unscharf ist. <br />\n"
+"<br />\n"
+"Sie können die Parameter in diesem Bildschirm ändern und die Flussrichtung "
+"ist wie folgt: <br />\n"
+"<br />\n"
+"<strong> Gerber/Excellon Objekt </strong> - &gt; Parameter ändern -&gt; "
+"Geometrie generieren -&gt; <strong> Geometrie Objekt </strong> -&gt; "
+"Werkzeuge hinzufügen (Parameter in der ausgewählten Registerkarte ändern) -"
+"&gt; CNCJob generieren -&gt; <strong> CNCJob Objekt </strong> -&gt; "
+"Überprüfen Sie GCode (durch Bearbeiten von CNC-Code) und/oder hängen Sie ihn "
+"an GCode an (nochmal in <strong> Ausgewählte Registerkarte)&nbsp; </strong> -"
+"&gt; Speichern Sie GCode </span> </li>\n"
+"</ol>\n"
+"\n"
+"<p><span style = \"font-size: 10px\"> Eine Liste der Tastenkombinationen ist "
+"über einen Menüeintrag in der <strong> Hilfe -&gt; Verknüpfungsliste </"
+"strong> oder über eine eigene Tastenkombination: <strng>F3</strong>. </Span> "
+"</p>"
+
+#: FlatCAMApp.py:7807
+msgid "[WARNING_NOTCL] Failed checking for latest version. Could not connect."
+msgstr ""
+"[WARNING_NOTCL] Fehler bei der Suche nach der neuesten Version. Konnte keine "
+"Verbindung herstellen."
+
+#: FlatCAMApp.py:7814
+msgid "[ERROR_NOTCL] Could not parse information about latest version."
+msgstr ""
+"[ERROR_NOTCL] Informationen zur neuesten Version konnten nicht analysiert "
+"werden."
+
+#: FlatCAMApp.py:7824
+msgid "[success] FlatCAM is up to date!"
+msgstr "[success] FlatCAM ist auf dem neuesten Version!"
+
+#: FlatCAMApp.py:7829
+msgid "Newer Version Available"
+msgstr "Neuere Version verfügbar"
+
+#: FlatCAMApp.py:7830
+msgid ""
+"There is a newer version of FlatCAM available for download:\n"
+"\n"
+msgstr ""
+"Es gibt eine neuere Version von FlatCAM zum Download:\n"
+"\n"
+
+#: FlatCAMApp.py:7832
+msgid "info"
+msgstr "Info"
+
+#: FlatCAMApp.py:7851
+msgid "[success] All plots disabled."
+msgstr "[success] Alle Diagramme sind deaktiviert."
+
+#: FlatCAMApp.py:7857
+msgid "[success] All non selected plots disabled."
+msgstr "[success] Alle nicht ausgewählten Diagramme sind deaktiviert."
+
+#: FlatCAMApp.py:7863
+msgid "[success] All plots enabled."
+msgstr "[success] Alle Diagramme aktiviert."
+
+#: FlatCAMApp.py:7974
+msgid "Saving FlatCAM Project"
+msgstr "FlatCAM-Projekt speichern"
+
+#: FlatCAMApp.py:7995 FlatCAMApp.py:8026
+#, python-format
+msgid "[success] Project saved to: %s"
+msgstr "[success] Projekt gespeichert in: %s"
+
+#: FlatCAMApp.py:8013
+#, python-format
+msgid "[ERROR_NOTCL] Failed to verify project file: %s. Retry to save it."
+msgstr ""
+"[ERROR_NOTCL] Fehler beim Überprüfen der Projektdatei:%s. Versuchen Sie es "
+"erneut zu speichern."
+
+#: FlatCAMApp.py:8020
+#, python-format
+msgid "[ERROR_NOTCL] Failed to parse saved project file: %s. Retry to save it."
+msgstr ""
+"[ERROR_NOTCL] Die gespeicherte Projektdatei konnte nicht analysiert werden:"
+"%s. Versuchen Sie es erneut zu speichern."
+
+#: FlatCAMApp.py:8028
+#, python-format
+msgid "[ERROR_NOTCL] Failed to save project file: %s. Retry to save it."
+msgstr ""
+"[ERROR_NOTCL] Projektdatei konnte nicht gespeichert werden:%s. Versuchen Sie "
+"es erneut zu speichern."
+
+#: FlatCAMObj.py:194
+#, python-brace-format
+msgid "[success] Name changed from {old} to {new}"
+msgstr "[success] Name geändert von {old} zu {new}"
+
+#: FlatCAMObj.py:535 FlatCAMObj.py:1741 FlatCAMObj.py:3006 FlatCAMObj.py:5156
+msgid "<span style=\"color:green;\"><b>Basic</b></span>"
+msgstr "<span style=\"color:green;\"><b>Basic</b></span>"
+
+#: FlatCAMObj.py:547 FlatCAMObj.py:1757 FlatCAMObj.py:3028 FlatCAMObj.py:5162
+msgid "<span style=\"color:red;\"><b>Advanced</b></span>"
+msgstr "<span style=\"color:red;\"><b>Erweitert</b></span>"
+
+#: FlatCAMObj.py:902 FlatCAMObj.py:957
+#, python-format
+msgid "[success] Isolation geometry created: %s"
+msgstr "[success] Isolationsgeometrie erstellt: %s"
+
+#: FlatCAMObj.py:1126
+msgid "Plotting Apertures"
+msgstr "Plotten Apertures"
+
+#: FlatCAMObj.py:1580 flatcamEditors/FlatCAMExcEditor.py:1293
+msgid "Total Drills"
+msgstr "Bohrungen insgesamt"
+
+#: FlatCAMObj.py:1606 flatcamEditors/FlatCAMExcEditor.py:1325
+msgid "Total Slots"
+msgstr "Schlitz insgesamt"
+
+#: FlatCAMObj.py:1813 FlatCAMObj.py:3078 FlatCAMObj.py:3384 FlatCAMObj.py:3571
+#: FlatCAMObj.py:3584 FlatCAMObj.py:3701 FlatCAMObj.py:4109 FlatCAMObj.py:4342
+#: FlatCAMObj.py:4748 flatcamEditors/FlatCAMExcEditor.py:1400
+#: flatcamTools/ToolCalculators.py:307 flatcamTools/ToolCalculators.py:318
+#: flatcamTools/ToolCalculators.py:330 flatcamTools/ToolCalculators.py:345
+#: flatcamTools/ToolCalculators.py:358 flatcamTools/ToolCalculators.py:372
+#: flatcamTools/ToolCalculators.py:383 flatcamTools/ToolCalculators.py:394
+#: flatcamTools/ToolCalculators.py:405 flatcamTools/ToolFilm.py:241
+#: flatcamTools/ToolFilm.py:248 flatcamTools/ToolNonCopperClear.py:479
+#: flatcamTools/ToolNonCopperClear.py:550
+#: flatcamTools/ToolNonCopperClear.py:626
+#: flatcamTools/ToolNonCopperClear.py:643 flatcamTools/ToolPaint.py:537
+#: flatcamTools/ToolPaint.py:607 flatcamTools/ToolPaint.py:742
+#: flatcamTools/ToolPaint.py:839 flatcamTools/ToolPaint.py:994
+#: flatcamTools/ToolPanelize.py:323 flatcamTools/ToolPanelize.py:335
+#: flatcamTools/ToolPanelize.py:348 flatcamTools/ToolPanelize.py:361
+#: flatcamTools/ToolPanelize.py:373 flatcamTools/ToolPanelize.py:384
+#: flatcamTools/ToolSolderPaste.py:755 flatcamTools/ToolSolderPaste.py:826
+msgid "[ERROR_NOTCL] Wrong value format entered, use a number."
+msgstr "[ERROR_NOTCL] Wrong value format entered, use a number."
+
+#: FlatCAMObj.py:2037 FlatCAMObj.py:2128 FlatCAMObj.py:2243
+msgid ""
+"[ERROR_NOTCL] Please select one or more tools from the list and try again."
+msgstr ""
+"[ERROR_NOTCL] Bitte wählen Sie ein oder mehrere Werkzeuge aus der Liste aus "
+"und versuchen Sie es erneut."
+
+#: FlatCAMObj.py:2044
+msgid ""
+"[ERROR_NOTCL] Milling tool for DRILLS is larger than hole size. Cancelled."
+msgstr ""
+"[ERROR_NOTCL] Das Fräswerkzeug für BOHRER ist größer als die Lochgröße. "
+"Abgebrochen."
+
+#: FlatCAMObj.py:2058 FlatCAMObj.py:2152 FlatCAMObj.py:2263
+msgid "Tool_nr"
+msgstr "Werkzeugnummer"
+
+#: FlatCAMObj.py:2058 FlatCAMObj.py:2152 FlatCAMObj.py:2263
+#: flatcamEditors/FlatCAMExcEditor.py:753
+#: flatcamEditors/FlatCAMExcEditor.py:1870 flatcamGUI/ObjectUI.py:556
+#: flatcamTools/ToolNonCopperClear.py:83 flatcamTools/ToolPaint.py:80
+#: flatcamTools/ToolSolderPaste.py:81
+msgid "Diameter"
+msgstr "Durchmesser"
+
+#: FlatCAMObj.py:2058 FlatCAMObj.py:2152 FlatCAMObj.py:2263
+msgid "Drills_Nr"
+msgstr "Bohrnummer"
+
+#: FlatCAMObj.py:2058 FlatCAMObj.py:2152 FlatCAMObj.py:2263
+msgid "Slots_Nr"
+msgstr "Schlitznummer"
+
+#: FlatCAMObj.py:2138
+msgid ""
+"[ERROR_NOTCL] Milling tool for SLOTS is larger than hole size. Cancelled."
+msgstr ""
+"[ERROR_NOTCL] Das Fräswerkzeug für SCHLITZ ist größer als die Lochgröße. "
+"Abgebrochen."
+
+#: FlatCAMObj.py:2303 FlatCAMObj.py:3997 FlatCAMObj.py:4208 FlatCAMObj.py:4523
+msgid ""
+"[ERROR_NOTCL] Wrong value format for self.defaults[\"z_pdepth\"] or self."
+"options[\"z_pdepth\"]"
+msgstr ""
+"[ERROR_NOTCL] Falsches Wertformat für self.defaults [\"z_pdepth\"] oder self."
+"options [\"z_pdepth\"]"
+
+#: FlatCAMObj.py:2315 FlatCAMObj.py:4009 FlatCAMObj.py:4220 FlatCAMObj.py:4535
+msgid ""
+"[ERROR_NOTCL] Wrong value format for self.defaults[\"feedrate_probe\"] or "
+"self.options[\"feedrate_probe\"]"
+msgstr ""
+"[ERROR_NOTCL] Falsches Wertformat für self.defaults [\"feedrate_probe\"] "
+"oder self.options [\"feedrate_probe\"]"
+
+#: FlatCAMObj.py:2347 FlatCAMObj.py:4410 FlatCAMObj.py:4415 FlatCAMObj.py:4561
+msgid "Generating CNC Code"
+msgstr "CNC-Code generieren"
+
+#: FlatCAMObj.py:2373 FlatCAMObj.py:4707 camlib.py:5141 camlib.py:5577
+#: camlib.py:5848
+msgid ""
+"[ERROR]The Toolchange X,Y field in Edit -> Preferences has to be in the "
+"format (x, y) \n"
+"but now there is only one value, not two. "
+msgstr ""
+"[ERROR] Das Feld X, Y des Werkzeugwechsels in Bearbeiten -> Voreinstellungen "
+"muss das Format (x, y) haben.\n"
+"Aber jetzt gibt es nur einen Wert, nicht zwei."
+
+#: FlatCAMObj.py:2720 FlatCAMObj.py:2962 FlatCAMObj.py:3247
+msgid "Path"
+msgstr "Pfad"
+
+#: FlatCAMObj.py:2720
+msgid "In"
+msgstr "Im"
+
+#: FlatCAMObj.py:2720
+msgid "Out"
+msgstr "Aus"
+
+#: FlatCAMObj.py:2720 FlatCAMObj.py:3043 FlatCAMObj.py:3616
+msgid "Custom"
+msgstr "Maßgeschneidert"
+
+#: FlatCAMObj.py:2721 FlatCAMObj.py:3627 FlatCAMObj.py:3628 FlatCAMObj.py:3637
+msgid "Iso"
+msgstr "Iso"
+
+#: FlatCAMObj.py:2721 FlatCAMObj.py:2964 FlatCAMObj.py:3249
+msgid "Rough"
+msgstr "Rau"
+
+#: FlatCAMObj.py:2721
+msgid "Finish"
+msgstr "Oberfläche"
+
+#: FlatCAMObj.py:2999 flatcamGUI/FlatCAMGUI.py:512 flatcamGUI/FlatCAMGUI.py:698
+#: flatcamGUI/FlatCAMGUI.py:1536 flatcamGUI/FlatCAMGUI.py:1546
+#: flatcamGUI/FlatCAMGUI.py:1870 flatcamGUI/ObjectUI.py:996
+msgid "Copy"
+msgstr "Kopieren"
+
+#: FlatCAMObj.py:3001 flatcamGUI/FlatCAMGUI.py:513 flatcamGUI/FlatCAMGUI.py:700
+#: flatcamGUI/FlatCAMGUI.py:1537 flatcamGUI/FlatCAMGUI.py:1547
+#: flatcamGUI/FlatCAMGUI.py:1872 flatcamGUI/ObjectUI.py:1004
+#: flatcamTools/ToolNonCopperClear.py:146 flatcamTools/ToolPaint.py:143
+#: flatcamTools/ToolSolderPaste.py:121 flatcamTools/ToolSolderPaste.py:480
+msgid "Delete"
+msgstr "Löschen"
+
+#: FlatCAMObj.py:3219
+msgid "[ERROR_NOTCL] Please enter the desired tool diameter in Float format."
+msgstr ""
+"[ERROR_NOTCL] Bitte geben Sie den gewünschten Werkzeugdurchmesser im Real-"
+"Format ein."
+
+#: FlatCAMObj.py:3294
+msgid "[success] Tool added in Tool Table."
+msgstr "[success] Werkzeug in der Werkzeugtabelle hinzugefügt."
+
+#: FlatCAMObj.py:3299
+msgid "[ERROR_NOTCL] Default Tool added. Wrong value format entered."
+msgstr ""
+"[ERROR_NOTCL] Standardwerkzeug hinzugefügt Falsches Wertformat eingegeben."
+
+#: FlatCAMObj.py:3329 FlatCAMObj.py:3339
+msgid "[WARNING_NOTCL] Failed. Select a tool to copy."
+msgstr ""
+"[WARNING_NOTCL] Fehlgeschlagen. Wählen Sie ein Werkzeug zum Kopieren aus."
+
+#: FlatCAMObj.py:3368
+msgid "[success] Tool was copied in Tool Table."
+msgstr "[success] Das Werkzeug wurde in die Werkzeugtabelle kopiert."
+
+#: FlatCAMObj.py:3401
+msgid "[success] Tool was edited in Tool Table."
+msgstr "[success] Das Werkzeug wurde in der Werkzeugtabelle bearbeitet."
+
+#: FlatCAMObj.py:3432 FlatCAMObj.py:3442
+msgid "[WARNING_NOTCL] Failed. Select a tool to delete."
+msgstr ""
+"[WARNING_NOTCL] Fehlgeschlagen. Wählen Sie ein Werkzeug zum Löschen aus."
+
+#: FlatCAMObj.py:3466
+msgid "[success] Tool was deleted in Tool Table."
+msgstr "[success] Werkzeug wurde in der Werkzeugtabelle gelöscht."
+
+#: FlatCAMObj.py:3880
+#, python-format
+msgid ""
+"[WARNING_NOTCL] This Geometry can't be processed because it is %s geometry."
+msgstr ""
+"[WARNING_NOTCL] Diese Geometrie kann nicht verarbeitet werden, da es sich um "
+"%s Geometrie handelt."
+
+#: FlatCAMObj.py:3897
+msgid "[ERROR_NOTCL] Wrong Tool Dia value format entered, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Werkzeug Dia-Wertformat eingegeben, verwenden Sie "
+"eine Zahl."
+
+#: FlatCAMObj.py:3924
+msgid "[ERROR_NOTCL] Failed. No tool selected in the tool table ..."
+msgstr ""
+"[ERROR_NOTCL] Gescheitert. Kein Werkzeug in der Werkzeugtabelle "
+"ausgewählt ..."
+
+#: FlatCAMObj.py:3962
+#, python-format
+msgid "FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s"
+msgstr "FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s"
+
+#: FlatCAMObj.py:4118 FlatCAMObj.py:4351
+msgid ""
+"[WARNING] Tool Offset is selected in Tool Table but no value is provided.\n"
+"Add a Tool Offset or change the Offset Type."
+msgstr ""
+"[WARNING] Werkzeugversatz ist in der Werkzeugtabelle ausgewählt, es wird "
+"jedoch kein Wert angegeben.\n"
+"Fügen Sie einen Werkzeugversatz hinzu oder ändern Sie den Versatztyp."
+
+#: FlatCAMObj.py:4232 flatcamTools/ToolSolderPaste.py:1106
+#: flatcamTools/ToolSolderPaste.py:1161
+msgid "[ERROR_NOTCL] Cancelled. Empty file, it has no geometry..."
+msgstr "[ERROR_NOTCL] Cancelled. Empty file, it has no geometry..."
+
+#: FlatCAMObj.py:4594 FlatCAMObj.py:4604 camlib.py:3410 camlib.py:3419
+msgid "[ERROR_NOTCL] Scale factor has to be a number: integer or float."
+msgstr ""
+"[ERROR_NOTCL] Der Skalierungsfaktor muss eine Zahl sein: Ganzzahl oder "
+"Fließkommazahl."
+
+#: FlatCAMObj.py:4642
+msgid "[success] Geometry Scale done."
+msgstr "[success] Geometrie Skalierung fertig."
+
+#: FlatCAMObj.py:4659 camlib.py:3481
+msgid ""
+"[ERROR_NOTCL] An (x,y) pair of values are needed. Probable you entered only "
+"one value in the Offset field."
+msgstr ""
+"[ERROR_NOTCL] Ein (x, y) Wertepaar wird benötigt. Wahrscheinlich haben Sie "
+"im Feld Offset nur einen Wert eingegeben."
+
+#: FlatCAMObj.py:4679
+msgid "[success] Geometry Offset done."
+msgstr "[success] Geometrie Offset fertig."
+
+#: FlatCAMObj.py:5224 FlatCAMObj.py:5229 flatcamTools/ToolSolderPaste.py:1360
+msgid "Export Machine Code ..."
+msgstr "Maschinencode exportieren ..."
+
+#: FlatCAMObj.py:5235 flatcamTools/ToolSolderPaste.py:1363
+msgid "[WARNING_NOTCL] Export Machine Code cancelled ..."
+msgstr "[WARNING_NOTCL] Export Machine Code cancelled ..."
+
+#: FlatCAMObj.py:5246
+#, python-format
+msgid "[success] Machine Code file saved to: %s"
+msgstr "[success] Maschinencode-Datei gespeichert in: %s"
+
+#: FlatCAMObj.py:5268
+#, python-format
+msgid "[ERROR]FlatCAMCNNJob.on_edit_code_click() -->%s"
+msgstr "[ERROR]FlatCAMCNNJob.on_edit_code_click() -->%s"
+
+#: FlatCAMObj.py:5385
+#, python-format
+msgid ""
+"[WARNING_NOTCL] This CNCJob object can't be processed because it is a %s "
+"CNCJob object."
+msgstr ""
+"[WARNING_NOTCL] Dieses CNC-Auftrag Objekt kann nicht verarbeitet werden, da "
+"es sich um ein %s CNC-Auftrag Objekt handelt."
+
+#: FlatCAMObj.py:5438
+msgid "[ERROR_NOTCL] G-code does not have a units code: either G20 or G21"
+msgstr "[ERROR_NOTCL] G-Code hat keinen Einheitencode: entweder G20 oder G21"
+
+#: FlatCAMObj.py:5451
+msgid ""
+"[ERROR_NOTCL] Cancelled. The Toolchange Custom code is enabled but it's "
+"empty."
+msgstr ""
+"[ERROR_NOTCL] Abgebrochen. Der benutzerdefinierte Code zum Ändern des "
+"Werkzeugs ist aktiviert, aber er ist leer."
+
+#: FlatCAMObj.py:5458
+msgid "[success] Toolchange G-code was replaced by a custom code."
+msgstr ""
+"[success] Der Werkzeugwechsel-G-Code wurde durch einen benutzerdefinierten "
+"Code ersetzt."
+
+#: FlatCAMObj.py:5473 flatcamTools/ToolSolderPaste.py:1389
+msgid "[WARNING_NOTCL] No such file or directory"
+msgstr "[WARNING_NOTCL] Keine solche Datei oder Ordner"
+
+#: FlatCAMObj.py:5492 FlatCAMObj.py:5504
+msgid ""
+"[WARNING_NOTCL] The used postprocessor file has to have in it's name: "
+"'toolchange_custom'"
+msgstr ""
+"[WARNING_NOTCL] Die verwendete Postprozessor-Datei muss im Namen enthalten "
+"sein: 'toolchange_custom'"
+
+#: FlatCAMObj.py:5510
+msgid "[ERROR] There is no postprocessor file."
+msgstr "[ERROR] Es gibt keine Postprozessor-Datei."
+
+#: ObjectCollection.py:416
+#, python-brace-format
+msgid "Object renamed from {old} to {new}"
+msgstr "Objekt umbenannt von {old} zu {new}"
+
+#: ObjectCollection.py:751
+#, python-format
+msgid "[ERROR] Cause of error: %s"
+msgstr "[ERROR] Fehlerursache: %s"
+
+#: camlib.py:200
+msgid "[ERROR_NOTCL] self.solid_geometry is neither BaseGeometry or list."
+msgstr ""
+"[ERROR_NOTCL] self.solid_geometry ist weder BaseGeometry noch eine Liste."
+
+#: camlib.py:1387
+msgid "[success] Object was mirrored ..."
+msgstr "[success] Objekt wurde gespiegelt ..."
+
+#: camlib.py:1389
+msgid "[ERROR_NOTCL] Failed to mirror. No object selected"
+msgstr "[ERROR_NOTCL] Spiegelung fehlgeschlagen Kein Objekt ausgewählt"
+
+#: camlib.py:1425
+msgid "[success] Object was rotated ..."
+msgstr "[success] Objekt wurde gedreht ..."
+
+#: camlib.py:1427
+msgid "[ERROR_NOTCL] Failed to rotate. No object selected"
+msgstr "[ERROR_NOTCL] Fehler beim Drehen. Kein Objekt ausgewählt"
+
+#: camlib.py:1461
+msgid "[success] Object was skewed ..."
+msgstr "[success] Objekt war schief ..."
+
+#: camlib.py:1463
+msgid "[ERROR_NOTCL] Failed to skew. No object selected"
+msgstr "[ERROR_NOTCL] Fehler beim Neigen Kein Objekt ausgewählt"
+
+#: camlib.py:2728 camlib.py:2832
+#, python-format
+msgid "[WARNING] Coordinates missing, line ignored: %s"
+msgstr "[WARNING] Koordinaten fehlen, Zeile wird ignoriert: %s"
+
+#: camlib.py:2729 camlib.py:2833
+msgid "[WARNING_NOTCL] GERBER file might be CORRUPT. Check the file !!!"
+msgstr ""
+"[WARNING_NOTCL] Die GERBER-Datei könnte CORRUPT sein. Überprüfen Sie die "
+"Datei !!!"
+
+#: camlib.py:2787
+#, python-format
+msgid ""
+"[ERROR] Region does not have enough points. File will be processed but there "
+"are parser errors. Line number: %s"
+msgstr ""
+"[ERROR] Region hat nicht genug Punkte. Die Datei wird verarbeitet, es treten "
+"jedoch Parserfehler auf. Linien Nummer: %s"
+
+#: camlib.py:3231
+#, python-format
+msgid ""
+"[ERROR]Gerber Parser ERROR.\n"
+"%s:"
+msgstr ""
+"[ERROR] Gerber Parser ERROR.\n"
+"%s:"
+
+#: camlib.py:3448
+msgid "[success] Gerber Scale done."
+msgstr "[success] Gerber-Skalierung abgeschlossen."
+
+#: camlib.py:3505
+msgid "[success] Gerber Offset done."
+msgstr "[success] Gerber Offset fertig."
+
+#: camlib.py:3887
+#, python-format
+msgid "[ERROR_NOTCL] This is GCODE mark: %s"
+msgstr "[ERROR_NOTCL] Dies ist die GCODE-Marke: %s"
+
+#: camlib.py:4431
+#, python-brace-format
+msgid ""
+"[ERROR] Excellon Parser error.\n"
+"Parsing Failed. Line {l_nr}: {line}\n"
+msgstr ""
+"[ERROR] Fehler beim Excellon-Parser.\n"
+"Parsing fehlgeschlagen. Zeile {l_nr}: {line}\n"
+
+#: camlib.py:4508
+msgid ""
+"[WARNING] Excellon.create_geometry() -> a drill location was skipped due of "
+"not having a tool associated.\n"
+"Check the resulting GCode."
+msgstr ""
+"[WARNING] Excellon.create_geometry () -> Eine Bohrstelle wurde übersprungen, "
+"da kein Werkzeug zugeordnet wurde.\n"
+"Überprüfen Sie den resultierenden GCode."
+
+#: camlib.py:5050
+#, python-format
+msgid "[ERROR] There is no such parameter: %s"
+msgstr "[ERROR] Es gibt keinen solchen Parameter: %s"
+
+#: camlib.py:5120
+msgid ""
+"[WARNING] The Cut Z parameter has positive value. It is the depth value to "
+"drill into material.\n"
+"The Cut Z parameter needs to have a negative value, assuming it is a typo "
+"therefore the app will convert the value to negative. Check the resulting "
+"CNC code (Gcode etc)."
+msgstr ""
+"[WARNING] Der Parameter Cut Z hat einen positiven Wert. Dies ist der "
+"Tiefenwert zum Bohren in Material.\n"
+"Der Parameter Cut Z muss einen negativen Wert haben, vorausgesetzt, es "
+"handelt sich um einen Tippfehler, daher konvertiert die App den Wert in "
+"einen negativen Wert. \n"
+"Überprüfen Sie den resultierenden CNC-Code (Gcode usw.)."
+
+#: camlib.py:5127 camlib.py:5600 camlib.py:5871
+#, python-format
+msgid ""
+"[WARNING] The Cut Z parameter is zero. There will be no cut, skipping %s file"
+msgstr ""
+"[WARNING] Der Parameter Cut Z ist Null. Es wird kein Schnitt ausgeführt, da "
+"die %s Datei übersprungen wird"
+
+#: camlib.py:5343 camlib.py:5438 camlib.py:5489
+msgid "[ERROR_NOTCL] The loaded Excellon file has no drills ..."
+msgstr "[ERROR_NOTCL] Die geladene Excellon-Datei hat keine Bohrer ..."
+
+#: camlib.py:5443
+msgid "[ERROR_NOTCL] Wrong optimization type selected."
+msgstr "[ERROR_NOTCL] Falscher Optimierungstyp ausgewählt."
+
+#: camlib.py:5588 camlib.py:5859
+msgid ""
+"[ERROR_NOTCL] Cut_Z parameter is None or zero. Most likely a bad "
+"combinations of other parameters."
+msgstr ""
+"[ERROR_NOTCL] Der Parameter Cut_Z ist None oder Null. Höchstwahrscheinlich "
+"eine schlechte Kombination anderer Parameter."
+
+#: camlib.py:5593 camlib.py:5864
+msgid ""
+"[WARNING] The Cut Z parameter has positive value. It is the depth value to "
+"cut into material.\n"
+"The Cut Z parameter needs to have a negative value, assuming it is a typo "
+"therefore the app will convert the value to negative.Check the resulting CNC "
+"code (Gcode etc)."
+msgstr ""
+"[WARNING] Der Parameter Cut Z hat einen positiven Wert. Es ist der "
+"Tiefenwert zum Schneiden in Material.\n"
+"Der Parameter Cut Z muss einen negativen Wert haben, vorausgesetzt es "
+"handelt sich um einen Tippfehler, daher konvertiert die App den Wert in "
+"einen negativen Wert. \n"
+"Überprüfen Sie den resultierenden CNC-Code (Gcode usw.)."
+
+#: camlib.py:5605 camlib.py:5876
+msgid "[ERROR_NOTCL] Travel Z parameter is None or zero."
+msgstr "[ERROR_NOTCL] Der Parameter für den Travel Z ist Kein oder Null."
+
+#: camlib.py:5609 camlib.py:5880
+msgid ""
+"[WARNING] The Travel Z parameter has negative value. It is the height value "
+"to travel between cuts.\n"
+"The Z Travel parameter needs to have a positive value, assuming it is a typo "
+"therefore the app will convert the value to positive.Check the resulting CNC "
+"code (Gcode etc)."
+msgstr ""
+"[WARNING] Der Parameter Travel Z hat einen negativen Wert. Dies ist der "
+"Höhenwert zwischen den Schnitten.\n"
+"Der Parameter Z Travel muss einen positiven Wert haben. Wenn es sich um "
+"einen Tippfehler handelt, konvertiert die App den Wert in einen positiven "
+"Wert. Überprüfen Sie den resultierenden CNC-Code (Gcode usw.)."
+
+#: camlib.py:5616 camlib.py:5887
+#, python-format
+msgid ""
+"[WARNING] The Z Travel parameter is zero. This is dangerous, skipping %s file"
+msgstr ""
+"[WARNING] Der Parameter Z-Weg ist Null. Dies ist gefährlich, da die %s Datei "
+"übersprungen wird"
+
+#: camlib.py:5746
+#, python-format
+msgid "[ERROR]Expected a Geometry, got %s"
+msgstr "[ERROR] Eine Geometrie erwartet,%s erhalten"
+
+#: camlib.py:5752
+msgid ""
+"[ERROR_NOTCL] Trying to generate a CNC Job from a Geometry object without "
+"solid_geometry."
+msgstr ""
+"[ERROR_NOTCL] Der Versuch, einen CNC-Auftrag aus einem Geometrieobjekt ohne "
+"solid_geometry zu generieren."
+
+#: camlib.py:5791
+msgid ""
+"[ERROR_NOTCL] The Tool Offset value is too negative to use for the "
+"current_geometry.\n"
+"Raise the value (in module) and try again."
+msgstr ""
+"[ERROR_NOTCL] Der Werkzeugkorrekturwert ist zu negativ, um ihn für "
+"current_geometry zu verwenden.\n"
+"Erhöhen Sie den Wert (im Modul) und versuchen Sie es erneut."
+
+#: camlib.py:6013
+msgid "[ERROR_NOTCL] There is no tool data in the SolderPaste geometry."
+msgstr ""
+"[ERROR_NOTCL] In der SolderPaste-Geometrie sind keine Werkzeugdaten "
+"vorhanden."
+
+#: flatcamEditors/FlatCAMExcEditor.py:44
+msgid "[WARNING_NOTCL] To add a drill first select a tool"
+msgstr ""
+"[WARNING_NOTCL] Um einen Bohrer hinzuzufügen, wählen Sie zuerst ein Werkzeug "
+"aus"
+
+#: flatcamEditors/FlatCAMExcEditor.py:53 flatcamEditors/FlatCAMExcEditor.py:143
+#: flatcamEditors/FlatCAMExcEditor.py:420
+#: flatcamEditors/FlatCAMExcEditor.py:445
+#: flatcamEditors/FlatCAMGrbEditor.py:223
+#: flatcamEditors/FlatCAMGrbEditor.py:604
+#: flatcamEditors/FlatCAMGrbEditor.py:628
+msgid "Click on target location ..."
+msgstr "Klicken Sie auf den Zielort ..."
+
+#: flatcamEditors/FlatCAMExcEditor.py:93
+msgid "[success] Done. Drill added."
+msgstr "[success] Erledigt. Bohrer hinzugefügt."
+
+#: flatcamEditors/FlatCAMExcEditor.py:135
+msgid "[WARNING_NOTCL] To add an Drill Array first select a tool in Tool Table"
+msgstr ""
+"[WARNING_NOTCL] Um ein Bohr-Array hinzuzufügen, wählen Sie zunächst ein "
+"Werkzeug in der Werkzeugtabelle aus"
+
+#: flatcamEditors/FlatCAMExcEditor.py:160
+msgid "Click on the Drill Circular Array Start position"
+msgstr "Klicken Sie auf die Startposition des Bohrkreis-Arrays"
+
+#: flatcamEditors/FlatCAMExcEditor.py:182
+#: flatcamEditors/FlatCAMGrbEditor.py:262
+msgid ""
+"[ERROR_NOTCL] The value is not Float. Check for comma instead of dot "
+"separator."
+msgstr ""
+"[ERROR_NOTCL] Der Wert ist nicht Real. Überprüfen Sie das Komma anstelle des "
+"Trennzeichens."
+
+#: flatcamEditors/FlatCAMExcEditor.py:185
+#: flatcamEditors/FlatCAMGrbEditor.py:265
+msgid "[ERROR_NOTCL] The value is mistyped. Check the value."
+msgstr ""
+"[ERROR_NOTCL] Der Wert ist falsch geschrieben. Überprüfen Sie den Wert."
+
+#: flatcamEditors/FlatCAMExcEditor.py:278
+msgid "[WARNING_NOTCL] Too many drills for the selected spacing angle."
+msgstr "[WARNING_NOTCL] Zu viele Bohrer für den ausgewählten Abstandswinkel."
+
+#: flatcamEditors/FlatCAMExcEditor.py:295
+msgid "[success] Done. Drill Array added."
+msgstr "[success] Erledigt. Bohrfeld hinzugefügt."
+
+#: flatcamEditors/FlatCAMExcEditor.py:306
+msgid "Click on the Drill(s) to resize ..."
+msgstr "Klicken Sie auf die Bohrer, um die Größe zu ändern ..."
+
+#: flatcamEditors/FlatCAMExcEditor.py:326
+msgid ""
+"[ERROR_NOTCL] Resize drill(s) failed. Please enter a diameter for resize."
+msgstr ""
+"[ERROR_NOTCL] Die Größe der Bohrer ist fehlgeschlagen. Bitte geben Sie einen "
+"Durchmesser für die Größenänderung ein."
+
+#: flatcamEditors/FlatCAMExcEditor.py:396
+msgid "[success] Done. Drill Resize completed."
+msgstr "[success] Erledigt. Bohren Sie die Größe neu."
+
+#: flatcamEditors/FlatCAMExcEditor.py:399
+msgid "[WARNING_NOTCL] Cancelled. No drills selected for resize ..."
+msgstr ""
+"[WARNING_NOTCL] Abgebrochen. Keine Bohrer zur Größenänderung ausgewählt ..."
+
+#: flatcamEditors/FlatCAMExcEditor.py:422
+#: flatcamEditors/FlatCAMGrbEditor.py:606
+msgid "Click on reference location ..."
+msgstr "Klicken Sie auf die Referenzposition ..."
+
+#: flatcamEditors/FlatCAMExcEditor.py:477
+msgid "[success] Done. Drill(s) Move completed."
+msgstr "[success] Erledigt. Bohrer Bewegen abgeschlossen."
+
+#: flatcamEditors/FlatCAMExcEditor.py:530
+msgid "[success] Done. Drill(s) copied."
+msgstr "[success] Erledigt. Bohrer kopiert."
+
+#: flatcamEditors/FlatCAMExcEditor.py:712
+msgid "Excellon Editor"
+msgstr "Excellon Editor"
+
+#: flatcamEditors/FlatCAMExcEditor.py:719
+#: flatcamEditors/FlatCAMGrbEditor.py:840
+msgid "Name:"
+msgstr "Name:"
+
+#: flatcamEditors/FlatCAMExcEditor.py:739 flatcamTools/ToolNonCopperClear.py:72
+#: flatcamTools/ToolPaint.py:69 flatcamTools/ToolSolderPaste.py:70
+msgid "Tools Table"
+msgstr "Werkzeugtabelle"
+
+#: flatcamEditors/FlatCAMExcEditor.py:741 flatcamGUI/ObjectUI.py:538
+msgid ""
+"Tools in this Excellon object\n"
+"when are used for drilling."
+msgstr ""
+"Werkzeuge in diesem Excellon-Objekt\n"
+"Wann werden zum Bohren verwendet."
+
+#: flatcamEditors/FlatCAMExcEditor.py:761
+msgid "Add/Delete Tool"
+msgstr "Werkzeug hinzufügen / löschen"
+
+#: flatcamEditors/FlatCAMExcEditor.py:763
+msgid ""
+"Add/Delete a tool to the tool list\n"
+"for this Excellon object."
+msgstr ""
+"Werkzeug zur Werkzeugliste hinzufügen / löschen\n"
+"für dieses Excellon-Objekt."
+
+#: flatcamEditors/FlatCAMExcEditor.py:771 flatcamTools/ToolCutOut.py:77
+msgid "Tool Dia:"
+msgstr "Werkzeugdurchmesser:"
+
+#: flatcamEditors/FlatCAMExcEditor.py:773 flatcamGUI/ObjectUI.py:975
+msgid "Diameter for the new tool"
+msgstr "Durchmesser für das neue Werkzeug"
+
+#: flatcamEditors/FlatCAMExcEditor.py:782
+msgid "Add Tool"
+msgstr "Werkzeug hinzufügen"
+
+#: flatcamEditors/FlatCAMExcEditor.py:784
+msgid ""
+"Add a new tool to the tool list\n"
+"with the diameter specified above."
+msgstr ""
+"Fügen Sie der Werkzeugliste ein neues Werkzeug hinzu\n"
+"mit dem oben angegebenen Durchmesser."
+
+#: flatcamEditors/FlatCAMExcEditor.py:794
+msgid "Delete Tool"
+msgstr "Werkzeug löschen"
+
+#: flatcamEditors/FlatCAMExcEditor.py:796
+msgid ""
+"Delete a tool in the tool list\n"
+"by selecting a row in the tool table."
+msgstr ""
+"Löschen Sie ein Werkzeug in der Werkzeugliste\n"
+"indem Sie eine Zeile in der Werkzeugtabelle auswählen."
+
+#: flatcamEditors/FlatCAMExcEditor.py:814
+msgid "Resize Drill(s)"
+msgstr "Größe der Bohrer ändern"
+
+#: flatcamEditors/FlatCAMExcEditor.py:816
+msgid "Resize a drill or a selection of drills."
+msgstr "Ändern Sie die Größe eines Bohrers oder einer Auswahl von Bohrern."
+
+#: flatcamEditors/FlatCAMExcEditor.py:823
+msgid "Resize Dia:"
+msgstr "Durchmesser ändern:"
+
+#: flatcamEditors/FlatCAMExcEditor.py:825
+msgid "Diameter to resize to."
+msgstr "Durchmesser zur Größenänderung."
+
+#: flatcamEditors/FlatCAMExcEditor.py:833
+msgid "Resize"
+msgstr "Größe ändern"
+
+#: flatcamEditors/FlatCAMExcEditor.py:835
+msgid "Resize drill(s)"
+msgstr "Bohrer verkleinern"
+
+#: flatcamEditors/FlatCAMExcEditor.py:857 flatcamGUI/FlatCAMGUI.py:1542
+msgid "Add Drill Array"
+msgstr "Bohrer-Array hinzufügen"
+
+#: flatcamEditors/FlatCAMExcEditor.py:859
+msgid "Add an array of drills (linear or circular array)"
+msgstr ""
+"Hinzufügen eines Arrays von Bohrern (lineares oder kreisförmiges Array)"
+
+#: flatcamEditors/FlatCAMExcEditor.py:865
+msgid ""
+"Select the type of drills array to create.\n"
+"It can be Linear X(Y) or Circular"
+msgstr ""
+"Wählen Sie den Typ des zu erstellenden Bohrfelds aus.\n"
+"Es kann lineares X (Y) oder rund sein"
+
+#: flatcamEditors/FlatCAMExcEditor.py:868
+#: flatcamEditors/FlatCAMGrbEditor.py:1077
+msgid "Linear"
+msgstr "Linear"
+
+#: flatcamEditors/FlatCAMExcEditor.py:869
+#: flatcamEditors/FlatCAMGrbEditor.py:1078
+msgid "Circular"
+msgstr "Kreisförmig"
+
+#: flatcamEditors/FlatCAMExcEditor.py:876
+msgid "Nr of drills:"
+msgstr "Anzahl der Bohrer:"
+
+#: flatcamEditors/FlatCAMExcEditor.py:878
+msgid "Specify how many drills to be in the array."
+msgstr "Geben Sie an, wie viele Drills im Array enthalten sein sollen."
+
+#: flatcamEditors/FlatCAMExcEditor.py:895
+#: flatcamEditors/FlatCAMExcEditor.py:940
+#: flatcamEditors/FlatCAMGrbEditor.py:1104
+#: flatcamEditors/FlatCAMGrbEditor.py:1149
+msgid "Direction:"
+msgstr "Richtung:"
+
+#: flatcamEditors/FlatCAMExcEditor.py:897
+#: flatcamEditors/FlatCAMGrbEditor.py:1106
+msgid ""
+"Direction on which the linear array is oriented:\n"
+"- 'X' - horizontal axis \n"
+"- 'Y' - vertical axis or \n"
+"- 'Angle' - a custom angle for the array inclination"
+msgstr ""
+"Richtung, auf die das lineare Array ausgerichtet ist:\n"
+"- 'X' - horizontale Achse\n"
+"- 'Y' - vertikale Achse oder\n"
+"- 'Winkel' - ein benutzerdefinierter Winkel für die Neigung des Arrays"
+
+#: flatcamEditors/FlatCAMExcEditor.py:906
+#: flatcamEditors/FlatCAMGrbEditor.py:1115
+msgid "Angle"
+msgstr "Winkel"
+
+#: flatcamEditors/FlatCAMExcEditor.py:910
+#: flatcamEditors/FlatCAMGrbEditor.py:1119
+msgid "Pitch:"
+msgstr "Abstand:"
+
+#: flatcamEditors/FlatCAMExcEditor.py:912
+#: flatcamEditors/FlatCAMGrbEditor.py:1121
+msgid "Pitch = Distance between elements of the array."
+msgstr "Abstand = Abstand zwischen Elementen des Arrays."
+
+#: flatcamEditors/FlatCAMExcEditor.py:919
+#: flatcamEditors/FlatCAMExcEditor.py:955
+#: flatcamEditors/FlatCAMGeoEditor.py:663
+#: flatcamEditors/FlatCAMGrbEditor.py:1128
+#: flatcamEditors/FlatCAMGrbEditor.py:1164
+#: flatcamEditors/FlatCAMGrbEditor.py:2822 flatcamTools/ToolTransform.py:68
+msgid "Angle:"
+msgstr "Winkel:"
+
+#: flatcamEditors/FlatCAMExcEditor.py:921
+#: flatcamEditors/FlatCAMGrbEditor.py:1130
+msgid ""
+"Angle at which the linear array is placed.\n"
+"The precision is of max 2 decimals.\n"
+"Min value is: -359.99 degrees.\n"
+"Max value is:  360.00 degrees."
+msgstr ""
+"Winkel, bei dem das lineare Array platziert wird.\n"
+"Die Genauigkeit beträgt maximal 2 Dezimalstellen.\n"
+"Der Mindestwert beträgt -359,99 Grad.\n"
+"Maximalwert ist: 360.00 Grad."
+
+#: flatcamEditors/FlatCAMExcEditor.py:942
+#: flatcamEditors/FlatCAMGrbEditor.py:1151
+msgid ""
+"Direction for circular array.Can be CW = clockwise or CCW = counter "
+"clockwise."
+msgstr ""
+"Richtung für kreisförmige Anordnung. Kann CW = Uhrzeigersinn oder CCW = "
+"Gegenuhrzeigersinn sein."
+
+#: flatcamEditors/FlatCAMExcEditor.py:957
+#: flatcamEditors/FlatCAMGrbEditor.py:1166
+msgid "Angle at which each element in circular array is placed."
+msgstr ""
+"Winkel, um den jedes Element in einer kreisförmigen Anordnung platziert wird."
+
+#: flatcamEditors/FlatCAMExcEditor.py:1413
+msgid ""
+"[WARNING_NOTCL] Tool already in the original or actual tool list.\n"
+"Save and reedit Excellon if you need to add this tool. "
+msgstr ""
+"[WARNING_NOTCL] Werkzeug bereits in der ursprünglichen oder tatsächlichen "
+"Werkzeugliste.\n"
+"Speichern und korrigieren Sie Excellon, wenn Sie dieses Tool hinzufügen "
+"möchten."
+
+#: flatcamEditors/FlatCAMExcEditor.py:1422 flatcamGUI/FlatCAMGUI.py:2888
+#, python-brace-format
+msgid "[success] Added new tool with dia: {dia} {units}"
+msgstr "[success] Neues Werkzeug mit Durchmesser hinzugefügt: {dia} {units}"
+
+#: flatcamEditors/FlatCAMExcEditor.py:1453
+#: flatcamEditors/FlatCAMGrbEditor.py:1638
+msgid "[WARNING_NOTCL] Select a tool in Tool Table"
+msgstr "[WARNING_NOTCL] Wählen Sie ein Werkzeug in der Werkzeugtabelle aus"
+
+#: flatcamEditors/FlatCAMExcEditor.py:1486
+#, python-brace-format
+msgid "[success] Deleted tool with dia: {del_dia} {units}"
+msgstr "[success] Gelöschtes Werkzeug mit Durchmesser: {del_dia} {units}"
+
+#: flatcamEditors/FlatCAMExcEditor.py:1924
+msgid ""
+"[ERROR_NOTCL] There are no Tools definitions in the file. Aborting Excellon "
+"creation."
+msgstr ""
+"[ERROR_NOTCL] Die Datei enthält keine Werkzeugdefinitionen. Abbruch der "
+"Excellon-Erstellung."
+
+#: flatcamEditors/FlatCAMExcEditor.py:1933
+msgid "Creating Excellon."
+msgstr "Excellon erstellen."
+
+#: flatcamEditors/FlatCAMExcEditor.py:1942
+msgid "[success] Excellon editing finished."
+msgstr "[success] Excellon-Bearbeitung abgeschlossen."
+
+#: flatcamEditors/FlatCAMExcEditor.py:1959
+msgid "[WARNING_NOTCL] Cancelled. There is no Tool/Drill selected"
+msgstr "[WARNING_NOTCL] Abgebrochen. Es ist kein Werkzeug / Bohrer ausgewählt"
+
+#: flatcamEditors/FlatCAMExcEditor.py:2458
+msgid "[success] Done. Drill(s) deleted."
+msgstr "[success] Erledigt. Bohrer gelöscht."
+
+#: flatcamEditors/FlatCAMExcEditor.py:2528
+#: flatcamEditors/FlatCAMGrbEditor.py:2619
+msgid "Click on the circular array Center position"
+msgstr "Klicken Sie auf die kreisförmige Anordnung in der Mitte"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:77 flatcamEditors/FlatCAMGrbEditor.py:994
+msgid "Buffer distance:"
+msgstr "Pufferabstand:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:78 flatcamEditors/FlatCAMGrbEditor.py:995
+msgid "Buffer corner:"
+msgstr "Pufferecke:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:80
+msgid ""
+"There are 3 types of corners:\n"
+" - 'Round': the corner is rounded for exterior buffer.\n"
+" - 'Square:' the corner is met in a sharp angle for exterior buffer.\n"
+" - 'Beveled:' the corner is a line that directly connects the features "
+"meeting in the corner"
+msgstr ""
+"Es gibt 3 Arten von Ecken:\n"
+"  - 'Rund': Die Ecke wird für den Außenpuffer abgerundet.\n"
+"  - 'Quadrat:' Die Ecke wird für den äußeren Puffer in einem spitzen Winkel "
+"getroffen.\n"
+"  - 'Abgeschrägt:' Die Ecke ist eine Linie, die die Features, die sich in "
+"der Ecke treffen, direkt verbindet"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:86
+#: flatcamEditors/FlatCAMGrbEditor.py:1003
+msgid "Round"
+msgstr "Runden"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:87
+#: flatcamEditors/FlatCAMGrbEditor.py:1004
+msgid "Square"
+msgstr "Quadrat"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:88
+#: flatcamEditors/FlatCAMGrbEditor.py:1005
+msgid "Beveled"
+msgstr "Abgeschrägt"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:95
+msgid "Buffer Interior"
+msgstr "Pufferinnenraum"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:97
+msgid "Buffer Exterior"
+msgstr "Puffer außen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:103
+msgid "Full Buffer"
+msgstr "Voller Puffer"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:124
+#: flatcamEditors/FlatCAMGeoEditor.py:2505
+msgid "Buffer Tool"
+msgstr "Pufferwerkzeug"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:135
+#: flatcamEditors/FlatCAMGeoEditor.py:152
+#: flatcamEditors/FlatCAMGeoEditor.py:169
+#: flatcamEditors/FlatCAMGeoEditor.py:2523
+#: flatcamEditors/FlatCAMGeoEditor.py:2549
+#: flatcamEditors/FlatCAMGeoEditor.py:2575
+#: flatcamEditors/FlatCAMGrbEditor.py:2662
+msgid ""
+"[WARNING_NOTCL] Buffer distance value is missing or wrong format. Add it and "
+"retry."
+msgstr ""
+"[WARNING_NOTCL] Pufferabstandswert fehlt oder falsches Format. Fügen Sie es "
+"hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:340
+msgid "Text Tool"
+msgstr "Textwerkzeug"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:398 flatcamGUI/FlatCAMGUI.py:764
+msgid "Tool"
+msgstr "Werkzeug"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:429 flatcamGUI/FlatCAMGUI.py:3833
+#: flatcamGUI/FlatCAMGUI.py:5039 flatcamGUI/FlatCAMGUI.py:5315
+#: flatcamGUI/FlatCAMGUI.py:5455 flatcamGUI/ObjectUI.py:260
+msgid "Tool dia:"
+msgstr "Werkzeugdurchmesser:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:431 flatcamGUI/FlatCAMGUI.py:5457
+msgid ""
+"Diameter of the tool to\n"
+"be used in the operation."
+msgstr ""
+"Durchmesser des Werkzeugs bis\n"
+"in der Operation verwendet werden."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:440 flatcamGUI/FlatCAMGUI.py:5221
+#: flatcamGUI/FlatCAMGUI.py:5466 flatcamTools/ToolNonCopperClear.py:165
+#: flatcamTools/ToolPaint.py:160
+msgid "Overlap Rate:"
+msgstr "Überlappungsrate:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:442 flatcamTools/ToolPaint.py:162
+#, python-format
+msgid ""
+"How much (fraction) of the tool width to overlap each tool pass.\n"
+"Example:\n"
+"A value here of 0.25 means 25% from the tool diameter found above.\n"
+"\n"
+"Adjust the value starting with lower values\n"
+"and increasing it if areas that should be painted are still \n"
+"not painted.\n"
+"Lower values = faster processing, faster execution on PCB.\n"
+"Higher values = slow processing and slow execution on CNC\n"
+"due of too many paths."
+msgstr ""
+"Wie viel (Bruchteil) der Werkzeugbreite, um jeden Werkzeugdurchgang zu "
+"überlappen.\n"
+"Beispiel:\n"
+"Ein Wert von 0,25 bedeutet hier 25% des oben angegebenen "
+"Werkzeugdurchmessers.\n"
+"\n"
+"Passen Sie den Wert an, indem Sie mit niedrigeren Werten beginnen\n"
+"und erhöhen Sie es, wenn Bereiche, die gemalt werden sollen, noch vorhanden "
+"sind\n"
+"nicht gemalt\n"
+"Niedrigere Werte = schnellere Verarbeitung, schnellere Ausführung auf "
+"Leiterplatten.\n"
+"Höhere Werte = langsame Bearbeitung und langsame Ausführung auf CNC\n"
+"wegen zu vieler Wege."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:458 flatcamGUI/FlatCAMGUI.py:5237
+#: flatcamGUI/FlatCAMGUI.py:5323 flatcamGUI/FlatCAMGUI.py:5476
+#: flatcamTools/ToolCutOut.py:86 flatcamTools/ToolNonCopperClear.py:181
+#: flatcamTools/ToolPaint.py:177
+msgid "Margin:"
+msgstr "Marge:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:460 flatcamGUI/FlatCAMGUI.py:5478
+#: flatcamTools/ToolPaint.py:179
+msgid ""
+"Distance by which to avoid\n"
+"the edges of the polygon to\n"
+"be painted."
+msgstr ""
+"Entfernung, um die es zu vermeiden ist\n"
+"die Kanten des Polygons bis\n"
+"gemalt werden."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:469 flatcamGUI/FlatCAMGUI.py:5246
+#: flatcamGUI/FlatCAMGUI.py:5487 flatcamTools/ToolNonCopperClear.py:190
+#: flatcamTools/ToolPaint.py:188
+msgid "Method:"
+msgstr "Methode:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:471 flatcamGUI/FlatCAMGUI.py:5489
+msgid ""
+"Algorithm to paint the polygon:<BR><B>Standard</B>: Fixed step inwards."
+"<BR><B>Seed-based</B>: Outwards from seed."
+msgstr ""
+"Algorithmus zum Malen des Polygons:<BR><B>Standard</B>: Feststehender "
+"Schritt nach innen. <BR><B> Samenbasiert</B>: Aus dem Samen heraus."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:477 flatcamGUI/FlatCAMGUI.py:5255
+#: flatcamGUI/FlatCAMGUI.py:5495
+msgid "Standard"
+msgstr "Standard"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:478 flatcamGUI/FlatCAMGUI.py:5256
+#: flatcamGUI/FlatCAMGUI.py:5496
+msgid "Seed-based"
+msgstr "Samenbasiert"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:479 flatcamGUI/FlatCAMGUI.py:5257
+#: flatcamGUI/FlatCAMGUI.py:5497
+msgid "Straight lines"
+msgstr "Gerade Linien"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:484 flatcamGUI/FlatCAMGUI.py:5262
+#: flatcamGUI/FlatCAMGUI.py:5502 flatcamTools/ToolNonCopperClear.py:206
+#: flatcamTools/ToolPaint.py:204
+msgid "Connect:"
+msgstr "Verbinden:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:486 flatcamGUI/FlatCAMGUI.py:5264
+#: flatcamGUI/FlatCAMGUI.py:5504 flatcamTools/ToolNonCopperClear.py:208
+#: flatcamTools/ToolPaint.py:206
+msgid ""
+"Draw lines between resulting\n"
+"segments to minimize tool lifts."
+msgstr ""
+"Zeichnen Sie Linien zwischen den Ergebnissen\n"
+"Segmente, um Werkzeuglifte zu minimieren."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:493 flatcamGUI/FlatCAMGUI.py:5271
+#: flatcamGUI/FlatCAMGUI.py:5512 flatcamTools/ToolNonCopperClear.py:215
+#: flatcamTools/ToolPaint.py:213
+msgid "Contour:"
+msgstr "Kontur:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:495 flatcamGUI/FlatCAMGUI.py:5273
+#: flatcamGUI/FlatCAMGUI.py:5514 flatcamTools/ToolNonCopperClear.py:217
+#: flatcamTools/ToolPaint.py:215
+msgid ""
+"Cut around the perimeter of the polygon\n"
+"to trim rough edges."
+msgstr ""
+"Schneiden Sie um den Umfang des Polygons herum\n"
+"Ecken und Kanten schneiden."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:507
+msgid "Paint"
+msgstr "Malen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:525 flatcamGUI/FlatCAMGUI.py:629
+#: flatcamGUI/FlatCAMGUI.py:1796 flatcamGUI/ObjectUI.py:1308
+#: flatcamTools/ToolPaint.py:340
+msgid "Paint Tool"
+msgstr "Werkzeug Malen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:561
+msgid "[WARNING_NOTCL] Paint cancelled. No shape selected."
+msgstr "[WARNING_NOTCL] Farbe abgebrochen. Keine Form ausgewählt"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:572 flatcamTools/ToolCutOut.py:352
+#: flatcamTools/ToolCutOut.py:496 flatcamTools/ToolCutOut.py:616
+#: flatcamTools/ToolCutOut.py:721 flatcamTools/ToolDblSided.py:363
+msgid ""
+"[WARNING_NOTCL] Tool diameter value is missing or wrong format. Add it and "
+"retry."
+msgstr ""
+"[WARNING_NOTCL] Werkzeugdurchmesserwert fehlt oder falsches Format. Fügen "
+"Sie es hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:583
+msgid ""
+"[WARNING_NOTCL] Overlap value is missing or wrong format. Add it and retry."
+msgstr ""
+"[WARNING_NOTCL] Überlappungswert fehlt oder falsches Format. Fügen Sie es "
+"hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:595
+msgid ""
+"[WARNING_NOTCL] Margin distance value is missing or wrong format. Add it and "
+"retry."
+msgstr ""
+"[WARNING_NOTCL] Randabstandswert fehlt oder falsches Format. Fügen Sie es "
+"hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:604
+#: flatcamEditors/FlatCAMGeoEditor.py:2530
+#: flatcamEditors/FlatCAMGeoEditor.py:2556
+#: flatcamEditors/FlatCAMGeoEditor.py:2582 flatcamTools/ToolMeasurement.py:202
+#: flatcamTools/ToolNonCopperClear.py:812 flatcamTools/ToolProperties.py:104
+msgid "Tools"
+msgstr "Werkzeuge"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:615
+#: flatcamEditors/FlatCAMGeoEditor.py:988
+#: flatcamEditors/FlatCAMGrbEditor.py:2774
+#: flatcamEditors/FlatCAMGrbEditor.py:3158 flatcamGUI/FlatCAMGUI.py:638
+#: flatcamGUI/FlatCAMGUI.py:1807 flatcamTools/ToolTransform.py:398
+msgid "Transform Tool"
+msgstr "Werkzeug Umwandeln"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:616
+#: flatcamEditors/FlatCAMGeoEditor.py:677
+#: flatcamEditors/FlatCAMGrbEditor.py:2775
+#: flatcamEditors/FlatCAMGrbEditor.py:2836 flatcamTools/ToolTransform.py:24
+#: flatcamTools/ToolTransform.py:82
+msgid "Rotate"
+msgstr "Drehen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:617
+#: flatcamEditors/FlatCAMGrbEditor.py:2776 flatcamTools/ToolTransform.py:25
+msgid "Skew/Shear"
+msgstr "Neigung/Schere"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:618
+#: flatcamEditors/FlatCAMGrbEditor.py:1049
+#: flatcamEditors/FlatCAMGrbEditor.py:2777 flatcamGUI/FlatCAMGUI.py:696
+#: flatcamGUI/FlatCAMGUI.py:1868 flatcamGUI/ObjectUI.py:100
+#: flatcamTools/ToolTransform.py:26
+msgid "Scale"
+msgstr "Skalieren"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:619
+#: flatcamEditors/FlatCAMGrbEditor.py:2778 flatcamTools/ToolTransform.py:27
+msgid "Mirror (Flip)"
+msgstr "Spiegeln (Flip)"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:620
+#: flatcamEditors/FlatCAMGrbEditor.py:2779 flatcamGUI/ObjectUI.py:127
+#: flatcamGUI/ObjectUI.py:888 flatcamGUI/ObjectUI.py:1446
+#: flatcamTools/ToolTransform.py:28
+msgid "Offset"
+msgstr "Versatz"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:631
+#: flatcamEditors/FlatCAMGrbEditor.py:2790
+#, python-format
+msgid "Editor %s"
+msgstr "Editor %s"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:665
+#: flatcamEditors/FlatCAMGrbEditor.py:2824 flatcamTools/ToolTransform.py:70
+msgid ""
+"Angle for Rotation action, in degrees.\n"
+"Float number between -360 and 359.\n"
+"Positive numbers for CW motion.\n"
+"Negative numbers for CCW motion."
+msgstr ""
+"Drehwinkel in Grad.\n"
+"Float-Nummer zwischen -360 und 359.\n"
+"Positive Zahlen für CW-Bewegung.\n"
+"Negative Zahlen für CCW-Bewegung."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:679
+#: flatcamEditors/FlatCAMGrbEditor.py:2838
+msgid ""
+"Rotate the selected shape(s).\n"
+"The point of reference is the middle of\n"
+"the bounding box for all selected shapes."
+msgstr ""
+"Die ausgewählten Formen drehen.\n"
+"Der Bezugspunkt ist die Mitte von\n"
+"der Begrenzungsrahmen für alle ausgewählten Formen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:702
+#: flatcamEditors/FlatCAMGrbEditor.py:2861 flatcamTools/ToolTransform.py:107
+msgid "Angle X:"
+msgstr "Winkel X:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:704
+#: flatcamEditors/FlatCAMGeoEditor.py:722
+#: flatcamEditors/FlatCAMGrbEditor.py:2863
+#: flatcamEditors/FlatCAMGrbEditor.py:2881 flatcamTools/ToolTransform.py:109
+#: flatcamTools/ToolTransform.py:127
+msgid ""
+"Angle for Skew action, in degrees.\n"
+"Float number between -360 and 359."
+msgstr ""
+"Winkel für die Schräglage in Grad.\n"
+"Float-Nummer zwischen -360 und 359."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:713
+#: flatcamEditors/FlatCAMGrbEditor.py:2872 flatcamTools/ToolTransform.py:118
+msgid "Skew X"
+msgstr "Neigung X"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:715
+#: flatcamEditors/FlatCAMGeoEditor.py:733
+#: flatcamEditors/FlatCAMGrbEditor.py:2874
+#: flatcamEditors/FlatCAMGrbEditor.py:2892
+msgid ""
+"Skew/shear the selected shape(s).\n"
+"The point of reference is the middle of\n"
+"the bounding box for all selected shapes."
+msgstr ""
+"Schrägstellung/Scherung der ausgewählten Form(en).\n"
+"Der Bezugspunkt ist die Mitte von\n"
+"der Begrenzungsrahmen für alle ausgewählten Formen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:720
+#: flatcamEditors/FlatCAMGrbEditor.py:2879 flatcamTools/ToolTransform.py:125
+msgid "Angle Y:"
+msgstr "Winkel Y:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:731
+#: flatcamEditors/FlatCAMGrbEditor.py:2890 flatcamTools/ToolTransform.py:136
+msgid "Skew Y"
+msgstr "Neigung Y"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:759
+#: flatcamEditors/FlatCAMGrbEditor.py:2918 flatcamTools/ToolTransform.py:164
+msgid "Factor X:"
+msgstr "Faktor X:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:761
+#: flatcamEditors/FlatCAMGrbEditor.py:2920 flatcamTools/ToolTransform.py:166
+msgid "Factor for Scale action over X axis."
+msgstr "Faktor für die Skalierungsaktion über der X-Achse."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:769
+#: flatcamEditors/FlatCAMGrbEditor.py:2928 flatcamTools/ToolTransform.py:174
+msgid "Scale X"
+msgstr "Maßstab X"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:771
+#: flatcamEditors/FlatCAMGeoEditor.py:788
+#: flatcamEditors/FlatCAMGrbEditor.py:2930
+#: flatcamEditors/FlatCAMGrbEditor.py:2947
+msgid ""
+"Scale the selected shape(s).\n"
+"The point of reference depends on \n"
+"the Scale reference checkbox state."
+msgstr ""
+"Skalieren Sie die ausgewählten Formen.\n"
+"Der Bezugspunkt hängt von ab\n"
+"das Kontrollkästchen Skalenreferenz."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:776
+#: flatcamEditors/FlatCAMGrbEditor.py:2935 flatcamTools/ToolTransform.py:181
+msgid "Factor Y:"
+msgstr "Faktor Y:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:778
+#: flatcamEditors/FlatCAMGrbEditor.py:2937 flatcamTools/ToolTransform.py:183
+msgid "Factor for Scale action over Y axis."
+msgstr "Faktor für die Skalierungsaktion über der Y-Achse."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:786
+#: flatcamEditors/FlatCAMGrbEditor.py:2945 flatcamTools/ToolTransform.py:191
+msgid "Scale Y"
+msgstr "Maßstab Y"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:795
+#: flatcamEditors/FlatCAMGrbEditor.py:2954 flatcamGUI/FlatCAMGUI.py:5861
+#: flatcamTools/ToolTransform.py:200
+msgid "Link"
+msgstr "Verknüpfung"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:797
+#: flatcamEditors/FlatCAMGrbEditor.py:2956
+msgid ""
+"Scale the selected shape(s)\n"
+"using the Scale Factor X for both axis."
+msgstr ""
+"Skalieren der ausgewählten Form (en)\n"
+"Verwenden des Skalierungsfaktors X für beide Achsen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:803
+#: flatcamEditors/FlatCAMGrbEditor.py:2962 flatcamGUI/FlatCAMGUI.py:5869
+#: flatcamTools/ToolTransform.py:208
+msgid "Scale Reference"
+msgstr "Skalenreferenz"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:805
+#: flatcamEditors/FlatCAMGrbEditor.py:2964
+msgid ""
+"Scale the selected shape(s)\n"
+"using the origin reference when checked,\n"
+"and the center of the biggest bounding box\n"
+"of the selected shapes when unchecked."
+msgstr ""
+"Skalieren der ausgewählten Form (en)\n"
+"unter Verwendung der Ursprungsreferenz, wenn geprüft\n"
+"und die Mitte der größten Begrenzungsbox\n"
+"der ausgewählten Formen, wenn nicht markiert."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:833
+#: flatcamEditors/FlatCAMGrbEditor.py:2993 flatcamTools/ToolTransform.py:238
+msgid "Value X:"
+msgstr "Wert X:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:835
+#: flatcamEditors/FlatCAMGrbEditor.py:2995 flatcamTools/ToolTransform.py:240
+msgid "Value for Offset action on X axis."
+msgstr "Wert für die Offset-Aktion auf der X-Achse."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:843
+#: flatcamEditors/FlatCAMGrbEditor.py:3003 flatcamTools/ToolTransform.py:248
+msgid "Offset X"
+msgstr "Versatz X"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:845
+#: flatcamEditors/FlatCAMGeoEditor.py:863
+#: flatcamEditors/FlatCAMGrbEditor.py:3005
+#: flatcamEditors/FlatCAMGrbEditor.py:3023
+msgid ""
+"Offset the selected shape(s).\n"
+"The point of reference is the middle of\n"
+"the bounding box for all selected shapes.\n"
+msgstr ""
+"Versetzt die ausgewählten Formen.\n"
+"Der Bezugspunkt ist die Mitte von\n"
+"der Begrenzungsrahmen für alle ausgewählten Formen.\n"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:851
+#: flatcamEditors/FlatCAMGrbEditor.py:3011 flatcamTools/ToolTransform.py:255
+msgid "Value Y:"
+msgstr "Wert Y:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:853
+#: flatcamEditors/FlatCAMGrbEditor.py:3013 flatcamTools/ToolTransform.py:257
+msgid "Value for Offset action on Y axis."
+msgstr "Wert für die Offset-Aktion auf der Y-Achse."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:861
+#: flatcamEditors/FlatCAMGrbEditor.py:3021 flatcamTools/ToolTransform.py:265
+msgid "Offset Y"
+msgstr "Versatz Y"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:892
+#: flatcamEditors/FlatCAMGrbEditor.py:3052 flatcamTools/ToolTransform.py:295
+msgid "Flip on X"
+msgstr "Flip auf X"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:894
+#: flatcamEditors/FlatCAMGeoEditor.py:902
+#: flatcamEditors/FlatCAMGrbEditor.py:3054
+#: flatcamEditors/FlatCAMGrbEditor.py:3062
+msgid ""
+"Flip the selected shape(s) over the X axis.\n"
+"Does not create a new shape."
+msgstr ""
+"Kippen Sie die ausgewählte Form (en) über die X-Achse.\n"
+"Erzeugt keine neue Form."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:900
+#: flatcamEditors/FlatCAMGrbEditor.py:3060 flatcamTools/ToolTransform.py:303
+msgid "Flip on Y"
+msgstr "Flip auf Y"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:909
+#: flatcamEditors/FlatCAMGrbEditor.py:3069 flatcamTools/ToolTransform.py:312
+msgid "Ref Pt"
+msgstr "Ref. Pt"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:911
+#: flatcamEditors/FlatCAMGrbEditor.py:3071
+msgid ""
+"Flip the selected shape(s)\n"
+"around the point in Point Entry Field.\n"
+"\n"
+"The point coordinates can be captured by\n"
+"left click on canvas together with pressing\n"
+"SHIFT key. \n"
+"Then click Add button to insert coordinates.\n"
+"Or enter the coords in format (x, y) in the\n"
+"Point Entry field and click Flip on X(Y)"
+msgstr ""
+"Die ausgewählten Formen umdrehen\n"
+"um den Punkt im Eingabefeld.\n"
+"\n"
+"Die Punktkoordinaten können mit erfasst werden\n"
+"Klicken Sie mit der linken Maustaste auf die Leinwand\n"
+"Shift Taste.\n"
+"Klicken Sie dann auf die Schaltfläche Hinzufügen, um die Koordinaten "
+"einzufügen.\n"
+"Oder geben Sie die Koordinaten im Format (x, y) in ein\n"
+"Punkt-Eingabefeld und klicken Sie auf X (Y) drehen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:923
+#: flatcamEditors/FlatCAMGrbEditor.py:3083 flatcamTools/ToolTransform.py:325
+msgid "Point:"
+msgstr "Punkt:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:925
+#: flatcamEditors/FlatCAMGrbEditor.py:3085
+msgid ""
+"Coordinates in format (x, y) used as reference for mirroring.\n"
+"The 'x' in (x, y) will be used when using Flip on X and\n"
+"the 'y' in (x, y) will be used when using Flip on Y."
+msgstr ""
+"Koordinaten im Format (x, y), die als Referenz für die Spiegelung verwendet "
+"werden.\n"
+"Das 'x' in (x, y) wird verwendet, wenn Sie bei X und\n"
+"Das 'y' in (x, y) wird verwendet, wenn Flip auf Y verwendet wird."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:935
+#: flatcamEditors/FlatCAMGrbEditor.py:3095 flatcamGUI/ObjectUI.py:988
+#: flatcamTools/ToolDblSided.py:160 flatcamTools/ToolDblSided.py:208
+#: flatcamTools/ToolNonCopperClear.py:134 flatcamTools/ToolPaint.py:131
+#: flatcamTools/ToolSolderPaste.py:115 flatcamTools/ToolSolderPaste.py:478
+#: flatcamTools/ToolTransform.py:337
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:937
+#: flatcamEditors/FlatCAMGrbEditor.py:3097 flatcamTools/ToolTransform.py:339
+msgid ""
+"The point coordinates can be captured by\n"
+"left click on canvas together with pressing\n"
+"SHIFT key. Then click Add button to insert."
+msgstr ""
+"Die Punktkoordinaten können mit erfasst werden\n"
+"Klicken Sie mit der linken Maustaste auf die Leinwand\n"
+"Shift Taste. Klicken Sie dann auf die Schaltfläche Hinzufügen, um sie "
+"einzufügen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1052
+#: flatcamEditors/FlatCAMGrbEditor.py:3222
+msgid "[WARNING_NOTCL] Transformation cancelled. No shape selected."
+msgstr "[WARNING_NOTCL] Transformation abgebrochen Keine Form ausgewählt"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1073
+#: flatcamEditors/FlatCAMGrbEditor.py:3242 flatcamTools/ToolTransform.py:468
+msgid "[ERROR_NOTCL] Wrong value format entered for Rotate, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Werteformat für Drehen eingegeben, verwenden Sie eine "
+"Zahl."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1110
+#: flatcamEditors/FlatCAMGrbEditor.py:3279 flatcamTools/ToolTransform.py:502
+msgid "[ERROR_NOTCL] Wrong value format entered for Skew X, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Werteformat für Skew X eingegeben, verwenden Sie eine "
+"Zahl."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1131
+#: flatcamEditors/FlatCAMGrbEditor.py:3300 flatcamTools/ToolTransform.py:520
+msgid "[ERROR_NOTCL] Wrong value format entered for Skew Y, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Werteformat für Skew Y eingegeben, verwenden Sie eine "
+"Zahl."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1152
+#: flatcamEditors/FlatCAMGrbEditor.py:3321 flatcamTools/ToolTransform.py:538
+msgid "[ERROR_NOTCL] Wrong value format entered for Scale X, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Wertformat für Waage X eingegeben, verwenden Sie eine "
+"Zahl."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1189
+#: flatcamEditors/FlatCAMGrbEditor.py:3358 flatcamTools/ToolTransform.py:572
+msgid "[ERROR_NOTCL] Wrong value format entered for Scale Y, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Werteformat für Skala Y eingegeben, verwenden Sie "
+"eine Zahl."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1221
+#: flatcamEditors/FlatCAMGrbEditor.py:3390 flatcamTools/ToolTransform.py:601
+msgid "[ERROR_NOTCL] Wrong value format entered for Offset X, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Wertformat für Offset X eingegeben, verwenden Sie "
+"eine Zahl."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1242
+#: flatcamEditors/FlatCAMGrbEditor.py:3411 flatcamTools/ToolTransform.py:619
+msgid "[ERROR_NOTCL] Wrong value format entered for Offset Y, use a number."
+msgstr ""
+"[ERROR_NOTCL] Falsches Wertformat für Offset Y eingegeben, verwenden Sie "
+"eine Zahl."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1260
+#: flatcamEditors/FlatCAMGrbEditor.py:3429
+msgid "[WARNING_NOTCL] No shape selected. Please Select a shape to rotate!"
+msgstr ""
+"[WARNING_NOTCL] Keine Form ausgewählt Bitte wählen Sie eine Form zum Drehen "
+"aus!"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1263
+#: flatcamEditors/FlatCAMGrbEditor.py:3432 flatcamTools/ToolTransform.py:640
+msgid "Appying Rotate"
+msgstr "Anwenden Drehen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1291
+#: flatcamEditors/FlatCAMGrbEditor.py:3460
+msgid "[success] Done. Rotate completed."
+msgstr "[success] Erledigt. Drehen abgeschlossen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1307
+#: flatcamEditors/FlatCAMGrbEditor.py:3476
+msgid "[WARNING_NOTCL] No shape selected. Please Select a shape to flip!"
+msgstr ""
+"[WARNING_NOTCL] Keine Form ausgewählt Bitte wähle eine Form zum Umdrehen!"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1310
+#: flatcamEditors/FlatCAMGrbEditor.py:3479 flatcamTools/ToolTransform.py:692
+msgid "Applying Flip"
+msgstr "Flip anwenden"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1340
+#: flatcamEditors/FlatCAMGrbEditor.py:3509 flatcamTools/ToolTransform.py:735
+msgid "[success] Flip on the Y axis done ..."
+msgstr "[success] Flip auf der Y-Achse erledigt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1343
+#: flatcamEditors/FlatCAMGrbEditor.py:3512 flatcamTools/ToolTransform.py:745
+msgid "[success] Flip on the X axis done ..."
+msgstr "[success] Flip auf der X-Achse erledigt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1362
+#: flatcamEditors/FlatCAMGrbEditor.py:3531
+msgid "[WARNING_NOTCL] No shape selected. Please Select a shape to shear/skew!"
+msgstr ""
+"[WARNING_NOTCL] Keine Form ausgewählt. Bitte wählen Sie eine Form zum "
+"Scheren / Schrägstellen!"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1365
+#: flatcamEditors/FlatCAMGrbEditor.py:3534 flatcamTools/ToolTransform.py:762
+msgid "Applying Skew"
+msgstr "Anwenden von Skew"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1390
+#: flatcamEditors/FlatCAMGrbEditor.py:3559 flatcamTools/ToolTransform.py:793
+#, python-format
+msgid "[success] Skew on the %s axis done ..."
+msgstr "[success] Neigung auf der %s Achse abgeschlossen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1394
+#: flatcamEditors/FlatCAMGrbEditor.py:3563 flatcamTools/ToolTransform.py:797
+#, python-format
+msgid "[ERROR_NOTCL] Due of %s, Skew action was not executed."
+msgstr ""
+"[ERROR_NOTCL] Aufgrund von %s wurde die Neigung-Aktion nicht ausgeführt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1405
+#: flatcamEditors/FlatCAMGrbEditor.py:3574
+msgid "[WARNING_NOTCL] No shape selected. Please Select a shape to scale!"
+msgstr ""
+"[WARNING_NOTCL] Keine Form ausgewählt. Bitte wählen Sie eine zu skalierende "
+"Form!"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1408
+#: flatcamEditors/FlatCAMGrbEditor.py:3577 flatcamTools/ToolTransform.py:811
+msgid "Applying Scale"
+msgstr "Maßstab anwenden"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1441
+#: flatcamEditors/FlatCAMGrbEditor.py:3610 flatcamTools/ToolTransform.py:849
+#, python-format
+msgid "[success] Scale on the %s axis done ..."
+msgstr "[success] Skalieren auf der %s Achse fertig ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1444
+#: flatcamEditors/FlatCAMGrbEditor.py:3613 flatcamTools/ToolTransform.py:852
+#, python-format
+msgid "[ERROR_NOTCL] Due of %s, Scale action was not executed."
+msgstr ""
+"[ERROR_NOTCL] Aufgrund von %s wurde die Skalieren Aktion nicht ausgeführt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1453
+#: flatcamEditors/FlatCAMGrbEditor.py:3622
+msgid "[WARNING_NOTCL] No shape selected. Please Select a shape to offset!"
+msgstr ""
+"[WARNING_NOTCL] Keine Form ausgewählt. Bitte wählen Sie eine Form zum "
+"Versetzen!"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1456
+#: flatcamEditors/FlatCAMGrbEditor.py:3625 flatcamTools/ToolTransform.py:864
+msgid "Applying Offset"
+msgstr "Offsetdruck anwenden"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1480
+#: flatcamEditors/FlatCAMGrbEditor.py:3649 flatcamTools/ToolTransform.py:894
+#, python-format
+msgid "[success] Offset on the %s axis done ..."
+msgstr "[success] Offsetdruck auf der %s Achse fertiggestellt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1484
+#: flatcamEditors/FlatCAMGrbEditor.py:3653 flatcamTools/ToolTransform.py:898
+#, python-format
+msgid "[ERROR_NOTCL] Due of %s, Offset action was not executed."
+msgstr ""
+"[ERROR_NOTCL] Aufgrund von %s wurde die Offsetdruck Aktion nicht ausgeführt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1488
+#: flatcamEditors/FlatCAMGrbEditor.py:3657
+msgid "Rotate ..."
+msgstr "Drehen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1489
+#: flatcamEditors/FlatCAMGeoEditor.py:1546
+#: flatcamEditors/FlatCAMGeoEditor.py:1563
+#: flatcamEditors/FlatCAMGrbEditor.py:3658
+#: flatcamEditors/FlatCAMGrbEditor.py:3715
+#: flatcamEditors/FlatCAMGrbEditor.py:3732
+msgid "Enter an Angle Value (degrees):"
+msgstr "Geben Sie einen Winkelwert (Grad) ein:"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1498
+#: flatcamEditors/FlatCAMGrbEditor.py:3667
+msgid "[success] Geometry shape rotate done..."
+msgstr "[success] Geometrieform drehen fertig ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1503
+#: flatcamEditors/FlatCAMGrbEditor.py:3672
+msgid "[WARNING_NOTCL] Geometry shape rotate cancelled..."
+msgstr "[WARNING_NOTCL] Geometrieform drehen abgebrochen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1509
+#: flatcamEditors/FlatCAMGrbEditor.py:3678
+msgid "Offset on X axis ..."
+msgstr "Versatz auf der X-Achse ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1510
+#: flatcamEditors/FlatCAMGeoEditor.py:1529
+#: flatcamEditors/FlatCAMGrbEditor.py:3679
+#: flatcamEditors/FlatCAMGrbEditor.py:3698
+#, python-format
+msgid "Enter a distance Value (%s):"
+msgstr "Geben Sie einen Abstand ein (%s):"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1519
+#: flatcamEditors/FlatCAMGrbEditor.py:3688
+msgid "[success] Geometry shape offset on X axis done..."
+msgstr "[success] Geometrieformversatz auf der X-Achse erfolgt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1523
+#: flatcamEditors/FlatCAMGrbEditor.py:3692
+msgid "[WARNING_NOTCL] Geometry shape offset X cancelled..."
+msgstr "[WARNING_NOTCL] Geometrieformversatz X abgebrochen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1528
+#: flatcamEditors/FlatCAMGrbEditor.py:3697
+msgid "Offset on Y axis ..."
+msgstr "Versatz auf der Y-Achse ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1538
+#: flatcamEditors/FlatCAMGrbEditor.py:3707
+msgid "[success] Geometry shape offset on Y axis done..."
+msgstr "[success] Geometrieformversatz auf Y-Achse erfolgt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1542
+#: flatcamEditors/FlatCAMGrbEditor.py:3711
+msgid "[WARNING_NOTCL] Geometry shape offset Y cancelled..."
+msgstr "[WARNING_NOTCL] Geometrieformversatz Y abgebrochen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1545
+#: flatcamEditors/FlatCAMGrbEditor.py:3714
+msgid "Skew on X axis ..."
+msgstr "Neigung auf der X-Achse ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1555
+#: flatcamEditors/FlatCAMGrbEditor.py:3724
+msgid "[success] Geometry shape skew on X axis done..."
+msgstr "[success] Geometrieformversatz auf X-Achse ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1559
+#: flatcamEditors/FlatCAMGrbEditor.py:3728
+msgid "[WARNING_NOTCL] Geometry shape skew X cancelled..."
+msgstr "[WARNING_NOTCL] Geometrieformversatz X abgebrochen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1562
+#: flatcamEditors/FlatCAMGrbEditor.py:3731
+msgid "Skew on Y axis ..."
+msgstr "Neigung auf der Y-Achse ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1572
+#: flatcamEditors/FlatCAMGrbEditor.py:3741
+msgid "[success] Geometry shape skew on Y axis done..."
+msgstr "[success] Geometrieformversatz auf Y-Achse erfolgt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1576
+#: flatcamEditors/FlatCAMGrbEditor.py:3745
+msgid "[WARNING_NOTCL] Geometry shape skew Y cancelled..."
+msgstr "[WARNING_NOTCL] Geometrieformversatz Y abgebrochen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1934
+#: flatcamEditors/FlatCAMGeoEditor.py:1973
+msgid "Click on CENTER ..."
+msgstr "Klicken Sie auf MITTE ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1941
+msgid "Click on Circle perimeter point to complete ..."
+msgstr "Klicken Sie auf Kreisumfangspunkt, um den Vorgang abzuschließen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1965
+msgid "[success] Done. Adding Circle completed."
+msgstr "[success] Erledigt. Hinzufügen des Kreises abgeschlossen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1992
+msgid "Click on Start arc point ..."
+msgstr "Klicken Sie auf Bogenstartpunkt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:1996
+msgid "Click on End arc point to complete ..."
+msgstr "Klicken Sie auf Bogenende beenden, um den Vorgang abzuschließen..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2151
+msgid "[success] Done. Arc completed."
+msgstr "[success] Erledigt. Bogen abgeschlossen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2163
+msgid "Click on 1st corner ..."
+msgstr "Klicken Sie auf die 1. Ecke ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2191
+msgid "[success] Done. Rectangle completed."
+msgstr "[success] Erledigt. Rechteck fertiggestellt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2203
+#: flatcamEditors/FlatCAMGrbEditor.py:452
+msgid "Click on 1st point ..."
+msgstr "Klicken Sie auf den 1. Punkt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2210
+#: flatcamEditors/FlatCAMGrbEditor.py:459
+msgid "Click on next Point or click Right mouse button to complete ..."
+msgstr ""
+"Klicken Sie auf den nächsten Punkt oder klicken Sie mit der rechten "
+"Maustaste, um den Vorgang abzuschließen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2233
+msgid "[success] Done. Polygon completed."
+msgstr "[success] Erledigt. Polygon abgeschlossen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2252
+#: flatcamEditors/FlatCAMGrbEditor.py:502
+msgid "[success] Done. Path completed."
+msgstr "[success] Erledigt. Pfad abgeschlossen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2354
+#: flatcamEditors/FlatCAMGeoEditor.py:3442
+msgid "[WARNING_NOTCL] Move cancelled. No shape selected."
+msgstr "[WARNING_NOTCL] Umzug abgebrochen. Keine Form ausgewählt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2358
+msgid "Click on reference point."
+msgstr "Klicken Sie auf den Referenzpunkt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2361
+msgid "Click on destination point."
+msgstr "Klicken Sie auf den Zielpunkt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2392
+msgid "[success] Done. Geometry(s) Move completed."
+msgstr "[success] Erledigt. Geometrie(n) Bewegung abgeschlossen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2437
+msgid "[success] Done. Geometry(s) Copy completed."
+msgstr "[success] Erledigt. Geometrie(n) Kopieren abgeschlossen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2449
+msgid "Click on the Destination point..."
+msgstr "Klicken Sie auf den Zielpunkt ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2463
+#, python-format
+msgid ""
+"[ERROR]Font not supported. Only Regular, Bold, Italic and BoldItalic are "
+"supported. Error: %s"
+msgstr ""
+"[ERROR] Schrift wird nicht unterstützt. Es werden nur Regular, Bold, Italic "
+"und BoldItalic unterstützt. Error: %s"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2473
+msgid "[success] Done. Adding Text completed."
+msgstr "[success] Erledigt. Hinzufügen von Text abgeschlossen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2501
+msgid "Create buffer geometry ..."
+msgstr "Puffergeometrie erstellen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2512
+#: flatcamEditors/FlatCAMGeoEditor.py:2538
+#: flatcamEditors/FlatCAMGeoEditor.py:2564
+msgid "[WARNING_NOTCL] Buffer cancelled. No shape selected."
+msgstr "[WARNING_NOTCL] Puffer abgebrochen. Keine Form ausgewählt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2534
+#: flatcamEditors/FlatCAMGrbEditor.py:2698
+msgid "[success] Done. Buffer Tool completed."
+msgstr "[success] Erledigt. Pufferwerkzeug abgeschlossen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2560
+msgid "[success] Done. Buffer Int Tool completed."
+msgstr "[success] Erledigt. Innenpufferwerkzeug abgeschlossen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2586
+msgid "[success] Done. Buffer Ext Tool completed."
+msgstr "[success] Erledigt. Außenpufferwerkzeug abgeschlossen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2619
+msgid "Create Paint geometry ..."
+msgstr "Malen geometrie erstellen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:2633
+#: flatcamEditors/FlatCAMGrbEditor.py:797
+msgid "Shape transformations ..."
+msgstr "Formtransformationen ..."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3077
+#, python-brace-format
+msgid "[WARNING] Editing MultiGeo Geometry, tool: {tool} with diameter: {dia}"
+msgstr ""
+"[WARNING] Bearbeiten von MultiGeo-Geometrie, Werkzeug: {tool} mit "
+"Durchmesser: {dia}"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3316
+#: flatcamEditors/FlatCAMGrbEditor.py:2267 flatcamGUI/FlatCAMGUI.py:2320
+#: flatcamGUI/FlatCAMGUI.py:2332
+msgid "[success] Done."
+msgstr "[success] Erledigt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3449
+msgid "[WARNING_NOTCL] Copy cancelled. No shape selected."
+msgstr "[WARNING_NOTCL] Kopieren abgebrochen Keine Form ausgewählt"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3456 flatcamGUI/FlatCAMGUI.py:2623
+#: flatcamGUI/FlatCAMGUI.py:2657 flatcamGUI/FlatCAMGUI.py:2675
+#: flatcamGUI/FlatCAMGUI.py:2813 flatcamGUI/FlatCAMGUI.py:2825
+#: flatcamGUI/FlatCAMGUI.py:2859
+msgid "Click on target point."
+msgstr "Klicken Sie auf den Zielpunkt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3699
+msgid ""
+"[WARNING_NOTCL] A selection of at least 2 geo items is required to do "
+"Intersection."
+msgstr ""
+"[WARNING_NOTCL] Eine Auswahl von mindestens 2 Geo-Elementen ist "
+"erforderlich, um die Kreuzung durchzuführen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3737
+#: flatcamEditors/FlatCAMGeoEditor.py:3774
+#: flatcamEditors/FlatCAMGeoEditor.py:3850
+msgid ""
+"[ERROR_NOTCL] Negative buffer value is not accepted. Use Buffer interior to "
+"generate an 'inside' shape"
+msgstr ""
+"[ERROR_NOTCL] Negativer Pufferwert wird nicht akzeptiert. Verwenden Sie den "
+"Pufferinnenraum, um eine Innenform zu erzeugen"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3745
+#: flatcamEditors/FlatCAMGeoEditor.py:3783
+#: flatcamEditors/FlatCAMGeoEditor.py:3858
+msgid "[WARNING_NOTCL] Nothing selected for buffering."
+msgstr "[WARNING_NOTCL] Nichts ist für die Pufferung ausgewählt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3749
+#: flatcamEditors/FlatCAMGeoEditor.py:3787
+#: flatcamEditors/FlatCAMGeoEditor.py:3862
+msgid "[WARNING_NOTCL] Invalid distance for buffering."
+msgstr "[WARNING_NOTCL] Ungültige Entfernung für die Pufferung"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3759
+#: flatcamEditors/FlatCAMGeoEditor.py:3871
+msgid ""
+"[ERROR_NOTCL] Failed, the result is empty. Choose a different buffer value."
+msgstr ""
+"[ERROR_NOTCL] Fehlgeschlagen, das Ergebnis ist leer. Wählen Sie einen "
+"anderen Pufferwert."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3767
+msgid "[success] Full buffer geometry created."
+msgstr "[success] Volle Puffergeometrie erstellt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3797
+msgid ""
+"[ERROR_NOTCL] Failed, the result is empty. Choose a smaller buffer value."
+msgstr ""
+"[ERROR_NOTCL] Fehlgeschlagen, das Ergebnis ist leer. Wählen Sie einen "
+"kleineren Pufferwert."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3812
+msgid "[success] Interior buffer geometry created."
+msgstr "[success] Innere Puffergeometrie erstellt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3883
+msgid "[success] Exterior buffer geometry created."
+msgstr "[success] Außenpuffergeometrie erstellt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3947
+msgid "[WARNING_NOTCL] Nothing selected for painting."
+msgstr "[WARNING_NOTCL] Nichts zum Malen ausgewählt."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3953
+msgid "[WARNING] Invalid value for {}"
+msgstr "[WARNING] Ungültiger Wert für {}"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:3959
+msgid ""
+"[ERROR_NOTCL] Could not do Paint. Overlap value has to be less than 1.00 "
+"(100%)."
+msgstr ""
+"[ERROR_NOTCL] Kann nicht Malen machen. Der Überlappungswert muss unter 1,00 "
+"(100%) liegen."
+
+#: flatcamEditors/FlatCAMGeoEditor.py:4018
+#, python-format
+msgid ""
+"[ERROR] Could not do Paint. Try a different combination of parameters. Or a "
+"different method of Paint\n"
+"%s"
+msgstr ""
+"[ERROR] Kann nicht Malen machen. Versuchen Sie es mit einer anderen "
+"Kombination von Parametern. Oder eine andere Methode von Malen\n"
+"%s"
+
+#: flatcamEditors/FlatCAMGeoEditor.py:4029
+msgid "[success] Paint done."
+msgstr "[success] Malen Sie fertig."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:58 flatcamEditors/FlatCAMGrbEditor.py:63
+msgid "Click to place ..."
+msgstr "Zum Platzieren klicken ..."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:149
+#: flatcamEditors/FlatCAMGrbEditor.py:386
+msgid ""
+"Incompatible aperture type. Select an aperture with type 'C', 'R' or 'O'."
+msgstr ""
+"Inkompatibler Blendentyp. Wählen Sie eine Blende mit dem Typ 'C', 'R' oder "
+"'O'."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:161
+msgid "[success] Done. Adding Pad completed."
+msgstr "[success] Erledigt. Hinzufügen von Pad abgeschlossen."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:215
+msgid "[WARNING_NOTCL] To add an Pad Array first select a tool in Tool Table"
+msgstr ""
+"[WARNING_NOTCL] Um ein Pad-Array hinzuzufügen, wählen Sie zunächst ein "
+"Werkzeug in der Werkzeugtabelle aus"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:240
+msgid "Click on the Pad Circular Array Start position"
+msgstr "Klicken Sie auf die Startposition des Pad-Kreis-Arrays"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:411
+msgid "[WARNING_NOTCL] Too many Pads for the selected spacing angle."
+msgstr "[WARNING_NOTCL] Zu viele Pad für den ausgewählten Abstandswinkel."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:433
+msgid "[success] Done. Pad Array added."
+msgstr "[success] Erledigt. Pad Array hinzugefügt."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:482
+msgid "[success] Done. Region completed."
+msgstr "[success] Erledigt. Region abgeschlossen."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:527
+msgid "Scale the selected Gerber apertures ..."
+msgstr "Skalieren Sie die ausgewählten Gerber-Öffnungen ..."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:564
+msgid "Buffer the selected apertures ..."
+msgstr "Die ausgewählten Öffnungen puffern ..."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:660
+msgid "[success] Done. Apertures Move completed."
+msgstr "[success] Erledigt. Öffnungsbewegung abgeschlossen."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:710
+msgid "[success] Done. Apertures copied."
+msgstr "[success] Erledigt. Blende kopiert."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:833 flatcamGUI/FlatCAMGUI.py:1530
+msgid "Gerber Editor"
+msgstr "Gerber-Editor"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:852 flatcamGUI/ObjectUI.py:192
+msgid "<b>Apertures:</b>"
+msgstr "<b> Blenden: </ b>"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:854 flatcamGUI/ObjectUI.py:194
+msgid "Apertures Table for the Gerber Object."
+msgstr "Blendentabelle für das Gerberobjekt."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:865
+#: flatcamEditors/FlatCAMGrbEditor.py:1970 flatcamGUI/ObjectUI.py:228
+msgid "Code"
+msgstr "Code"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:865
+#: flatcamEditors/FlatCAMGrbEditor.py:1970 flatcamGUI/ObjectUI.py:228
+#: flatcamGUI/ObjectUI.py:888 flatcamGUI/ObjectUI.py:1446
+msgid "Type"
+msgstr "Typ"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:865
+#: flatcamEditors/FlatCAMGrbEditor.py:1970 flatcamGUI/ObjectUI.py:228
+msgid "Size"
+msgstr "Größe"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:865
+#: flatcamEditors/FlatCAMGrbEditor.py:1970 flatcamGUI/ObjectUI.py:228
+msgid "Dim"
+msgstr "Maße"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:869 flatcamGUI/ObjectUI.py:232
+msgid "Index"
+msgstr "Index"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:871 flatcamGUI/ObjectUI.py:234
+msgid "Aperture Code"
+msgstr "Öffnungscode"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:873 flatcamGUI/ObjectUI.py:236
+msgid "Type of aperture: circular, rectangle, macros etc"
+msgstr "Öffnungsart: kreisförmig, rechteckig, Makros usw"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:875
+#: flatcamEditors/FlatCAMGrbEditor.py:908 flatcamGUI/ObjectUI.py:238
+msgid "Aperture Size:"
+msgstr "Öffnungsgröße:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:877 flatcamGUI/ObjectUI.py:240
+msgid ""
+"Aperture Dimensions:\n"
+" - (width, height) for R, O type.\n"
+" - (dia, nVertices) for P type"
+msgstr ""
+"Blendenmaße:\n"
+"  - (Breite, Höhe) für R, O-Typ.\n"
+"  - (dia, nVertices) für P-Typ"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:898
+msgid "Aperture Code:"
+msgstr "Öffnungscode:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:900
+msgid "Code for the new aperture"
+msgstr "Code für die neue Blende"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:910
+msgid ""
+"Size for the new aperture.\n"
+"If aperture type is 'R' or 'O' then\n"
+"this value is automatically\n"
+"calculated as:\n"
+"sqrt(width**2 + height**2)"
+msgstr ""
+"Größe für die neue Blende.\n"
+"Wenn der Blendentyp 'R' oder 'O' ist, dann\n"
+"Dieser Wert wird automatisch übernommen\n"
+"berechnet als:\n"
+"Quadrat (Breite ** 2 + Höhe ** 2)"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:922
+msgid "Aperture Type:"
+msgstr "Blendentyp:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:924
+msgid ""
+"Select the type of new aperture. Can be:\n"
+"C = circular\n"
+"R = rectangular\n"
+"O = oblong"
+msgstr ""
+"Wählen Sie den Typ der neuen Blende. Kann sein:\n"
+"C = kreisförmig\n"
+"R = rechteckig\n"
+"O = länglich"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:935
+msgid "Aperture Dim:"
+msgstr "Öffnungsmaße:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:937
+msgid ""
+"Dimensions for the new aperture.\n"
+"Active only for rectangular apertures (type R).\n"
+"The format is (width, height)"
+msgstr ""
+"Abmessungen für die neue Blende.\n"
+"Aktiv nur für rechteckige Öffnungen (Typ R).\n"
+"Das Format ist (Breite, Höhe)"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:946
+msgid "Add Aperture:"
+msgstr "Blende hinzufügen:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:948
+msgid "Add an aperture to the aperture list"
+msgstr "Fügen Sie der Blendenliste eine Blende hinzu"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:952
+#: flatcamEditors/FlatCAMGrbEditor.py:965
+msgid "Go"
+msgstr "Gehen"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:954
+msgid "Add a new aperture to the aperture list."
+msgstr "Fügen Sie der Blendenliste eine neue Blende hinzu."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:958
+msgid "Del Aperture:"
+msgstr "Blende löschen:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:960
+msgid ""
+"Delete a aperture in the aperture list.\n"
+"It will delete also the associated geometry."
+msgstr ""
+"Löschen Sie eine Blende in der Blendenliste.\n"
+"Es wird auch die zugehörige Geometrie gelöscht."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:967
+msgid "Delete a aperture in the aperture list"
+msgstr "Löschen Sie eine Blende in der Blendenliste"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:982
+msgid "Buffer Aperture:"
+msgstr "Pufferblende:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:984
+msgid "Buffer a aperture in the aperture list"
+msgstr "Puffern Sie eine Blende in der Blendenliste"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:997
+msgid ""
+"There are 3 types of corners:\n"
+" - 'Round': the corner is rounded.\n"
+" - 'Square:' the corner is met in a sharp angle.\n"
+" - 'Beveled:' the corner is a line that directly connects the features "
+"meeting in the corner"
+msgstr ""
+"Es gibt 3 Arten von Ecken:\n"
+"  - 'Kreis': Die Ecke ist abgerundet.\n"
+"  - 'Quadrat:' Die Ecke wird in einem spitzen Winkel getroffen.\n"
+"  - 'Abgeschrägt:' Die Ecke ist eine Linie, die die Features, die sich in "
+"der Ecke treffen, direkt verbindet"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1012 flatcamGUI/FlatCAMGUI.py:695
+#: flatcamGUI/FlatCAMGUI.py:1867
+msgid "Buffer"
+msgstr "Puffer"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1026
+msgid "Scale Aperture:"
+msgstr "Skalenöffnung:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1028
+msgid "Scale a aperture in the aperture list"
+msgstr "Skalieren Sie eine Blende in der Blendenliste"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1036
+msgid "Scale factor:"
+msgstr "Skalierungsfaktor:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1038
+msgid ""
+"The factor by which to scale the selected aperture.\n"
+"Values can be between 0.0000 and 999.9999"
+msgstr ""
+"Der Faktor, um den die ausgewählte Blende skaliert werden soll.\n"
+"Die Werte können zwischen 0,0000 und 999,9999 liegen"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1066 flatcamGUI/FlatCAMGUI.py:690
+#: flatcamGUI/FlatCAMGUI.py:1862
+msgid "Add Pad Array"
+msgstr "Pad-Array hinzufügen"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1068
+msgid "Add an array of pads (linear or circular array)"
+msgstr "Hinzufügen eines Arrays von Pads (lineares oder kreisförmiges Array)"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1074
+msgid ""
+"Select the type of pads array to create.\n"
+"It can be Linear X(Y) or Circular"
+msgstr ""
+"Wählen Sie den zu erstellenden Pad-Array-Typ aus.\n"
+"Es kann lineares X (Y) oder rund sein"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1085
+msgid "Nr of pads:"
+msgstr "Anzahl der Pads:"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1087
+msgid "Specify how many pads to be in the array."
+msgstr "Geben Sie an, wie viele Pads sich im Array befinden sollen."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1536
+#: flatcamEditors/FlatCAMGrbEditor.py:1540
+msgid ""
+"[WARNING_NOTCL] Aperture code value is missing or wrong format. Add it and "
+"retry."
+msgstr ""
+"[WARNING_NOTCL] Blendencodewert fehlt oder falsches Format. Fügen Sie es "
+"hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1577
+msgid ""
+"[WARNING_NOTCL] Aperture dimensions value is missing or wrong format. Add it "
+"in format (width, height) and retry."
+msgstr ""
+"[WARNING_NOTCL] Wert für Blendenmaße fehlt oder falsches Format. Fügen Sie "
+"es im Format (Breite, Höhe) hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1589
+msgid ""
+"[WARNING_NOTCL] Aperture size value is missing or wrong format. Add it and "
+"retry."
+msgstr ""
+"[WARNING_NOTCL] Blendengrößenwert fehlt oder falsches Format. Fügen Sie es "
+"hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1601
+msgid "[WARNING_NOTCL] Aperture already in the aperture table."
+msgstr "[WARNING_NOTCL] Blende bereits in der Blendentabelle."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1608
+#, python-brace-format
+msgid "[success] Added new aperture with code: {apid}"
+msgstr "[success] Neue Blende mit Code hinzugefügt: {apid}"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1660
+#, python-brace-format
+msgid "[success] Deleted aperture with code: {del_dia}"
+msgstr "[success] Blende mit Code gelöscht: {del_dia}"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:1902
+#, python-format
+msgid "Adding aperture: %s geo ..."
+msgstr "Blende hinzufügen:%s geo ..."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2058
+msgid ""
+"[ERROR_NOTCL] There are no Aperture definitions in the file. Aborting Gerber "
+"creation."
+msgstr ""
+"[ERROR_NOTCL] Die Datei enthält keine Aperture-Definitionen. Abbruch der "
+"Gerber-Erstellung."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2067
+msgid "Creating Gerber."
+msgstr "Gerber erstellen."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2075
+msgid "[success] Gerber editing finished."
+msgstr "[success] Gerber-Bearbeitung ist beendet."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2092
+msgid "[WARNING_NOTCL] Cancelled. No aperture is selected"
+msgstr "[WARNING_NOTCL] Abgebrochen. Es ist keine Blende ausgewählt"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2555
+msgid "[success] Done. Apertures deleted."
+msgstr "[success] Erledigt. Blenden gelöscht"
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2683
+msgid ""
+"[WARNING_NOTCL] No aperture to buffer. Select at least one aperture and try "
+"again."
+msgstr ""
+"[WARNING_NOTCL] Keine Blende zum Puffern Wählen Sie mindestens eine Blende "
+"und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2712
+msgid ""
+"[WARNING_NOTCL] Scale factor value is missing or wrong format. Add it and "
+"retry."
+msgstr ""
+"[WARNING_NOTCL] Der Skalierungsfaktor ist nicht vorhanden oder das Format "
+"ist falsch. Fügen Sie es hinzu und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2730
+msgid ""
+"[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try "
+"again."
+msgstr ""
+"[WARNING_NOTCL] Keine zu skalierende Blende Wählen Sie mindestens eine "
+"Blende und versuchen Sie es erneut."
+
+#: flatcamEditors/FlatCAMGrbEditor.py:2746
+msgid "[success] Done. Scale Tool completed."
+msgstr "[success] Erledigt. Skalierungswerkzeug abgeschlossen."
+
+#: flatcamGUI/FlatCAMGUI.py:50
+msgid "&File"
+msgstr "&Datei"
+
+#: flatcamGUI/FlatCAMGUI.py:55
+msgid "&New Project ...\tCTRL+N"
+msgstr "&Neues Projekt ...\tCTRL+N"
+
+#: flatcamGUI/FlatCAMGUI.py:57
+msgid "Will create a new, blank project"
+msgstr "Erzeugt ein neues leeres Projekt"
+
+#: flatcamGUI/FlatCAMGUI.py:62
+msgid "&New"
+msgstr "&Neu"
+
+#: flatcamGUI/FlatCAMGUI.py:65
+msgid "Geometry\tN"
+msgstr "Geometrie\tN"
+
+#: flatcamGUI/FlatCAMGUI.py:67
+msgid "Will create a new, empty Geometry Object."
+msgstr "Erzeugt ein neues, leeres Geometrieobjekt."
+
+#: flatcamGUI/FlatCAMGUI.py:69
+msgid "Gerber\tB"
+msgstr "Gerber\tB"
+
+#: flatcamGUI/FlatCAMGUI.py:71
+msgid "Will create a new, empty Gerber Object."
+msgstr "Erzeugt ein neues, leeres Gerber-Objekt."
+
+#: flatcamGUI/FlatCAMGUI.py:73
+msgid "Excellon\tL"
+msgstr "Excellon\tL"
+
+#: flatcamGUI/FlatCAMGUI.py:75
+msgid "Will create a new, empty Excellon Object."
+msgstr "Erzeugt ein neues, leeres Excellon-Objekt."
+
+#: flatcamGUI/FlatCAMGUI.py:78
+msgid "Open"
+msgstr "Öffnen"
+
+#: flatcamGUI/FlatCAMGUI.py:83
+msgid "Open &Gerber ...\tCTRL+G"
+msgstr "Offen &Gerber ...\tCTRL+G"
+
+#: flatcamGUI/FlatCAMGUI.py:90
+msgid "Open &Excellon ...\tCTRL+E"
+msgstr "Offen &Excellon ...\tCTRL+E"
+
+#: flatcamGUI/FlatCAMGUI.py:95
+msgid "Open G-&Code ..."
+msgstr "Offen G-&Code ..."
+
+#: flatcamGUI/FlatCAMGUI.py:99
+msgid "Open &Project ..."
+msgstr "Offen &Projekt..."
+
+#: flatcamGUI/FlatCAMGUI.py:105
+msgid "Open Config ..."
+msgstr "Öffne Config ..."
+
+#: flatcamGUI/FlatCAMGUI.py:109
+msgid "Recent files"
+msgstr "Neueste Dateien"
+
+#: flatcamGUI/FlatCAMGUI.py:115
+msgid "Scripting"
+msgstr "Scripting"
+
+#: flatcamGUI/FlatCAMGUI.py:118
+msgid "New Script ..."
+msgstr "Neues Skript ..."
+
+#: flatcamGUI/FlatCAMGUI.py:120
+msgid "Open Script ..."
+msgstr "Skript öffnen ..."
+
+#: flatcamGUI/FlatCAMGUI.py:122
+msgid "Run Script ...\tSHIFT+S"
+msgstr "Skript ausführen ...\tSHIFT+S"
+
+#: flatcamGUI/FlatCAMGUI.py:125
+msgid ""
+"Will run the opened Tcl Script thus\n"
+"enabling the automation of certain\n"
+"functions of FlatCAM."
+msgstr ""
+"Läuft also das geöffnete Tcl-Skript\n"
+"Ermöglichung der Automatisierung bestimmter\n"
+"Funktionen von FlatCAM."
+
+#: flatcamGUI/FlatCAMGUI.py:138
+msgid "Import"
+msgstr "Importieren"
+
+#: flatcamGUI/FlatCAMGUI.py:140
+msgid "&SVG as Geometry Object ..."
+msgstr "&SVG als Geometrieobjekt ..."
+
+#: flatcamGUI/FlatCAMGUI.py:143
+msgid "&SVG as Gerber Object ..."
+msgstr "&SVG als Gerberobjekt ..."
+
+#: flatcamGUI/FlatCAMGUI.py:148
+msgid "&DXF as Geometry Object ..."
+msgstr "&DXF als Geometrieobjekt ..."
+
+#: flatcamGUI/FlatCAMGUI.py:151
+msgid "&DXF as Gerber Object ..."
+msgstr "&DXF als Gerberobjekt ..."
+
+#: flatcamGUI/FlatCAMGUI.py:156
+msgid "Export"
+msgstr "Ausführen"
+
+#: flatcamGUI/FlatCAMGUI.py:159
+msgid "Export &SVG ..."
+msgstr "SVG exportieren ..."
+
+#: flatcamGUI/FlatCAMGUI.py:162
+msgid "Export DXF ..."
+msgstr "DXF exportieren ..."
+
+#: flatcamGUI/FlatCAMGUI.py:167
+msgid "Export &PNG ..."
+msgstr "PNG exportieren ..."
+
+#: flatcamGUI/FlatCAMGUI.py:169
+msgid ""
+"Will export an image in PNG format,\n"
+"the saved image will contain the visual \n"
+"information currently in FlatCAM Plot Area."
+msgstr ""
+"Exportiert ein Bild im PNG-Format,\n"
+"Das gespeicherte Bild enthält das Bild\n"
+"Informationen derzeit im FlatCAM-Plotbereich."
+
+#: flatcamGUI/FlatCAMGUI.py:177
+msgid "Export &Excellon ..."
+msgstr "Excellon exportieren ..."
+
+#: flatcamGUI/FlatCAMGUI.py:180
+msgid ""
+"Will export an Excellon Object as Excellon file,\n"
+"the coordinates format, the file units and zeros\n"
+"are set in Preferences -> Excellon Export."
+msgstr ""
+"Exportieren Exportiert ein Excellon-Objekt als Excellon-Datei.\n"
+"Das Koordinatenformat, die Dateieinheiten und Nullen\n"
+"werden in den Einstellungen -> Excellon Export.Excellon eingestellt ..."
+
+#: flatcamGUI/FlatCAMGUI.py:191
+msgid "Save &Defaults"
+msgstr "Standardeinstellungen speichern"
+
+#: flatcamGUI/FlatCAMGUI.py:197 flatcamGUI/FlatCAMGUI.py:514
+msgid "Save"
+msgstr "Speichern"
+
+#: flatcamGUI/FlatCAMGUI.py:199
+msgid "&Save Project ..."
+msgstr "Projekt speichern ..."
+
+#: flatcamGUI/FlatCAMGUI.py:204
+msgid "Save Project &As ...\tCTRL+S"
+msgstr "Projekt speichern als ...\tCTRL+S"
+
+#: flatcamGUI/FlatCAMGUI.py:208
+msgid "Save Project C&opy ..."
+msgstr "Projektkopie speichern ..."
+
+#: flatcamGUI/FlatCAMGUI.py:216
+msgid "E&xit"
+msgstr "Ausgang"
+
+#: flatcamGUI/FlatCAMGUI.py:222
+msgid "&Edit"
+msgstr "Bearbeiten"
+
+#: flatcamGUI/FlatCAMGUI.py:225
+msgid "Edit Object\tE"
+msgstr "Objekt bearbeiten\tE"
+
+#: flatcamGUI/FlatCAMGUI.py:226
+msgid "Close Editor\tCTRL+S"
+msgstr "Schließen Sie Editor\tSTRG+S"
+
+#: flatcamGUI/FlatCAMGUI.py:234
+msgid "Conversion"
+msgstr "Umwandlung"
+
+#: flatcamGUI/FlatCAMGUI.py:236
+msgid "&Join Geo/Gerber/Exc -> Geo"
+msgstr "Beitreten Geo/Gerber/Exc -> Geo"
+
+#: flatcamGUI/FlatCAMGUI.py:238
+msgid ""
+"Merge a selection of objects, which can be of type:\n"
+"- Gerber\n"
+"- Excellon\n"
+"- Geometry\n"
+"into a new combo Geometry object."
+msgstr ""
+"Zusammenführen einer Auswahl von Objekten, die vom Typ sein können:\n"
+"- Gerber\n"
+"- Excellon\n"
+"- Geometrie\n"
+"in ein neues Geometrieobjekt kombinieren."
+
+#: flatcamGUI/FlatCAMGUI.py:245
+msgid "Join Excellon(s) -> Excellon"
+msgstr "Beitreten Excellon(s) -> Excellon"
+
+#: flatcamGUI/FlatCAMGUI.py:247
+msgid "Merge a selection of Excellon objects into a new combo Excellon object."
+msgstr ""
+"Fassen Sie eine Auswahl von Excellon-Objekten in einem neuen Excellon-Objekt "
+"zusammen."
+
+#: flatcamGUI/FlatCAMGUI.py:250
+msgid "Join Gerber(s) -> Gerber"
+msgstr "Beitreten Gerber(s) -> Gerber"
+
+#: flatcamGUI/FlatCAMGUI.py:252
+msgid "Merge a selection of Gerber objects into a new combo Gerber object."
+msgstr ""
+"Mischen Sie eine Auswahl von Gerber-Objekten in ein neues Gerber-"
+"Kombinationsobjekt."
+
+#: flatcamGUI/FlatCAMGUI.py:257
+msgid "Convert Single to MultiGeo"
+msgstr "Konvertieren Sie Single in MultiGeo"
+
+#: flatcamGUI/FlatCAMGUI.py:259
+msgid ""
+"Will convert a Geometry object from single_geometry type\n"
+"to a multi_geometry type."
+msgstr ""
+"Konvertiert ein Geometrieobjekt vom Typ single_geometry\n"
+"zu einem multi_geometry-Typ."
+
+#: flatcamGUI/FlatCAMGUI.py:263
+msgid "Convert Multi to SingleGeo"
+msgstr "Konvertieren Sie Multi in SingleGeo"
+
+#: flatcamGUI/FlatCAMGUI.py:265
+msgid ""
+"Will convert a Geometry object from multi_geometry type\n"
+"to a single_geometry type."
+msgstr ""
+"Konvertiert ein Geometrieobjekt vom Typ multi_geometry\n"
+"zu einem single_geometry-Typ."
+
+#: flatcamGUI/FlatCAMGUI.py:272
+msgid "&Copy Object\tCTRL+C"
+msgstr "Objekt kopieren\tSTRG+C"
+
+#: flatcamGUI/FlatCAMGUI.py:274
+msgid "Copy as &Geom"
+msgstr "Als Geom kopieren"
+
+#: flatcamGUI/FlatCAMGUI.py:277
+msgid "&Delete\tDEL"
+msgstr "Löschen\tDEL"
+
+#: flatcamGUI/FlatCAMGUI.py:281
+msgid "Se&t Origin\tO"
+msgstr "Ursprung festlegen\tO"
+
+#: flatcamGUI/FlatCAMGUI.py:282
+msgid "Jump to Location\tJ"
+msgstr "Zum Ort springen\tJ"
+
+#: flatcamGUI/FlatCAMGUI.py:287
+msgid "Toggle Units\tQ"
+msgstr "Einheiten umschalten\tQ"
+
+#: flatcamGUI/FlatCAMGUI.py:289
+msgid "&Select All\tCTRL+A"
+msgstr "Wählen Sie Alle\tSTRG+A"
+
+#: flatcamGUI/FlatCAMGUI.py:293
+msgid "&Preferences\tSHIFT+P"
+msgstr "Einstellungen\tSHIFT+P"
+
+#: flatcamGUI/FlatCAMGUI.py:296
+msgid "&Options"
+msgstr "&Optionen"
+
+#: flatcamGUI/FlatCAMGUI.py:311
+msgid "&Rotate Selection\tSHIFT+(R)"
+msgstr "Auswahl drehen\tSHIFT+(R)"
+
+#: flatcamGUI/FlatCAMGUI.py:316
+msgid "&Skew on X axis\tSHIFT+X"
+msgstr "Neigung auf der X-Achse\tSHIFT+X"
+
+#: flatcamGUI/FlatCAMGUI.py:318
+msgid "S&kew on Y axis\tSHIFT+Y"
+msgstr "Neigung auf der Y-Achse\tSHIFT+Y"
+
+#: flatcamGUI/FlatCAMGUI.py:323
+msgid "Flip on &X axis\tX"
+msgstr "X-Achse kippen\tX"
+
+#: flatcamGUI/FlatCAMGUI.py:325
+msgid "Flip on &Y axis\tY"
+msgstr "Y-Achse kippen\tY"
+
+#: flatcamGUI/FlatCAMGUI.py:330
+msgid "View source\tALT+S"
+msgstr "Quelltext anzeigen\tALT+S"
+
+#: flatcamGUI/FlatCAMGUI.py:335
+msgid "&View"
+msgstr "&Blick"
+
+#: flatcamGUI/FlatCAMGUI.py:336
+msgid "Enable all plots\tALT+1"
+msgstr "Aktivieren Sie alle Diagramme\tALT+1"
+
+#: flatcamGUI/FlatCAMGUI.py:338
+msgid "Disable all plots\tALT+2"
+msgstr "Deaktivieren Sie alle Diagramme\tALT+2"
+
+#: flatcamGUI/FlatCAMGUI.py:340
+msgid "Disable non-selected\tALT+3"
+msgstr "Deaktivieren Sie nicht ausgewählt\tALT+3"
+
+#: flatcamGUI/FlatCAMGUI.py:343
+msgid "&Zoom Fit\tV"
+msgstr "Zoomen passen\tV"
+
+#: flatcamGUI/FlatCAMGUI.py:344
+msgid "&Zoom In\t-"
+msgstr "Hineinzoomen\t-"
+
+#: flatcamGUI/FlatCAMGUI.py:345
+msgid "&Zoom Out\t="
+msgstr "Rauszoomen\t="
+
+#: flatcamGUI/FlatCAMGUI.py:349
+msgid "Toggle Code Editor\tCTRL+E"
+msgstr "Code-Editor umschalten\tSTRG+E"
+
+#: flatcamGUI/FlatCAMGUI.py:352
+msgid "&Toggle FullScreen\tALT+F10"
+msgstr "FullScreen umschalten\tALT+F10"
+
+#: flatcamGUI/FlatCAMGUI.py:354
+msgid "&Toggle Plot Area\tCTRL+F10"
+msgstr "Plotbereich umschalten\tSTRG+F10"
+
+#: flatcamGUI/FlatCAMGUI.py:356
+msgid "&Toggle Project/Sel/Tool\t`"
+msgstr "Projekt/Auswahl/Werkzeug umschalten\t`"
+
+#: flatcamGUI/FlatCAMGUI.py:359
+msgid "&Toggle Grid Snap\tG"
+msgstr "Schaltet den Rasterfang ein\tG"
+
+#: flatcamGUI/FlatCAMGUI.py:361
+msgid "&Toggle Axis\tSHIFT+G"
+msgstr "Achse umschalten\tSHIFT+G"
+
+#: flatcamGUI/FlatCAMGUI.py:364
+msgid "Toggle Workspace\tSHIFT+W"
+msgstr "Arbeitsbereich umschalten\tSHIFT+W"
+
+#: flatcamGUI/FlatCAMGUI.py:368
+msgid "&Tool"
+msgstr "Werkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:370
+msgid "&Command Line\tS"
+msgstr "Befehlszeile\tS"
+
+#: flatcamGUI/FlatCAMGUI.py:373
+msgid "&Help"
+msgstr "&Hilfe"
+
+#: flatcamGUI/FlatCAMGUI.py:374
+msgid "Help\tF1"
+msgstr "Hilfe\tF1"
+
+#: flatcamGUI/FlatCAMGUI.py:375
+msgid "FlatCAM.org"
+msgstr "FlatCAM.org"
+
+#: flatcamGUI/FlatCAMGUI.py:378
+msgid "Shortcuts List\tF3"
+msgstr "Tastenkürzel Liste\tF3"
+
+#: flatcamGUI/FlatCAMGUI.py:379
+msgid "YouTube Channel\tF4"
+msgstr "Youtube Kanal\tF4"
+
+#: flatcamGUI/FlatCAMGUI.py:381
+msgid "About"
+msgstr "Über"
+
+#: flatcamGUI/FlatCAMGUI.py:392
+msgid "Add Circle\tO"
+msgstr "Kreis hinzufügen\tO"
+
+#: flatcamGUI/FlatCAMGUI.py:394
+msgid "Add Arc\tA"
+msgstr "Bogen hinzufügen\tA"
+
+#: flatcamGUI/FlatCAMGUI.py:397
+msgid "Add Rectangle\tR"
+msgstr "Rechteck hinzufügen\tR"
+
+#: flatcamGUI/FlatCAMGUI.py:400
+msgid "Add Polygon\tN"
+msgstr "Polygon hinzufügen\tN"
+
+#: flatcamGUI/FlatCAMGUI.py:402
+msgid "Add Path\tP"
+msgstr "Pfad hinzufügen\tP"
+
+#: flatcamGUI/FlatCAMGUI.py:404
+msgid "Add Text\tT"
+msgstr "Text hinzufügen\tT"
+
+#: flatcamGUI/FlatCAMGUI.py:407
+msgid "Polygon Union\tU"
+msgstr "Polygon-Vereinigung\tU"
+
+#: flatcamGUI/FlatCAMGUI.py:409
+msgid "Polygon Intersection\tE"
+msgstr "Polygonschnitt\tE"
+
+#: flatcamGUI/FlatCAMGUI.py:411
+msgid "Polygon Subtraction\tS"
+msgstr "Polygon-Subtraktion\tS"
+
+#: flatcamGUI/FlatCAMGUI.py:415
+msgid "Cut Path\tX"
+msgstr "Pfad ausschneiden\tX"
+
+#: flatcamGUI/FlatCAMGUI.py:417
+msgid "Copy Geom\tC"
+msgstr "Geometrie kopieren\tC"
+
+#: flatcamGUI/FlatCAMGUI.py:419
+msgid "Delete Shape\tDEL"
+msgstr "Form löschen\tDEL"
+
+#: flatcamGUI/FlatCAMGUI.py:422 flatcamGUI/FlatCAMGUI.py:489
+msgid "Move\tM"
+msgstr "Bewegung\tM"
+
+#: flatcamGUI/FlatCAMGUI.py:424
+msgid "Buffer Tool\tB"
+msgstr "Pufferwerkzeug\tB"
+
+#: flatcamGUI/FlatCAMGUI.py:427
+msgid "Paint Tool\tI"
+msgstr "Malenwerkzeug\tI"
+
+#: flatcamGUI/FlatCAMGUI.py:430
+msgid "Transform Tool\tALT+R"
+msgstr "Transformationswerkzeug\tALT+R"
+
+#: flatcamGUI/FlatCAMGUI.py:434
+msgid "Toggle Corner Snap\tK"
+msgstr "Eckfang umschalten\tK"
+
+#: flatcamGUI/FlatCAMGUI.py:437
+msgid ">Excellon Editor<"
+msgstr ">Excellon Editor<"
+
+#: flatcamGUI/FlatCAMGUI.py:441
+msgid "Add Drill Array\tA"
+msgstr "Bohrfeld hinzufügen\tA"
+
+#: flatcamGUI/FlatCAMGUI.py:443
+msgid "Add Drill\tD"
+msgstr "Bohrer hinzufügen\tD"
+
+#: flatcamGUI/FlatCAMGUI.py:447
+msgid "Resize Drill(S)\tR"
+msgstr "Bohrer verkleinern\tR"
+
+#: flatcamGUI/FlatCAMGUI.py:449 flatcamGUI/FlatCAMGUI.py:482
+msgid "Copy\tC"
+msgstr "Kopieren\tC"
+
+#: flatcamGUI/FlatCAMGUI.py:451 flatcamGUI/FlatCAMGUI.py:484
+msgid "Delete\tDEL"
+msgstr "Löschen\tDEL"
+
+#: flatcamGUI/FlatCAMGUI.py:456
+msgid "Move Drill(s)\tM"
+msgstr "Bohrer verschieben\tM"
+
+#: flatcamGUI/FlatCAMGUI.py:460
+msgid ">Gerber Editor<"
+msgstr ">Gerber-Editor<"
+
+#: flatcamGUI/FlatCAMGUI.py:464
+msgid "Add Pad\tP"
+msgstr "Pad hinzufügen\tP"
+
+#: flatcamGUI/FlatCAMGUI.py:466
+msgid "Add Pad Array\tA"
+msgstr "Pad-Array hinzufügen\tA"
+
+#: flatcamGUI/FlatCAMGUI.py:468
+msgid "Add Track\tT"
+msgstr "Track hinzufügen\tA"
+
+#: flatcamGUI/FlatCAMGUI.py:470
+msgid "Add Region\tN"
+msgstr "Region hinzufügen\tN"
+
+#: flatcamGUI/FlatCAMGUI.py:474
+msgid "Buffer\tB"
+msgstr "Puffer\tB"
+
+#: flatcamGUI/FlatCAMGUI.py:476
+msgid "Scale\tS"
+msgstr "Skalieren\tS"
+
+#: flatcamGUI/FlatCAMGUI.py:478
+msgid "Transform\tALT+R"
+msgstr "Transformationswerkzeug\tSTRG+R"
+
+#: flatcamGUI/FlatCAMGUI.py:505
+msgid "Enable Plot"
+msgstr "Diagramm aktivieren"
+
+#: flatcamGUI/FlatCAMGUI.py:506
+msgid "Disable Plot"
+msgstr "Diagramm deaktivieren"
+
+#: flatcamGUI/FlatCAMGUI.py:508
+msgid "Generate CNC"
+msgstr "CNC generieren"
+
+#: flatcamGUI/FlatCAMGUI.py:509
+msgid "View Source"
+msgstr "Quelltext anzeigen"
+
+#: flatcamGUI/FlatCAMGUI.py:511 flatcamGUI/FlatCAMGUI.py:1548
+msgid "Edit"
+msgstr "Bearbeiten"
+
+#: flatcamGUI/FlatCAMGUI.py:517 flatcamGUI/FlatCAMGUI.py:1554
+#: flatcamTools/ToolProperties.py:25
+msgid "Properties"
+msgstr "Eigenschaften"
+
+#: flatcamGUI/FlatCAMGUI.py:546
+msgid "File Toolbar"
+msgstr "Dateisymbolleiste"
+
+#: flatcamGUI/FlatCAMGUI.py:550
+msgid "Edit Toolbar"
+msgstr "Symbolleiste bearbeiten"
+
+#: flatcamGUI/FlatCAMGUI.py:554
+msgid "View Toolbar"
+msgstr "Symbolleiste anzeigen"
+
+#: flatcamGUI/FlatCAMGUI.py:558
+msgid "Shell Toolbar"
+msgstr "Shell-Symbolleiste"
+
+#: flatcamGUI/FlatCAMGUI.py:562
+msgid "Tools Toolbar"
+msgstr "Werkzeugleiste"
+
+#: flatcamGUI/FlatCAMGUI.py:566
+msgid "Excellon Editor Toolbar"
+msgstr "Excellon Editor-Symbolleiste"
+
+#: flatcamGUI/FlatCAMGUI.py:570
+msgid "Geometry Editor Toolbar"
+msgstr "Geometrie Editor-Symbolleiste"
+
+#: flatcamGUI/FlatCAMGUI.py:574
+msgid "Gerber Editor Toolbar"
+msgstr "Gerber Editor-Symbolleiste"
+
+#: flatcamGUI/FlatCAMGUI.py:578
+msgid "Grid Toolbar"
+msgstr "Raster-Symbolleiste"
+
+#: flatcamGUI/FlatCAMGUI.py:597 flatcamGUI/FlatCAMGUI.py:1765
+msgid "Open project"
+msgstr "Offenes Projekt"
+
+#: flatcamGUI/FlatCAMGUI.py:598 flatcamGUI/FlatCAMGUI.py:1766
+msgid "Save project"
+msgstr "Projekt speichern"
+
+#: flatcamGUI/FlatCAMGUI.py:601 flatcamGUI/FlatCAMGUI.py:1769
+msgid "New Blank Geometry"
+msgstr "Neue leere Geometrie"
+
+#: flatcamGUI/FlatCAMGUI.py:602
+msgid "New Blank Gerber"
+msgstr "Neue leere Gerber"
+
+#: flatcamGUI/FlatCAMGUI.py:603 flatcamGUI/FlatCAMGUI.py:1770
+msgid "New Blank Excellon"
+msgstr "Neuer unbelegter Excellon"
+
+#: flatcamGUI/FlatCAMGUI.py:605 flatcamGUI/FlatCAMGUI.py:1772
+msgid "Editor"
+msgstr "Editor"
+
+#: flatcamGUI/FlatCAMGUI.py:607 flatcamGUI/FlatCAMGUI.py:1774
+msgid "Save Object and close the Editor"
+msgstr "Speichern Sie das Objekt und schließen Sie den Editor"
+
+#: flatcamGUI/FlatCAMGUI.py:611 flatcamGUI/FlatCAMGUI.py:1778
+msgid "&Delete"
+msgstr "&Löschen"
+
+#: flatcamGUI/FlatCAMGUI.py:614 flatcamGUI/FlatCAMGUI.py:1781
+msgid "&Replot"
+msgstr "&Replotieren"
+
+#: flatcamGUI/FlatCAMGUI.py:615 flatcamGUI/FlatCAMGUI.py:1782
+msgid "&Clear plot"
+msgstr "&Plot klar löschen"
+
+#: flatcamGUI/FlatCAMGUI.py:616 flatcamGUI/FlatCAMGUI.py:1783
+msgid "Zoom In"
+msgstr "Hineinzoomen"
+
+#: flatcamGUI/FlatCAMGUI.py:617 flatcamGUI/FlatCAMGUI.py:1784
+msgid "Zoom Out"
+msgstr "Rauszoomen"
+
+#: flatcamGUI/FlatCAMGUI.py:618 flatcamGUI/FlatCAMGUI.py:1518
+#: flatcamGUI/FlatCAMGUI.py:1785
+msgid "Zoom Fit"
+msgstr "Passenzoomen"
+
+#: flatcamGUI/FlatCAMGUI.py:623 flatcamGUI/FlatCAMGUI.py:1790
+msgid "&Command Line"
+msgstr "Befehlszeile"
+
+#: flatcamGUI/FlatCAMGUI.py:626 flatcamGUI/FlatCAMGUI.py:1793
+msgid "2Sided Tool"
+msgstr "2Seitiges Werkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:627 flatcamGUI/FlatCAMGUI.py:1794
+msgid "&Cutout Tool"
+msgstr "Ausschnittwerkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:628 flatcamGUI/FlatCAMGUI.py:1795
+#: flatcamGUI/ObjectUI.py:392 flatcamTools/ToolNonCopperClear.py:284
+msgid "NCC Tool"
+msgstr "NCC Werkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:632 flatcamGUI/FlatCAMGUI.py:1799
+msgid "Panel Tool"
+msgstr "Platte Werkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:633 flatcamGUI/FlatCAMGUI.py:1800
+#: flatcamTools/ToolFilm.py:204
+msgid "Film Tool"
+msgstr "Filmwerkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:634 flatcamGUI/FlatCAMGUI.py:1802
+msgid "SolderPaste Tool"
+msgstr "Lötpaste-Werkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:637 flatcamGUI/FlatCAMGUI.py:1806
+msgid "Calculators Tool"
+msgstr "Rechnerwerkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:641 flatcamGUI/FlatCAMGUI.py:655
+#: flatcamGUI/FlatCAMGUI.py:688 flatcamGUI/FlatCAMGUI.py:1810
+#: flatcamGUI/FlatCAMGUI.py:1860
+msgid "Select"
+msgstr "Wählen"
+
+#: flatcamGUI/FlatCAMGUI.py:642 flatcamGUI/FlatCAMGUI.py:1811
+msgid "Add Drill Hole"
+msgstr "Bohrloch hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:644 flatcamGUI/FlatCAMGUI.py:1813
+msgid "Add Drill Hole Array"
+msgstr "Bohrlochfeld hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:645 flatcamGUI/FlatCAMGUI.py:1814
+msgid "Resize Drill"
+msgstr "Bohrergröße ändern"
+
+#: flatcamGUI/FlatCAMGUI.py:648 flatcamGUI/FlatCAMGUI.py:1817
+msgid "Copy Drill"
+msgstr "Bohrer kopieren"
+
+#: flatcamGUI/FlatCAMGUI.py:649 flatcamGUI/FlatCAMGUI.py:1819
+msgid "Delete Drill"
+msgstr "Bohrer löschen"
+
+#: flatcamGUI/FlatCAMGUI.py:652 flatcamGUI/FlatCAMGUI.py:1822
+msgid "Move Drill"
+msgstr "Bohrer bewegen"
+
+#: flatcamGUI/FlatCAMGUI.py:656 flatcamGUI/FlatCAMGUI.py:1826
+msgid "Add Circle"
+msgstr "Kreis hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:657 flatcamGUI/FlatCAMGUI.py:1827
+msgid "Add Arc"
+msgstr "Bogen hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:659 flatcamGUI/FlatCAMGUI.py:1829
+msgid "Add Rectangle"
+msgstr "Rechteck hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:662 flatcamGUI/FlatCAMGUI.py:1832
+msgid "Add Path"
+msgstr "Pfad hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:663 flatcamGUI/FlatCAMGUI.py:1834
+msgid "Add Polygon"
+msgstr "Polygon hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:665 flatcamGUI/FlatCAMGUI.py:1836
+msgid "Add Text"
+msgstr "Text hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:666 flatcamGUI/FlatCAMGUI.py:1838
+msgid "Add Buffer"
+msgstr "Puffer hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:667 flatcamGUI/FlatCAMGUI.py:1839
+msgid "Paint Shape"
+msgstr "Malen Form"
+
+#: flatcamGUI/FlatCAMGUI.py:670 flatcamGUI/FlatCAMGUI.py:1842
+msgid "Polygon Union"
+msgstr "Polygon-Vereinigung"
+
+#: flatcamGUI/FlatCAMGUI.py:672 flatcamGUI/FlatCAMGUI.py:1844
+msgid "Polygon Intersection"
+msgstr "Polygonschnitt"
+
+#: flatcamGUI/FlatCAMGUI.py:674 flatcamGUI/FlatCAMGUI.py:1846
+msgid "Polygon Subtraction"
+msgstr "Polygon-Subtraktion"
+
+#: flatcamGUI/FlatCAMGUI.py:677 flatcamGUI/FlatCAMGUI.py:1849
+msgid "Cut Path"
+msgstr "Pfad ausschneiden"
+
+#: flatcamGUI/FlatCAMGUI.py:678
+msgid "Copy Shape(s)"
+msgstr "Form kopieren"
+
+#: flatcamGUI/FlatCAMGUI.py:681
+msgid "Delete Shape '-'"
+msgstr "Form löschen"
+
+#: flatcamGUI/FlatCAMGUI.py:683 flatcamGUI/FlatCAMGUI.py:702
+#: flatcamGUI/FlatCAMGUI.py:1854 flatcamGUI/FlatCAMGUI.py:1874
+msgid "Transformations"
+msgstr "Transformationen"
+
+#: flatcamGUI/FlatCAMGUI.py:685
+msgid "Move Objects "
+msgstr "Objekte verschieben "
+
+#: flatcamGUI/FlatCAMGUI.py:689 flatcamGUI/FlatCAMGUI.py:1861
+msgid "Add Pad"
+msgstr "Pad hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:691 flatcamGUI/FlatCAMGUI.py:1863
+msgid "Add Track"
+msgstr "Track hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:692 flatcamGUI/FlatCAMGUI.py:1864
+msgid "Add Region"
+msgstr "Region hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:704 flatcamGUI/FlatCAMGUI.py:1528
+#: flatcamGUI/FlatCAMGUI.py:1538 flatcamGUI/FlatCAMGUI.py:1553
+#: flatcamGUI/FlatCAMGUI.py:1876 flatcamTools/ToolMove.py:26
+msgid "Move"
+msgstr "Bewegung"
+
+#: flatcamGUI/FlatCAMGUI.py:710 flatcamGUI/FlatCAMGUI.py:1882
+msgid "Snap to grid"
+msgstr "Am Raster ausrichten"
+
+#: flatcamGUI/FlatCAMGUI.py:713 flatcamGUI/FlatCAMGUI.py:1885
+msgid "Grid X snapping distance"
+msgstr "Raster X Fangdistanz"
+
+#: flatcamGUI/FlatCAMGUI.py:718 flatcamGUI/FlatCAMGUI.py:1890
+msgid "Grid Y snapping distance"
+msgstr "Raster Y Fangdistanz"
+
+#: flatcamGUI/FlatCAMGUI.py:724 flatcamGUI/FlatCAMGUI.py:1896
+msgid ""
+"When active, value on Grid_X\n"
+"is copied to the Grid_Y value."
+msgstr ""
+"Wenn aktiv, Wert auf Grid_X\n"
+"wird in den Wert von Grid_Y kopiert."
+
+#: flatcamGUI/FlatCAMGUI.py:730 flatcamGUI/FlatCAMGUI.py:1902
+msgid "Snap to corner"
+msgstr "In der Ecke ausrichten"
+
+#: flatcamGUI/FlatCAMGUI.py:734 flatcamGUI/FlatCAMGUI.py:1906
+#: flatcamGUI/FlatCAMGUI.py:3197
+msgid "Max. magnet distance"
+msgstr "Max. Magnetabstand"
+
+#: flatcamGUI/FlatCAMGUI.py:748 flatcamGUI/FlatCAMGUI.py:1512
+msgid "Project"
+msgstr "Projekt"
+
+#: flatcamGUI/FlatCAMGUI.py:757
+msgid "Selected"
+msgstr "Ausgewählt"
+
+#: flatcamGUI/FlatCAMGUI.py:776 flatcamGUI/FlatCAMGUI.py:784
+msgid "Plot Area"
+msgstr "Grundstücksfläche"
+
+#: flatcamGUI/FlatCAMGUI.py:808
+msgid "General"
+msgstr "Allgemeines"
+
+#: flatcamGUI/FlatCAMGUI.py:817
+msgid "APP.  DEFAULTS"
+msgstr "Anwendungsvorgaben"
+
+#: flatcamGUI/FlatCAMGUI.py:818
+msgid "PROJ. OPTIONS "
+msgstr "Projektoptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:829
+msgid "GERBER"
+msgstr "GERBER"
+
+#: flatcamGUI/FlatCAMGUI.py:838
+msgid "EXCELLON"
+msgstr "EXCELLON"
+
+#: flatcamGUI/FlatCAMGUI.py:847
+msgid "GEOMETRY"
+msgstr "GEOMETRY"
+
+#: flatcamGUI/FlatCAMGUI.py:857
+msgid "CNC-JOB"
+msgstr "CNC-Auftrag"
+
+#: flatcamGUI/FlatCAMGUI.py:866
+msgid "TOOLS"
+msgstr "WERKZEUGE"
+
+#: flatcamGUI/FlatCAMGUI.py:883
+msgid "Import Preferences"
+msgstr "Importeinstellungen"
+
+#: flatcamGUI/FlatCAMGUI.py:886
+msgid ""
+"Import a full set of FlatCAM settings from a file\n"
+"previously saved on HDD.\n"
+"\n"
+"FlatCAM automatically save a 'factory_defaults' file\n"
+"on the first start. Do not delete that file."
+msgstr ""
+"Importieren Sie einen vollständigen Satz von FlatCAM-Einstellungen aus einer "
+"Datei\n"
+"zuvor auf der Festplatte gespeichert.\n"
+"\n"
+"FlatCAM speichert automatisch eine 'factory_defaults'-Datei\n"
+"beim ersten Start. Löschen Sie diese Datei nicht."
+
+#: flatcamGUI/FlatCAMGUI.py:893
+msgid "Export Preferences"
+msgstr "Voreinstellungen exportieren"
+
+#: flatcamGUI/FlatCAMGUI.py:896
+msgid ""
+"Export a full set of FlatCAM settings in a file\n"
+"that is saved on HDD."
+msgstr ""
+"Exportieren Sie einen vollständigen Satz von FlatCAM-Einstellungen in eine "
+"Datei\n"
+"das ist auf der Festplatte gespeichert."
+
+#: flatcamGUI/FlatCAMGUI.py:901
+msgid "Open Pref Folder"
+msgstr "Öffnen Sie den Ordner \"Einstellungen\""
+
+#: flatcamGUI/FlatCAMGUI.py:904
+msgid "Open the folder where FlatCAM save the preferences files."
+msgstr ""
+"Öffnen Sie den Ordner, in dem FlatCAM die Voreinstellungsdateien speichert."
+
+#: flatcamGUI/FlatCAMGUI.py:912
+msgid "Save Preferences"
+msgstr "Voreinstellungen speichern"
+
+#: flatcamGUI/FlatCAMGUI.py:915
+msgid ""
+"Save the current settings in the 'current_defaults' file\n"
+"which is the file storing the working default preferences."
+msgstr ""
+"Speichern Sie die aktuellen Einstellungen in der Datei 'current_defaults'\n"
+"Dies ist die Datei, in der die Arbeitseinstellungen gespeichert sind."
+
+#: flatcamGUI/FlatCAMGUI.py:941
+msgid ""
+"<b>General Shortcut list</b><br>\n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>F3</strong></"
+"td>\n"
+"                        <td width=\"194\"><span style=\"color:"
+"#006400\"><strong>&nbsp;SHOW SHORTCUT LIST</strong></span></td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>1</strong></td>\n"
+"                        <td>&nbsp;Switch to Project Tab</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>2</strong></td>\n"
+"                        <td>&nbsp;Switch to Selected Tab</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>3</strong></td>\n"
+"                        <td>&nbsp;Switch to Tool Tab</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>E</strong></td>\n"
+"                        <td>&nbsp;Edit Object (if selected)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>G</strong></td>\n"
+"                        <td>&nbsp;Grid On/Off</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Jump to Coordinates</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>L</strong></td>\n"
+"                        <td>&nbsp;New Excellon</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Move Obj</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>N</strong></td>\n"
+"                        <td>&nbsp;New Geometry</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>O</strong></td>\n"
+"                        <td>&nbsp;Set Origin</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Q</strong></td>\n"
+"                        <td>&nbsp;Change Units</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>P</strong></td>\n"
+"                        <td>&nbsp;Open Properties Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>R</strong></td>\n"
+"                        <td>&nbsp;Rotate by 90 degree CW</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>S</strong></td>\n"
+"                        <td>&nbsp;Shell Toggle</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Add a Tool (when in Geometry Selected Tab "
+"or in Tools NCC or Tools Paint)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>V</strong></td>\n"
+"                        <td>&nbsp;Zoom Fit</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>X</strong></td>\n"
+"                        <td>&nbsp;Flip on X_axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Y</strong></td>\n"
+"                        <td>&nbsp;Flip on Y_axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>&#39;=&#39;</strong></td>\n"
+"                        <td>&nbsp;Zoom Out</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>&#39;-&#39;</strong></td>\n"
+"                        <td>&nbsp;Zoom In</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+A</strong></td>\n"
+"                        <td>&nbsp;Select All</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+C</strong></td>\n"
+"                        <td>&nbsp;Copy Obj</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+E</strong></td>\n"
+"                        <td>&nbsp;Open Excellon File</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+G</strong></td>\n"
+"                        <td>&nbsp;Open Gerber File</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+N</strong></td>\n"
+"                        <td>&nbsp;New Project</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+M</strong></td>\n"
+"                        <td>&nbsp;Measurement Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+O</strong></td>\n"
+"                        <td>&nbsp;Open Project</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+"                        <td>&nbsp;Save Project As</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+F10</strong></td>\n"
+"                        <td>&nbsp;Toggle Plot Area</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+C</strong></td>\n"
+"                        <td>&nbsp;Copy Obj_Name</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+E</strong></td>\n"
+"                        <td>&nbsp;Toggle Code Editor</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+G</strong></td>\n"
+"                        <td>&nbsp;Toggle the axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+P</strong></td>\n"
+"                        <td>&nbsp;Open Preferences Window</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+R</strong></td>\n"
+"                        <td>&nbsp;Rotate by 90 degree CCW</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+S</strong></td>\n"
+"                        <td>&nbsp;Run a Script</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+W</strong></td>\n"
+"                        <td>&nbsp;Toggle the workspace</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+X</strong></td>\n"
+"                        <td>&nbsp;Skew on X axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+Y</strong></td>\n"
+"                        <td>&nbsp;Skew on Y axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+C</strong></td>\n"
+"                        <td>&nbsp;Calculators Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+D</strong></td>\n"
+"                        <td>&nbsp;2-Sided PCB Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+K</strong></td>\n"
+"                        <td>&nbsp;Solder Paste Dispensing Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+L</strong></td>\n"
+"                        <td>&nbsp;Film PCB Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+N</strong></td>\n"
+"                        <td>&nbsp;Non-Copper Clearing Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+P</strong></td>\n"
+"                        <td>&nbsp;Paint Area Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+"                        <td>&nbsp;Transformations Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+S</strong></td>\n"
+"                        <td>&nbsp;View File Source</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+U</strong></td>\n"
+"                        <td>&nbsp;Cutout PCB Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+1</strong></td>\n"
+"                        <td>&nbsp;Enable all Plots</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+2</strong></td>\n"
+"                        <td>&nbsp;Disable all Plots</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+3</strong></td>\n"
+"                        <td>&nbsp;Disable Non-selected Plots</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+F10</strong></td>\n"
+"                        <td>&nbsp;Toggle Full Screen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>F1</strong></td>\n"
+"                        <td>&nbsp;Open Online Manual</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>F4</strong></td>\n"
+"                        <td>&nbsp;Open Online Tutorials</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Delete Object</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Alternate: Delete Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>'`'</strong></td>\n"
+"                        <td>&nbsp;(left to Key_1)Toogle Notebook Area (Left "
+"Side)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SPACE</strong></td>\n"
+"                        <td>&nbsp;En(Dis)able Obj Plot</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Escape</strong></td>\n"
+"                        <td>&nbsp;Deselects all objects</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"    \n"
+"            "
+msgstr ""
+"<b>Allgemeine Shortcut-Liste</b><br>\n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>F3</strong></"
+"td>\n"
+"                        <td width=\"194\"><span style=\"color:"
+"#006400\"><strong>&nbsp;SHOWCUT-LISTE ANZEIGEN</strong></span></td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>1</strong></td>\n"
+"                        <td>&nbsp;Wechseln Sie zur Registerkarte Projekt</"
+"td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>2</strong></td>\n"
+"                        <td>&nbsp;Wechseln Sie zur ausgewählten "
+"Registerkarte</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>3</strong></td>\n"
+"                        <td>&nbsp;Wechseln Sie zur Registerkarte \"Tool\"</"
+"td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>E</strong></td>\n"
+"                        <td>&nbsp;Objekt bearbeiten (falls ausgewählt)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>G</strong></td>\n"
+"                        <td>&nbsp;Netz ein/aus</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Zu den Koordinaten springen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>L</strong></td>\n"
+"                        <td>&nbsp;Neue Excellon</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Objekt verschieben</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>N</strong></td>\n"
+"                        <td>&nbsp;Neue Geometrie</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>O</strong></td>\n"
+"                        <td>&nbsp;Ursprung festlegen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Q</strong></td>\n"
+"                        <td>&nbsp;Einheiten ändern</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>P</strong></td>\n"
+"                        <td>&nbsp;Öffnen Sie das Eigenschaftenwerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>R</strong></td>\n"
+"                        <td>&nbsp;Um 90 Grad nach links drehen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>S</strong></td>\n"
+"                        <td>&nbsp;Shell umschalten</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Hinzufügen eines Werkzeugs (auf der "
+"Registerkarte \"Geometrie\" oder in \"NCC Werkzeuge\" oder \"Paint Werkzeuge"
+"\")</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>V</strong></td>\n"
+"                        <td>&nbsp;Zoomen passen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>X</strong></td>\n"
+"                        <td>&nbsp;X-Achse kippen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Y</strong></td>\n"
+"                        <td>&nbsp;Y-Achse kippen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>&#39;=&#39;</strong></td>\n"
+"                        <td>&nbsp;Rauszoomen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>&#39;-&#39;</strong></td>\n"
+"                        <td>&nbsp;Hineinzoomen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+A</strong></td>\n"
+"                        <td>&nbsp;Wählen Sie Alle</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+C</strong></td>\n"
+"                        <td>&nbsp;Objekt kopieren</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+E</strong></td>\n"
+"                        <td>&nbsp;Excellon-Datei öffnen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+G</strong></td>\n"
+"                        <td>&nbsp;Gerber-Datei öffnen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+N</strong></td>\n"
+"                        <td>&nbsp;Neues Projekt</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+M</strong></td>\n"
+"                        <td>&nbsp;Messwerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+O</strong></td>\n"
+"                        <td>&nbsp;Offenes Projekt</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+S</strong></td>\n"
+"                        <td>&nbsp;Projekt speichern als</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+F10</strong></td>\n"
+"                        <td>&nbsp;Plotbereich umschalten</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+C</strong></td>\n"
+"                        <td>&nbsp;Objektnamen kopieren</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+E</strong></td>\n"
+"                        <td>&nbsp;Code-Editor umschalten</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+G</strong></td>\n"
+"                        <td>&nbsp;Toggle the axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+P</strong></td>\n"
+"                        <td>&nbsp;Öffnen Sie das Einstellungsfenster</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+R</strong></td>\n"
+"                        <td>&nbsp;Um 90 Grad nach links drehen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+S</strong></td>\n"
+"                        <td>&nbsp;Führen Sie ein Skript aus</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+W</strong></td>\n"
+"                        <td>&nbsp;Arbeitsbereich umschalten</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+X</strong></td>\n"
+"                        <td>&nbsp;Neigung auf der X-Achse</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+Y</strong></td>\n"
+"                        <td>&nbsp;Neigung auf der Y-Achse</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+C</strong></td>\n"
+"                        <td>&nbsp;Rechnerwerzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+D</strong></td>\n"
+"                        <td>&nbsp;2-seitiges PCBwerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+K</strong></td>\n"
+"                        <td>&nbsp;Lötpastenwerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+L</strong></td>\n"
+"                        <td>&nbsp;Film PCB Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+N</strong></td>\n"
+"                        <td>&nbsp;Nicht-Kupfer löschen Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+P</strong></td>\n"
+"                        <td>&nbsp;Paint Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+"                        <td>&nbsp;Transformationen\" Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+S</strong></td>\n"
+"                        <td>&nbsp;Dateiquelle anzeigen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+U</strong></td>\n"
+"                        <td>&nbsp;PCB-Werkzeug ausschneiden</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+1</strong></td>\n"
+"                        <td>&nbsp;Alle Plots aktivieren</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+2</strong></td>\n"
+"                        <td>&nbsp;Deaktivieren Sie alle Plots</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+3</strong></td>\n"
+"                        <td>&nbsp;Deaktivieren Sie nicht ausgewählte Plots</"
+"td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+F10</strong></td>\n"
+"                        <td>&nbsp;Vollbild umschalten</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>F1</strong></td>\n"
+"                        <td>&nbsp;Online-Handbuch öffnen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>F4</strong></td>\n"
+"                        <td>&nbsp;Online-Tutorials öffnen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Objekt löschen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Alternativ: Werkzeug löschen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>'`'</strong></td>\n"
+"                        <td>&nbsp;(links zu Taste 1) Notebookbereich "
+"umschalten (linke Seite)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Leertaste</strong></td>\n"
+"                        <td>&nbsp;En (dis) fähiges Obj-Diagramm</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ESC</strong></td>\n"
+"                        <td>&nbsp;Hebt die Auswahl aller Objekte auf</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"    \n"
+"            "
+
+#: flatcamGUI/FlatCAMGUI.py:1218
+msgid ""
+"<b>Editor Shortcut list</b><br>\n"
+"            <br>\n"
+"            <strong><span style=\"color:#0000ff\">GEOMETRY EDITOR</span></"
+"strong><br>\n"
+"    \n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>A</strong></"
+"td>\n"
+"                        <td width=\"194\">&nbsp;Draw an Arc</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>B</strong></td>\n"
+"                        <td>&nbsp;Buffer Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>C</strong></td>\n"
+"                        <td>&nbsp;Copy Geo Item</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>E</strong></td>\n"
+"                        <td>&nbsp;Polygon Intersection Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>I</strong></td>\n"
+"                        <td>&nbsp;Paint Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Jump to Location (x, y)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>K</strong></td>\n"
+"                        <td>&nbsp;Toggle Corner Snap</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Move Geo Item</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>N</strong></td>\n"
+"                        <td>&nbsp;Draw a Polygon</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>O</strong></td>\n"
+"                        <td>&nbsp;Draw a Circle</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>P</strong></td>\n"
+"                        <td>&nbsp;Draw a Path</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>R</strong></td>\n"
+"                        <td>&nbsp;Draw Rectangle</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>S</strong></td>\n"
+"                        <td>&nbsp;Polygon Substraction Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Add Text Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>U</strong></td>\n"
+"                        <td>&nbsp;Polygon Union Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>X</strong></td>\n"
+"                        <td>&nbsp;Flip shape on X axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Y</strong></td>\n"
+"                        <td>&nbsp;Flip shape on Y axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+X</strong></td>\n"
+"                        <td>&nbsp;Skew shape on X axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+Y</strong></td>\n"
+"                        <td>&nbsp;Skew shape on Y axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+"                        <td>&nbsp;Editor Transformation Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+X</strong></td>\n"
+"                        <td>&nbsp;Offset shape on X axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+Y</strong></td>\n"
+"                        <td>&nbsp;Offset shape on Y axis</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+M</strong></td>\n"
+"                        <td>&nbsp;Measurement Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+"                        <td>&nbsp;Save Object and Exit Editor</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+X</strong></td>\n"
+"                        <td>&nbsp;Polygon Cut Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Space</strong></td>\n"
+"                        <td>&nbsp;Rotate Geometry</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ENTER</strong></td>\n"
+"                        <td>&nbsp;Finish drawing for certain tools</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ESC</strong></td>\n"
+"                        <td>&nbsp;Abort and return to Select</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Delete Shape</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"            <br>\n"
+"            <br>\n"
+"            <strong><span style=\"color:#ff0000\">EXCELLON EDITOR</span></"
+"strong><br>\n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>A</strong></"
+"td>\n"
+"                        <td width=\"194\">&nbsp;Add Drill Array</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>C</strong></td>\n"
+"                        <td>&nbsp;Copy Drill(s)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>D</strong></td>\n"
+"                        <td>&nbsp;Add Drill</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Jump to Location (x, y)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Move Drill(s)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>R</strong></td>\n"
+"                        <td>&nbsp;Resize Drill(s)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Add a new Tool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Delete Drill(s)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Alternate: Delete Tool(s)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ESC</strong></td>\n"
+"                        <td>&nbsp;Abort and return to Select</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+"                        <td>&nbsp;Save Object and Exit Editor</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"            <br>\n"
+"            <br>\n"
+"            <strong><span style=\"color:#00ff00\">GERBER EDITOR</span></"
+"strong><br>\n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>A</strong></"
+"td>\n"
+"                        <td width=\"194\">&nbsp;Add Pad Array</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>B</strong></td>\n"
+"                        <td>&nbsp;Buffer</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>C</strong></td>\n"
+"                        <td>&nbsp;Copy</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Jump to Location (x, y)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Move</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>N</strong></td>\n"
+"                        <td>&nbsp;Add Region</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>P</strong></td>\n"
+"                        <td>&nbsp;Add Pad</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>S</strong></td>\n"
+"                        <td>&nbsp;Scale</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Add Track</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Delete</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Alternate: Delete Apertures</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ESC</strong></td>\n"
+"                        <td>&nbsp;Abort and return to Select</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+"                        <td>&nbsp;Save Object and Exit Editor</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+"                        <td>&nbsp;Transformation Tool</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"                    "
+msgstr ""
+"<b>Liste der Editor-Verknüpfungen</b><br>\n"
+"            <br>\n"
+"            <strong><span style=\"color:#0000ff\">GEOMETRIE-HERAUSGEBER</"
+"span></strong><br>\n"
+"    \n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>A</strong></"
+"td>\n"
+"                        <td width=\"194\">&nbsp;Zeichne einen Bogen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>B</strong></td>\n"
+"                        <td>&nbsp;Pufferwerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>C</strong></td>\n"
+"                        <td>&nbsp;Geo-Element kopieren</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>E</strong></td>\n"
+"                        <td>&nbsp;Polygonschnitt-Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>I</strong></td>\n"
+"                        <td>&nbsp;Paint Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Zum Standort springen (x, y)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>K</strong></td>\n"
+"                        <td>&nbsp;Eckfang umschalten</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Geo-Element verschieben</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>N</strong></td>\n"
+"                        <td>&nbsp;Zeichnen Sie ein Polygon</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>O</strong></td>\n"
+"                        <td>&nbsp;Zeichne einen Kreise</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>P</strong></td>\n"
+"                        <td>&nbsp;Zeichne einen Pfad</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>R</strong></td>\n"
+"                        <td>&nbsp;Rechteck zeichnen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>S</strong></td>\n"
+"                        <td>&nbsp;Polygon-Subtraktionswerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Textwerkzeug hinzufügen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>U</strong></td>\n"
+"                        <td>&nbsp;Polygon-Union-Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>X</strong></td>\n"
+"                        <td>&nbsp;Form auf X-Achse kippen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Y</strong></td>\n"
+"                        <td>&nbsp;Form auf Y-Achse kippen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+X</strong></td>\n"
+"                        <td>&nbsp;Neigung auf der X-Achse</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>SHIFT+Y</strong></td>\n"
+"                        <td>&nbsp;Neigung auf der Y-Achse</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+"                        <td>&nbsp;Editor-Umwandlungstool</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+X</strong></td>\n"
+"                        <td>&nbsp;Versatzform auf der X-Achse</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+Y</strong></td>\n"
+"                        <td>&nbsp;Versatzform auf der Y-Achse</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+M</strong></td>\n"
+"                        <td>&nbsp;Messwerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+S</strong></td>\n"
+"                        <td>&nbsp;Editor schließen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+X</strong></td>\n"
+"                        <td>&nbsp;Polygon-Schneidewerkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Leertaste</strong></td>\n"
+"                        <td>&nbsp;Geometrie drehen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Enter-Taste</strong></td>\n"
+"                        <td>&nbsp;Beenden Sie das Zeichnen für bestimmte "
+"Werkzeuge</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ESC</strong></td>\n"
+"                        <td>&nbsp;Abbrechen und zurück zu Auswahl</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Form löschen</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"            <br>\n"
+"            <br>\n"
+"            <strong><span style=\"color:#ff0000\">EXCELLON EDITOR</span></"
+"strong><br>\n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>A</strong></"
+"td>\n"
+"                        <td width=\"194\">&nbsp;Bohrer-Array hinzufügen</"
+"td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>C</strong></td>\n"
+"                        <td>&nbsp;Bohrer kopieren</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>D</strong></td>\n"
+"                        <td>&nbsp;Bohrer hinzufügen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Zum Standort springen (x, y)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Bohrer bewegen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>R</strong></td>\n"
+"                        <td>&nbsp;Größe der Bohrer ändern</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Fügen Sie ein neues Tool hinzu</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Bohrer löschen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Alternative: Werkzeug löschen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ESC</strong></td>\n"
+"                        <td>&nbsp;Abbrechen und zurück zu Auswahl</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+S</strong></td>\n"
+"                        <td>&nbsp;Editor schließen</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"            <br>\n"
+"            <br>\n"
+"            <strong><span style=\"color:#00ff00\">GERBER EDITOR</span></"
+"strong><br>\n"
+"            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+"\"width:283px\">\n"
+"                <tbody>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\" width=\"89\"><strong>A</strong></"
+"td>\n"
+"                        <td width=\"194\">&nbsp;Pad-Array hinzufügen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>B</strong></td>\n"
+"                        <td>&nbsp;Puffer Werkzeug</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>C</strong></td>\n"
+"                        <td>&nbsp;Kopieren</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>J</strong></td>\n"
+"                        <td>&nbsp;Zum Standort springen (x, y)</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>M</strong></td>\n"
+"                        <td>&nbsp;Bewegung</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>N</strong></td>\n"
+"                        <td>&nbsp;Region hinzufügen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>P</strong></td>\n"
+"                        <td>&nbsp;Pad hinzufügen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>S</strong></td>\n"
+"                        <td>&nbsp;Skalieren</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>T</strong></td>\n"
+"                        <td>&nbsp;Track hinzufügen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Löschen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>Del</strong></td>\n"
+"                        <td>&nbsp;Alternativ: Blenden löschen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ESC</strong></td>\n"
+"                        <td>&nbsp;Abbrechen und zurück zu Auswahl</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>STRG+S</strong></td>\n"
+"                        <td>&nbsp;Editor schließen</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\">&nbsp;</td>\n"
+"                        <td>&nbsp;</td>\n"
+"                    </tr>\n"
+"                    <tr height=\"20\">\n"
+"                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+"                        <td>&nbsp;Transformations Werkzeug</td>\n"
+"                    </tr>\n"
+"                </tbody>\n"
+"            </table>\n"
+"                    "
+
+#: flatcamGUI/FlatCAMGUI.py:1506
+msgid "Disable"
+msgstr "Deaktivieren"
+
+#: flatcamGUI/FlatCAMGUI.py:1508
+msgid "New"
+msgstr "Neu"
+
+#: flatcamGUI/FlatCAMGUI.py:1509
+msgid "Geometry"
+msgstr "Geometrie"
+
+#: flatcamGUI/FlatCAMGUI.py:1510
+msgid "Excellon"
+msgstr "Excellon"
+
+#: flatcamGUI/FlatCAMGUI.py:1515
+msgid "Grids"
+msgstr "Raster"
+
+#: flatcamGUI/FlatCAMGUI.py:1517
+msgid "View"
+msgstr "Aussicht"
+
+#: flatcamGUI/FlatCAMGUI.py:1519
+msgid "Clear Plot"
+msgstr "Plot klar löschen"
+
+#: flatcamGUI/FlatCAMGUI.py:1520
+msgid "Replot"
+msgstr "Replotieren"
+
+#: flatcamGUI/FlatCAMGUI.py:1523
+msgid "Geo Editor"
+msgstr "Geo-Editor"
+
+#: flatcamGUI/FlatCAMGUI.py:1524
+msgid "Line"
+msgstr "Linie"
+
+#: flatcamGUI/FlatCAMGUI.py:1525
+msgid "Rectangle"
+msgstr "Rechteck"
+
+#: flatcamGUI/FlatCAMGUI.py:1526 flatcamGUI/FlatCAMGUI.py:5021
+#: flatcamGUI/ObjectUI.py:1360
+msgid "Cut"
+msgstr "Schnitt"
+
+#: flatcamGUI/FlatCAMGUI.py:1531
+msgid "Pad"
+msgstr "Pad"
+
+#: flatcamGUI/FlatCAMGUI.py:1532
+msgid "Pad Array"
+msgstr "Pad-Array"
+
+#: flatcamGUI/FlatCAMGUI.py:1533
+msgid "Track"
+msgstr "Track"
+
+#: flatcamGUI/FlatCAMGUI.py:1534
+msgid "Region"
+msgstr "Region"
+
+#: flatcamGUI/FlatCAMGUI.py:1540
+msgid "Exc Editor"
+msgstr "Exc-Editor"
+
+#: flatcamGUI/FlatCAMGUI.py:1541
+msgid "Add Drill"
+msgstr "Bohrer hinzufügen"
+
+#: flatcamGUI/FlatCAMGUI.py:1543
+msgid "Copy Drill(s)"
+msgstr "Bohrer kopieren"
+
+#: flatcamGUI/FlatCAMGUI.py:1574
+msgid "Print Preview"
+msgstr "Druckvorschau"
+
+#: flatcamGUI/FlatCAMGUI.py:1575
+msgid "Print Code"
+msgstr "Code drucken"
+
+#: flatcamGUI/FlatCAMGUI.py:1576
+msgid "Find in Code"
+msgstr "Im Code suchen"
+
+#: flatcamGUI/FlatCAMGUI.py:1581
+msgid "Replace With"
+msgstr "Ersetzen mit"
+
+#: flatcamGUI/FlatCAMGUI.py:1585 flatcamGUI/FlatCAMGUI.py:5019
+#: flatcamGUI/FlatCAMGUI.py:5529 flatcamGUI/ObjectUI.py:1358
+msgid "All"
+msgstr "Alles"
+
+#: flatcamGUI/FlatCAMGUI.py:1587
+msgid ""
+"When checked it will replace all instances in the 'Find' box\n"
+"with the text in the 'Replace' box.."
+msgstr ""
+"Wenn diese Option aktiviert ist, werden alle Instanzen im Feld \"Suchen\" "
+"ersetzt\n"
+"mit dem Text im Feld \"Ersetzen\" .."
+
+#: flatcamGUI/FlatCAMGUI.py:1590
+msgid "Open Code"
+msgstr "Code öffnen"
+
+#: flatcamGUI/FlatCAMGUI.py:1591
+msgid "Save Code"
+msgstr "Code speichern"
+
+#: flatcamGUI/FlatCAMGUI.py:1626
+msgid ""
+"Relative neasurement.\n"
+"Reference is last click position"
+msgstr ""
+"Relative Messung\n"
+"Referenz ist Position des letzten Klicks"
+
+#: flatcamGUI/FlatCAMGUI.py:1632
+msgid ""
+"Absolute neasurement.\n"
+"Reference is (X=0, Y= 0) position"
+msgstr ""
+"Absolute Messung.\n"
+"Referenz ist (X = 0, Y = 0)"
+
+#: flatcamGUI/FlatCAMGUI.py:1825
+msgid "Select 'Esc'"
+msgstr "Wählen"
+
+#: flatcamGUI/FlatCAMGUI.py:1850
+msgid "Copy Objects"
+msgstr "Objekte kopieren"
+
+#: flatcamGUI/FlatCAMGUI.py:1852
+msgid "Delete Shape"
+msgstr "Form löschen"
+
+#: flatcamGUI/FlatCAMGUI.py:1857
+msgid "Move Objects"
+msgstr "Objekte verschieben"
+
+#: flatcamGUI/FlatCAMGUI.py:2266
+msgid ""
+"Please first select a geometry item to be cutted\n"
+"then select the geometry item that will be cutted\n"
+"out of the first item. In the end press ~X~ key or\n"
+"the toolbar button."
+msgstr ""
+"Bitte wählen Sie zuerst ein zu schneidendes Geometrieelement aus\n"
+"Wählen Sie dann das Geometrieelement aus, das geschnitten werden soll\n"
+"aus dem ersten Artikel. Zum Schluss drücken Sie die Taste ~ X ~ oder\n"
+"die Symbolleisten-Schaltfläche."
+
+#: flatcamGUI/FlatCAMGUI.py:2273 flatcamGUI/FlatCAMGUI.py:2405
+#: flatcamGUI/FlatCAMGUI.py:2464 flatcamGUI/FlatCAMGUI.py:2484
+msgid "Warning"
+msgstr "Warnung"
+
+#: flatcamGUI/FlatCAMGUI.py:2340 flatcamGUI/FlatCAMGUI.py:2537
+#: flatcamGUI/FlatCAMGUI.py:2735
+msgid "[WARNING_NOTCL] Cancelled."
+msgstr "[WARNING_NOTCL] Abgebrochen."
+
+#: flatcamGUI/FlatCAMGUI.py:2400
+msgid ""
+"Please select geometry items \n"
+"on which to perform Intersection Tool."
+msgstr ""
+"Bitte wählen Sie Geometrieelemente aus\n"
+"auf dem das Verschneidungswerkzeug ausgeführt werden soll."
+
+#: flatcamGUI/FlatCAMGUI.py:2459
+msgid ""
+"Please select geometry items \n"
+"on which to perform Substraction Tool."
+msgstr ""
+"Bitte wählen Sie Geometrieelemente aus\n"
+"auf dem das Subtraktionswerkzeug ausgeführt werden soll."
+
+#: flatcamGUI/FlatCAMGUI.py:2479
+msgid ""
+"Please select geometry items \n"
+"on which to perform union."
+msgstr ""
+"Bitte wählen Sie Geometrieelemente aus\n"
+"auf dem die Polygonverbindung ausgeführt werden soll."
+
+#: flatcamGUI/FlatCAMGUI.py:2552 flatcamGUI/FlatCAMGUI.py:2752
+msgid "[WARNING_NOTCL] Cancelled. Nothing selected to delete."
+msgstr "[WARNING_NOTCL] Abgebrochen. Nichts zum Löschen ausgewählt."
+
+#: flatcamGUI/FlatCAMGUI.py:2629 flatcamGUI/FlatCAMGUI.py:2819
+msgid "[WARNING_NOTCL] Cancelled. Nothing selected to copy."
+msgstr "[WARNING_NOTCL] Abgebrochen. Nichts zum Kopieren ausgewählt."
+
+#: flatcamGUI/FlatCAMGUI.py:2663 flatcamGUI/FlatCAMGUI.py:2865
+msgid "[WARNING_NOTCL] Cancelled. Nothing selected to move."
+msgstr "[WARNING_NOTCL] Abgebrochen. Nichts ausgewählt, um sich zu bewegen."
+
+#: flatcamGUI/FlatCAMGUI.py:2879
+msgid "New Tool ..."
+msgstr "Neues Werkzeug ..."
+
+#: flatcamGUI/FlatCAMGUI.py:2880
+msgid "Enter a Tool Diameter:"
+msgstr "Geben Sie einen Werkzeugdurchmesser ein:"
+
+#: flatcamGUI/FlatCAMGUI.py:3182
+msgid "Grid X value:"
+msgstr "Raster X-Wert:"
+
+#: flatcamGUI/FlatCAMGUI.py:3184
+msgid "This is the Grid snap value on X axis."
+msgstr "Dies ist der Rasterfangwert auf der X-Achse."
+
+#: flatcamGUI/FlatCAMGUI.py:3189
+msgid "Grid Y value:"
+msgstr "Raster Y-Wert:"
+
+#: flatcamGUI/FlatCAMGUI.py:3191
+msgid "This is the Grid snap value on Y axis."
+msgstr "Dies ist der Rasterfangwert auf der Y-Achse."
+
+#: flatcamGUI/FlatCAMGUI.py:3196
+msgid "Snap Max:"
+msgstr "Maximalwert:"
+
+#: flatcamGUI/FlatCAMGUI.py:3201
+msgid "Workspace:"
+msgstr "Arbeitsplatz:"
+
+#: flatcamGUI/FlatCAMGUI.py:3203
+msgid ""
+"Draw a delimiting rectangle on canvas.\n"
+"The purpose is to illustrate the limits for our work."
+msgstr ""
+"Zeichnen Sie ein begrenzendes Rechteck auf die Leinwand.\n"
+"Ziel ist es, die Grenzen unserer Arbeit aufzuzeigen."
+
+#: flatcamGUI/FlatCAMGUI.py:3206
+msgid "Wk. format:"
+msgstr "Arbeitsbereichformat:"
+
+#: flatcamGUI/FlatCAMGUI.py:3208
+msgid ""
+"Select the type of rectangle to be used on canvas,\n"
+"as valid workspace."
+msgstr ""
+"Wählen Sie den Typ des Rechtecks für die Leinwand aus.\n"
+"als gültiger Arbeitsbereich."
+
+#: flatcamGUI/FlatCAMGUI.py:3221
+msgid "Plot Fill:"
+msgstr "Plot füllen:"
+
+#: flatcamGUI/FlatCAMGUI.py:3223
+msgid ""
+"Set the fill color for plotted objects.\n"
+"First 6 digits are the color and the last 2\n"
+"digits are for alpha (transparency) level."
+msgstr ""
+"Legen Sie die Füllfarbe für geplottete Objekte fest.\n"
+"Die ersten 6 Ziffern sind die Farbe und die letzten 2\n"
+"Ziffern sind für Alpha (Transparenz)."
+
+#: flatcamGUI/FlatCAMGUI.py:3237 flatcamGUI/FlatCAMGUI.py:3287
+#: flatcamGUI/FlatCAMGUI.py:3337
+msgid "Alpha Level:"
+msgstr "Alpha-Ebene:"
+
+#: flatcamGUI/FlatCAMGUI.py:3239
+msgid "Set the fill transparency for plotted objects."
+msgstr "Legen Sie die Füllungstransparenz für geplottete Objekte fest."
+
+#: flatcamGUI/FlatCAMGUI.py:3256
+msgid "Plot Line:"
+msgstr "Handlungsstrang:"
+
+#: flatcamGUI/FlatCAMGUI.py:3258
+msgid "Set the line color for plotted objects."
+msgstr "Legen Sie die Linienfarbe für geplottete Objekte fest."
+
+#: flatcamGUI/FlatCAMGUI.py:3270
+msgid "Sel. Fill:"
+msgstr "Ausgewählte Füllung:"
+
+#: flatcamGUI/FlatCAMGUI.py:3272
+msgid ""
+"Set the fill color for the selection box\n"
+"in case that the selection is done from left to right.\n"
+"First 6 digits are the color and the last 2\n"
+"digits are for alpha (transparency) level."
+msgstr ""
+"Legen Sie die Füllfarbe für das Auswahlfeld fest\n"
+"falls die Auswahl von links nach rechts erfolgt.\n"
+"Die ersten 6 Ziffern sind die Farbe und die letzten 2\n"
+"Ziffern sind für Alpha (Transparenz)."
+
+#: flatcamGUI/FlatCAMGUI.py:3289
+msgid "Set the fill transparency for the 'left to right' selection box."
+msgstr ""
+"Legen Sie die Füllungstransparenz für das Auswahlfeld \"von links nach rechts"
+"\" fest."
+
+#: flatcamGUI/FlatCAMGUI.py:3306
+msgid "Sel. Line:"
+msgstr "Auswahlzeile:"
+
+#: flatcamGUI/FlatCAMGUI.py:3308
+msgid "Set the line color for the 'left to right' selection box."
+msgstr ""
+"Legen Sie die Linienfarbe für das Auswahlfeld \"von links nach rechts\" fest."
+
+#: flatcamGUI/FlatCAMGUI.py:3320
+msgid "Sel2. Fill:"
+msgstr "Auswahl2 Füllung:"
+
+#: flatcamGUI/FlatCAMGUI.py:3322
+msgid ""
+"Set the fill color for the selection box\n"
+"in case that the selection is done from right to left.\n"
+"First 6 digits are the color and the last 2\n"
+"digits are for alpha (transparency) level."
+msgstr ""
+"Legen Sie die Füllfarbe für das Auswahlfeld fest\n"
+"falls die Auswahl von rechts nach links erfolgt.\n"
+"Die ersten 6 Ziffern sind die Farbe und die letzten 2\n"
+"Ziffern sind für Alpha (Transparenz)."
+
+#: flatcamGUI/FlatCAMGUI.py:3339
+msgid "Set the fill transparency for selection 'right to left' box."
+msgstr ""
+"Legen Sie die Füllungstransparenz für die Auswahl von rechts nach links fest."
+
+#: flatcamGUI/FlatCAMGUI.py:3356
+msgid "Sel2. Line:"
+msgstr "Auswahl 2 Zeile:"
+
+#: flatcamGUI/FlatCAMGUI.py:3358
+msgid "Set the line color for the 'right to left' selection box."
+msgstr ""
+"Legen Sie die Linienfarbe für das Auswahlfeld 'von rechts nach links' fest."
+
+#: flatcamGUI/FlatCAMGUI.py:3370
+msgid "Editor Draw:"
+msgstr "Editor zeichnen:"
+
+#: flatcamGUI/FlatCAMGUI.py:3372
+msgid "Set the color for the shape."
+msgstr "Legen Sie die Farbe für die Form fest."
+
+#: flatcamGUI/FlatCAMGUI.py:3384
+msgid "Editor Draw Sel.:"
+msgstr "Editor Draw Sel.:"
+
+#: flatcamGUI/FlatCAMGUI.py:3386
+msgid "Set the color of the shape when selected."
+msgstr "Legt die Farbe der Form fest, wenn sie ausgewählt wird."
+
+#: flatcamGUI/FlatCAMGUI.py:3433
+msgid "GUI Settings"
+msgstr "GUI-Einstellungen"
+
+#: flatcamGUI/FlatCAMGUI.py:3440
+msgid "Layout:"
+msgstr "Layout:"
+
+#: flatcamGUI/FlatCAMGUI.py:3442
+msgid ""
+"Select an layout for FlatCAM.\n"
+"It is applied immediately."
+msgstr ""
+"Wählen Sie ein Layout für FlatCAM.\n"
+"Es wird sofort angewendet."
+
+#: flatcamGUI/FlatCAMGUI.py:3458
+msgid "Style:"
+msgstr "Stil:"
+
+#: flatcamGUI/FlatCAMGUI.py:3460
+msgid ""
+"Select an style for FlatCAM.\n"
+"It will be applied at the next app start."
+msgstr ""
+"Wählen Sie einen Stil für FlatCAM.\n"
+"Es wird beim nächsten Start der App angewendet."
+
+#: flatcamGUI/FlatCAMGUI.py:3471
+msgid "HDPI Support:"
+msgstr "HDPI-Unterstützung:"
+
+#: flatcamGUI/FlatCAMGUI.py:3473
+msgid ""
+"Enable High DPI support for FlatCAM.\n"
+"It will be applied at the next app start."
+msgstr ""
+"Aktivieren Sie die High DPI-Unterstützung für FlatCAM.\n"
+"Es wird beim nächsten Start der App angewendet."
+
+#: flatcamGUI/FlatCAMGUI.py:3486
+msgid "Clear GUI Settings:"
+msgstr "GUI-Einstellungen löschen:"
+
+#: flatcamGUI/FlatCAMGUI.py:3488
+msgid ""
+"Clear the GUI settings for FlatCAM,\n"
+"such as: layout, gui state, style, hdpi support etc."
+msgstr ""
+"Löschen Sie die GUI-Einstellungen für FlatCAM.\n"
+"wie zum Beispiel: Layout, GUI-Status, Stil, HDPI-Unterstützung usw."
+
+#: flatcamGUI/FlatCAMGUI.py:3491
+msgid "Clear"
+msgstr "Klären"
+
+#: flatcamGUI/FlatCAMGUI.py:3495
+msgid "Hover Shape:"
+msgstr "Schwebeflug-Form:"
+
+#: flatcamGUI/FlatCAMGUI.py:3497
+msgid ""
+"Enable display of a hover shape for FlatCAM objects.\n"
+"It is displayed whenever the mouse cursor is hovering\n"
+"over any kind of not-selected object."
+msgstr ""
+"Anzeige der Hover-Form für FlatCAM-Objekte aktivieren.\n"
+"Es wird angezeigt, wenn sich der Mauszeiger in der Maus befindet\n"
+"über jede Art von nicht ausgewähltem Objekt."
+
+#: flatcamGUI/FlatCAMGUI.py:3537
+msgid "Are you sure you want to delete the GUI Settings? \n"
+msgstr "Möchten Sie die GUI-Einstellungen wirklich löschen?\n"
+
+#: flatcamGUI/FlatCAMGUI.py:3540
+msgid "Clear GUI Settings"
+msgstr "Löschen Sie die GUI-Einstellungen"
+
+#: flatcamGUI/FlatCAMGUI.py:3561
+msgid "App Preferences"
+msgstr "App-Einstellungen"
+
+#: flatcamGUI/FlatCAMGUI.py:3567
+msgid "<b>Units:</b>"
+msgstr "<b>Einheiten:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3568
+msgid ""
+"The default value for FlatCAM units.\n"
+"Whatever is selected here is set every time\n"
+"FLatCAM is started."
+msgstr ""
+"Der Standardwert für FlatCAM-Einheiten.\n"
+"Was hier ausgewählt wird, wird jedes Mal eingestellt\n"
+"FLatCAM wird gestartet."
+
+#: flatcamGUI/FlatCAMGUI.py:3575
+msgid "<b>APP. LEVEL:</b>"
+msgstr "<b>Bewerbungsebene:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3576
+msgid ""
+"Choose the default level of usage for FlatCAM.\n"
+"BASIC level -> reduced functionality, best for beginner's.\n"
+"ADVANCED level -> full functionality.\n"
+"\n"
+"The choice here will influence the parameters in\n"
+"the Selected Tab for all kinds of FlatCAM objects."
+msgstr ""
+"Wählen Sie die Standardbenutzungsstufe für FlatCAM.\n"
+"BASIC-Level -> reduzierte Funktionalität, am besten für Anfänger.\n"
+"ERWEITERTE Stufe -> volle Funktionalität.\n"
+"\n"
+"Die Auswahl hier beeinflusst die Parameter in\n"
+"Die Registerkarte Ausgewählt für alle Arten von FlatCAM-Objekten."
+
+#: flatcamGUI/FlatCAMGUI.py:3581 flatcamGUI/FlatCAMGUI.py:4206
+msgid "Basic"
+msgstr "Basic"
+
+#: flatcamGUI/FlatCAMGUI.py:3582
+msgid "Advanced"
+msgstr "Erweitert"
+
+#: flatcamGUI/FlatCAMGUI.py:3585
+msgid "<b>Languages:</b>"
+msgstr "<b>Sprachen:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3586
+msgid "Set the language used throughout FlatCAM."
+msgstr "Stellen Sie die Sprache ein, die in FlatCAM verwendet wird."
+
+#: flatcamGUI/FlatCAMGUI.py:3589
+msgid "Apply Language"
+msgstr "Sprache anwenden"
+
+#: flatcamGUI/FlatCAMGUI.py:3592
+msgid "Shell at StartUp:"
+msgstr "Shell beim Start:"
+
+#: flatcamGUI/FlatCAMGUI.py:3594 flatcamGUI/FlatCAMGUI.py:3599
+msgid ""
+"Check this box if you want the shell to\n"
+"start automatically at startup."
+msgstr ""
+"Aktivieren Sie dieses Kontrollkästchen, wenn die Shell gewünscht wird\n"
+"automatisch beim Start starten"
+
+#: flatcamGUI/FlatCAMGUI.py:3604
+msgid "Version Check:"
+msgstr "Versionsprüfung:"
+
+#: flatcamGUI/FlatCAMGUI.py:3606 flatcamGUI/FlatCAMGUI.py:3611
+msgid ""
+"Check this box if you want to check\n"
+"for a new version automatically at startup."
+msgstr ""
+"Aktivieren Sie dieses Kontrollkästchen,\n"
+"wenn Sie das Kontrollkästchen aktivieren möchten\n"
+"für eine neue Version automatisch beim Start."
+
+#: flatcamGUI/FlatCAMGUI.py:3616
+msgid "Send Stats:"
+msgstr "Statistiken senden:"
+
+#: flatcamGUI/FlatCAMGUI.py:3618 flatcamGUI/FlatCAMGUI.py:3623
+msgid ""
+"Check this box if you agree to send anonymous\n"
+"stats automatically at startup, to help improve FlatCAM."
+msgstr ""
+"Aktivieren Sie dieses Kontrollkästchen, wenn Sie der anonymen Nachricht "
+"zustimmen\n"
+"wird beim Start automatisch aktualisiert, um FlatCAM zu verbessern."
+
+#: flatcamGUI/FlatCAMGUI.py:3630
+msgid "<b>Pan Button:</b>"
+msgstr "<b>Pan-Taste:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3631
+msgid ""
+"Select the mouse button to use for panning:\n"
+"- MMB --> Middle Mouse Button\n"
+"- RMB --> Right Mouse Button"
+msgstr ""
+"Wählen Sie die Maustaste aus, die Sie zum Verschieben verwenden möchten:\n"
+"- MMB -> Mittlere Maustaste\n"
+"- RMB -> Rechte Maustaste"
+
+#: flatcamGUI/FlatCAMGUI.py:3634
+msgid "MMB"
+msgstr "MMB"
+
+#: flatcamGUI/FlatCAMGUI.py:3635
+msgid "RMB"
+msgstr "RMB"
+
+#: flatcamGUI/FlatCAMGUI.py:3638
+msgid "<b>Multiple Sel:</b>"
+msgstr "<b>Mehrfachauswahl:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3639
+msgid "Select the key used for multiple selection."
+msgstr "Wählen Sie den Schlüssel für die Mehrfachauswahl aus."
+
+#: flatcamGUI/FlatCAMGUI.py:3640
+msgid "CTRL"
+msgstr "CTRL"
+
+#: flatcamGUI/FlatCAMGUI.py:3641
+msgid "SHIFT"
+msgstr "SHIFT"
+
+#: flatcamGUI/FlatCAMGUI.py:3644
+msgid "Project at StartUp:"
+msgstr "Projekt beim Start:"
+
+#: flatcamGUI/FlatCAMGUI.py:3646 flatcamGUI/FlatCAMGUI.py:3651
+msgid ""
+"Check this box if you want the project/selected/tool tab area to\n"
+"to be shown automatically at startup."
+msgstr ""
+"Aktivieren Sie dieses Kontrollkästchen, wenn der\n"
+"Bereich Projekt / Ausgewähltes / Werkzeugregister\n"
+"angezeigt werden soll\n"
+"beim Start automatisch angezeigt werden."
+
+#: flatcamGUI/FlatCAMGUI.py:3656
+msgid "Project AutoHide:"
+msgstr "Projekt autoausblenden:"
+
+#: flatcamGUI/FlatCAMGUI.py:3658 flatcamGUI/FlatCAMGUI.py:3664
+msgid ""
+"Check this box if you want the project/selected/tool tab area to\n"
+"hide automatically when there are no objects loaded and\n"
+"to show whenever a new object is created."
+msgstr ""
+"Aktivieren Sie dieses Kontrollkästchen, wenn \n"
+"der Bereich Projekt / Ausgewähltes / Werkzeugregister \n"
+"angezeigt werden soll automatisch ausblenden, wenn \n"
+"keine Objekte geladen sind und anzeigen, wenn ein \n"
+"neues Objekt erstellt wird."
+
+#: flatcamGUI/FlatCAMGUI.py:3670
+msgid "<b>Enable ToolTips:</b>"
+msgstr "<b> QuickInfos aktivieren: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3672 flatcamGUI/FlatCAMGUI.py:3677
+msgid ""
+"Check this box if you want to have toolTips displayed\n"
+"when hovering with mouse over items throughout the App."
+msgstr ""
+"Aktivieren Sie dieses Kontrollkästchen, wenn QuickInfos angezeigt werden "
+"sollen\n"
+"wenn Sie mit der Maus über Elemente in der App fahren."
+
+#: flatcamGUI/FlatCAMGUI.py:3680
+msgid "Workers number:"
+msgstr "Arbeiter Nummer:"
+
+#: flatcamGUI/FlatCAMGUI.py:3682 flatcamGUI/FlatCAMGUI.py:3691
+msgid ""
+"The number of Qthreads made available to the App.\n"
+"A bigger number may finish the jobs more quickly but\n"
+"depending on your computer speed, may make the App\n"
+"unresponsive. Can have a value between 2 and 16.\n"
+"Default value is 2.\n"
+"After change, it will be applied at next App start."
+msgstr ""
+"Die Anzahl der für die App verfügbaren Qthreads.\n"
+"Eine größere Anzahl kann die Jobs aber schneller beenden\n"
+"Je nach Geschwindigkeit Ihres Computers kann die App erstellt werden\n"
+"reagiert nicht Kann einen Wert zwischen 2 und 16 haben.\n"
+"Der Standardwert ist 2.\n"
+"Nach dem Ändern wird es beim nächsten Start der App angewendet."
+
+#: flatcamGUI/FlatCAMGUI.py:3732
+msgid "Save Compressed Project"
+msgstr "Speichern Sie das komprimierte Projekt"
+
+#: flatcamGUI/FlatCAMGUI.py:3734
+msgid ""
+"Whether to save a compressed or uncompressed project.\n"
+"When checked it will save a compressed FlatCAM project."
+msgstr ""
+"Gibt an, ob ein komprimiertes oder unkomprimiertes Projekt gespeichert "
+"werden soll.\n"
+"Wenn diese Option aktiviert ist, wird ein komprimiertes FlatCAM-Projekt "
+"gespeichert."
+
+#: flatcamGUI/FlatCAMGUI.py:3745
+msgid "Compression Level:"
+msgstr "Kompressionsstufe:"
+
+#: flatcamGUI/FlatCAMGUI.py:3747
+msgid ""
+"The level of compression used when saving\n"
+"a FlatCAM project. Higher value means better compression\n"
+"but require more RAM usage and more processing time."
+msgstr ""
+"Die beim Speichern verwendete Komprimierungsstufe\n"
+"ein FlatCAM-Projekt. Ein höherer Wert bedeutet eine bessere Komprimierung\n"
+"erfordern jedoch mehr RAM-Auslastung und mehr Verarbeitungszeit."
+
+#: flatcamGUI/FlatCAMGUI.py:3773 flatcamGUI/FlatCAMGUI.py:4014
+#: flatcamGUI/FlatCAMGUI.py:4669 flatcamGUI/FlatCAMGUI.py:4993
+#: flatcamGUI/ObjectUI.py:150 flatcamGUI/ObjectUI.py:505
+#: flatcamGUI/ObjectUI.py:830 flatcamGUI/ObjectUI.py:1344
+msgid "<b>Plot Options:</b>"
+msgstr "<b> Diagrammoptionen: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3780 flatcamGUI/FlatCAMGUI.py:4026
+#: flatcamGUI/ObjectUI.py:506
+msgid "Solid"
+msgstr "Solide"
+
+#: flatcamGUI/FlatCAMGUI.py:3782 flatcamGUI/ObjectUI.py:158
+msgid "Solid color polygons."
+msgstr "Einfarbige Polygone."
+
+#: flatcamGUI/FlatCAMGUI.py:3787
+msgid "M-Color"
+msgstr "M-farbig"
+
+#: flatcamGUI/FlatCAMGUI.py:3789 flatcamGUI/ObjectUI.py:166
+msgid "Draw polygons in different colors."
+msgstr "Zeichnen Sie Polygone in verschiedenen Farben."
+
+#: flatcamGUI/FlatCAMGUI.py:3794 flatcamGUI/FlatCAMGUI.py:4020
+#: flatcamGUI/FlatCAMGUI.py:4673 flatcamGUI/ObjectUI.py:172
+msgid "Plot"
+msgstr "Zeichn"
+
+#: flatcamGUI/FlatCAMGUI.py:3796 flatcamGUI/FlatCAMGUI.py:4675
+#: flatcamGUI/ObjectUI.py:174 flatcamGUI/ObjectUI.py:546
+#: flatcamGUI/ObjectUI.py:876 flatcamGUI/ObjectUI.py:1431
+msgid "Plot (show) this object."
+msgstr "Plotten (zeigen) dieses Objekt."
+
+#: flatcamGUI/FlatCAMGUI.py:3801 flatcamGUI/FlatCAMGUI.py:4682
+#: flatcamGUI/FlatCAMGUI.py:5029
+msgid "Circle Steps:"
+msgstr "Kreisschritte:"
+
+#: flatcamGUI/FlatCAMGUI.py:3803
+msgid ""
+"The number of circle steps for Gerber \n"
+"circular aperture linear approximation."
+msgstr ""
+"Die Anzahl der Kreisschritte für Gerber\n"
+"lineare Approximation mit kreisförmiger Apertur."
+
+#: flatcamGUI/FlatCAMGUI.py:3818
+msgid "Gerber Options"
+msgstr "Gerber-Optionen"
+
+#: flatcamGUI/FlatCAMGUI.py:3822 flatcamGUI/ObjectUI.py:251
+msgid "<b>Isolation Routing:</b>"
+msgstr "<b> Isolierungsrouting: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3824 flatcamGUI/ObjectUI.py:253
+msgid ""
+"Create a Geometry object with\n"
+"toolpaths to cut outside polygons."
+msgstr ""
+"Erstellen Sie ein Geometrieobjekt mit\n"
+"Werkzeugwege zum Schneiden von \n"
+"äußeren Polygonen."
+
+#: flatcamGUI/FlatCAMGUI.py:3835 flatcamGUI/FlatCAMGUI.py:4392
+#: flatcamGUI/FlatCAMGUI.py:5317 flatcamGUI/ObjectUI.py:785
+#: flatcamGUI/ObjectUI.py:801
+msgid "Diameter of the cutting tool."
+msgstr "Durchmesser des Schneidewerkzeugs."
+
+#: flatcamGUI/FlatCAMGUI.py:3842
+msgid "Width (# passes):"
+msgstr "Breite (# passt):"
+
+#: flatcamGUI/FlatCAMGUI.py:3844 flatcamGUI/ObjectUI.py:275
+msgid ""
+"Width of the isolation gap in\n"
+"number (integer) of tool widths."
+msgstr ""
+"Breite der Isolationslücke in\n"
+"Anzahl (Ganzzahl) der Werkzeugbreiten."
+
+#: flatcamGUI/FlatCAMGUI.py:3852 flatcamGUI/ObjectUI.py:283
+msgid "Pass overlap:"
+msgstr "Passüberlappung:"
+
+#: flatcamGUI/FlatCAMGUI.py:3854 flatcamGUI/ObjectUI.py:285
+#, python-format
+msgid ""
+"How much (fraction) of the tool width to overlap each tool pass.\n"
+"Example:\n"
+"A value here of 0.25 means an overlap of 25%% from the tool diameter found "
+"above."
+msgstr ""
+"Wie viel (Bruchteil) der Werkzeugbreite, um jeden \n"
+"Werkzeugdurchgang zu überlappen.\n"
+"Beispiel:\n"
+"Ein Wert von 0,25 bedeutet hier eine Überlappung von 25% \n"
+"vom oben angegebenen Werkzeugdurchmesser."
+
+#: flatcamGUI/FlatCAMGUI.py:3862 flatcamGUI/ObjectUI.py:295
+msgid "Milling Type:"
+msgstr "Fräsart:"
+
+#: flatcamGUI/FlatCAMGUI.py:3864 flatcamGUI/ObjectUI.py:297
+msgid ""
+"Milling type:\n"
+"- climb / best for precision milling and to reduce tool usage\n"
+"- conventional / useful when there is no backlash compensation"
+msgstr ""
+"Fräsart:\n"
+"- Besteigung für präzises Fräsen und zur Verringerung des "
+"Werkzeugverbrauchs\n"
+"- konventionell / nützlich, wenn kein Spielausgleich vorliegt"
+
+#: flatcamGUI/FlatCAMGUI.py:3869 flatcamGUI/ObjectUI.py:302
+msgid "Climb"
+msgstr "Steigen"
+
+#: flatcamGUI/FlatCAMGUI.py:3870 flatcamGUI/ObjectUI.py:303
+msgid "Conv."
+msgstr "Konv."
+
+#: flatcamGUI/FlatCAMGUI.py:3874
+msgid "Combine Passes"
+msgstr "Kombinieren Sie Pässe"
+
+#: flatcamGUI/FlatCAMGUI.py:3876 flatcamGUI/ObjectUI.py:309
+msgid "Combine all passes into one object"
+msgstr "Kombinieren Sie alle Durchgänge in einem Objekt"
+
+#: flatcamGUI/FlatCAMGUI.py:3881
+msgid "<b>Clear non-copper:</b>"
+msgstr "<b> Nicht-Kupfer löschen: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3883 flatcamGUI/FlatCAMGUI.py:5205
+#: flatcamGUI/ObjectUI.py:386
+msgid ""
+"Create a Geometry object with\n"
+"toolpaths to cut all non-copper regions."
+msgstr ""
+"Erstellen Sie ein Geometrieobjekt mit\n"
+"Werkzeugwege, um alle Nicht-Kupfer-Bereiche zu schneiden."
+
+#: flatcamGUI/FlatCAMGUI.py:3892 flatcamGUI/FlatCAMGUI.py:3918
+#: flatcamGUI/ObjectUI.py:430 flatcamGUI/ObjectUI.py:464
+msgid "Boundary Margin:"
+msgstr "Grenzmarge:"
+
+#: flatcamGUI/FlatCAMGUI.py:3894 flatcamGUI/ObjectUI.py:432
+msgid ""
+"Specify the edge of the PCB\n"
+"by drawing a box around all\n"
+"objects with this minimum\n"
+"distance."
+msgstr ""
+"Bestimmen Sie den Rand der Leiterplatte\n"
+"indem Sie eine Box um alle ziehen\n"
+"Objekte mit diesem Minimum\n"
+"Entfernung."
+
+#: flatcamGUI/FlatCAMGUI.py:3904 flatcamGUI/FlatCAMGUI.py:3927
+msgid "Rounded corners"
+msgstr "Abgerundete Ecken"
+
+#: flatcamGUI/FlatCAMGUI.py:3906
+msgid ""
+"Creates a Geometry objects with polygons\n"
+"covering the copper-free areas of the PCB."
+msgstr ""
+"Erzeugt ein Geometrieobjekt mit Polygonen\n"
+"bedeckt die kupferfreien Bereiche der Leiterplatte."
+
+#: flatcamGUI/FlatCAMGUI.py:3912 flatcamGUI/ObjectUI.py:454
+msgid "<b>Bounding Box:</b>"
+msgstr "<b> Begrenzungsbox: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3920 flatcamGUI/ObjectUI.py:466
+msgid ""
+"Distance of the edges of the box\n"
+"to the nearest polygon."
+msgstr ""
+"Abstand der Kanten der Box\n"
+"zum nächsten Polygon."
+
+#: flatcamGUI/FlatCAMGUI.py:3929 flatcamGUI/ObjectUI.py:476
+msgid ""
+"If the bounding box is \n"
+"to have rounded corners\n"
+"their radius is equal to\n"
+"the margin."
+msgstr ""
+"Wenn der Begrenzungsrahmen ist\n"
+"abgerundete Ecken haben\n"
+"ihr Radius ist gleich\n"
+"der Abstand."
+
+#: flatcamGUI/FlatCAMGUI.py:3943
+msgid "Gerber Adv. Options"
+msgstr "Erweiterte Optionen von Gerber"
+
+#: flatcamGUI/FlatCAMGUI.py:3947
+msgid "<b>Advanced Param.:</b>"
+msgstr "<b>Erweiterte Parameter:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:3949
+msgid ""
+"A list of Gerber advanced parameters.\n"
+"Those parameters are available only for\n"
+"Advanced App. Level."
+msgstr ""
+"Eine Liste der erweiterten Gerber-Parameter.\n"
+"Diese Parameter sind nur für verfügbar\n"
+"Fortgeschrittene Anwendungsebene."
+
+#: flatcamGUI/FlatCAMGUI.py:3959 flatcamGUI/ObjectUI.py:314
+msgid "\"Follow\""
+msgstr "\"Folgen\""
+
+#: flatcamGUI/FlatCAMGUI.py:3961 flatcamGUI/ObjectUI.py:316
+msgid ""
+"Generate a 'Follow' geometry.\n"
+"This means that it will cut through\n"
+"the middle of the trace."
+msgstr ""
+"Generieren Sie eine 'Folgen'-Geometrie.\n"
+"Dies bedeutet, dass es durchschneiden wird\n"
+"die Mitte der Spur"
+
+#: flatcamGUI/FlatCAMGUI.py:3969
+msgid "Table Show/Hide"
+msgstr "Tabelle anzeigen / ausblenden"
+
+#: flatcamGUI/FlatCAMGUI.py:3971
+msgid ""
+"Toggle the display of the Gerber Apertures Table.\n"
+"Also, on hide, it will delete all mark shapes\n"
+"that are drawn on canvas."
+msgstr ""
+"Anzeige der Gerber-Blendentabelle umschalten.\n"
+"Beim Ausblenden werden auch alle Markierungsformen gelöscht\n"
+"das sind auf leinwand gezeichnet."
+
+#: flatcamGUI/FlatCAMGUI.py:3979
+msgid "Ap. Scale Factor:"
+msgstr "Öffnungsmaßstab:"
+
+#: flatcamGUI/FlatCAMGUI.py:3981
+msgid ""
+"Change the size of the selected apertures.\n"
+"Factor by which to multiply\n"
+"geometric features of this object."
+msgstr ""
+"Ändern Sie die Größe der ausgewählten Blenden.\n"
+"Faktor, mit dem sich multiplizieren soll\n"
+"geometrische Merkmale dieses Objekts."
+
+#: flatcamGUI/FlatCAMGUI.py:3991
+msgid "Ap. Buffer Factor:"
+msgstr "Blendenpufferfaktor:"
+
+#: flatcamGUI/FlatCAMGUI.py:3993
+msgid ""
+"Change the size of the selected apertures.\n"
+"Factor by which to expand/shrink\n"
+"geometric features of this object."
+msgstr ""
+"Ändern Sie die Größe der ausgewählten Blenden.\n"
+"Faktor, um den / das erweitert / verkleinert werden soll\n"
+"geometrische Merkmale dieses Objekts."
+
+#: flatcamGUI/FlatCAMGUI.py:4011
+msgid "Excellon General"
+msgstr "Excellon Allgemeines"
+
+#: flatcamGUI/FlatCAMGUI.py:4033
+msgid "<b>Excellon Format:</b>"
+msgstr "<b>Excellon-Format:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4035
+msgid ""
+"The NC drill files, usually named Excellon files\n"
+"are files that can be found in different formats.\n"
+"Here we set the format used when the provided\n"
+"coordinates are not using period.\n"
+"\n"
+"Possible presets:\n"
+"\n"
+"PROTEUS 3:3 MM LZ\n"
+"DipTrace 5:2 MM TZ\n"
+"DipTrace 4:3 MM LZ\n"
+"\n"
+"EAGLE 3:3 MM TZ\n"
+"EAGLE 4:3 MM TZ\n"
+"EAGLE 2:5 INCH TZ\n"
+"EAGLE 3:5 INCH TZ\n"
+"\n"
+"ALTIUM 2:4 INCH LZ\n"
+"Sprint Layout 2:4 INCH LZ\n"
+"KiCAD 3:5 INCH TZ"
+msgstr ""
+"Die NC-Bohrdateien, normalerweise Excellon-Dateien genannt\n"
+"sind Dateien, die in verschiedenen Formaten vorliegen.\n"
+"Hier legen wir das verwendete Format fest\n"
+"Koordinaten verwenden keine Periode.\n"
+"\n"
+"Mögliche Voreinstellungen:\n"
+"\n"
+"PROTEUS 3: 3 MM LZ\n"
+"DipTrace 5: 2 MM TZ\n"
+"DipTrace 4: 3 MM LZ\n"
+"\n"
+"Eagle 3: 3 MM TZ\n"
+"Eagle 4: 3 MM TZ\n"
+"Eagle 2: 5 ZOLL TZ\n"
+"Eagle 3: 5 ZOLL TZ\n"
+"\n"
+"ALTIUM 2: 4 ZOLL LZ\n"
+"Sprint-Layout 2: 4 ZOLL LZ\n"
+"KiCAD 3: 5 ZOLL TZ"
+
+#: flatcamGUI/FlatCAMGUI.py:4060
+msgid "INCH:"
+msgstr "ZOLL:"
+
+#: flatcamGUI/FlatCAMGUI.py:4063
+msgid "Default values for INCH are 2:4"
+msgstr "Die Standardwerte für ZOLL sind 2: 4"
+
+#: flatcamGUI/FlatCAMGUI.py:4071 flatcamGUI/FlatCAMGUI.py:4104
+#: flatcamGUI/FlatCAMGUI.py:4581
+msgid ""
+"This numbers signify the number of digits in\n"
+"the whole part of Excellon coordinates."
+msgstr ""
+"Diese Zahlen geben die Anzahl der Ziffern in an\n"
+"der gesamte Teil der Excellon-Koordinaten."
+
+#: flatcamGUI/FlatCAMGUI.py:4085 flatcamGUI/FlatCAMGUI.py:4118
+#: flatcamGUI/FlatCAMGUI.py:4595
+msgid ""
+"This numbers signify the number of digits in\n"
+"the decimal part of Excellon coordinates."
+msgstr ""
+"Diese Zahlen geben die Anzahl der Ziffern in an\n"
+"der Dezimalteil der Excellon-Koordinaten."
+
+#: flatcamGUI/FlatCAMGUI.py:4093
+msgid "METRIC:"
+msgstr "METRISCH:"
+
+#: flatcamGUI/FlatCAMGUI.py:4096
+msgid "Default values for METRIC are 3:3"
+msgstr "Die Standardwerte für METRISCH sind 3: 3"
+
+#: flatcamGUI/FlatCAMGUI.py:4127
+msgid "Default <b>Zeros</b>:"
+msgstr "Standard <b>Nullen</b>:"
+
+#: flatcamGUI/FlatCAMGUI.py:4130 flatcamGUI/FlatCAMGUI.py:4630
+msgid ""
+"This sets the type of Excellon zeros.\n"
+"If LZ then Leading Zeros are kept and\n"
+"Trailing Zeros are removed.\n"
+"If TZ is checked then Trailing Zeros are kept\n"
+"and Leading Zeros are removed."
+msgstr ""
+"Hiermit wird der Typ der Excellon-Nullen festgelegt.\n"
+"Wenn LZ, dann werden führende Nullen beibehalten und\n"
+"Nachgestellte Nullen werden entfernt.\n"
+"Wenn TZ aktiviert ist, werden nachfolgende Nullen beibehalten\n"
+"und führende Nullen werden entfernt."
+
+#: flatcamGUI/FlatCAMGUI.py:4138 flatcamGUI/FlatCAMGUI.py:4637
+msgid "LZ"
+msgstr "LZ"
+
+#: flatcamGUI/FlatCAMGUI.py:4139 flatcamGUI/FlatCAMGUI.py:4638
+msgid "TZ"
+msgstr "TZ"
+
+#: flatcamGUI/FlatCAMGUI.py:4141
+msgid ""
+"This sets the default type of Excellon zeros.\n"
+"If it is not detected in the parsed file the value here\n"
+"will be used.If LZ then Leading Zeros are kept and\n"
+"Trailing Zeros are removed.\n"
+"If TZ is checked then Trailing Zeros are kept\n"
+"and Leading Zeros are removed."
+msgstr ""
+"Hiermit wird der Standardtyp von Excellon-Nullen festgelegt.\n"
+"Wird in der geparsten Datei der Wert hier nicht gefunden\n"
+"wird verwendet. Wenn LZ, dann werden führende Nullen beibehalten und\n"
+"Nachgestellte Nullen werden entfernt.\n"
+"Wenn TZ aktiviert ist, werden nachfolgende Nullen beibehalten\n"
+"und führende Nullen werden entfernt."
+
+#: flatcamGUI/FlatCAMGUI.py:4155
+msgid "Default <b>Units</b>:"
+msgstr "Standard <b>einheiten</b>:"
+
+#: flatcamGUI/FlatCAMGUI.py:4158
+msgid ""
+"This sets the default units of Excellon files.\n"
+"If it is not detected in the parsed file the value here\n"
+"will be used.Some Excellon files don't have an header\n"
+"therefore this parameter will be used."
+msgstr ""
+"Dadurch werden die Standardeinheiten von Excellon-Dateien festgelegt.\n"
+"Wird in der geparsten Datei der Wert hier nicht gefunden\n"
+"wird verwendet. Einige Excellon-Dateien haben keinen Header\n"
+"Daher wird dieser Parameter verwendet."
+
+#: flatcamGUI/FlatCAMGUI.py:4166 flatcamGUI/FlatCAMGUI.py:4557
+msgid "INCH"
+msgstr "ZOLL"
+
+#: flatcamGUI/FlatCAMGUI.py:4167 flatcamGUI/FlatCAMGUI.py:4558
+msgid "MM"
+msgstr "MM"
+
+#: flatcamGUI/FlatCAMGUI.py:4169
+msgid ""
+"This sets the units of Excellon files.\n"
+"Some Excellon files don't have an header\n"
+"therefore this parameter will be used."
+msgstr ""
+"Damit werden die Einheiten von Excellon-Dateien festgelegt.\n"
+"Einige Excellon-Dateien haben keinen Header\n"
+"Daher wird dieser Parameter verwendet."
+
+#: flatcamGUI/FlatCAMGUI.py:4185
+msgid "<b>Excellon Optimization:</b>"
+msgstr "<b>Optimierung der Excellons:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4192
+msgid "Algorithm:   "
+msgstr "Algorithmus:"
+
+#: flatcamGUI/FlatCAMGUI.py:4195 flatcamGUI/FlatCAMGUI.py:4208
+msgid ""
+"This sets the optimization type for the Excellon drill path.\n"
+"If MH is checked then Google OR-Tools algorithm with MetaHeuristic\n"
+"Guided Local Path is used. Default search time is 3sec.\n"
+"Use set_sys excellon_search_time value Tcl Command to set other values.\n"
+"If Basic is checked then Google OR-Tools Basic algorithm is used.\n"
+"\n"
+"If DISABLED, then FlatCAM works in 32bit mode and it uses \n"
+"Travelling Salesman algorithm for path optimization."
+msgstr ""
+"Dadurch wird der Optimierungstyp für den Excellon-Bohrpfad festgelegt.\n"
+"Wenn MH geprüft wird, wird der Algorithmus von Google OR-Tools mit "
+"MetaHeuristic verwendet\n"
+"Geführter lokaler Pfad wird verwendet. Die Standard-Suchzeit beträgt 3 "
+"Sekunden.\n"
+"Verwenden Sie den set_sys excellon_search_time value Tcl-Befehl, um andere "
+"Werte festzulegen.\n"
+"Wenn Basic aktiviert ist, wird der Google OR-Tools Basic-Algorithmus "
+"verwendet.\n"
+"\n"
+"Wenn DEAKTIVIERT, arbeitet FlatCAM im 32-Bit-Modus und verwendet es\n"
+"Traveling Salesman-Algorithmus zur Pfadoptimierung."
+
+#: flatcamGUI/FlatCAMGUI.py:4205
+msgid "MH"
+msgstr "MH"
+
+#: flatcamGUI/FlatCAMGUI.py:4220
+msgid "Optimization Time:   "
+msgstr "Optimierungszeit:"
+
+#: flatcamGUI/FlatCAMGUI.py:4223
+msgid ""
+"When OR-Tools Metaheuristic (MH) is enabled there is a\n"
+"maximum threshold for how much time is spent doing the\n"
+"path optimization. This max duration is set here.\n"
+"In seconds."
+msgstr ""
+"Wenn OR-Tools Metaheuristic (MH) aktiviert ist, wird ein angezeigt\n"
+"maximale Schwelle für die Zeit, die das\n"
+"Pfadoptimierung. Diese maximale Dauer wird hier eingestellt.\n"
+"In Sekunden."
+
+#: flatcamGUI/FlatCAMGUI.py:4264
+msgid "Excellon Options"
+msgstr "Excellon-Optionen"
+
+#: flatcamGUI/FlatCAMGUI.py:4267 flatcamGUI/ObjectUI.py:584
+msgid "<b>Create CNC Job</b>"
+msgstr "<b>CNC-Job erstellen</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4269
+msgid ""
+"Parameters used to create a CNC Job object\n"
+"for this drill object."
+msgstr ""
+"Parameter, die zum Erstellen eines CNC-Auftragsobjekts verwendet werden\n"
+"für dieses Bohrobjekt."
+
+#: flatcamGUI/FlatCAMGUI.py:4277 flatcamGUI/FlatCAMGUI.py:4733
+#: flatcamGUI/FlatCAMGUI.py:5741 flatcamGUI/ObjectUI.py:595
+#: flatcamGUI/ObjectUI.py:1059 flatcamTools/ToolCalculators.py:108
+msgid "Cut Z:"
+msgstr "Schnitt Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:4279 flatcamGUI/ObjectUI.py:597
+msgid ""
+"Drill depth (negative)\n"
+"below the copper surface."
+msgstr ""
+"Bohrtiefe (negativ)\n"
+"unter der Kupferoberfläche."
+
+#: flatcamGUI/FlatCAMGUI.py:4286 flatcamGUI/FlatCAMGUI.py:4766
+#: flatcamGUI/ObjectUI.py:605 flatcamGUI/ObjectUI.py:1095
+msgid "Travel Z:"
+msgstr "Reise Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:4288 flatcamGUI/ObjectUI.py:607
+msgid ""
+"Tool height when travelling\n"
+"across the XY plane."
+msgstr ""
+"Werkzeughöhe auf Reisen\n"
+"über die XY-Ebene."
+
+#: flatcamGUI/FlatCAMGUI.py:4296 flatcamGUI/FlatCAMGUI.py:4776
+msgid "Tool change:"
+msgstr "Werkzeugwechsel:"
+
+#: flatcamGUI/FlatCAMGUI.py:4298 flatcamGUI/FlatCAMGUI.py:4778
+#: flatcamGUI/ObjectUI.py:617
+msgid ""
+"Include tool-change sequence\n"
+"in G-Code (Pause for tool change)."
+msgstr ""
+"Werkzeugwechselfolge einbeziehen\n"
+"im G-Code (Pause für Werkzeugwechsel)."
+
+#: flatcamGUI/FlatCAMGUI.py:4305 flatcamGUI/FlatCAMGUI.py:4786
+msgid "Toolchange Z:"
+msgstr "Werkzeugwechsel Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:4307 flatcamGUI/FlatCAMGUI.py:4788
+msgid "Toolchange Z position."
+msgstr "Toolchange Z position."
+
+#: flatcamGUI/FlatCAMGUI.py:4313
+msgid "Feedrate:"
+msgstr "Vorschubgeschwindigkeit:"
+
+#: flatcamGUI/FlatCAMGUI.py:4315
+msgid ""
+"Tool speed while drilling\n"
+"(in units per minute)."
+msgstr ""
+"Werkzeuggeschwindigkeit beim Bohren\n"
+"(in Einheiten pro Minute)."
+
+#: flatcamGUI/FlatCAMGUI.py:4323
+msgid "Spindle Speed:"
+msgstr "Spulengeschwindigkeit:"
+
+#: flatcamGUI/FlatCAMGUI.py:4325 flatcamGUI/FlatCAMGUI.py:4818
+#: flatcamGUI/ObjectUI.py:681
+msgid ""
+"Speed of the spindle\n"
+"in RPM (optional)"
+msgstr ""
+"Geschwindigkeit der Spindel\n"
+"in RPM (optional)"
+
+#: flatcamGUI/FlatCAMGUI.py:4333 flatcamGUI/FlatCAMGUI.py:4826
+#: flatcamGUI/ObjectUI.py:689 flatcamGUI/ObjectUI.py:1218
+msgid "Dwell:"
+msgstr "Wohnen:"
+
+#: flatcamGUI/FlatCAMGUI.py:4335 flatcamGUI/FlatCAMGUI.py:4828
+#: flatcamGUI/ObjectUI.py:691 flatcamGUI/ObjectUI.py:1221
+msgid ""
+"Pause to allow the spindle to reach its\n"
+"speed before cutting."
+msgstr ""
+"Pause, damit die Spindel ihre erreichen kann\n"
+"Geschwindigkeit vor dem Schneiden."
+
+#: flatcamGUI/FlatCAMGUI.py:4338 flatcamGUI/FlatCAMGUI.py:4831
+msgid "Duration:"
+msgstr "Dauer:"
+
+#: flatcamGUI/FlatCAMGUI.py:4340 flatcamGUI/FlatCAMGUI.py:4833
+#: flatcamGUI/ObjectUI.py:696 flatcamGUI/ObjectUI.py:1228
+msgid "Number of milliseconds for spindle to dwell."
+msgstr "Anzahl der Millisekunden, die die Spindel halten soll."
+
+#: flatcamGUI/FlatCAMGUI.py:4352 flatcamGUI/FlatCAMGUI.py:4843
+#: flatcamGUI/ObjectUI.py:704
+msgid "Postprocessor:"
+msgstr "Postprozessor:"
+
+#: flatcamGUI/FlatCAMGUI.py:4354
+msgid ""
+"The postprocessor file that dictates\n"
+"gcode output."
+msgstr ""
+"Die Postprozessor-Datei, die diktiert\n"
+"gcode ausgabe."
+
+#: flatcamGUI/FlatCAMGUI.py:4364
+msgid "<b>Gcode:    </b>"
+msgstr "<b>Gcode:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4366
+msgid ""
+"Choose what to use for GCode generation:\n"
+"'Drills', 'Slots' or 'Both'.\n"
+"When choosing 'Slots' or 'Both', slots will be\n"
+"converted to drills."
+msgstr ""
+"Wählen Sie aus, was für die GCode-Generierung verwendet werden soll:\n"
+"'Bohrer', 'Schlüssel' oder 'Beide'.\n"
+"Wenn Sie \"Schlüssel\" oder \"Beide\" wählen, werden die Schlüssel "
+"angezeigt\n"
+"in Bohrer umgewandelt."
+
+#: flatcamGUI/FlatCAMGUI.py:4371 flatcamGUI/ObjectUI.py:556
+#: flatcamGUI/ObjectUI.py:752
+msgid "Drills"
+msgstr "Bohrer"
+
+#: flatcamGUI/FlatCAMGUI.py:4372 flatcamGUI/ObjectUI.py:556
+#: flatcamGUI/ObjectUI.py:753
+msgid "Slots"
+msgstr "Schlüssel"
+
+#: flatcamGUI/FlatCAMGUI.py:4373 flatcamGUI/ObjectUI.py:754
+msgid "Both"
+msgstr "Both"
+
+#: flatcamGUI/FlatCAMGUI.py:4382 flatcamGUI/ObjectUI.py:769
+msgid "<b>Mill Holes</b>"
+msgstr "<b> Löcher bohren </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4384 flatcamGUI/ObjectUI.py:771
+msgid "Create Geometry for milling holes."
+msgstr "Erstellen Sie Geometrie zum Fräsen von Löchern."
+
+#: flatcamGUI/FlatCAMGUI.py:4390
+msgid "Drill Tool dia:"
+msgstr "Bohrwerkzeug Durchmesser:"
+
+#: flatcamGUI/FlatCAMGUI.py:4397
+msgid "Slot Tool dia:"
+msgstr "Schlitzwerkzeug Durchmesser:"
+
+#: flatcamGUI/FlatCAMGUI.py:4399
+msgid ""
+"Diameter of the cutting tool\n"
+"when milling slots."
+msgstr ""
+"Durchmesser des Schneidewerkzeugs\n"
+"beim Fräsen von Schlitzen."
+
+#: flatcamGUI/FlatCAMGUI.py:4411
+msgid "Defaults"
+msgstr "Standardwerte"
+
+#: flatcamGUI/FlatCAMGUI.py:4424
+msgid "Excellon Adv. Options"
+msgstr "Excellon erweiterte Optionen"
+
+#: flatcamGUI/FlatCAMGUI.py:4430 flatcamGUI/FlatCAMGUI.py:4866
+msgid "<b>Advanced Options:</b>"
+msgstr "<b>Erweiterte Optionen:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4432
+msgid ""
+"Parameters used to create a CNC Job object\n"
+"for this drill object that are shown when App Level is Advanced."
+msgstr ""
+"Parameter, die zum Erstellen eines CNC-Auftragsobjekts verwendet werden\n"
+"für dieses Drill-Objekt, das angezeigt wird, wenn die App-Ebene Erweitert "
+"ist."
+
+#: flatcamGUI/FlatCAMGUI.py:4440
+msgid "Offset Z:"
+msgstr "Versatz Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:4442 flatcamGUI/ObjectUI.py:574
+msgid ""
+"Some drill bits (the larger ones) need to drill deeper\n"
+"to create the desired exit hole diameter due of the tip shape.\n"
+"The value here can compensate the Cut Z parameter."
+msgstr ""
+"Einige Bohrer (die größeren) müssen tiefer bohren\n"
+"um den gewünschten Austrittslochdurchmesser aufgrund der Spitzenform zu "
+"erzeugen.\n"
+"Der Wert hier kann den Parameter Cut Z ausgleichen."
+
+#: flatcamGUI/FlatCAMGUI.py:4449 flatcamGUI/FlatCAMGUI.py:4877
+msgid "Toolchange X,Y:"
+msgstr "Werkzeugwechsel X, Y:"
+
+#: flatcamGUI/FlatCAMGUI.py:4451 flatcamGUI/FlatCAMGUI.py:4879
+msgid "Toolchange X,Y position."
+msgstr "Werkzeugwechsel X, Y Position."
+
+#: flatcamGUI/FlatCAMGUI.py:4457 flatcamGUI/FlatCAMGUI.py:4886
+#: flatcamGUI/ObjectUI.py:634
+msgid "Start move Z:"
+msgstr "Startbewegung Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:4459
+msgid ""
+"Height of the tool just after start.\n"
+"Delete the value if you don't need this feature."
+msgstr ""
+"Höhe des Werkzeugs gleich nach dem Start.\n"
+"Löschen Sie den Wert, wenn Sie diese Funktion nicht benötigen."
+
+#: flatcamGUI/FlatCAMGUI.py:4466 flatcamGUI/FlatCAMGUI.py:4896
+#: flatcamGUI/ObjectUI.py:644 flatcamGUI/ObjectUI.py:1141
+msgid "End move Z:"
+msgstr "Bewegung beenden Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:4468 flatcamGUI/FlatCAMGUI.py:4898
+msgid ""
+"Height of the tool after\n"
+"the last move at the end of the job."
+msgstr ""
+"Höhe des Werkzeugs nach\n"
+"die letzte Bewegung am Ende des Jobs."
+
+#: flatcamGUI/FlatCAMGUI.py:4475 flatcamGUI/FlatCAMGUI.py:4906
+#: flatcamGUI/ObjectUI.py:665
+msgid "Feedrate Rapids:"
+msgstr "Vorschubgeschwindigkeit:"
+
+#: flatcamGUI/FlatCAMGUI.py:4477 flatcamGUI/ObjectUI.py:667
+msgid ""
+"Tool speed while drilling\n"
+"(in units per minute).\n"
+"This is for the rapid move G00.\n"
+"It is useful only for Marlin,\n"
+"ignore for any other cases."
+msgstr ""
+"Werkzeuggeschwindigkeit beim Bohren\n"
+"(in Einheiten pro Minute).\n"
+"Dies ist für die schnelle Bewegung G00.\n"
+"Es ist nur für Marlin nützlich,\n"
+"für andere Fälle ignorieren."
+
+#: flatcamGUI/FlatCAMGUI.py:4488 flatcamGUI/FlatCAMGUI.py:4930
+#: flatcamGUI/ObjectUI.py:715 flatcamGUI/ObjectUI.py:1250
+msgid "Probe Z depth:"
+msgstr "Sonde Z Tiefe:"
+
+#: flatcamGUI/FlatCAMGUI.py:4490 flatcamGUI/FlatCAMGUI.py:4932
+#: flatcamGUI/ObjectUI.py:717 flatcamGUI/ObjectUI.py:1253
+msgid ""
+"The maximum depth that the probe is allowed\n"
+"to probe. Negative value, in current units."
+msgstr ""
+"Die maximale Tiefe, in der die Sonde zulässig ist\n"
+"zu untersuchen. Negativer Wert in aktuellen Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:4498 flatcamGUI/FlatCAMGUI.py:4940
+#: flatcamGUI/ObjectUI.py:727 flatcamGUI/ObjectUI.py:1264
+msgid "Feedrate Probe:"
+msgstr "Vorschubsonde:"
+
+#: flatcamGUI/FlatCAMGUI.py:4500 flatcamGUI/FlatCAMGUI.py:4942
+#: flatcamGUI/ObjectUI.py:729 flatcamGUI/ObjectUI.py:1267
+msgid "The feedrate used while the probe is probing."
+msgstr "Der Vorschub während der Sondenmessung."
+
+#: flatcamGUI/FlatCAMGUI.py:4506 flatcamGUI/FlatCAMGUI.py:4949
+msgid "Fast Plunge:"
+msgstr "Schneller Sprung:"
+
+#: flatcamGUI/FlatCAMGUI.py:4508 flatcamGUI/FlatCAMGUI.py:4951
+msgid ""
+"By checking this, the vertical move from\n"
+"Z_Toolchange to Z_move is done with G0,\n"
+"meaning the fastest speed available.\n"
+"WARNING: the move is done at Toolchange X,Y coords."
+msgstr ""
+"Wenn Sie dies überprüfen, bewegen Sie sich vertikal\n"
+"Z_Toolchange zu Z_move erfolgt mit G0,\n"
+"Das bedeutet die schnellste verfügbare Geschwindigkeit.\n"
+"WARNUNG: Die Verschiebung erfolgt bei Toolchange X, Y-Koordinaten."
+
+#: flatcamGUI/FlatCAMGUI.py:4517
+msgid "Fast Retract:"
+msgstr "Schneller Rückzug:"
+
+#: flatcamGUI/FlatCAMGUI.py:4519
+msgid ""
+"Exit hole strategy.\n"
+" - When uncheked, while exiting the drilled hole the drill bit\n"
+"will travel slow, with set feedrate (G1), up to zero depth and then\n"
+"travel as fast as possible (G0) to the Z Move (travel height).\n"
+" - When checked the travel from Z cut (cut depth) to Z_move\n"
+"(travel height) is done as fast as possible (G0) in one move."
+msgstr ""
+"Verlassen Sie die Lochstrategie.\n"
+"  - Ungeprüft, beim Verlassen des Bohrlochs der Bohrer\n"
+"fährt langsam, mit eingestelltem Vorschub (G1), bis zur Nulltiefe und dann\n"
+"Fahren Sie so schnell wie möglich (G0) bis Z Move (Fahrhöhe).\n"
+"  - Wenn Sie den Weg von Z-Schnitt (Schnitttiefe) nach Z_Move prüfen\n"
+"(Fahrhöhe) erfolgt so schnell wie möglich (G0) in einem Zug."
+
+#: flatcamGUI/FlatCAMGUI.py:4538
+msgid "Excellon Export"
+msgstr "Excellon Export"
+
+#: flatcamGUI/FlatCAMGUI.py:4541
+msgid "<b>Export Options:</b>"
+msgstr "<b>Exportoptionen:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4543
+msgid ""
+"The parameters set here are used in the file exported\n"
+"when using the File -> Export -> Export Excellon menu entry."
+msgstr ""
+"Die hier eingestellten Parameter werden in der exportierten Datei verwendet\n"
+"bei Verwendung des Menüeintrags Datei -> Exportieren -> Exportieren von "
+"Excellon."
+
+#: flatcamGUI/FlatCAMGUI.py:4552
+msgid "<b>Units</b>:"
+msgstr "<b>Einheiten</b>:"
+
+#: flatcamGUI/FlatCAMGUI.py:4554 flatcamGUI/FlatCAMGUI.py:4560
+msgid "The units used in the Excellon file."
+msgstr "Die in der Excellon-Datei verwendeten Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:4566
+msgid "<b>Int/Decimals:</b>"
+msgstr "<b>Ganzzahl / Dezimalzahl:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4568
+msgid ""
+"The NC drill files, usually named Excellon files\n"
+"are files that can be found in different formats.\n"
+"Here we set the format used when the provided\n"
+"coordinates are not using period."
+msgstr ""
+"Die NC-Bohrdateien, normalerweise Excellon-Dateien genannt\n"
+"sind Dateien, die in verschiedenen Formaten vorliegen.\n"
+"Hier legen wir das verwendete Format fest\n"
+"Koordinaten verwenden keine Periode."
+
+#: flatcamGUI/FlatCAMGUI.py:4604
+msgid "<b>Format:</b>"
+msgstr "<b>Format:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4606 flatcamGUI/FlatCAMGUI.py:4616
+msgid ""
+"Select the kind of coordinates format used.\n"
+"Coordinates can be saved with decimal point or without.\n"
+"When there is no decimal point, it is required to specify\n"
+"the number of digits for integer part and the number of decimals.\n"
+"Also it will have to be specified if LZ = leading zeros are kept\n"
+"or TZ = trailing zeros are kept."
+msgstr ""
+"Wählen Sie das verwendete Koordinatenformat aus.\n"
+"Koordinaten können mit oder ohne Dezimalpunkt gespeichert werden.\n"
+"Wenn kein Dezimalzeichen vorhanden ist, muss dies angegeben werden\n"
+"Die Anzahl der Ziffern für den ganzzahligen Teil und die Anzahl der "
+"Dezimalstellen.\n"
+"Es muss auch angegeben werden, wenn LZ = führende Nullen beibehalten werden\n"
+"oder TZ = nachfolgende Nullen bleiben erhalten."
+
+#: flatcamGUI/FlatCAMGUI.py:4613
+msgid "Decimal"
+msgstr "Dezimal"
+
+#: flatcamGUI/FlatCAMGUI.py:4614
+msgid "No-Decimal"
+msgstr "Keine Dezimalzahl"
+
+#: flatcamGUI/FlatCAMGUI.py:4627
+msgid "<b>Zeros</b>:"
+msgstr "<b>Nullen</b>:"
+
+#: flatcamGUI/FlatCAMGUI.py:4640
+msgid ""
+"This sets the default type of Excellon zeros.\n"
+"If LZ then Leading Zeros are kept and\n"
+"Trailing Zeros are removed.\n"
+"If TZ is checked then Trailing Zeros are kept\n"
+"and Leading Zeros are removed."
+msgstr ""
+"Hiermit wird der Standardtyp von Excellon-Nullen festgelegt.\n"
+"Wenn LZ, dann werden führende Nullen beibehalten und\n"
+"Nachgestellte Nullen werden entfernt.\n"
+"Wenn TZ aktiviert ist, werden nachfolgende Nullen beibehalten\n"
+"und führende Nullen werden entfernt."
+
+#: flatcamGUI/FlatCAMGUI.py:4666
+msgid "Geometry General"
+msgstr "Geometrie Allgemein"
+
+#: flatcamGUI/FlatCAMGUI.py:4684
+msgid ""
+"The number of circle steps for <b>Geometry</b> \n"
+"circle and arc shapes linear approximation."
+msgstr ""
+"Die Anzahl der Kreisschritte für die <b>Geometrie</b>\n"
+"Kreis- und Bogenformen lineare Annäherung."
+
+#: flatcamGUI/FlatCAMGUI.py:4692
+msgid "<b>Tools</b>"
+msgstr "<b>Werkzeuge</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4699
+msgid "Tool dia:                   "
+msgstr "Werkzeugdurchmesser:"
+
+#: flatcamGUI/FlatCAMGUI.py:4701
+msgid ""
+"The diameter of the cutting\n"
+"tool.."
+msgstr ""
+"Der Durchmesser des Schnitts\n"
+"Werkzeug.."
+
+#: flatcamGUI/FlatCAMGUI.py:4716
+msgid "Geometry Options"
+msgstr "Geometrieoptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:4721
+msgid "<b>Create CNC Job:</b>"
+msgstr "<b>CNC-Auftrag erstellen:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:4723
+msgid ""
+"Create a CNC Job object\n"
+"tracing the contours of this\n"
+"Geometry object."
+msgstr ""
+"Erstellen Sie ein CNC-Auftragsobjekt\n"
+"die Konturen davon nachzeichnen\n"
+"Geometrieobjekt."
+
+#: flatcamGUI/FlatCAMGUI.py:4735 flatcamGUI/ObjectUI.py:1062
+msgid ""
+"Cutting depth (negative)\n"
+"below the copper surface."
+msgstr ""
+"Schnitttiefe (negativ)\n"
+"unter der Kupferoberfläche."
+
+#: flatcamGUI/FlatCAMGUI.py:4743
+msgid "Multidepth"
+msgstr "Mehrere tiefe"
+
+#: flatcamGUI/FlatCAMGUI.py:4745
+msgid "Multidepth usage: True or False."
+msgstr "Mehrere Tiefe-Nutzung: Richtig oder Falsch."
+
+#: flatcamGUI/FlatCAMGUI.py:4750
+msgid "Depth/Pass:"
+msgstr "Tiefe / Pass:"
+
+#: flatcamGUI/FlatCAMGUI.py:4752
+msgid ""
+"The depth to cut on each pass,\n"
+"when multidepth is enabled.\n"
+"It has positive value although\n"
+"it is a fraction from the depth\n"
+"which has negative value."
+msgstr ""
+"Die Tiefe, die bei jedem Durchlauf geschnitten werden muss,\n"
+"Wenn Mehrere Tiefe aktiviert ist.\n"
+"Es hat zwar einen positiven Wert\n"
+"es ist ein Bruch aus der Tiefe\n"
+"was einen negativen Wert hat."
+
+#: flatcamGUI/FlatCAMGUI.py:4768 flatcamGUI/ObjectUI.py:1098
+msgid ""
+"Height of the tool when\n"
+"moving without cutting."
+msgstr ""
+"Höhe des Werkzeugs, wenn\n"
+"bewegen ohne zu schneiden"
+
+#: flatcamGUI/FlatCAMGUI.py:4795 flatcamGUI/ObjectUI.py:1153
+msgid "Feed Rate X-Y:"
+msgstr "Vorschubrate X-Y:"
+
+#: flatcamGUI/FlatCAMGUI.py:4797 flatcamGUI/ObjectUI.py:1156
+msgid ""
+"Cutting speed in the XY\n"
+"plane in units per minute"
+msgstr ""
+"Schnittgeschwindigkeit im XY\n"
+"Flugzeug in Einheiten pro Minute"
+
+#: flatcamGUI/FlatCAMGUI.py:4805
+msgid "Feed Rate Z:"
+msgstr "Vorschubrate Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:4807
+msgid ""
+"Cutting speed in the XY\n"
+"plane in units per minute.\n"
+"It is called also Plunge."
+msgstr ""
+"Schnittgeschwindigkeit im XY\n"
+"Flugzeug in Einheiten pro Minute.\n"
+"Es heißt auch Sturz."
+
+#: flatcamGUI/FlatCAMGUI.py:4816 flatcamGUI/ObjectUI.py:679
+#: flatcamGUI/ObjectUI.py:1205
+msgid "Spindle speed:"
+msgstr "Spulengeschwindigkeit:"
+
+#: flatcamGUI/FlatCAMGUI.py:4845
+msgid ""
+"The postprocessor file that dictates\n"
+"Machine Code output."
+msgstr ""
+"Die Postprozessor-Datei, die diktiert\n"
+"Maschinencode-Ausgabe."
+
+#: flatcamGUI/FlatCAMGUI.py:4861
+msgid "Geometry Adv. Options"
+msgstr "Geometrie Erw. Optionen"
+
+#: flatcamGUI/FlatCAMGUI.py:4868
+msgid ""
+"Parameters to create a CNC Job object\n"
+"tracing the contours of a Geometry object."
+msgstr ""
+"Parameter zum Erstellen eines CNC-Auftragsobjekts\n"
+"Verfolgung der Konturen eines Geometrieobjekts."
+
+#: flatcamGUI/FlatCAMGUI.py:4888
+msgid ""
+"Height of the tool just after starting the work.\n"
+"Delete the value if you don't need this feature."
+msgstr ""
+"Höhe des Werkzeugs unmittelbar nach Beginn der Arbeit.\n"
+"Löschen Sie den Wert, wenn Sie diese Funktion nicht benötigen."
+
+#: flatcamGUI/FlatCAMGUI.py:4908
+msgid ""
+"Cutting speed in the XY plane\n"
+"(in units per minute).\n"
+"This is for the rapid move G00.\n"
+"It is useful only for Marlin,\n"
+"ignore for any other cases."
+msgstr ""
+"Schnittgeschwindigkeit in der XY-Ebene\n"
+"(in Einheiten pro Minute).\n"
+"Dies ist für die schnelle Bewegung G00.\n"
+"Es ist nur für Marlin nützlich,\n"
+"für andere Fälle ignorieren."
+
+#: flatcamGUI/FlatCAMGUI.py:4920
+msgid "Re-cut 1st pt."
+msgstr "1. Punkt erneut schneiden"
+
+#: flatcamGUI/FlatCAMGUI.py:4922 flatcamGUI/ObjectUI.py:1196
+msgid ""
+"In order to remove possible\n"
+"copper leftovers where first cut\n"
+"meet with last cut, we generate an\n"
+"extended cut over the first cut section."
+msgstr ""
+"Um zu entfernen möglich\n"
+"Kupferreste wurden zuerst geschnitten\n"
+"Beim letzten Schnitt treffen wir einen\n"
+"verlängerter Schnitt über dem ersten Schnittabschnitt."
+
+#: flatcamGUI/FlatCAMGUI.py:4961
+msgid "Seg. X size:"
+msgstr "Seg. X Größe:"
+
+#: flatcamGUI/FlatCAMGUI.py:4963
+msgid ""
+"The size of the trace segment on the X axis.\n"
+"Useful for auto-leveling.\n"
+"A value of 0 means no segmentation on the X axis."
+msgstr ""
+"Die Größe des Trace-Segments auf der X-Achse.\n"
+"Nützlich für die automatische Nivellierung.\n"
+"Ein Wert von 0 bedeutet keine Segmentierung auf der X-Achse."
+
+#: flatcamGUI/FlatCAMGUI.py:4972
+msgid "Seg. Y size:"
+msgstr "Seg. Y Größe:"
+
+#: flatcamGUI/FlatCAMGUI.py:4974
+msgid ""
+"The size of the trace segment on the Y axis.\n"
+"Useful for auto-leveling.\n"
+"A value of 0 means no segmentation on the Y axis."
+msgstr ""
+"Die Größe des Trace-Segments auf der Y-Achse.\n"
+"Nützlich für die automatische Nivellierung.\n"
+"Ein Wert von 0 bedeutet keine Segmentierung auf der Y-Achse."
+
+#: flatcamGUI/FlatCAMGUI.py:4990
+msgid "CNC Job General"
+msgstr "CNC-Job Allgemein"
+
+#: flatcamGUI/FlatCAMGUI.py:5003 flatcamGUI/ObjectUI.py:544
+#: flatcamGUI/ObjectUI.py:874 flatcamGUI/ObjectUI.py:1428
+msgid "Plot Object"
+msgstr "Plotobjekt"
+
+#: flatcamGUI/FlatCAMGUI.py:5010
+msgid "Plot kind:"
+msgstr "Darstellungsart:"
+
+#: flatcamGUI/FlatCAMGUI.py:5012 flatcamGUI/ObjectUI.py:1350
+msgid ""
+"This selects the kind of geometries on the canvas to plot.\n"
+"Those can be either of type 'Travel' which means the moves\n"
+"above the work piece or it can be of type 'Cut',\n"
+"which means the moves that cut into the material."
+msgstr ""
+"Dadurch wird die Art der Geometrien auf der zu plottenden Leinwand "
+"ausgewählt.\n"
+"Dies kann entweder vom Typ 'Reise' sein, was die Bewegungen bedeutet\n"
+"über dem Werkstück oder es kann vom Typ 'Ausschneiden' sein,\n"
+"was bedeutet, dass die Bewegungen, die in das Material geschnitten werden."
+
+#: flatcamGUI/FlatCAMGUI.py:5020 flatcamGUI/ObjectUI.py:1359
+msgid "Travel"
+msgstr "Reise"
+
+#: flatcamGUI/FlatCAMGUI.py:5031
+msgid ""
+"The number of circle steps for <b>GCode</b> \n"
+"circle and arc shapes linear approximation."
+msgstr ""
+"Die Anzahl der Kreisschritte für <b>GCode</b>\n"
+"Kreis- und Bogenformen lineare Annäherung."
+
+#: flatcamGUI/FlatCAMGUI.py:5041
+msgid ""
+"Diameter of the tool to be\n"
+"rendered in the plot."
+msgstr ""
+"Durchmesser des Werkzeugs sein\n"
+"in der Handlung gerendert."
+
+#: flatcamGUI/FlatCAMGUI.py:5049
+msgid "Coords dec.:"
+msgstr "Koordinate Dezimalzahlen:"
+
+#: flatcamGUI/FlatCAMGUI.py:5051
+msgid ""
+"The number of decimals to be used for \n"
+"the X, Y, Z coordinates in CNC code (GCODE, etc.)"
+msgstr ""
+"Die Anzahl der Dezimalstellen, für die verwendet werden soll\n"
+"die X-, Y-, Z-Koordinaten im CNC-Code (GCODE usw.)"
+
+#: flatcamGUI/FlatCAMGUI.py:5059
+msgid "Feedrate dec.:"
+msgstr "Vorschub-Nachkommastellen:"
+
+#: flatcamGUI/FlatCAMGUI.py:5061
+msgid ""
+"The number of decimals to be used for \n"
+"the Feedrate parameter in CNC code (GCODE, etc.)"
+msgstr ""
+"Die Anzahl der Dezimalstellen, für die verwendet werden soll\n"
+"der Vorschubparameter im CNC-Code (GCODE usw.)"
+
+#: flatcamGUI/FlatCAMGUI.py:5076
+msgid "CNC Job Options"
+msgstr "CNC-Auftragsoptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5079 flatcamGUI/FlatCAMGUI.py:5120
+msgid "<b>Export G-Code:</b>"
+msgstr "<b>G-Code exportieren:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:5081 flatcamGUI/FlatCAMGUI.py:5122
+#: flatcamGUI/ObjectUI.py:1464
+msgid ""
+"Export and save G-Code to\n"
+"make this object to a file."
+msgstr ""
+"Exportieren und speichern Sie den G-Code nach\n"
+"Machen Sie dieses Objekt in eine Datei."
+
+#: flatcamGUI/FlatCAMGUI.py:5087
+msgid "Prepend to G-Code:"
+msgstr "Voranstellen an G-Code:"
+
+#: flatcamGUI/FlatCAMGUI.py:5089
+msgid ""
+"Type here any G-Code commands you would\n"
+"like to add at the beginning of the G-Code file."
+msgstr ""
+"Geben Sie hier alle G-Code-Befehle ein\n"
+"gerne am Anfang der G-Code-Datei hinzufügen."
+
+#: flatcamGUI/FlatCAMGUI.py:5098
+msgid "Append to G-Code:"
+msgstr "An G-Code anhängen:"
+
+#: flatcamGUI/FlatCAMGUI.py:5100 flatcamGUI/ObjectUI.py:1486
+msgid ""
+"Type here any G-Code commands you would\n"
+"like to append to the generated file.\n"
+"I.e.: M2 (End of program)"
+msgstr ""
+"Geben Sie hier alle G-Code-Befehle ein\n"
+"gerne an die generierte Datei anhängen.\n"
+"I.e .: M2 (Programmende)"
+
+#: flatcamGUI/FlatCAMGUI.py:5117
+msgid "CNC Job Adv. Options"
+msgstr "Erw. CNC-Joboptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5128 flatcamGUI/ObjectUI.py:1504
+msgid "Toolchange G-Code:"
+msgstr "Werkzeugwechsel G-Code:"
+
+#: flatcamGUI/FlatCAMGUI.py:5130
+msgid ""
+"Type here any G-Code commands you would\n"
+"like to be executed when Toolchange event is encountered.\n"
+"This will constitute a Custom Toolchange GCode,\n"
+"or a Toolchange Macro."
+msgstr ""
+"Geben Sie hier alle G-Code-Befehle ein\n"
+"Wird ausgeführt, wenn ein Werkzeugwechselereignis auftritt.\n"
+"Dies stellt einen benutzerdefinierten Werkzeugwechsel-GCode dar.\n"
+"oder ein Werkzeugwechsel-Makro."
+
+#: flatcamGUI/FlatCAMGUI.py:5144 flatcamGUI/ObjectUI.py:1526
+msgid "Use Toolchange Macro"
+msgstr "Verwenden Sie das Werkzeug zum Ändern des Makros"
+
+#: flatcamGUI/FlatCAMGUI.py:5146 flatcamGUI/ObjectUI.py:1529
+msgid ""
+"Check this box if you want to use\n"
+"a Custom Toolchange GCode (macro)."
+msgstr ""
+"Aktivieren Sie dieses Kontrollkästchen, wenn Sie verwenden möchten\n"
+"ein benutzerdefiniertes Werkzeug ändert GCode (Makro)."
+
+#: flatcamGUI/FlatCAMGUI.py:5158 flatcamGUI/ObjectUI.py:1538
+msgid ""
+"A list of the FlatCAM variables that can be used\n"
+"in the Toolchange event.\n"
+"They have to be surrounded by the '%' symbol"
+msgstr ""
+"Eine Liste der FlatCAM-Variablen, die verwendet werden können\n"
+"im Werkzeugwechselereignis.\n"
+"Sie müssen mit dem \"%\" -Symbol umgeben sein"
+
+#: flatcamGUI/FlatCAMGUI.py:5165 flatcamGUI/ObjectUI.py:1545
+msgid "Parameters"
+msgstr "Parameters"
+
+#: flatcamGUI/FlatCAMGUI.py:5168 flatcamGUI/ObjectUI.py:1548
+msgid "FlatCAM CNC parameters"
+msgstr "FlatCAM CNC-Parameter"
+
+#: flatcamGUI/FlatCAMGUI.py:5169 flatcamGUI/ObjectUI.py:1549
+msgid "tool = tool number"
+msgstr "tool = Werkzeugnummer"
+
+#: flatcamGUI/FlatCAMGUI.py:5170 flatcamGUI/ObjectUI.py:1550
+msgid "tooldia = tool diameter"
+msgstr "tooldia = Werkzeugdurchmesser"
+
+#: flatcamGUI/FlatCAMGUI.py:5171 flatcamGUI/ObjectUI.py:1551
+msgid "t_drills = for Excellon, total number of drills"
+msgstr "t_drills = für Excellon die Gesamtzahl der Bohrer"
+
+#: flatcamGUI/FlatCAMGUI.py:5172 flatcamGUI/ObjectUI.py:1552
+msgid "x_toolchange = X coord for Toolchange"
+msgstr "x_toolchange = X-Koord für Werkzeugwechsel"
+
+#: flatcamGUI/FlatCAMGUI.py:5173 flatcamGUI/ObjectUI.py:1553
+msgid "y_toolchange = Y coord for Toolchange"
+msgstr "y_toolchange = Y-Koord für Werkzeugwechsel"
+
+#: flatcamGUI/FlatCAMGUI.py:5174 flatcamGUI/ObjectUI.py:1554
+msgid "z_toolchange = Z coord for Toolchange"
+msgstr "z_toolchange = Z-Koord für Werkzeugwechsel"
+
+#: flatcamGUI/FlatCAMGUI.py:5175
+msgid "z_cut = Z depth for the cut"
+msgstr "z_cut = Z Tiefe für den Schnitt"
+
+#: flatcamGUI/FlatCAMGUI.py:5176
+msgid "z_move = Z height for travel"
+msgstr "z_move = Z Höhe für die Reise"
+
+#: flatcamGUI/FlatCAMGUI.py:5177 flatcamGUI/ObjectUI.py:1557
+msgid "z_depthpercut = the step value for multidepth cut"
+msgstr "z_depthpercut =der Schrittwert für den mehrstufigen Schnitt"
+
+#: flatcamGUI/FlatCAMGUI.py:5178 flatcamGUI/ObjectUI.py:1558
+msgid "spindlesspeed = the value for the spindle speed"
+msgstr "spindlesspeed =der Wert für die Spindeldrehzahl"
+
+#: flatcamGUI/FlatCAMGUI.py:5179 flatcamGUI/ObjectUI.py:1559
+msgid "dwelltime = time to dwell to allow the spindle to reach it's set RPM"
+msgstr ""
+"dwelltime = Zeit zum Verweilen, damit die Spindel ihre eingestellte Drehzahl "
+"erreicht"
+
+#: flatcamGUI/FlatCAMGUI.py:5200
+msgid "NCC Tool Options"
+msgstr "NCC-Tooloptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5203 flatcamGUI/FlatCAMGUI.py:5304
+#: flatcamGUI/FlatCAMGUI.py:5383 flatcamGUI/FlatCAMGUI.py:5442
+#: flatcamGUI/FlatCAMGUI.py:5545 flatcamGUI/FlatCAMGUI.py:5606
+#: flatcamGUI/FlatCAMGUI.py:5805 flatcamGUI/FlatCAMGUI.py:5932
+msgid "<b>Parameters:</b>"
+msgstr "<b>Parameter:</b>"
+
+#: flatcamGUI/FlatCAMGUI.py:5213 flatcamGUI/FlatCAMGUI.py:5943
+msgid "Tools dia:"
+msgstr "Werkzeug durchmesser:"
+
+#: flatcamGUI/FlatCAMGUI.py:5215
+msgid "Diameters of the cutting tools, separated by ','"
+msgstr "Durchmesser der Schneidwerkzeuge, getrennt durch ','"
+
+#: flatcamGUI/FlatCAMGUI.py:5223 flatcamTools/ToolNonCopperClear.py:167
+#, python-format
+msgid ""
+"How much (fraction) of the tool width to overlap each tool pass.\n"
+"Example:\n"
+"A value here of 0.25 means 25% from the tool diameter found above.\n"
+"\n"
+"Adjust the value starting with lower values\n"
+"and increasing it if areas that should be cleared are still \n"
+"not cleared.\n"
+"Lower values = faster processing, faster execution on PCB.\n"
+"Higher values = slow processing and slow execution on CNC\n"
+"due of too many paths."
+msgstr ""
+"Wie viel (Bruchteil) der Werkzeugbreite, um jeden Werkzeugdurchgang zu "
+"überlappen.\n"
+"Beispiel:\n"
+"Ein Wert von 0,25 bedeutet hier 25% des oben angegebenen "
+"Werkzeugdurchmessers.\n"
+"\n"
+"Passen Sie den Wert an, indem Sie mit niedrigeren Werten beginnen\n"
+"und erhöhen Sie es, wenn Bereiche, die gelöscht werden sollten, noch "
+"vorhanden sind\n"
+"ungeklärt.\n"
+"Niedrigere Werte = schnellere Verarbeitung, schnellere Ausführung auf "
+"Leiterplatten.\n"
+"Höhere Werte = langsame Bearbeitung und langsame Ausführung auf der CNC\n"
+"wegen zu vieler Wege."
+
+#: flatcamGUI/FlatCAMGUI.py:5239 flatcamTools/ToolNonCopperClear.py:183
+msgid "Bounding box margin."
+msgstr "Begrenzungsrahmenrand."
+
+#: flatcamGUI/FlatCAMGUI.py:5248 flatcamTools/ToolNonCopperClear.py:192
+#: flatcamTools/ToolPaint.py:190
+msgid ""
+"Algorithm for non-copper clearing:<BR><B>Standard</B>: Fixed step inwards."
+"<BR><B>Seed-based</B>: Outwards from seed.<BR><B>Line-based</B>: Parallel "
+"lines."
+msgstr ""
+"Algorithmus für das Nicht-Kupfer-Clearing: <B> Standard </B>: Feststehender "
+"Schritt nach innen. <B> Seed-based </B>: Ausgehend vom Saatgut. <BR> <B> "
+"Line-based</B>: Parallele Linien."
+
+#: flatcamGUI/FlatCAMGUI.py:5280 flatcamTools/ToolNonCopperClear.py:224
+#: flatcamTools/ToolPaint.py:222
+msgid "Rest M.:"
+msgstr "Rest M.:"
+
+#: flatcamGUI/FlatCAMGUI.py:5282
+msgid ""
+"If checked, use 'rest machining'.\n"
+"Basically it will clear copper outside PCB features,\n"
+"using the biggest tool and continue with the next tools,\n"
+"from bigger to smaller, to clear areas of copper that\n"
+"could not be cleared by previous tool.\n"
+"If not checked, use the standard algorithm."
+msgstr ""
+"Wenn aktiviert, verwenden Sie \"Restbearbeitung\".\n"
+"Grundsätzlich wird Kupfer außerhalb der PCB-Merkmale gelöscht.\n"
+"das größte Werkzeug verwenden und mit den nächsten Werkzeugen fortfahren,\n"
+"von größeren zu kleineren, um Kupferbereiche zu reinigen\n"
+"konnte nicht mit dem vorherigen Tool gelöscht werden.\n"
+"Wenn nicht aktiviert, verwenden Sie den Standardalgorithmus."
+
+#: flatcamGUI/FlatCAMGUI.py:5301
+msgid "Cutout Tool Options"
+msgstr "Ausschnittwerkzeug-Optionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5306 flatcamGUI/ObjectUI.py:402
+msgid ""
+"Create toolpaths to cut around\n"
+"the PCB and separate it from\n"
+"the original board."
+msgstr ""
+"Erstellen Sie Werkzeugwege zum Schneiden\n"
+"die PCB und trennen Sie es von\n"
+"das ursprüngliche Brett."
+
+#: flatcamGUI/FlatCAMGUI.py:5325
+msgid ""
+"Distance from objects at which\n"
+"to draw the cutout."
+msgstr ""
+"Entfernung von Objekten bei denen\n"
+"den Ausschnitt zeichnen."
+
+#: flatcamGUI/FlatCAMGUI.py:5332 flatcamTools/ToolCutOut.py:96
+msgid "Gap size:"
+msgstr "Spaltgröße:"
+
+#: flatcamGUI/FlatCAMGUI.py:5334
+msgid ""
+"Size of the gaps in the toolpath\n"
+"that will remain to hold the\n"
+"board in place."
+msgstr ""
+"Größe der Lücken im Werkzeugweg\n"
+"das wird bleiben, um das zu halten\n"
+"Board an Ort und Stelle."
+
+#: flatcamGUI/FlatCAMGUI.py:5342 flatcamTools/ToolCutOut.py:133
+msgid "Gaps:"
+msgstr "Spalt:"
+
+#: flatcamGUI/FlatCAMGUI.py:5344
+msgid ""
+"Number of bridge gaps used for the cutout.\n"
+"There can be maximum 8 bridges/gaps.\n"
+"The choices are:\n"
+"- lr    - left + right\n"
+"- tb    - top + bottom\n"
+"- 4     - left + right +top + bottom\n"
+"- 2lr   - 2*left + 2*right\n"
+"- 2tb  - 2*top + 2*bottom\n"
+"- 8     - 2*left + 2*right +2*top + 2*bottom"
+msgstr ""
+"Anzahl der für den Ausschnitt verwendeten Brückenlücken.\n"
+"Es können maximal 8 Brücken / Lücken vorhanden sein.\n"
+"Die Wahlmöglichkeiten sind:\n"
+"- lr \t- links + rechts\n"
+"- tb \t- oben + unten\n"
+"- 4 \t- links + rechts + oben + unten\n"
+"- 2lr \t- 2 * links + 2 * rechts\n"
+"- 2 tb \t- 2 * oben + 2 * unten\n"
+"- 8 \t- 2 * links + 2 * rechts + 2 * oben + 2 * unten"
+
+#: flatcamGUI/FlatCAMGUI.py:5365 flatcamTools/ToolCutOut.py:115
+msgid "Convex Sh.:"
+msgstr "Konvexe Form .:"
+
+#: flatcamGUI/FlatCAMGUI.py:5367 flatcamTools/ToolCutOut.py:117
+msgid "Create a convex shape surrounding the entire PCB."
+msgstr "Erstellen Sie eine konvexe Form, die die gesamte Leiterplatte umgibt."
+
+#: flatcamGUI/FlatCAMGUI.py:5380
+msgid "2Sided Tool Options"
+msgstr "2Seitige Werkzeugoptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5385
+msgid ""
+"A tool to help in creating a double sided\n"
+"PCB using alignment holes."
+msgstr ""
+"Ein Werkzeug, das beim Erstellen eines doppelseitigen Dokuments hilft\n"
+"PCB mit Ausrichtungslöchern."
+
+#: flatcamGUI/FlatCAMGUI.py:5395 flatcamTools/ToolDblSided.py:235
+msgid "Drill diam.:"
+msgstr "Bohrdurchmesser:"
+
+#: flatcamGUI/FlatCAMGUI.py:5397 flatcamTools/ToolDblSided.py:226
+#: flatcamTools/ToolDblSided.py:237
+msgid "Diameter of the drill for the alignment holes."
+msgstr "Durchmesser des Bohrers für die Ausrichtungslöcher."
+
+#: flatcamGUI/FlatCAMGUI.py:5404
+msgid "X"
+msgstr "X"
+
+#: flatcamGUI/FlatCAMGUI.py:5405
+msgid "Y"
+msgstr "Y"
+
+#: flatcamGUI/FlatCAMGUI.py:5406 flatcamTools/ToolDblSided.py:120
+msgid "Mirror Axis:"
+msgstr "Spiegelachse:"
+
+#: flatcamGUI/FlatCAMGUI.py:5408 flatcamTools/ToolDblSided.py:122
+msgid "Mirror vertically (X) or horizontally (Y)."
+msgstr "Vertikal spiegeln (X) oder horizontal (Y)."
+
+#: flatcamGUI/FlatCAMGUI.py:5417
+msgid "Point"
+msgstr "Punkt"
+
+#: flatcamGUI/FlatCAMGUI.py:5418
+msgid "Box"
+msgstr "Box"
+
+#: flatcamGUI/FlatCAMGUI.py:5419 flatcamTools/ToolDblSided.py:133
+msgid "Axis Ref:"
+msgstr "Achsenreferenz:"
+
+#: flatcamGUI/FlatCAMGUI.py:5421
+msgid ""
+"The axis should pass through a <b>point</b> or cut\n"
+" a specified <b>box</b> (in a Geometry object) in \n"
+"the middle."
+msgstr ""
+"Die Achse sollte einen <b>Punkt</b> durchlaufen oder schneiden\n"
+"ein angegebenes <b>Feld</b> (in einem Geometrieobjekt) in\n"
+"die Mitte."
+
+#: flatcamGUI/FlatCAMGUI.py:5437
+msgid "Paint Tool Options"
+msgstr "Optionen für das Paint werkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:5444 flatcamGUI/ObjectUI.py:1299
+msgid ""
+"Creates tool paths to cover the\n"
+"whole area of a polygon (remove\n"
+"all copper). You will be asked\n"
+"to click on the desired polygon."
+msgstr ""
+"Erzeugt Werkzeugpfade zur Abdeckung\n"
+"gesamte Fläche eines Polygons (entfernen\n"
+"alles Kupfer). Du wirst gefragt\n"
+"Klicken Sie auf das gewünschte Polygon."
+
+#: flatcamGUI/FlatCAMGUI.py:5468
+msgid ""
+"How much (fraction) of the tool\n"
+"width to overlap each tool pass."
+msgstr ""
+"Wie viel (Bruchteil) des Werkzeugs\n"
+"Breite, um jeden Werkzeugdurchgang zu überlappen."
+
+#: flatcamGUI/FlatCAMGUI.py:5522 flatcamTools/ToolPaint.py:237
+msgid "Selection:"
+msgstr "Auswahl:"
+
+#: flatcamGUI/FlatCAMGUI.py:5524
+msgid "How to select the polygons to paint."
+msgstr "So wählen Sie die Polygone zum Malen aus."
+
+#: flatcamGUI/FlatCAMGUI.py:5528
+msgid "Single"
+msgstr "Single"
+
+#: flatcamGUI/FlatCAMGUI.py:5542
+msgid "Film Tool Options"
+msgstr "Filmwerkzeugoptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5547
+msgid ""
+"Create a PCB film from a Gerber or Geometry\n"
+"FlatCAM object.\n"
+"The file is saved in SVG format."
+msgstr ""
+"Erstellen Sie einen PCB-Film aus einem Gerber oder einer Geometrie\n"
+"FlatCAM-Objekt\n"
+"Die Datei wird im SVG-Format gespeichert."
+
+#: flatcamGUI/FlatCAMGUI.py:5556
+msgid "Pos"
+msgstr "Positiv"
+
+#: flatcamGUI/FlatCAMGUI.py:5557
+msgid "Neg"
+msgstr "Negativ"
+
+#: flatcamGUI/FlatCAMGUI.py:5558 flatcamTools/ToolFilm.py:116
+msgid "Film Type:"
+msgstr "Filmtyp:"
+
+#: flatcamGUI/FlatCAMGUI.py:5560 flatcamTools/ToolFilm.py:118
+msgid ""
+"Generate a Positive black film or a Negative film.\n"
+"Positive means that it will print the features\n"
+"with black on a white canvas.\n"
+"Negative means that it will print the features\n"
+"with white on a black canvas.\n"
+"The Film format is SVG."
+msgstr ""
+"Erzeugen Sie einen positiven schwarzen Film oder einen Negativfilm.\n"
+"Positiv bedeutet, dass die Funktionen gedruckt werden\n"
+"mit schwarz auf einer weißen leinwand.\n"
+"Negativ bedeutet, dass die Features gedruckt werden\n"
+"mit weiß auf einer schwarzen leinwand.\n"
+"Das Filmformat ist SVG."
+
+#: flatcamGUI/FlatCAMGUI.py:5571 flatcamTools/ToolFilm.py:130
+msgid "Border:"
+msgstr "Rand:"
+
+#: flatcamGUI/FlatCAMGUI.py:5573 flatcamTools/ToolFilm.py:132
+msgid ""
+"Specify a border around the object.\n"
+"Only for negative film.\n"
+"It helps if we use as a Box Object the same \n"
+"object as in Film Object. It will create a thick\n"
+"black bar around the actual print allowing for a\n"
+"better delimitation of the outline features which are of\n"
+"white color like the rest and which may confound with the\n"
+"surroundings if not for this border."
+msgstr ""
+"Geben Sie einen Rahmen um das Objekt an.\n"
+"Nur für Negativfilm.\n"
+"Es hilft, wenn wir als Boxobjekt das gleiche verwenden\n"
+"Objekt wie in Filmobjekt. Es wird ein dickes schaffen\n"
+"schwarzer Balken um den tatsächlichen Druck, so dass a\n"
+"bessere Abgrenzung der Gliederungsmerkmale von\n"
+"weiße Farbe wie der Rest und die mit der verwechseln kann\n"
+"Umgebung, wenn nicht für diese Grenze."
+
+#: flatcamGUI/FlatCAMGUI.py:5586 flatcamTools/ToolFilm.py:144
+msgid "Scale Stroke:"
+msgstr "Skalierungshub:"
+
+#: flatcamGUI/FlatCAMGUI.py:5588 flatcamTools/ToolFilm.py:146
+msgid ""
+"Scale the line stroke thickness of each feature in the SVG file.\n"
+"It means that the line that envelope each SVG feature will be thicker or "
+"thinner,\n"
+"therefore the fine features may be more affected by this parameter."
+msgstr ""
+"Skalieren Sie die Strichstärke der einzelnen Features in der SVG-Datei.\n"
+"Dies bedeutet, dass die Linie, die jedes SVG-Feature einhüllt, dicker oder "
+"dünner ist.\n"
+"Daher können die Feinheiten von diesem Parameter stärker beeinflusst werden."
+
+#: flatcamGUI/FlatCAMGUI.py:5603
+msgid "Panelize Tool Options"
+msgstr "Panelize Werkzeugoptionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5608
+msgid ""
+"Create an object that contains an array of (x, y) elements,\n"
+"each element is a copy of the source object spaced\n"
+"at a X distance, Y distance of each other."
+msgstr ""
+"Erstellen Sie ein Objekt, das ein Array von (x, y) Elementen enthält.\n"
+"Jedes Element ist eine Kopie des Quellobjekts\n"
+"in einem X-Abstand, Y-Abstand voneinander."
+
+#: flatcamGUI/FlatCAMGUI.py:5619 flatcamTools/ToolPanelize.py:113
+msgid "Spacing cols:"
+msgstr "Abstandspalten:"
+
+#: flatcamGUI/FlatCAMGUI.py:5621 flatcamTools/ToolPanelize.py:115
+msgid ""
+"Spacing between columns of the desired panel.\n"
+"In current units."
+msgstr ""
+"Abstand zwischen den Spalten des gewünschten Bereichs.\n"
+"In aktuellen Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:5629 flatcamTools/ToolPanelize.py:122
+msgid "Spacing rows:"
+msgstr "Abstand Reihen:"
+
+#: flatcamGUI/FlatCAMGUI.py:5631 flatcamTools/ToolPanelize.py:124
+msgid ""
+"Spacing between rows of the desired panel.\n"
+"In current units."
+msgstr ""
+"Abstand zwischen den Reihen des gewünschten Feldes.\n"
+"In aktuellen Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:5639 flatcamTools/ToolPanelize.py:131
+msgid "Columns:"
+msgstr "Säulen:"
+
+#: flatcamGUI/FlatCAMGUI.py:5641 flatcamTools/ToolPanelize.py:133
+msgid "Number of columns of the desired panel"
+msgstr "Anzahl der Spalten des gewünschten Bereichs"
+
+#: flatcamGUI/FlatCAMGUI.py:5648 flatcamTools/ToolPanelize.py:139
+msgid "Rows:"
+msgstr "Reihen:"
+
+#: flatcamGUI/FlatCAMGUI.py:5650 flatcamTools/ToolPanelize.py:141
+msgid "Number of rows of the desired panel"
+msgstr "Anzahl der Zeilen des gewünschten Panels"
+
+#: flatcamGUI/FlatCAMGUI.py:5656
+msgid "Gerber"
+msgstr "Gerber"
+
+#: flatcamGUI/FlatCAMGUI.py:5657
+msgid "Geo"
+msgstr "Geo"
+
+#: flatcamGUI/FlatCAMGUI.py:5658 flatcamTools/ToolPanelize.py:148
+msgid "Panel Type:"
+msgstr "Panel-Typ:"
+
+#: flatcamGUI/FlatCAMGUI.py:5660
+msgid ""
+"Choose the type of object for the panel object:\n"
+"- Gerber\n"
+"- Geometry"
+msgstr ""
+"Wählen Sie den Objekttyp für das Panel-Objekt:\n"
+"- Gerber\n"
+"- Geometrie"
+
+#: flatcamGUI/FlatCAMGUI.py:5669
+msgid "Constrain within:"
+msgstr "Beschränkung innerhalb:"
+
+#: flatcamGUI/FlatCAMGUI.py:5671 flatcamTools/ToolPanelize.py:160
+msgid ""
+"Area define by DX and DY within to constrain the panel.\n"
+"DX and DY values are in current units.\n"
+"Regardless of how many columns and rows are desired,\n"
+"the final panel will have as many columns and rows as\n"
+"they fit completely within selected area."
+msgstr ""
+"Bereich definieren durch DX und DY innerhalb des Panels.\n"
+"DX- und DY-Werte sind in aktuellen Einheiten angegeben.\n"
+"Unabhängig davon, wie viele Spalten und Zeilen gewünscht werden,\n"
+"Das letzte Panel enthält so viele Spalten und Zeilen wie\n"
+"Sie passen vollständig in den ausgewählten Bereich."
+
+#: flatcamGUI/FlatCAMGUI.py:5680 flatcamTools/ToolPanelize.py:169
+msgid "Width (DX):"
+msgstr "Breite (DX):"
+
+#: flatcamGUI/FlatCAMGUI.py:5682 flatcamTools/ToolPanelize.py:171
+msgid ""
+"The width (DX) within which the panel must fit.\n"
+"In current units."
+msgstr ""
+"Die Breite (DX), in die das Panel passen muss.\n"
+"In aktuellen Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:5689 flatcamTools/ToolPanelize.py:177
+msgid "Height (DY):"
+msgstr "Höhe (DY):"
+
+#: flatcamGUI/FlatCAMGUI.py:5691 flatcamTools/ToolPanelize.py:179
+msgid ""
+"The height (DY)within which the panel must fit.\n"
+"In current units."
+msgstr ""
+"Die Höhe (DY), in die die Platte passen muss.\n"
+"In aktuellen Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:5705
+msgid "Calculators Tool Options"
+msgstr "Rechner-Tool-Optionen"
+
+#: flatcamGUI/FlatCAMGUI.py:5708
+msgid "<b>V-Shape Tool Calculator:</b>"
+msgstr "<b> V-Shape-Werkzeug Rechner: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:5710
+msgid ""
+"Calculate the tool diameter for a given V-shape tool,\n"
+"having the tip diameter, tip angle and\n"
+"depth-of-cut as parameters."
+msgstr ""
+"Berechnen Sie den Werkzeugdurchmesser für ein gegebenes V-förmiges "
+"Werkzeug.\n"
+"mit dem Spitzendurchmesser, Spitzenwinkel und\n"
+"Schnitttiefe als Parameter."
+
+#: flatcamGUI/FlatCAMGUI.py:5721 flatcamTools/ToolCalculators.py:94
+msgid "Tip Diameter:"
+msgstr "Spitzendurchmesser"
+
+#: flatcamGUI/FlatCAMGUI.py:5723
+msgid ""
+"This is the tool tip diameter.\n"
+"It is specified by manufacturer."
+msgstr ""
+"Dies ist der Werkzeugspitzendurchmesser.\n"
+"Es wird vom Hersteller angegeben."
+
+#: flatcamGUI/FlatCAMGUI.py:5731
+msgid "Tip angle:"
+msgstr "Spitzenwinkel:"
+
+#: flatcamGUI/FlatCAMGUI.py:5733
+msgid ""
+"This is the angle on the tip of the tool.\n"
+"It is specified by manufacturer."
+msgstr ""
+"Dies ist der Winkel an der Spitze des Werkzeugs.\n"
+"Es wird vom Hersteller angegeben."
+
+#: flatcamGUI/FlatCAMGUI.py:5743
+msgid ""
+"This is depth to cut into material.\n"
+"In the CNCJob object it is the CutZ parameter."
+msgstr ""
+"Dies ist die Tiefe zum Schneiden in Material.\n"
+"Im CNCJob-Objekt ist dies der Parameter CutZ."
+
+#: flatcamGUI/FlatCAMGUI.py:5750
+msgid "<b>ElectroPlating Calculator:</b>"
+msgstr "<b> Galvano-Rechner: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:5752 flatcamTools/ToolCalculators.py:152
+msgid ""
+"This calculator is useful for those who plate the via/pad/drill holes,\n"
+"using a method like grahite ink or calcium hypophosphite ink or palladium "
+"chloride."
+msgstr ""
+"Dieser Rechner ist nützlich für diejenigen, die die Durchgangslöcher / "
+"Bohrungen / Bohrungen\n"
+"unter Verwendung einer Methode wie Grahit-Tinte oder Calcium-Hypophosphit-"
+"Tinte oder Palladiumchlorid."
+
+#: flatcamGUI/FlatCAMGUI.py:5762 flatcamTools/ToolCalculators.py:161
+msgid "Board Length:"
+msgstr "PCB Länge:"
+
+#: flatcamGUI/FlatCAMGUI.py:5764 flatcamTools/ToolCalculators.py:165
+msgid "This is the board length. In centimeters."
+msgstr "Dies ist die Boardlänge. In Zentimeter"
+
+#: flatcamGUI/FlatCAMGUI.py:5770 flatcamTools/ToolCalculators.py:167
+msgid "Board Width:"
+msgstr "PCB Breite:"
+
+#: flatcamGUI/FlatCAMGUI.py:5772 flatcamTools/ToolCalculators.py:171
+msgid "This is the board width.In centimeters."
+msgstr "Dies ist die Breite der Platte in Zentimetern."
+
+#: flatcamGUI/FlatCAMGUI.py:5777 flatcamTools/ToolCalculators.py:173
+msgid "Current Density:"
+msgstr "Stromdichte:"
+
+#: flatcamGUI/FlatCAMGUI.py:5780 flatcamTools/ToolCalculators.py:177
+msgid ""
+"Current density to pass through the board. \n"
+"In Amps per Square Feet ASF."
+msgstr ""
+"Stromdichte durch die Platine.\n"
+"In Ampere pro Quadratfuß ASF."
+
+#: flatcamGUI/FlatCAMGUI.py:5786 flatcamTools/ToolCalculators.py:181
+msgid "Copper Growth:"
+msgstr "Kupferwachstum:"
+
+#: flatcamGUI/FlatCAMGUI.py:5789 flatcamTools/ToolCalculators.py:185
+msgid ""
+"How thick the copper growth is intended to be.\n"
+"In microns."
+msgstr ""
+"Wie dick soll das Kupferwachstum sein.\n"
+"In Mikrometern"
+
+#: flatcamGUI/FlatCAMGUI.py:5802
+msgid "Transform Tool Options"
+msgstr "Optionen für das Umwandlungswerkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:5807
+msgid ""
+"Various transformations that can be applied\n"
+"on a FlatCAM object."
+msgstr ""
+"Verschiedene Transformationen, die angewendet werden können\n"
+"auf einem FlatCAM-Objekt."
+
+#: flatcamGUI/FlatCAMGUI.py:5817
+msgid "Rotate Angle:"
+msgstr "Winkel drehen:"
+
+#: flatcamGUI/FlatCAMGUI.py:5819
+msgid "Angle for rotation. In degrees."
+msgstr "Drehwinkel. In grad."
+
+#: flatcamGUI/FlatCAMGUI.py:5826
+msgid "Skew_X angle:"
+msgstr "Neigungswinkel X:"
+
+#: flatcamGUI/FlatCAMGUI.py:5828
+msgid "Angle for Skew/Shear on X axis. In degrees."
+msgstr "Winkel für Neigung / Scherung auf der X-Achse. In grad."
+
+#: flatcamGUI/FlatCAMGUI.py:5835
+msgid "Skew_Y angle:"
+msgstr "Neigungswinkel Y:"
+
+#: flatcamGUI/FlatCAMGUI.py:5837
+msgid "Angle for Skew/Shear on Y axis. In degrees."
+msgstr "Winkel für Neigung / Scherung auf der Y-Achse. In grad."
+
+#: flatcamGUI/FlatCAMGUI.py:5844
+msgid "Scale_X factor:"
+msgstr "Skalierung des X-Faktors:"
+
+#: flatcamGUI/FlatCAMGUI.py:5846
+msgid "Factor for scaling on X axis."
+msgstr "Faktor für die Skalierung auf der X-Achse."
+
+#: flatcamGUI/FlatCAMGUI.py:5853
+msgid "Scale_Y factor:"
+msgstr "Skalierung des Y-Faktors:"
+
+#: flatcamGUI/FlatCAMGUI.py:5855
+msgid "Factor for scaling on Y axis."
+msgstr "Faktor für die Skalierung auf der Y-Achse."
+
+#: flatcamGUI/FlatCAMGUI.py:5863
+msgid ""
+"Scale the selected object(s)\n"
+"using the Scale_X factor for both axis."
+msgstr ""
+"Skalieren Sie die ausgewählten Objekte\n"
+"Verwenden des Skalierungsfaktors X für beide Achsen."
+
+#: flatcamGUI/FlatCAMGUI.py:5871 flatcamTools/ToolTransform.py:210
+msgid ""
+"Scale the selected object(s)\n"
+"using the origin reference when checked,\n"
+"and the center of the biggest bounding box\n"
+"of the selected objects when unchecked."
+msgstr ""
+"Skalieren Sie die ausgewählten Objekte\n"
+"unter Verwendung der Ursprungsreferenz, wenn geprüft\n"
+"und die Mitte der größten Begrenzungsbox\n"
+"der ausgewählten Objekte, wenn sie nicht markiert sind."
+
+#: flatcamGUI/FlatCAMGUI.py:5880
+msgid "Offset_X val:"
+msgstr "Offset X Wert:"
+
+#: flatcamGUI/FlatCAMGUI.py:5882
+msgid "Distance to offset on X axis. In current units."
+msgstr "Abstand zum Offset auf der X-Achse. In aktuellen Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:5889
+msgid "Offset_Y val:"
+msgstr "Offset Y-Wert:"
+
+#: flatcamGUI/FlatCAMGUI.py:5891
+msgid "Distance to offset on Y axis. In current units."
+msgstr "Abstand zum Offset auf der Y-Achse. In aktuellen Einheiten."
+
+#: flatcamGUI/FlatCAMGUI.py:5897
+msgid "Mirror Reference"
+msgstr "Spiegelreferenz"
+
+#: flatcamGUI/FlatCAMGUI.py:5899 flatcamTools/ToolTransform.py:314
+msgid ""
+"Flip the selected object(s)\n"
+"around the point in Point Entry Field.\n"
+"\n"
+"The point coordinates can be captured by\n"
+"left click on canvas together with pressing\n"
+"SHIFT key. \n"
+"Then click Add button to insert coordinates.\n"
+"Or enter the coords in format (x, y) in the\n"
+"Point Entry field and click Flip on X(Y)"
+msgstr ""
+"Die ausgewählten Objekte kippen\n"
+"um den Punkt im Eingabefeld.\n"
+"\n"
+"Die Punktkoordinaten können mit erfasst werden\n"
+"Klicken Sie mit der linken Maustaste auf die Leinwand\n"
+"Shift Taste.\n"
+"Klicken Sie dann auf die Schaltfläche Hinzufügen, um die Koordinaten "
+"einzufügen.\n"
+"Oder geben Sie die Koordinaten im Format (x, y) in ein\n"
+"Punkt-Eingabefeld und klicken Sie auf X (Y) drehen"
+
+#: flatcamGUI/FlatCAMGUI.py:5910
+msgid " Mirror Ref. Point:"
+msgstr "Spiegelref. Punkt:"
+
+#: flatcamGUI/FlatCAMGUI.py:5912 flatcamTools/ToolTransform.py:327
+msgid ""
+"Coordinates in format (x, y) used as reference for mirroring.\n"
+"The 'x' in (x, y) will be used when using Flip on X and\n"
+"the 'y' in (x, y) will be used when using Flip on Y and"
+msgstr ""
+"Koordinaten im Format (x, y), die als Referenz für die Spiegelung verwendet "
+"werden.\n"
+"Das 'x' in (x, y) wird verwendet, wenn Sie bei X und\n"
+"Das 'y' in (x, y) wird verwendet, wenn Flip auf Y und verwendet wird"
+
+#: flatcamGUI/FlatCAMGUI.py:5929
+msgid "SolderPaste Tool Options"
+msgstr "Optionen für das Lötpaste-Werkzeug"
+
+#: flatcamGUI/FlatCAMGUI.py:5934
+msgid ""
+"A tool to create GCode for dispensing\n"
+"solder paste onto a PCB."
+msgstr ""
+"Ein Werkzeug zum Erstellen von GCode für die Ausgabe\n"
+"Lotpaste auf eine Leiterplatte."
+
+#: flatcamGUI/FlatCAMGUI.py:5945
+msgid "Diameters of nozzle tools, separated by ','"
+msgstr "Durchmesser der Düsenwerkzeuge, getrennt durch ','"
+
+#: flatcamGUI/FlatCAMGUI.py:5952
+msgid "<b>New Nozzle Dia:</b>"
+msgstr "<b> Neuer Düsendurchmesser: </b>"
+
+#: flatcamGUI/FlatCAMGUI.py:5954 flatcamTools/ToolSolderPaste.py:103
+msgid "Diameter for the new Nozzle tool to add in the Tool Table"
+msgstr ""
+"Durchmesser für das neue Düsenwerkzeug, das in die Werkzeugtabelle eingefügt "
+"werden soll"
+
+#: flatcamGUI/FlatCAMGUI.py:5962 flatcamTools/ToolSolderPaste.py:166
+msgid "Z Dispense Start:"
+msgstr "Z Dosierbeginn:"
+
+#: flatcamGUI/FlatCAMGUI.py:5964 flatcamTools/ToolSolderPaste.py:168
+msgid "The height (Z) when solder paste dispensing starts."
+msgstr "Die Höhe (Z) bei der Lotpastendosierung."
+
+#: flatcamGUI/FlatCAMGUI.py:5971 flatcamTools/ToolSolderPaste.py:174
+msgid "Z Dispense:"
+msgstr "Z-Abgabe:"
+
+#: flatcamGUI/FlatCAMGUI.py:5973 flatcamTools/ToolSolderPaste.py:176
+msgid "The height (Z) when doing solder paste dispensing."
+msgstr "Die Höhe (Z) bei der Lotpastendosierung."
+
+#: flatcamGUI/FlatCAMGUI.py:5980 flatcamTools/ToolSolderPaste.py:183
+msgid "Z Dispense Stop:"
+msgstr "Z Abgabestopp:"
+
+#: flatcamGUI/FlatCAMGUI.py:5982 flatcamTools/ToolSolderPaste.py:185
+msgid "The height (Z) when solder paste dispensing stops."
+msgstr "Die Höhe (Z) bei der Lotpastendosierung stoppt."
+
+#: flatcamGUI/FlatCAMGUI.py:5989 flatcamTools/ToolSolderPaste.py:191
+msgid "Z Travel:"
+msgstr "Z Reise:"
+
+#: flatcamGUI/FlatCAMGUI.py:5991 flatcamTools/ToolSolderPaste.py:193
+msgid ""
+"The height (Z) for travel between pads\n"
+"(without dispensing solder paste)."
+msgstr ""
+"Die Höhe (Z) für den Weg zwischen Pads\n"
+"(ohne Lotpaste zu dosieren)."
+
+#: flatcamGUI/FlatCAMGUI.py:5999 flatcamTools/ToolSolderPaste.py:200
+msgid "Z Toolchange:"
+msgstr "Z Werkzeugwechsel:"
+
+#: flatcamGUI/FlatCAMGUI.py:6001 flatcamTools/ToolSolderPaste.py:202
+msgid "The height (Z) for tool (nozzle) change."
+msgstr "Die Höhe (Z) für Werkzeug (Düse) ändert sich."
+
+#: flatcamGUI/FlatCAMGUI.py:6008 flatcamTools/ToolSolderPaste.py:208
+msgid "XY Toolchange:"
+msgstr "XY-Werkzeugwechsel:"
+
+#: flatcamGUI/FlatCAMGUI.py:6010 flatcamTools/ToolSolderPaste.py:210
+msgid ""
+"The X,Y location for tool (nozzle) change.\n"
+"The format is (x, y) where x and y are real numbers."
+msgstr ""
+"Die X, Y-Position für Werkzeug (Düse) ändert sich.\n"
+"Das Format ist (x, y), wobei x und y reelle Zahlen sind."
+
+#: flatcamGUI/FlatCAMGUI.py:6018 flatcamTools/ToolSolderPaste.py:217
+msgid "Feedrate X-Y:"
+msgstr "Vorschub X-Y:"
+
+#: flatcamGUI/FlatCAMGUI.py:6020 flatcamTools/ToolSolderPaste.py:219
+msgid "Feedrate (speed) while moving on the X-Y plane."
+msgstr "Vorschub (Geschwindigkeit) während der Bewegung auf der X-Y-Ebene."
+
+#: flatcamGUI/FlatCAMGUI.py:6027 flatcamTools/ToolSolderPaste.py:225
+msgid "Feedrate Z:"
+msgstr "Vorschub Z:"
+
+#: flatcamGUI/FlatCAMGUI.py:6029 flatcamTools/ToolSolderPaste.py:227
+msgid ""
+"Feedrate (speed) while moving vertically\n"
+"(on Z plane)."
+msgstr ""
+"Vorschub (Geschwindigkeit) bei vertikaler Bewegung\n"
+"(auf der Z-Ebene)."
+
+#: flatcamGUI/FlatCAMGUI.py:6037 flatcamTools/ToolSolderPaste.py:234
+msgid "Feedrate Z Dispense:"
+msgstr "Vorschub Z Dosierung:"
+
+#: flatcamGUI/FlatCAMGUI.py:6039 flatcamTools/ToolSolderPaste.py:236
+msgid ""
+"Feedrate (speed) while moving up vertically\n"
+" to Dispense position (on Z plane)."
+msgstr ""
+"Vorschub (Geschwindigkeit) bei vertikaler Bewegung\n"
+"  zur Ausgabeposition (auf der Z-Ebene)."
+
+#: flatcamGUI/FlatCAMGUI.py:6047 flatcamTools/ToolSolderPaste.py:243
+msgid "Spindle Speed FWD:"
+msgstr "Spindeldrehzahl FWD:"
+
+#: flatcamGUI/FlatCAMGUI.py:6049 flatcamTools/ToolSolderPaste.py:245
+msgid ""
+"The dispenser speed while pushing solder paste\n"
+"through the dispenser nozzle."
+msgstr ""
+"Die Spendergeschwindigkeit beim Schieben der Lötpaste\n"
+"durch die Spenderdüse."
+
+#: flatcamGUI/FlatCAMGUI.py:6057 flatcamTools/ToolSolderPaste.py:252
+msgid "Dwell FWD:"
+msgstr "Verweilzeit FWD:"
+
+#: flatcamGUI/FlatCAMGUI.py:6059 flatcamTools/ToolSolderPaste.py:254
+msgid "Pause after solder dispensing."
+msgstr "Pause nach dem Löten."
+
+#: flatcamGUI/FlatCAMGUI.py:6066 flatcamTools/ToolSolderPaste.py:260
+msgid "Spindle Speed REV:"
+msgstr "Spindeldrehzahl REV:"
+
+#: flatcamGUI/FlatCAMGUI.py:6068 flatcamTools/ToolSolderPaste.py:262
+msgid ""
+"The dispenser speed while retracting solder paste\n"
+"through the dispenser nozzle."
+msgstr ""
+"Die Spendergeschwindigkeit beim Einfahren der Lötpaste\n"
+"durch die Spenderdüse."
+
+#: flatcamGUI/FlatCAMGUI.py:6076 flatcamTools/ToolSolderPaste.py:269
+msgid "Dwell REV:"
+msgstr "Verweilen REV:"
+
+#: flatcamGUI/FlatCAMGUI.py:6078 flatcamTools/ToolSolderPaste.py:271
+msgid ""
+"Pause after solder paste dispenser retracted,\n"
+"to allow pressure equilibrium."
+msgstr ""
+"Pause nachdem Lotpastendispenser eingefahren wurde,\n"
+"das Druckgleichgewicht zu ermöglichen."
+
+#: flatcamGUI/FlatCAMGUI.py:6085 flatcamTools/ToolSolderPaste.py:277
+msgid "PostProcessors:"
+msgstr "Postprozessoren:"
+
+#: flatcamGUI/FlatCAMGUI.py:6087 flatcamTools/ToolSolderPaste.py:279
+msgid "Files that control the GCode generation."
+msgstr "Dateien, die die GCode-Generierung steuern."
+
+#: flatcamGUI/FlatCAMGUI.py:6117 flatcamGUI/FlatCAMGUI.py:6123
+msgid "Idle."
+msgstr "Untätig"
+
+#: flatcamGUI/FlatCAMGUI.py:6147
+msgid "Application started ..."
+msgstr "Bewerbung gestartet ..."
+
+#: flatcamGUI/FlatCAMGUI.py:6148
+msgid "Hello!"
+msgstr "Hello!"
+
+#: flatcamGUI/ObjectUI.py:33
+msgid "FlatCAM Object"
+msgstr "FlatCAM-Objekt"
+
+#: flatcamGUI/ObjectUI.py:58
+msgid ""
+"BASIC is suitable for a beginner. Many parameters\n"
+"are hidden from the user in this mode.\n"
+"ADVANCED mode will make available all parameters.\n"
+"\n"
+"To change the application LEVEL, go to:\n"
+"Edit -> Preferences -> General and check:\n"
+"'APP. LEVEL' radio button."
+msgstr ""
+"BASIC ist für Anfänger geeignet. Viele Parameter\n"
+"werden in diesem Modus für den Benutzer ausgeblendet.\n"
+"Im ADVANCED-Modus werden alle Parameter verfügbar.\n"
+"\n"
+"Um die Anwendung LEVEL zu ändern, gehen Sie zu:\n"
+"Bearbeiten -> Einstellungen -> Allgemein und überprüfen Sie:\n"
+"Optionsfeld \"Anwendungsebene\"."
+
+#: flatcamGUI/ObjectUI.py:79
+msgid "<b>Scale:</b>"
+msgstr "<b> Skalierung: </b>"
+
+#: flatcamGUI/ObjectUI.py:81
+msgid "Change the size of the object."
+msgstr "Ändern Sie die Größe des Objekts."
+
+#: flatcamGUI/ObjectUI.py:89
+msgid "Factor:"
+msgstr "Faktor:"
+
+#: flatcamGUI/ObjectUI.py:91
+msgid ""
+"Factor by which to multiply\n"
+"geometric features of this object."
+msgstr ""
+"Faktor, mit dem sich multiplizieren soll\n"
+"geometrische Merkmale dieses Objekts."
+
+#: flatcamGUI/ObjectUI.py:102
+msgid "Perform scaling operation."
+msgstr "Führen Sie die Skalierung durch."
+
+#: flatcamGUI/ObjectUI.py:108
+msgid "<b>Offset:</b>"
+msgstr "<b>Versatz:</b>"
+
+#: flatcamGUI/ObjectUI.py:110
+msgid "Change the position of this object."
+msgstr "Ändern Sie die Position dieses Objekts."
+
+#: flatcamGUI/ObjectUI.py:117
+msgid "Vector:"
+msgstr "Vektor:"
+
+#: flatcamGUI/ObjectUI.py:119
+msgid ""
+"Amount by which to move the object\n"
+"in the x and y axes in (x, y) format."
+msgstr ""
+"Betrag, um den das Objekt verschoben werden soll\n"
+"in der x- und y-Achse im (x, y) -Format."
+
+#: flatcamGUI/ObjectUI.py:129
+msgid "Perform the offset operation."
+msgstr "Führen Sie den Versatzvorgang aus."
+
+#: flatcamGUI/ObjectUI.py:143
+msgid "Gerber Object"
+msgstr "Gerber-Objekt"
+
+#: flatcamGUI/ObjectUI.py:156
+msgid "Solid   "
+msgstr "Solide"
+
+#: flatcamGUI/ObjectUI.py:164
+msgid "M-Color   "
+msgstr "Mehrfarbig"
+
+#: flatcamGUI/ObjectUI.py:182 flatcamGUI/ObjectUI.py:517
+#: flatcamGUI/ObjectUI.py:836 flatcamGUI/ObjectUI.py:1366
+msgid "<b>Name:</b>"
+msgstr "<b>Name:</b>"
+
+#: flatcamGUI/ObjectUI.py:203
+msgid ""
+"Toggle the display of the Gerber Apertures Table.\n"
+"When unchecked, it will delete all mark shapes\n"
+"that are drawn on canvas."
+msgstr ""
+"Schaltet die Anzeige der Gerber-Apertur-Tabelle um.\n"
+"Wenn das Kontrollkästchen deaktiviert ist, werden alle Markierungsformen "
+"gelöscht\n"
+"das sind auf leinwand gezeichnet."
+
+#: flatcamGUI/ObjectUI.py:214
+msgid "Mark All"
+msgstr "Alles mark"
+
+#: flatcamGUI/ObjectUI.py:216
+msgid ""
+"When checked it will display all the apertures.\n"
+"When unchecked, it will delete all mark shapes\n"
+"that are drawn on canvas."
+msgstr ""
+"Wenn diese Option aktiviert ist, werden alle Öffnungen angezeigt.\n"
+"Wenn das Kontrollkästchen deaktiviert ist, werden alle Markierungsformen "
+"gelöscht\n"
+"das sind auf leinwand gezeichnet."
+
+#: flatcamGUI/ObjectUI.py:244
+msgid "Mark the aperture instances on canvas."
+msgstr "Markieren Sie die Blendeninstanzen auf der Leinwand."
+
+#: flatcamGUI/ObjectUI.py:262
+msgid ""
+"Diameter of the cutting tool.\n"
+"If you want to have an isolation path\n"
+"inside the actual shape of the Gerber\n"
+"feature, use a negative value for\n"
+"this parameter."
+msgstr ""
+"Durchmesser des Schneidewerkzeugs.\n"
+"Wenn Sie einen Isolationspfad haben möchten\n"
+"in der tatsächlichen Form des Gerber\n"
+"verwenden Sie einen negativen Wert für\n"
+"dieser Parameter."
+
+#: flatcamGUI/ObjectUI.py:273
+msgid "Passes:"
+msgstr "Durchgang:"
+
+#: flatcamGUI/ObjectUI.py:307
+msgid "Combine"
+msgstr "Kombinieren"
+
+#: flatcamGUI/ObjectUI.py:323
+msgid "<b>Generate Isolation Geometry:</b>"
+msgstr "<b> Isolationsgeometrie erzeugen: </b>"
+
+#: flatcamGUI/ObjectUI.py:325
+msgid ""
+"Create a Geometry object with toolpaths to cut \n"
+"isolation outside, inside or on both sides of the\n"
+"object. For a Gerber object outside means outside\n"
+"of the Gerber feature and inside means inside of\n"
+"the Gerber feature, if possible at all. This means\n"
+"that only if the Gerber feature has openings inside, they\n"
+"will be isolated. If what is wanted is to cut isolation\n"
+"inside the actual Gerber feature, use a negative tool\n"
+"diameter above."
+msgstr ""
+"Erstellen Sie ein Geometrieobjekt mit zu schneidenden Werkzeugwegen\n"
+"Isolierung außen, innen oder auf beiden Seiten des\n"
+"Objekt. Für ein Gerber-Objekt bedeutet draußen außerhalb\n"
+"der Gerber-Funktion und inside bedeutet inside\n"
+"die Gerber-Funktion, wenn überhaupt möglich. Das heisst\n"
+"Nur wenn das Gerber-Feature Öffnungen enthält, können sie\n"
+"wird isoliert werden. Wenn es darum geht, die Isolation abzuschneiden\n"
+"Verwenden Sie in der Gerber-Funktion ein negatives Werkzeug\n"
+"Durchmesser oben."
+
+#: flatcamGUI/ObjectUI.py:344
+msgid "FULL Geo"
+msgstr "Volle Geo"
+
+#: flatcamGUI/ObjectUI.py:346
+msgid ""
+"Create the Geometry Object\n"
+"for isolation routing. It contains both\n"
+"the interiors and exteriors geometry."
+msgstr ""
+"Erstellen Sie das Geometrieobjekt\n"
+"für Isolationsrouting. Es enthält beides\n"
+"die Innen- und Außengeometrie."
+
+#: flatcamGUI/ObjectUI.py:355
+msgid "Ext Geo"
+msgstr "Äußere Geo"
+
+#: flatcamGUI/ObjectUI.py:357
+msgid ""
+"Create the Geometry Object\n"
+"for isolation routing containing\n"
+"only the exteriors geometry."
+msgstr ""
+"Erstellen Sie das Geometrieobjekt\n"
+"für Isolationsrouting enthalten\n"
+"nur die äußere Geometrie."
+
+#: flatcamGUI/ObjectUI.py:364
+msgid "Int Geo"
+msgstr "Innengeo"
+
+#: flatcamGUI/ObjectUI.py:366
+msgid ""
+"Create the Geometry Object\n"
+"for isolation routing containing\n"
+"only the interiors geometry."
+msgstr ""
+"Erstellen Sie das Geometrieobjekt\n"
+"für Isolationsrouting enthalten\n"
+"nur die Innengeometrie."
+
+#: flatcamGUI/ObjectUI.py:384
+msgid "<b>Clear N-copper:</b>"
+msgstr "<b> N-Kupfer löschen: </b>"
+
+#: flatcamGUI/ObjectUI.py:394 flatcamTools/ToolNonCopperClear.py:240
+msgid ""
+"Create the Geometry Object\n"
+"for non-copper routing."
+msgstr ""
+"Erstellen Sie das Geometrieobjekt\n"
+"für kupferfreies Routing."
+
+#: flatcamGUI/ObjectUI.py:400
+msgid "<b>Board cutout:</b>"
+msgstr "<b> Kartenausschnitt: </b>"
+
+#: flatcamGUI/ObjectUI.py:408
+msgid "Cutout Tool"
+msgstr "Ausschnittwerkzeug"
+
+#: flatcamGUI/ObjectUI.py:410
+msgid ""
+"Generate the geometry for\n"
+"the board cutout."
+msgstr ""
+"Generieren Sie die Geometrie für\n"
+"der Brettausschnitt."
+
+#: flatcamGUI/ObjectUI.py:416
+msgid "<b>Non-copper regions:</b>"
+msgstr "<b> Regionen ohne Kupfer: </b>"
+
+#: flatcamGUI/ObjectUI.py:418
+msgid ""
+"Create polygons covering the\n"
+"areas without copper on the PCB.\n"
+"Equivalent to the inverse of this\n"
+"object. Can be used to remove all\n"
+"copper from a specified region."
+msgstr ""
+"Erstellen Sie Polygone für die\n"
+"Bereiche ohne Kupfer auf der Leiterplatte.\n"
+"Entspricht der Umkehrung davon\n"
+"Objekt. Kann verwendet werden, um alle zu entfernen\n"
+"Kupfer aus einer bestimmten Region."
+
+#: flatcamGUI/ObjectUI.py:443 flatcamGUI/ObjectUI.py:474
+msgid "Rounded Geo"
+msgstr "Abgerundete Geo"
+
+#: flatcamGUI/ObjectUI.py:445
+msgid "Resulting geometry will have rounded corners."
+msgstr "Die resultierende Geometrie hat abgerundete Ecken."
+
+#: flatcamGUI/ObjectUI.py:450 flatcamGUI/ObjectUI.py:484
+#: flatcamTools/ToolCutOut.py:167 flatcamTools/ToolCutOut.py:187
+#: flatcamTools/ToolCutOut.py:238 flatcamTools/ToolSolderPaste.py:127
+msgid "Generate Geo"
+msgstr "Geo erzeugen"
+
+#: flatcamGUI/ObjectUI.py:456
+msgid ""
+"Create a geometry surrounding the Gerber object.\n"
+"Square shape."
+msgstr ""
+"Erstellen Sie eine Geometrie, die das Gerber-Objekt umgibt.\n"
+"Quadratische Form"
+
+#: flatcamGUI/ObjectUI.py:486
+msgid "Generate the Geometry object."
+msgstr "Generieren Sie das Geometrieobjekt."
+
+#: flatcamGUI/ObjectUI.py:497
+msgid "Excellon Object"
+msgstr "Excellon-Objekt"
+
+#: flatcamGUI/ObjectUI.py:508
+msgid "Solid circles."
+msgstr "Feste Kreise"
+
+#: flatcamGUI/ObjectUI.py:536 flatcamGUI/ObjectUI.py:855
+msgid "<b>Tools Table</b>"
+msgstr "<b> Werkzeugtabelle </b>"
+
+#: flatcamGUI/ObjectUI.py:557
+msgid "Offset Z"
+msgstr "Versatz Z"
+
+#: flatcamGUI/ObjectUI.py:561
+msgid ""
+"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."
+msgstr ""
+"Dies ist die Werkzeugnummer.\n"
+"Wenn Werkzeugwechsel aktiviert ist, wird dieser Wert beim\n"
+"Werkzeugwechselereignis angegeben\n"
+"wird als T1, T2 ... Tn im Maschinencode angezeigt."
+
+#: flatcamGUI/ObjectUI.py:565 flatcamGUI/ObjectUI.py:901
+#: flatcamTools/ToolNonCopperClear.py:97 flatcamTools/ToolPaint.py:94
+msgid ""
+"Tool Diameter. It's value (in current FlatCAM units) \n"
+"is the cut width into the material."
+msgstr ""
+"Werkzeugdurchmesser Der Wert (in aktuellen FlatCAM-Einheiten)\n"
+"ist die Schnittbreite in das Material."
+
+#: flatcamGUI/ObjectUI.py:568
+msgid ""
+"The number of Drill holes. Holes that are drilled with\n"
+"a drill bit."
+msgstr ""
+"Die Anzahl der Bohrlöcher.\n"
+"Löcher, mit denen gebohrt\n"
+"wird ein Bohrer"
+
+#: flatcamGUI/ObjectUI.py:571
+msgid ""
+"The number of Slot holes. Holes that are created by\n"
+"milling them with an endmill bit."
+msgstr ""
+"Die Anzahl der Langlöcher. Löcher, die von erstellt werden\n"
+"Fräsen mit einem Schaftfräser."
+
+#: flatcamGUI/ObjectUI.py:578
+msgid "Toggle display of the drills for the current tool."
+msgstr "Anzeige der Bohrer für das aktuelle Werkzeug umschalten."
+
+#: flatcamGUI/ObjectUI.py:586
+msgid ""
+"Create a CNC Job object\n"
+"for this drill object."
+msgstr ""
+"Erstellen Sie ein CNC-Auftragsobjekt\n"
+"für dieses Bohrobjekt."
+
+#: flatcamGUI/ObjectUI.py:615 flatcamGUI/ObjectUI.py:1115
+msgid "Tool change"
+msgstr "Werkzeugwechsel"
+
+#: flatcamGUI/ObjectUI.py:623 flatcamGUI/ObjectUI.py:1108
+msgid "Tool change Z:"
+msgstr "Werkzeugwechsel Z:"
+
+#: flatcamGUI/ObjectUI.py:625 flatcamGUI/ObjectUI.py:1111
+msgid ""
+"Z-axis position (height) for\n"
+"tool change."
+msgstr ""
+"Z-Achsenposition (Höhe) für\n"
+"Werkzeugwechsel."
+
+#: flatcamGUI/ObjectUI.py:636
+msgid ""
+"Tool height just before starting the work.\n"
+"Delete the value if you don't need this feature."
+msgstr ""
+"Werkzeughöhe kurz vor Arbeitsbeginn.\n"
+"Löschen Sie den Wert, wenn Sie diese Funktion nicht benötigen."
+
+#: flatcamGUI/ObjectUI.py:646
+msgid ""
+"Z-axis position (height) for\n"
+"the last move."
+msgstr ""
+"Z-Achsenposition (Höhe) für\n"
+"der letzte Zug"
+
+#: flatcamGUI/ObjectUI.py:654
+msgid "Feedrate (Plunge):"
+msgstr "Vorschub (Tauchgang):"
+
+#: flatcamGUI/ObjectUI.py:656
+msgid ""
+"Tool speed while drilling\n"
+"(in units per minute).\n"
+"This is for linear move G01."
+msgstr ""
+"Werkzeuggeschwindigkeit beim Bohren\n"
+"(in Einheiten pro Minute).\n"
+"Dies ist für die lineare Bewegung G01."
+
+#: flatcamGUI/ObjectUI.py:706
+msgid ""
+"The json file that dictates\n"
+"gcode output."
+msgstr ""
+"Die Json-Datei, die diktiert\n"
+"gcode ausgabe."
+
+#: flatcamGUI/ObjectUI.py:738
+msgid ""
+"Select from the Tools Table above\n"
+"the tools you want to include."
+msgstr ""
+"Wählen Sie aus der Tools-Tabelle oben\n"
+"die Werkzeuge, die Sie einschließen möchten."
+
+#: flatcamGUI/ObjectUI.py:745
+msgid "<b>Type:    </b>"
+msgstr "<b> Typ: </b>"
+
+#: flatcamGUI/ObjectUI.py:747
+msgid ""
+"Choose what to use for GCode generation:\n"
+"'Drills', 'Slots' or 'Both'.\n"
+"When choosing 'Slots' or 'Both', slots will be\n"
+"converted to a series of drills."
+msgstr ""
+"Wählen Sie aus, was für die GCode-Generierung verwendet werden soll:\n"
+"'Drills', 'Slots' oder 'Both'.\n"
+"Wenn Sie \"Slots\" oder \"Both\" wählen, werden die Slots angezeigt\n"
+"in eine Reihe von Bohrern umgewandelt."
+
+#: flatcamGUI/ObjectUI.py:762
+msgid "Create GCode"
+msgstr "GCode erstellen"
+
+#: flatcamGUI/ObjectUI.py:764
+msgid "Generate the CNC Job."
+msgstr "Generieren Sie den CNC-Job."
+
+#: flatcamGUI/ObjectUI.py:776
+msgid ""
+"Select from the Tools Table above\n"
+" the hole dias that are to be milled."
+msgstr ""
+"Wählen Sie aus der Werkzeugtabelle oben\n"
+" das Loch, das gefräst werden soll."
+
+#: flatcamGUI/ObjectUI.py:783
+msgid "Drills Tool dia:"
+msgstr "Bohrer Werkzeugdurchmesser:"
+
+#: flatcamGUI/ObjectUI.py:790
+msgid "Mill Drills Geo"
+msgstr "Mühle bohrt Geo"
+
+#: flatcamGUI/ObjectUI.py:792
+msgid ""
+"Create the Geometry Object\n"
+"for milling DRILLS toolpaths."
+msgstr ""
+"Erstellen Sie das Geometrieobjekt\n"
+"zum Fräsen von BOHRER-Werkzeugwegen."
+
+#: flatcamGUI/ObjectUI.py:799
+msgid "Slots Tool dia:"
+msgstr "Schlitzwerkzeugdurchmesser:"
+
+#: flatcamGUI/ObjectUI.py:806
+msgid "Mill Slots Geo"
+msgstr "Fräsen der Schlitze"
+
+#: flatcamGUI/ObjectUI.py:808
+msgid ""
+"Create the Geometry Object\n"
+"for milling SLOTS toolpaths."
+msgstr ""
+"Erstellen Sie das Geometrieobjekt\n"
+"zum Fräsen von Werkzeugwegen."
+
+#: flatcamGUI/ObjectUI.py:826
+msgid "Geometry Object"
+msgstr "Geometrieobjekt"
+
+#: flatcamGUI/ObjectUI.py:857
+msgid ""
+"Tools in this Geometry object used for cutting.\n"
+"The 'Offset' entry will set an offset for the cut.\n"
+"'Offset' can be inside, outside, on path (none) and custom.\n"
+"'Type' entry is only informative and it allow to know the \n"
+"intent of using the current tool. \n"
+"It can be Rough(ing), Finish(ing) or Iso(lation).\n"
+"The 'Tool type'(TT) can be circular with 1 to 4 teeths(C1..C4),\n"
+"ball(B), or V-Shaped(V). \n"
+"When V-shaped is selected the 'Type' entry is automatically \n"
+"set to Isolation, the CutZ parameter in the UI form is\n"
+"grayed out and Cut Z is automatically calculated from the newly \n"
+"showed UI form entries named V-Tip Dia and V-Tip Angle."
+msgstr ""
+"Werkzeuge in diesem Geometrieobjekt, die zum Schneiden verwendet werden.\n"
+"Der Eintrag 'Versatz' legt einen Versatz für den Schnitt fest.\n"
+"'Versatz' kann innen, außen, auf Pfad (keine) und benutzerdefiniert sein.\n"
+"Der Eintrag \"Typ\" ist nur informativ und ermöglicht die Kenntnis der\n"
+"Absicht, das aktuelle Werkzeug zu verwenden.\n"
+"Es kann Rough, Finish oder ISO sein.\n"
+"Der 'Werkzeugtyp' (TT) kann kreisförmig mit 1 bis 4 Zähnen (C1..C4) sein.\n"
+"Kugel (B) oder V-Form (V).\n"
+"Wenn V-förmig ausgewählt ist, wird der Eintrag \"Typ\" automatisch "
+"angezeigt\n"
+"Auf Isolation eingestellt ist der Parameter CutZ im UI-Formular\n"
+"ausgegraut und Cut Z wird automatisch aus dem neuen berechnet\n"
+"Zeigt UI-Formulareinträge mit den Namen V-Tip Dia und V-Tip Angle an."
+
+#: flatcamGUI/ObjectUI.py:888 flatcamGUI/ObjectUI.py:1446
+msgid "Dia"
+msgstr "Durchm"
+
+#: flatcamGUI/ObjectUI.py:888 flatcamGUI/ObjectUI.py:1446
+msgid "TT"
+msgstr "TT"
+
+#: flatcamGUI/ObjectUI.py:895
+msgid ""
+"This is the Tool Number.\n"
+"When ToolChange is checked, on toolchange event this value\n"
+"will be showed as a T1, T2 ... Tn"
+msgstr ""
+"Dies ist die Werkzeugnummer.\n"
+"Wenn der Werkzeugwechsel aktiviert ist, wird dieser Wert beim "
+"Werkzeugwechselereignis angezeigt\n"
+"wird als T1, T2 ... Tn angezeigt"
+
+#: flatcamGUI/ObjectUI.py:906
+msgid ""
+"The value for the Offset can be:\n"
+"- Path -> There is no offset, the tool cut will be done through the geometry "
+"line.\n"
+"- In(side) -> The tool cut will follow the geometry inside. It will create a "
+"'pocket'.\n"
+"- Out(side) -> The tool cut will follow the geometry line on the outside."
+msgstr ""
+"Der Wert für den Offset kann sein:\n"
+"- Pfad -> Es gibt keinen Versatz, der Werkzeugschnitt erfolgt durch die "
+"Geometrielinie.\n"
+"- In (Seite) -> Der Werkzeugschnitt folgt der Innengeometrie. Es wird eine "
+"\"Tasche\" erstellt.\n"
+"- Out (Seite) -> Der Werkzeugschnitt folgt der Geometrielinie an der "
+"Außenseite."
+
+#: flatcamGUI/ObjectUI.py:913
+msgid ""
+"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"
+"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"
+"For Isolation we need a lower Feedrate as it use a milling bit with a fine "
+"tip."
+msgstr ""
+"Der Typ (Vorgang) hat nur informativen Wert. Normalerweise bilden die UI-"
+"Formwerte\n"
+"werden je nach Operationstyp ausgewählt und dienen als Erinnerung.\n"
+"Kann 'Schruppen', 'Ausarbeiten' oder 'Isolieren' sein.\n"
+"Beim Schruppen können wir einen niedrigeren Vorschub und einen mehrstufigen "
+"Schnitt wählen.\n"
+"Für das Finishing können wir einen höheren Vorschub wählen, ohne "
+"multiDepth.\n"
+"Für die Isolation benötigen wir einen niedrigeren Vorschub, da ein Fräser "
+"mit feiner Spitze verwendet wird."
+
+#: flatcamGUI/ObjectUI.py:922
+msgid ""
+"The Tool Type (TT) can be:\n"
+"- Circular with 1 ... 4 teeth -> it is informative only. Being circular the "
+"cut width in material\n"
+"is exactly the tool diameter.\n"
+"- Ball -> informative only and make reference to the Ball type endmill.\n"
+"- V-Shape -> it will disable de Z-Cut parameter in the UI form and enable "
+"two additional UI form\n"
+"fields: V-Tip Dia and V-Tip Angle. Adjusting those two values will adjust "
+"the Z-Cut parameter such\n"
+"as the cut width into material will be equal with the value in the Tool "
+"Diameter column of this table.\n"
+"Choosing the V-Shape Tool Type automatically will select the Operation Type "
+"as Isolation."
+msgstr ""
+"Der Werkzeugtyp (TT) kann sein:\n"
+"- Rundschreiben mit 1 ... 4 Zähnen -> es ist nur informativ. Die "
+"Schnittbreite im Material ist kreisförmig\n"
+"ist genau der Werkzeugdurchmesser.\n"
+"- Ball -> nur informativ und verweisen Sie auf das Schaftfräser vom Typ "
+"Ball.\n"
+"- V-Shape -> Deaktiviert den Z-Cut-Parameter im UI-Formular und aktiviert "
+"zwei zusätzliche UI-Form\n"
+"Felder: V-Tip Dia und V-Tip Angle. Durch Anpassen dieser beiden Werte wird "
+"der Z-Cut-Parameter wie z\n"
+"da die Schnittbreite in Material gleich dem Wert in der Spalte "
+"Werkzeugdurchmesser dieser Tabelle ist.\n"
+"Durch die Auswahl des V-Shape-Werkzeugtyps wird der Operationstyp "
+"automatisch als Isolation ausgewählt."
+
+#: flatcamGUI/ObjectUI.py:933
+msgid ""
+"Plot column. It is visible only for MultiGeo geometries, meaning geometries "
+"that holds the geometry\n"
+"data into the tools. For those geometries, deleting the tool will delete the "
+"geometry data also,\n"
+"so be WARNED. From the checkboxes on each row it can be enabled/disabled the "
+"plot on canvas\n"
+"for the corresponding tool."
+msgstr ""
+"Plotspalte Sie ist nur für MultiGeo-Geometrien sichtbar. Dies bedeutet, dass "
+"Geometrien die Geometrie enthalten\n"
+"Daten in die Werkzeuge. Durch das Löschen des Werkzeugs werden für diese "
+"Geometrien auch die Geometriedaten gelöscht.\n"
+"also sei WARNUNG. Über die Kontrollkästchen in jeder Zeile kann der Plot auf "
+"der Leinwand aktiviert / deaktiviert werden\n"
+"für das entsprechende Werkzeug."
+
+#: flatcamGUI/ObjectUI.py:946
+msgid "Tool Offset:"
+msgstr "Werkzeugversatz:"
+
+#: flatcamGUI/ObjectUI.py:949
+msgid ""
+"The value to offset the cut when \n"
+"the Offset type selected is 'Offset'.\n"
+"The value can be positive for 'outside'\n"
+"cut and negative for 'inside' cut."
+msgstr ""
+"Der Wert, mit dem der Schnitt versetzt werden soll\n"
+"Der ausgewählte Versatztyp ist 'Versatz'.\n"
+"Der Wert kann für \"außerhalb\" positiv sein\n"
+"Cut und Negativ für \"Inside\" Cut."
+
+#: flatcamGUI/ObjectUI.py:972
+msgid "<b>Tool Dia:</b>"
+msgstr "<b> Werkzeugdurchmesser: </b>"
+
+#: flatcamGUI/ObjectUI.py:991 flatcamTools/ToolNonCopperClear.py:136
+#: flatcamTools/ToolPaint.py:133
+msgid ""
+"Add a new tool to the Tool Table\n"
+"with the diameter specified above."
+msgstr ""
+"Fügen Sie der Werkzeugtabelle ein neues Werkzeug hinzu\n"
+"mit dem oben angegebenen Durchmesser."
+
+#: flatcamGUI/ObjectUI.py:999
+msgid ""
+"Copy a selection of tools in the Tool Table\n"
+"by first selecting a row in the Tool Table."
+msgstr ""
+"Kopieren Sie eine Auswahl von Werkzeugen in die Werkzeugtabelle\n"
+"indem Sie zuerst eine Zeile in der Werkzeugtabelle auswählen."
+
+#: flatcamGUI/ObjectUI.py:1007
+msgid ""
+"Delete a selection of tools in the Tool Table\n"
+"by first selecting a row in the Tool Table."
+msgstr ""
+"Löschen Sie eine Auswahl von Werkzeugen in der Werkzeugtabelle\n"
+"indem Sie zuerst eine Zeile in der Werkzeugtabelle auswählen."
+
+#: flatcamGUI/ObjectUI.py:1023
+msgid "<b>Tool Data</b>"
+msgstr "<b> Werkzeugdaten </b>"
+
+#: flatcamGUI/ObjectUI.py:1026
+msgid ""
+"The data used for creating GCode.\n"
+"Each tool store it's own set of such data."
+msgstr ""
+"Die Daten, die zum Erstellen von GCode verwendet werden.\n"
+"Jedes Werkzeug speichert seinen eigenen Satz solcher Daten."
+
+#: flatcamGUI/ObjectUI.py:1036
+msgid "V-Tip Dia:"
+msgstr "V-Tip-Durchm:"
+
+#: flatcamGUI/ObjectUI.py:1039
+msgid "The tip diameter for V-Shape Tool"
+msgstr "Der Spitzendurchmesser für das V-Shape-Werkzeug"
+
+#: flatcamGUI/ObjectUI.py:1047
+msgid "V-Tip Angle:"
+msgstr "V-Tip-Winkel:"
+
+#: flatcamGUI/ObjectUI.py:1050
+msgid ""
+"The tip angle for V-Shape Tool.\n"
+"In degree."
+msgstr ""
+"Der Spitzenwinkel für das V-Shape-Werkzeug.\n"
+"In grad."
+
+#: flatcamGUI/ObjectUI.py:1071
+msgid "Multi-Depth:"
+msgstr "Mehrfache Tiefe:"
+
+#: flatcamGUI/ObjectUI.py:1074
+msgid ""
+"Use multiple passes to limit\n"
+"the cut depth in each pass. Will\n"
+"cut multiple times until Cut Z is\n"
+"reached.\n"
+"To the right, input the depth of \n"
+"each pass (positive value)."
+msgstr ""
+
+#: flatcamGUI/ObjectUI.py:1087
+msgid "Depth of each pass (positive)."
+msgstr "Tiefe jedes Durchgangs (positiv)."
+
+#: flatcamGUI/ObjectUI.py:1118
+msgid ""
+"Include tool-change sequence\n"
+"in the Machine Code (Pause for tool change)."
+msgstr ""
+"Werkzeugwechselfolge einbeziehen\n"
+"im Maschinencode (Pause für Werkzeugwechsel)."
+
+#: flatcamGUI/ObjectUI.py:1144
+msgid ""
+"This is the height (Z) at which the CNC\n"
+"will go as the last move."
+msgstr ""
+"Dies ist die Höhe (Z), auf der die CNC steht\n"
+"wird als letzter Zug gehen."
+
+#: flatcamGUI/ObjectUI.py:1165
+msgid "Feed Rate Z (Plunge):"
+msgstr "Vorschubrate Z (Eintauchen):"
+
+#: flatcamGUI/ObjectUI.py:1168
+msgid ""
+"Cutting speed in the Z\n"
+"plane in units per minute"
+msgstr ""
+"Schnittgeschwindigkeit in der Z\n"
+"Flugzeug in Einheiten pro Minute"
+
+#: flatcamGUI/ObjectUI.py:1177
+msgid "Feed Rate Rapids:"
+msgstr "Vorschubgeschwindigkeit:"
+
+#: flatcamGUI/ObjectUI.py:1180
+msgid ""
+"Cutting speed in the XY\n"
+"plane in units per minute\n"
+"(in units per minute).\n"
+"This is for the rapid move G00.\n"
+"It is useful only for Marlin,\n"
+"ignore for any other cases."
+msgstr ""
+"Schnittgeschwindigkeit im XY\n"
+"Flugzeug in Einheiten pro Minute\n"
+"(in Einheiten pro Minute).\n"
+"Dies ist für die schnelle Bewegung G00.\n"
+"Es ist nur für Marlin nützlich,\n"
+"für andere Fälle ignorieren."
+
+#: flatcamGUI/ObjectUI.py:1193
+msgid "Cut over 1st pt"
+msgstr "1. Punkt schneiden"
+
+#: flatcamGUI/ObjectUI.py:1208
+msgid ""
+"Speed of the spindle in RPM (optional).\n"
+"If LASER postprocessor is used,\n"
+"this value is the power of laser."
+msgstr ""
+"Drehzahl der Spindel in U / min (optional).\n"
+"Wenn LASER-Postprozessor verwendet wird,\n"
+"Dieser Wert ist die Leistung des Lasers."
+
+#: flatcamGUI/ObjectUI.py:1237
+msgid "PostProcessor:"
+msgstr "Postprozessor:"
+
+#: flatcamGUI/ObjectUI.py:1240
+msgid ""
+"The Postprocessor file that dictates\n"
+"the Machine Code (like GCode, RML, HPGL) output."
+msgstr ""
+"Die Postprozessor-Datei, die diktiert\n"
+"den Maschinencode (wie GCode, RML, HPGL)."
+
+#: flatcamGUI/ObjectUI.py:1278
+msgid ""
+"Add at least one tool in the tool-table.\n"
+"Click the header to select all, or Ctrl + LMB\n"
+"for custom selection of tools."
+msgstr ""
+"Fügen Sie mindestens ein Werkzeug in der Werkzeugtabelle hinzu.\n"
+"Klicken Sie auf die Kopfzeile, um alle auszuwählen, oder drücken Sie Strg + "
+"LMB\n"
+"zur benutzerdefinierten Auswahl von Werkzeugen."
+
+#: flatcamGUI/ObjectUI.py:1285
+msgid "Generate"
+msgstr "Generieren"
+
+#: flatcamGUI/ObjectUI.py:1288
+msgid "Generate the CNC Job object."
+msgstr "Generieren Sie das CNC-Job-Objekt."
+
+#: flatcamGUI/ObjectUI.py:1296
+msgid "<b>Paint Area:</b>"
+msgstr "<b> Paint Bereich: </b>"
+
+#: flatcamGUI/ObjectUI.py:1311
+msgid "Launch Paint Tool in Tools Tab."
+msgstr "Starten Sie das Paint Werkzeug in der Registerkarte \"Tools\"."
+
+#: flatcamGUI/ObjectUI.py:1328
+msgid "CNC Job Object"
+msgstr "CNC-Auftragsobjekt"
+
+#: flatcamGUI/ObjectUI.py:1347
+msgid "<b>Plot kind:</b>"
+msgstr "<b> Plotart: </b>"
+
+#: flatcamGUI/ObjectUI.py:1372
+msgid "<b>Travelled dist.:</b>"
+msgstr "<b> Zurückgelegte Distanz: </b>"
+
+#: flatcamGUI/ObjectUI.py:1375 flatcamGUI/ObjectUI.py:1382
+msgid ""
+"This is the total travelled distance on X-Y plane.\n"
+"In current units."
+msgstr ""
+"Dies ist die Gesamtstrecke auf der X-Y-Ebene.\n"
+"In aktuellen Einheiten."
+
+#: flatcamGUI/ObjectUI.py:1410
+msgid "<b>CNC Tools Table</b>"
+msgstr "<b> CNC-Werkzeugtabelle </b>"
+
+#: flatcamGUI/ObjectUI.py:1413
+msgid ""
+"Tools in this CNCJob object used for cutting.\n"
+"The tool diameter is used for plotting on canvas.\n"
+"The 'Offset' entry will set an offset for the cut.\n"
+"'Offset' can be inside, outside, on path (none) and custom.\n"
+"'Type' entry is only informative and it allow to know the \n"
+"intent of using the current tool. \n"
+"It can be Rough(ing), Finish(ing) or Iso(lation).\n"
+"The 'Tool type'(TT) can be circular with 1 to 4 teeths(C1..C4),\n"
+"ball(B), or V-Shaped(V)."
+msgstr ""
+"Werkzeuge in diesem CNCJob-Objekt, die zum Schneiden verwendet werden.\n"
+"Der Werkzeugdurchmesser wird zum Plotten auf Leinwand verwendet.\n"
+"Der Eintrag 'Versatz' legt einen Versatz für den Schnitt fest.\n"
+"'Versatz' kann innen, außen, auf Pfad (keine) und benutzerdefiniert sein.\n"
+"Der Eintrag \"Typ\" ist nur informativ und ermöglicht die Kenntnis der\n"
+"Absicht, das aktuelle Werkzeug zu verwenden.\n"
+"Es kann Schruppen, Schlichten oder Isolieren sein.\n"
+"Der 'Werkzeugtyp' (TT) kann kreisförmig mit 1 bis 4 Zähnen (C1..C4) sein.\n"
+"Kugel (B) oder V-Form (V)."
+
+#: flatcamGUI/ObjectUI.py:1447
+msgid "P"
+msgstr "P"
+
+#: flatcamGUI/ObjectUI.py:1453
+msgid "Update Plot"
+msgstr "Plot aktualisieren"
+
+#: flatcamGUI/ObjectUI.py:1455
+msgid "Update the plot."
+msgstr "Aktualisieren Sie die Darstellung."
+
+#: flatcamGUI/ObjectUI.py:1462
+msgid "<b>Export CNC Code:</b>"
+msgstr "<b> CNC-Code exportieren: </b>"
+
+#: flatcamGUI/ObjectUI.py:1470
+msgid "Prepend to CNC Code:"
+msgstr "CNC-Code voranstellen:"
+
+#: flatcamGUI/ObjectUI.py:1473
+msgid ""
+"Type here any G-Code commands you would\n"
+"like to add to the beginning of the generated file."
+msgstr ""
+"Geben Sie hier alle G-Code-Befehle ein\n"
+"gerne an den Anfang der generierten Datei hinzufügen."
+
+#: flatcamGUI/ObjectUI.py:1483
+msgid "Append to CNC Code"
+msgstr "An CNC Code anhängen"
+
+#: flatcamGUI/ObjectUI.py:1507
+msgid ""
+"Type here any G-Code commands you would\n"
+"like to be executed when Toolchange event is encountered.\n"
+"This will constitute a Custom Toolchange GCode,\n"
+"or a Toolchange Macro.\n"
+"The FlatCAM variables are surrounded by '%' symbol.\n"
+"\n"
+"WARNING: it can be used only with a postprocessor file\n"
+"that has 'toolchange_custom' in it's name and this is built\n"
+"having as template the 'Toolchange Custom' posprocessor file."
+msgstr ""
+"Geben Sie hier alle G-Code-Befehle ein\n"
+"Wird ausgeführt, wenn ein Werkzeugwechselereignis auftritt.\n"
+"Dies stellt einen benutzerdefinierten Werkzeugwechsel-GCode dar.\n"
+"oder ein Werkzeugwechsel-Makro.\n"
+"Die FlatCAM-Variablen sind vom '%'-Symbol umgeben.\n"
+"\n"
+"WARNUNG: Es kann nur mit einer Postprozessor-Datei verwendet werden\n"
+"das hat \"toolchange_custom\" im Namen und das ist gebaut\n"
+"mit der \"Toolchange Custom\" -Prozessordatei als Vorlage."
+
+#: flatcamGUI/ObjectUI.py:1555
+msgid "z_cut = depth where to cut"
+msgstr "z_cut = Tiefe, wo geschnitten werden soll"
+
+#: flatcamGUI/ObjectUI.py:1556
+msgid "z_move = height where to travel"
+msgstr "z_move = Höhe wo zu reisen"
+
+#: flatcamGUI/ObjectUI.py:1574
+msgid "View CNC Code"
+msgstr "CNC-Code anzeigen"
+
+#: flatcamGUI/ObjectUI.py:1577
+msgid ""
+"Opens TAB to view/modify/print G-Code\n"
+"file."
+msgstr ""
+"Öffnet die Registerkarte zum Anzeigen / Ändern / Drucken von G-Code\n"
+"Datei."
+
+#: flatcamGUI/ObjectUI.py:1583
+msgid "Save CNC Code"
+msgstr "CNC-Code speichern"
+
+#: flatcamGUI/ObjectUI.py:1586
+msgid ""
+"Opens dialog to save G-Code\n"
+"file."
+msgstr ""
+"Öffnet den Dialog zum Speichern des G-Codes\n"
+"Datei."
+
+#: flatcamTools/ToolCalculators.py:24
+msgid "Calculators"
+msgstr "Rechner"
+
+#: flatcamTools/ToolCalculators.py:25
+msgid "V-Shape Tool Calculator"
+msgstr "V-Shape-Werkzeugrechner"
+
+#: flatcamTools/ToolCalculators.py:26
+msgid "Units Calculator"
+msgstr "Einheitenrechner"
+
+#: flatcamTools/ToolCalculators.py:27
+msgid "ElectroPlating Calculator"
+msgstr "Galvanikrechner"
+
+#: flatcamTools/ToolCalculators.py:68
+msgid "Here you enter the value to be converted from INCH to MM"
+msgstr ""
+"Hier geben Sie den Wert ein, der von Zoll in Metrik konvertiert werden soll"
+
+#: flatcamTools/ToolCalculators.py:73
+msgid "Here you enter the value to be converted from MM to INCH"
+msgstr ""
+"Hier geben Sie den Wert ein, der von Metrik in Zoll konvertiert werden soll"
+
+#: flatcamTools/ToolCalculators.py:98
+msgid ""
+"This is the diameter of the tool tip.\n"
+"The manufacturer specifies it."
+msgstr ""
+"Dies ist der Durchmesser der Werkzeugspitze.\n"
+"Der Hersteller gibt es an."
+
+#: flatcamTools/ToolCalculators.py:101
+msgid "Tip Angle:"
+msgstr "Spitzenwinkel:"
+
+#: flatcamTools/ToolCalculators.py:105
+msgid ""
+"This is the angle of the tip of the tool.\n"
+"It is specified by manufacturer."
+msgstr ""
+"Dies ist der Winkel der Werkzeugspitze.\n"
+"Es wird vom Hersteller angegeben."
+
+#: flatcamTools/ToolCalculators.py:112
+msgid ""
+"This is the depth to cut into the material.\n"
+"In the CNCJob is the CutZ parameter."
+msgstr ""
+"Dies ist die Tiefe, in die das Material geschnitten werden soll.\n"
+"Im CNCJob befindet sich der Parameter CutZ."
+
+#: flatcamTools/ToolCalculators.py:115
+msgid "Tool Diameter:"
+msgstr "Werkzeugdurchm:"
+
+#: flatcamTools/ToolCalculators.py:119
+msgid ""
+"This is the tool diameter to be entered into\n"
+"FlatCAM Gerber section.\n"
+"In the CNCJob section it is called >Tool dia<."
+msgstr ""
+"Dies ist der Werkzeugdurchmesser, in den eingegeben werden soll\n"
+"FlatCAM-Gerber-Bereich.\n"
+"Im CNCJob-Bereich heißt es >Werkzeugdurchmesser<."
+
+#: flatcamTools/ToolCalculators.py:131 flatcamTools/ToolCalculators.py:214
+msgid "Calculate"
+msgstr "Berechnung"
+
+#: flatcamTools/ToolCalculators.py:134
+msgid ""
+"Calculate either the Cut Z or the effective tool diameter,\n"
+"  depending on which is desired and which is known. "
+msgstr ""
+"Berechnen Sie entweder den Schnitt Z oder den effektiven "
+"Werkzeugdurchmesser.\n"
+" je nachdem, was gewünscht und bekannt ist."
+
+#: flatcamTools/ToolCalculators.py:190
+msgid "Current Value:"
+msgstr "Aktueller Wert:"
+
+#: flatcamTools/ToolCalculators.py:194
+msgid ""
+"This is the current intensity value\n"
+"to be set on the Power Supply. In Amps."
+msgstr ""
+"Dies ist der aktuelle Intensitätswert\n"
+"am Netzteil eingestellt werden. In Ampere"
+
+#: flatcamTools/ToolCalculators.py:198
+msgid "Time:"
+msgstr "Zeit:"
+
+#: flatcamTools/ToolCalculators.py:202
+msgid ""
+"This is the calculated time required for the procedure.\n"
+"In minutes."
+msgstr ""
+"Dies ist die berechnete Zeit, die für das Verfahren benötigt wird.\n"
+"In Minuten."
+
+#: flatcamTools/ToolCalculators.py:217
+msgid ""
+"Calculate the current intensity value and the procedure time,\n"
+"  depending on the parameters above"
+msgstr ""
+"Berechnen Sie den aktuellen Intensitätswert und \n"
+"die Behandlungszeit in Abhängigkeit von \n"
+"den obigen Parametern"
+
+#: flatcamTools/ToolCutOut.py:17
+msgid "Cutout PCB"
+msgstr "Ausschnitt PCB"
+
+#: flatcamTools/ToolCutOut.py:53
+msgid "Obj Type:"
+msgstr "Obj-Typ:"
+
+#: flatcamTools/ToolCutOut.py:55
+msgid ""
+"Specify the type of object to be cutout.\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."
+msgstr ""
+"Geben Sie den Objekttyp an, der ausgeschnitten werden soll.\n"
+"Es kann vom Typ sein: Gerber oder Geometrie.\n"
+"Was hier ausgewählt wird, bestimmt die Art\n"
+"von Objekten, die die Combobox 'Object' füllen."
+
+#: flatcamTools/ToolCutOut.py:69 flatcamTools/ToolPanelize.py:71
+msgid "Object:"
+msgstr "Objekt:"
+
+#: flatcamTools/ToolCutOut.py:71
+msgid "Object to be cutout.                        "
+msgstr "Objekt, das ausgeschnitten werden soll."
+
+#: flatcamTools/ToolCutOut.py:79
+msgid ""
+"Diameter of the tool used to cutout\n"
+"the PCB shape out of the surrounding material."
+msgstr ""
+"Durchmesser des zum Ausschneiden verwendeten Werkzeugs\n"
+"die PCB-Form aus dem umgebenden Material."
+
+#: flatcamTools/ToolCutOut.py:88
+msgid ""
+"Margin over bounds. A positive value here\n"
+"will make the cutout of the PCB further from\n"
+"the actual PCB border"
+msgstr ""
+"Marge über Grenzen. Ein positiver Wert hier\n"
+"macht den Ausschnitt der Leiterplatte weiter aus\n"
+"die tatsächliche PCB-Grenze"
+
+#: flatcamTools/ToolCutOut.py:98
+msgid ""
+"The size of the bridge gaps in the cutout\n"
+"used to keep the board connected to\n"
+"the surrounding material (the one \n"
+"from which the PCB is cutout)."
+msgstr ""
+"Die Größe der Brückenlücken im Ausschnitt\n"
+"verwendet, um die Platine verbunden zu halten\n"
+"das umgebende Material (das eine\n"
+"von denen die Leiterplatte ausgeschnitten ist)."
+
+#: flatcamTools/ToolCutOut.py:122
+msgid "A. Automatic Bridge Gaps"
+msgstr "A. Automatische Brückenlücken"
+
+#: flatcamTools/ToolCutOut.py:124
+msgid "This section handle creation of automatic bridge gaps."
+msgstr "Dieser Abschnitt behandelt die Erstellung automatischer Brückenlücken."
+
+#: flatcamTools/ToolCutOut.py:135
+msgid ""
+"Number of gaps used for the Automatic cutout.\n"
+"There can be maximum 8 bridges/gaps.\n"
+"The choices are:\n"
+"- lr    - left + right\n"
+"- tb    - top + bottom\n"
+"- 4     - left + right +top + bottom\n"
+"- 2lr   - 2*left + 2*right\n"
+"- 2tb  - 2*top + 2*bottom\n"
+"- 8     - 2*left + 2*right +2*top + 2*bottom"
+msgstr ""
+"Anzahl der Lücken, die für den automatischen Ausschnitt verwendet werden.\n"
+"Es können maximal 8 Brücken / Lücken vorhanden sein.\n"
+"Die Wahlmöglichkeiten sind:\n"
+"- lr \t- links + rechts\n"
+"- tb \t- oben + unten\n"
+"- 4 \t- links + rechts + oben + unten\n"
+"- 2lr \t- 2 * links + 2 * rechts\n"
+"- 2 tb \t- 2 * oben + 2 * unten\n"
+"- 8 \t- 2 * links + 2 * rechts + 2 * oben + 2 * unten"
+
+#: flatcamTools/ToolCutOut.py:158
+msgid "FreeForm:"
+msgstr "Freie Form:"
+
+#: flatcamTools/ToolCutOut.py:160
+msgid ""
+"The cutout shape can be of ny shape.\n"
+"Useful when the PCB has a non-rectangular shape."
+msgstr ""
+"Die Ausschnittsform kann jede Form haben.\n"
+"Nützlich, wenn die Leiterplatte eine nicht rechteckige Form hat."
+
+#: flatcamTools/ToolCutOut.py:169
+msgid ""
+"Cutout the selected object.\n"
+"The cutout shape can be of any shape.\n"
+"Useful when the PCB has a non-rectangular shape."
+msgstr ""
+"Schneiden Sie das ausgewählte Objekt aus.\n"
+"Die Ausschnittform kann eine beliebige Form haben.\n"
+"Nützlich, wenn die Leiterplatte eine nicht rechteckige Form hat."
+
+#: flatcamTools/ToolCutOut.py:178
+msgid "Rectangular:"
+msgstr "Rechteckig:"
+
+#: flatcamTools/ToolCutOut.py:180
+msgid ""
+"The resulting cutout shape is\n"
+"always a rectangle shape and it will be\n"
+"the bounding box of the Object."
+msgstr ""
+"Die resultierende Ausschnittform ist\n"
+"immer eine rechteckige Form und es wird sein\n"
+"der Begrenzungsrahmen des Objekts."
+
+#: flatcamTools/ToolCutOut.py:189
+msgid ""
+"Cutout the selected object.\n"
+"The resulting cutout shape is\n"
+"always a rectangle shape and it will be\n"
+"the bounding box of the Object."
+msgstr ""
+"Schneiden Sie das ausgewählte Objekt aus.\n"
+"Die resultierende Ausschnittform ist\n"
+"immer eine rechteckige Form und es wird sein\n"
+"der Begrenzungsrahmen des Objekts."
+
+#: flatcamTools/ToolCutOut.py:197
+msgid "B. Manual Bridge Gaps"
+msgstr "B. Manuelle Brückenlücken"
+
+#: flatcamTools/ToolCutOut.py:199
+msgid ""
+"This section handle creation of manual bridge gaps.\n"
+"This is done by mouse clicking on the perimeter of the\n"
+"Geometry object that is used as a cutout object. "
+msgstr ""
+"In diesem Abschnitt werden manuelle Brückenlücken erstellt.\n"
+"Dies erfolgt durch einen Mausklick auf den Umfang des\n"
+"Geometrieobjekt, das als Ausschnittsobjekt verwendet wird."
+
+#: flatcamTools/ToolCutOut.py:215
+msgid "Geo Obj:"
+msgstr "Geo-Objekt:"
+
+#: flatcamTools/ToolCutOut.py:217
+msgid "Geometry object used to create the manual cutout."
+msgstr "Geometrieobjekt zum Erstellen des manuellen Ausschnitts."
+
+#: flatcamTools/ToolCutOut.py:228
+msgid "Manual Geo:"
+msgstr "Manuelle Geo:"
+
+#: flatcamTools/ToolCutOut.py:230 flatcamTools/ToolCutOut.py:240
+msgid ""
+"If the object to be cutout is a Gerber\n"
+"first create a Geometry that surrounds it,\n"
+"to be used as the cutout, if one doesn't exist yet.\n"
+"Select the source Gerber file in the top object combobox."
+msgstr ""
+"Wenn das auszuschneidende Objekt ein Gerber ist\n"
+"erstelle eine Geometrie, die sie umgibt,\n"
+"als Ausschnitt verwendet werden, falls noch nicht vorhanden.\n"
+"Wählen Sie in der oberen Objekt-Combobox die Quell-Gerber-Datei aus."
+
+#: flatcamTools/ToolCutOut.py:250
+msgid "Manual Add Bridge Gaps:"
+msgstr "Manuelles Hinzufügen von Brückenlücken:"
+
+#: flatcamTools/ToolCutOut.py:252
+msgid ""
+"Use the left mouse button (LMB) click\n"
+"to create a bridge gap to separate the PCB from\n"
+"the surrounding material."
+msgstr ""
+"Klicken Sie mit der linken Maustaste (LMB)\n"
+"Erstellen einer Brückenlücke, um die Leiterplatte von zu trennen\n"
+"das umgebende Material."
+
+#: flatcamTools/ToolCutOut.py:259
+msgid "Generate Gap"
+msgstr "Lücke erzeugen"
+
+#: flatcamTools/ToolCutOut.py:261
+msgid ""
+"Use the left mouse button (LMB) click\n"
+"to create a bridge gap to separate the PCB from\n"
+"the surrounding material.\n"
+"The LMB click has to be done on the perimeter of\n"
+"the Geometry object used as a cutout geometry."
+msgstr ""
+"Klicken Sie mit der linken Maustaste (LMB)\n"
+"Erstellen einer Brückenlücke, um die Leiterplatte von zu trennen\n"
+"das umgebende Material.\n"
+"Der LMB-Klick muss am Umfang von erfolgen\n"
+"das Geometrieobjekt, das als Ausschnittsgeometrie verwendet wird."
+
+#: flatcamTools/ToolCutOut.py:338 flatcamTools/ToolCutOut.py:483
+#: flatcamTools/ToolNonCopperClear.py:665 flatcamTools/ToolPaint.py:763
+#: flatcamTools/ToolPanelize.py:293 flatcamTools/ToolPanelize.py:307
+#, python-format
+msgid "[ERROR_NOTCL] Could not retrieve object: %s"
+msgstr "[ERROR_NOTCL] Objekt konnte nicht abgerufen werden:%s"
+
+#: flatcamTools/ToolCutOut.py:342
+msgid ""
+"[ERROR_NOTCL] There is no object selected for Cutout.\n"
+"Select one and try again."
+msgstr ""
+"[ERROR_NOTCL] Es ist kein Objekt für den Ausschnitt ausgewählt.\n"
+"Wählen Sie eine aus und versuchen Sie es erneut."
+
+#: flatcamTools/ToolCutOut.py:358
+msgid ""
+"[WARNING_NOTCL] Tool Diameter is zero value. Change it to a positive real "
+"number."
+msgstr ""
+"[WARNING_NOTCL] Werkzeugdurchmesser ist Nullwert. Ändern Sie es in eine "
+"positive reelle Zahl."
+
+#: flatcamTools/ToolCutOut.py:368 flatcamTools/ToolCutOut.py:511
+#: flatcamTools/ToolCutOut.py:736
+msgid ""
+"[WARNING_NOTCL] Margin value is missing or wrong format. Add it and retry."
+msgstr ""
+"[WARNING_NOTCL] Margin-Wert fehlt oder falsches Format. Fügen Sie es hinzu "
+"und versuchen Sie es erneut."
+
+#: flatcamTools/ToolCutOut.py:379 flatcamTools/ToolCutOut.py:522
+#: flatcamTools/ToolCutOut.py:631
+msgid ""
+"[WARNING_NOTCL] Gap size value is missing or wrong format. Add it and retry."
+msgstr ""
+"[WARNING_NOTCL] Lückengrößenwert fehlt oder falsches Format. Fügen Sie es "
+"hinzu und versuchen Sie es erneut."
+
+#: flatcamTools/ToolCutOut.py:386 flatcamTools/ToolCutOut.py:529
+msgid "[WARNING_NOTCL] Number of gaps value is missing. Add it and retry."
+msgstr ""
+"[WARNING_NOTCL] Anzahl der Lücken fehlt. Fügen Sie es hinzu und versuchen "
+"Sie es erneut."
+
+#: flatcamTools/ToolCutOut.py:390 flatcamTools/ToolCutOut.py:533
+msgid ""
+"[WARNING_NOTCL] Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 "
+"or 8. Fill in a correct value and retry. "
+msgstr ""
+"[WARNING_NOTCL] Der Lückenwert kann nur einer der folgenden Werte sein: "
+"'lr', 'tb', '2lr', '2tb', 4 oder 8. Geben Sie einen korrekten Wert ein und "
+"versuchen Sie es erneut."
+
+#: flatcamTools/ToolCutOut.py:395 flatcamTools/ToolCutOut.py:538
+msgid ""
+"[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."
+msgstr ""
+"[ERROR] Der Ausschneidevorgang kann bei einer Multi-Geo-Geometrie nicht "
+"ausgeführt werden.\n"
+"Optional kann diese Multi-Geo-Geometrie in Single-Geo-Geometrie konvertiert "
+"werden.\n"
+"und danach Cutout durchführen."
+
+#: flatcamTools/ToolCutOut.py:467 flatcamTools/ToolCutOut.py:601
+msgid "[success] Any form CutOut operation finished."
+msgstr "[success] Jede Form CutOut-Operation ist abgeschlossen."
+
+#: flatcamTools/ToolCutOut.py:487 flatcamTools/ToolPaint.py:767
+#: flatcamTools/ToolPanelize.py:299
+#, python-format
+msgid "[ERROR_NOTCL] Object not found: %s"
+msgstr "[ERROR_NOTCL] Objekt nicht gefunden:%s"
+
+#: flatcamTools/ToolCutOut.py:501 flatcamTools/ToolCutOut.py:621
+#: flatcamTools/ToolCutOut.py:726
+msgid ""
+"[ERROR_NOTCL] Tool Diameter is zero value. Change it to a positive real "
+"number."
+msgstr ""
+"[ERROR_NOTCL] Werkzeugdurchmesser ist Nullwert. Ändern Sie es in eine "
+"positive reelle Zahl."
+
+#: flatcamTools/ToolCutOut.py:606
+msgid ""
+"Click on the selected geometry object perimeter to create a bridge gap ..."
+msgstr ""
+"Klicken Sie auf den ausgewählten Umfang des Geometrieobjekts, um eine "
+"Brückenlücke zu erstellen ..."
+
+#: flatcamTools/ToolCutOut.py:647
+msgid "Making manual bridge gap..."
+msgstr "Manuelle Brückenlücke herstellen ..."
+
+#: flatcamTools/ToolCutOut.py:670
+#, python-format
+msgid "[ERROR_NOTCL] Could not retrieve Geometry object: %s"
+msgstr "[ERROR_NOTCL] Das Geometrieobjekt konnte nicht abgerufen werden:%s"
+
+#: flatcamTools/ToolCutOut.py:674
+#, python-format
+msgid "[ERROR_NOTCL] Geometry object for manual cutout not found: %s"
+msgstr ""
+"[ERROR_NOTCL] Geometrieobjekt für manuellen Ausschnitt nicht gefunden:%s"
+
+#: flatcamTools/ToolCutOut.py:684
+msgid "[success] Added manual Bridge Gap."
+msgstr "[success] Manuelle Brückenlücke hinzugefügt."
+
+#: flatcamTools/ToolCutOut.py:701
+#, python-format
+msgid "[ERROR_NOTCL] Could not retrieve Gerber object: %s"
+msgstr "[ERROR_NOTCL] Gerber-Objekt konnte nicht abgerufen werden:%s"
+
+#: flatcamTools/ToolCutOut.py:705
+msgid ""
+"[ERROR_NOTCL] There is no Gerber object selected for Cutout.\n"
+"Select one and try again."
+msgstr ""
+"[ERROR_NOTCL] Es ist kein Gerber-Objekt für den Ausschnitt ausgewählt.\n"
+"Wählen Sie eine aus und versuchen Sie es erneut."
+
+#: flatcamTools/ToolCutOut.py:710
+msgid ""
+"[ERROR_NOTCL] The selected object has to be of Gerber type.\n"
+"Select a Gerber file and try again."
+msgstr ""
+"[ERROR_NOTCL] Das ausgewählte Objekt muss vom Typ Gerber sein.\n"
+"Wählen Sie eine Gerber-Datei aus und versuchen Sie es erneut."
+
+#: flatcamTools/ToolDblSided.py:18
+msgid "2-Sided PCB"
+msgstr "2-seitige PCB"
+
+#: flatcamTools/ToolDblSided.py:52 flatcamTools/ToolDblSided.py:76
+#: flatcamTools/ToolDblSided.py:100
+msgid "Mirror"
+msgstr "Spiegeln"
+
+#: flatcamTools/ToolDblSided.py:54 flatcamTools/ToolDblSided.py:78
+#: flatcamTools/ToolDblSided.py:102
+msgid ""
+"Mirrors (flips) the specified object around \n"
+"the specified axis. Does not create a new \n"
+"object, but modifies it."
+msgstr ""
+"Spiegelt das angegebene Objekt um\n"
+"die angegebene Achse. Erstellt kein neues\n"
+"Objekt, ändert es aber."
+
+#: flatcamTools/ToolDblSided.py:73
+msgid "Excellon Object to be mirrored."
+msgstr "Zu spiegelndes Excellon-Objekt."
+
+#: flatcamTools/ToolDblSided.py:97
+msgid "Geometry Obj to be mirrored."
+msgstr "Geometrie-Objekt, das gespiegelt werden soll."
+
+#: flatcamTools/ToolDblSided.py:135
+msgid ""
+"The axis should pass through a <b>point</b> or cut\n"
+" a specified <b>box</b> (in a FlatCAM object) through \n"
+"the center."
+msgstr ""
+"Die Achse sollte einen <b> Punkt </b> durchlaufen oder schneiden\n"
+"eine angegebene <b> Box </b> (in einem FlatCAM-Objekt) durch\n"
+"das Zentrum."
+
+#: flatcamTools/ToolDblSided.py:152
+msgid "Point/Box Reference:"
+msgstr "Punkt / Box-Referenz:"
+
+#: flatcamTools/ToolDblSided.py:154
+msgid ""
+"If 'Point' is selected above it store the coordinates (x, y) through which\n"
+"the mirroring axis passes.\n"
+"If 'Box' is selected above, select here a FlatCAM object (Gerber, Exc or "
+"Geo).\n"
+"Through the center of this object pass the mirroring axis selected above."
+msgstr ""
+"Wenn oben \"Punkt\" ausgewählt ist, werden die Koordinaten (x, y) "
+"gespeichert, durch die\n"
+"die Spiegelachse vergeht.\n"
+"Wenn oben 'Box' ausgewählt ist, wählen Sie hier ein FlatCAM-Objekt (Gerber, "
+"Exc oder Geo) aus.\n"
+"Durch die Mitte dieses Objekts fahren Sie die oben ausgewählte Spiegelachse."
+
+#: flatcamTools/ToolDblSided.py:162
+msgid ""
+"Add the coordinates in format <b>(x, y)</b> through which the mirroring "
+"axis \n"
+" selected in 'MIRROR AXIS' pass.\n"
+"The (x, y) coordinates are captured by pressing SHIFT key\n"
+"and left mouse button click on canvas or you can enter the coords manually."
+msgstr ""
+"Fügen Sie die Koordinaten im Format <b> (x, y) </b> hinzu, durch das die "
+"Spiegelachse verläuft\n"
+"ausgewählt in 'SPIEGEL-ACHSE'.\n"
+"Die (x, y) -Koordinaten werden durch Drücken der UMSCHALTTASTE erfasst\n"
+"Klicken Sie mit der linken Maustaste auf die Leinwand oder geben Sie die "
+"Koordinaten manuell ein."
+
+#: flatcamTools/ToolDblSided.py:182
+msgid "Gerber   Reference Box Object"
+msgstr "Gerber Referenzfeldobjekt"
+
+#: flatcamTools/ToolDblSided.py:183
+msgid "Excellon Reference Box Object"
+msgstr "Excellon Reference Referenzfeldobjekt"
+
+#: flatcamTools/ToolDblSided.py:184
+msgid "Geometry Reference Box Object"
+msgstr "Geometrie-Referenzfeldobjekt"
+
+#: flatcamTools/ToolDblSided.py:193
+msgid "Alignment Drill Coordinates:"
+msgstr "Ausrichtungsbohrkoordinaten:"
+
+#: flatcamTools/ToolDblSided.py:195
+msgid ""
+"Alignment holes (x1, y1), (x2, y2), ... on one side of the mirror axis. For "
+"each set of (x, y) coordinates\n"
+"entered here, a pair of drills will be created:\n"
+"\n"
+"- one drill at the coordinates from the field\n"
+"- one drill in mirror position over the axis selected above in the 'Mirror "
+"Axis'."
+msgstr ""
+"Ausrichtungslöcher (x1, y1), (x2, y2), ... auf einer Seite der Spiegelachse. "
+"Für jeden Satz von (x, y) -Koordinaten\n"
+"Hier eingegeben, wird ein Paar Bohrer erstellt:\n"
+"\n"
+"- ein Bohrer an den Koordinaten vom Feld\n"
+"- ein Bohrer in Spiegelposition über der oben in der 'Spiegelachse' "
+"ausgewählten Achse."
+
+#: flatcamTools/ToolDblSided.py:210
+msgid ""
+"Add alignment drill holes coords in the format: (x1, y1), (x2, y2), ... \n"
+"on one side of the mirror axis.\n"
+"\n"
+"The coordinates set can be obtained:\n"
+"- press SHIFT key and left mouse clicking on canvas. Then click Add.\n"
+"- press SHIFT key and left mouse clicking on canvas. Then CTRL+V in the "
+"field.\n"
+"- press SHIFT key and left mouse clicking on canvas. Then RMB click in the "
+"field and click Paste.\n"
+"- by entering the coords manually in the format: (x1, y1), (x2, y2), ..."
+msgstr ""
+"Fügen Sie Ausrichtungsbohrungskoordinaten im Format hinzu: (x1, y1), (x2, "
+"y2), ...\n"
+"auf einer Seite der Spiegelachse.\n"
+"\n"
+"Die gesetzten Koordinaten können erhalten werden:\n"
+"- Drücken Sie die SHIFT-Taste und klicken Sie mit der linken Maustaste auf "
+"die Leinwand. Klicken Sie dann auf Hinzufügen.\n"
+"- Drücken Sie die SHIFT-Taste und klicken Sie mit der linken Maustaste auf "
+"die Leinwand. Dann STRG + V im Feld.\n"
+"- Drücken Sie die SHIFT-Taste und klicken Sie mit der linken Maustaste auf "
+"die Leinwand. Klicken Sie dann auf RMB, und klicken Sie auf Einfügen.\n"
+"- durch manuelle Eingabe der Koordinaten im Format: (x1, y1), (x2, y2), ..."
+
+#: flatcamTools/ToolDblSided.py:224
+msgid "Alignment Drill Diameter"
+msgstr "Durchmesser des Ausrichtungsbohrers"
+
+#: flatcamTools/ToolDblSided.py:247
+msgid "Create Excellon Object"
+msgstr "Excellon-Objekt erstellen"
+
+#: flatcamTools/ToolDblSided.py:249
+msgid ""
+"Creates an Excellon Object containing the\n"
+"specified alignment holes and their mirror\n"
+"images."
+msgstr ""
+"Erstellt ein Excellon-Objekt, das das enthält\n"
+"spezifizierte Ausrichtungslöcher und deren Spiegel\n"
+"Bilder."
+
+#: flatcamTools/ToolDblSided.py:255
+msgid "Reset"
+msgstr "Zurücksetzen"
+
+#: flatcamTools/ToolDblSided.py:257
+msgid "Resets all the fields."
+msgstr "Setzt alle Felder zurück."
+
+#: flatcamTools/ToolDblSided.py:302
+msgid "2-Sided Tool"
+msgstr "2-seitiges Werkzeug"
+
+#: flatcamTools/ToolDblSided.py:327
+msgid ""
+"[WARNING_NOTCL] 'Point' reference is selected and 'Point' coordinates are "
+"missing. Add them and retry."
+msgstr ""
+"[WARNING_NOTCL] 'Point'-Referenz ist ausgewählt und' Point'-Koordinaten "
+"fehlen. Fügen Sie sie hinzu und versuchen Sie es erneut."
+
+#: flatcamTools/ToolDblSided.py:346
+msgid ""
+"[WARNING_NOTCL] There is no Box reference object loaded. Load one and retry."
+msgstr ""
+"[WARNING_NOTCL] Es ist kein Box-Referenzobjekt geladen. Laden Sie einen und "
+"versuchen Sie es erneut."
+
+#: flatcamTools/ToolDblSided.py:368
+msgid ""
+"[WARNING_NOTCL] No value or wrong format in Drill Dia entry. Add it and "
+"retry."
+msgstr ""
+"[WARNING_NOTCL] Kein Wert oder falsches Format im Eintrag Bohrdurchmesser. "
+"Fügen Sie es hinzu und versuchen Sie es erneut."
+
+#: flatcamTools/ToolDblSided.py:375
+msgid ""
+"[WARNING_NOTCL] There are no Alignment Drill Coordinates to use. Add them "
+"and retry."
+msgstr ""
+"[WARNING_NOTCL] Es sind keine Ausrichtungsbohrkoordinaten vorhanden. Fügen "
+"Sie sie hinzu und versuchen Sie es erneut."
+
+#: flatcamTools/ToolDblSided.py:397
+msgid "[success] Excellon object with alignment drills created..."
+msgstr "[success] Excellon-Objekt mit Ausrichtungsbohrern erstellt ..."
+
+#: flatcamTools/ToolDblSided.py:406
+msgid "[WARNING_NOTCL] There is no Gerber object loaded ..."
+msgstr "[WARNING_NOTCL] Es ist kein Gerber-Objekt geladen ..."
+
+#: flatcamTools/ToolDblSided.py:410 flatcamTools/ToolDblSided.py:453
+#: flatcamTools/ToolDblSided.py:497
+msgid ""
+"[ERROR_NOTCL] Only Gerber, Excellon and Geometry objects can be mirrored."
+msgstr ""
+"[ERROR_NOTCL] Nur Gerber-, Excellon- und Geometrie-Objekte können gespiegelt "
+"werden."
+
+#: flatcamTools/ToolDblSided.py:420
+msgid ""
+"[WARNING_NOTCL] 'Point' coordinates missing. Using Origin (0, 0) as "
+"mirroring reference."
+msgstr ""
+"[WARNING_NOTCL] 'Point'-Koordinaten fehlen. Verwenden von Origin (0, 0) als "
+"Spiegelreferenz."
+
+#: flatcamTools/ToolDblSided.py:430 flatcamTools/ToolDblSided.py:474
+#: flatcamTools/ToolDblSided.py:511
+msgid "[WARNING_NOTCL] There is no Box object loaded ..."
+msgstr "[WARNING_NOTCL] Es ist kein Box-Objekt geladen ..."
+
+#: flatcamTools/ToolDblSided.py:440
+#, python-format
+msgid "[success] Gerber %s was mirrored..."
+msgstr "[success] Gerber %s wurde gespiegelt ..."
+
+#: flatcamTools/ToolDblSided.py:449
+msgid "[WARNING_NOTCL] There is no Excellon object loaded ..."
+msgstr "[WARNING_NOTCL] Es ist kein Excellon-Objekt geladen ..."
+
+#: flatcamTools/ToolDblSided.py:464
+msgid ""
+"[WARNING_NOTCL] There are no Point coordinates in the Point field. Add "
+"coords and try again ..."
+msgstr ""
+"[WARNING_NOTCL] Das Punktfeld enthält keine Punktkoordinaten. Fügen Sie "
+"Coords hinzu und versuchen Sie es erneut ..."
+
+#: flatcamTools/ToolDblSided.py:484
+#, python-format
+msgid "[success] Excellon %s was mirrored..."
+msgstr "[success] Excellon %s wurde gespiegelt ..."
+
+#: flatcamTools/ToolDblSided.py:493
+msgid "[WARNING_NOTCL] There is no Geometry object loaded ..."
+msgstr "[WARNING_NOTCL] Es wurde kein Geometrieobjekt geladen ..."
+
+#: flatcamTools/ToolDblSided.py:521
+#, python-format
+msgid "[success] Geometry %s was mirrored..."
+msgstr "[success] Geometrie %s wurde gespiegelt ..."
+
+#: flatcamTools/ToolFilm.py:25
+msgid "Film PCB"
+msgstr "Film PCB"
+
+#: flatcamTools/ToolFilm.py:56 flatcamTools/ToolImage.py:53
+#: flatcamTools/ToolPanelize.py:56
+msgid "Object Type:"
+msgstr "Objekttyp:"
+
+#: flatcamTools/ToolFilm.py:58
+msgid ""
+"Specify the type of object for which to create the film.\n"
+"The object can be of type: Gerber or Geometry.\n"
+"The selection here decide the type of objects that will be\n"
+"in the Film Object combobox."
+msgstr ""
+"Geben Sie den Objekttyp an, für den der Film erstellt werden soll.\n"
+"Das Objekt kann vom Typ sein: Gerber oder Geometrie.\n"
+"Die Auswahl hier bestimmt den Objekttyp\n"
+"im Filmobjekt-Kombinationsfeld."
+
+#: flatcamTools/ToolFilm.py:71
+msgid "Film Object:"
+msgstr "Filmobjekt:"
+
+#: flatcamTools/ToolFilm.py:73
+msgid "Object for which to create the film."
+msgstr "Objekt, für das der Film erstellt werden soll."
+
+#: flatcamTools/ToolFilm.py:89 flatcamTools/ToolPanelize.py:89
+msgid "Box Type:"
+msgstr "Box-Typ:"
+
+#: flatcamTools/ToolFilm.py:91
+msgid ""
+"Specify the type of object to be used as an container for\n"
+"film creation. It can be: Gerber or Geometry type.The selection here decide "
+"the type of objects that will be\n"
+"in the Box Object combobox."
+msgstr ""
+"Geben Sie den Objekttyp an, für den ein Container verwendet werden soll\n"
+"Filmschaffung. Es kann sein: Gerber- oder Geometrietyp. Die Auswahl hier "
+"bestimmt den Objekttyp\n"
+"im Kombinationsfeld Box-Objekt."
+
+#: flatcamTools/ToolFilm.py:104 flatcamTools/ToolPanelize.py:104
+msgid "Box Object:"
+msgstr "Box-Objekt:"
+
+#: flatcamTools/ToolFilm.py:106
+msgid ""
+"The actual object that is used a container for the\n"
+" selected object for which we create the film.\n"
+"Usually it is the PCB outline but it can be also the\n"
+"same object for which the film is created."
+msgstr ""
+"Das eigentliche Objekt, für das ein Container verwendet wird\n"
+"  ausgewähltes Objekt, für das wir den Film erstellen.\n"
+"Normalerweise ist es die Leiterplattenkontur, aber es kann auch die\n"
+"das gleiche Objekt, für das der Film erstellt wurde."
+
+#: flatcamTools/ToolFilm.py:157
+msgid "Save Film"
+msgstr "Film speichern"
+
+#: flatcamTools/ToolFilm.py:159
+msgid ""
+"Create a Film for the selected object, within\n"
+"the specified box. Does not create a new \n"
+" FlatCAM object, but directly save it in SVG format\n"
+"which can be opened with Inkscape."
+msgstr ""
+"Erstellen Sie einen Film für das ausgewählte Objekt\n"
+"die angegebene Box Erstellt kein neues\n"
+"  FlatCAM-Objekt, speichern Sie es jedoch direkt im SVG-Format\n"
+"die mit Inkscape geöffnet werden kann."
+
+#: flatcamTools/ToolFilm.py:225
+msgid ""
+"[ERROR_NOTCL] No FlatCAM object selected. Load an object for Film and retry."
+msgstr ""
+"[ERROR_NOTCL] Kein FlatCAM-Objekt ausgewählt. Laden Sie ein Objekt für Film "
+"und versuchen Sie es erneut."
+
+#: flatcamTools/ToolFilm.py:231
+msgid ""
+"[ERROR_NOTCL] No FlatCAM object selected. Load an object for Box and retry."
+msgstr ""
+"[ERROR_NOTCL] Kein FlatCAM-Objekt ausgewählt. Laden Sie ein Objekt für Box "
+"und versuchen Sie es erneut."
+
+#: flatcamTools/ToolFilm.py:255
+msgid "Generating Film ..."
+msgstr "Film wird erstellt ..."
+
+#: flatcamTools/ToolFilm.py:260 flatcamTools/ToolFilm.py:264
+msgid "Export SVG positive"
+msgstr "SVG positiv exportieren"
+
+#: flatcamTools/ToolFilm.py:269
+msgid "[WARNING_NOTCL] Export SVG positive cancelled."
+msgstr "[WARNING_NOTCL] Export des SVG-Positivs wurde abgebrochen."
+
+#: flatcamTools/ToolFilm.py:276 flatcamTools/ToolFilm.py:280
+msgid "Export SVG negative"
+msgstr "Exportieren Sie SVG negativ"
+
+#: flatcamTools/ToolFilm.py:285
+msgid "[WARNING_NOTCL] Export SVG negative cancelled."
+msgstr "[WARNING_NOTCL] Export des SVG-Negativs wurde abgebrochen."
+
+#: flatcamTools/ToolImage.py:25
+msgid "Image as Object"
+msgstr "Bild als Objekt"
+
+#: flatcamTools/ToolImage.py:31
+msgid "Image to PCB"
+msgstr "Bild auf PCB"
+
+#: flatcamTools/ToolImage.py:55
+msgid ""
+"Specify the type of object to create from the image.\n"
+"It can be of type: Gerber or Geometry."
+msgstr ""
+"Geben Sie den Objekttyp an, der aus dem Bild erstellt werden soll.\n"
+"Es kann vom Typ sein: Gerber oder Geometrie."
+
+#: flatcamTools/ToolImage.py:63
+msgid "DPI value:"
+msgstr "DPI-Wert:"
+
+#: flatcamTools/ToolImage.py:65
+msgid "Specify a DPI value for the image."
+msgstr "Geben Sie einen DPI-Wert für das Bild an."
+
+#: flatcamTools/ToolImage.py:72
+msgid "Level of detail"
+msgstr "Detaillierungsgrad"
+
+#: flatcamTools/ToolImage.py:81
+msgid "Image type"
+msgstr "Bildtyp"
+
+#: flatcamTools/ToolImage.py:83
+msgid ""
+"Choose a method for the image interpretation.\n"
+"B/W means a black & white image. Color means a colored image."
+msgstr ""
+"Wählen Sie eine Methode für die Bildinterpretation.\n"
+"B / W steht für ein Schwarzweißbild. Farbe bedeutet ein farbiges Bild."
+
+#: flatcamTools/ToolImage.py:90 flatcamTools/ToolImage.py:103
+#: flatcamTools/ToolImage.py:114 flatcamTools/ToolImage.py:125
+msgid "Mask value"
+msgstr "Maskenwert"
+
+#: flatcamTools/ToolImage.py:92
+msgid ""
+"Mask for monochrome image.\n"
+"Takes values between [0 ... 255].\n"
+"Decides the level of details to include\n"
+"in the resulting geometry.\n"
+"0 means no detail and 255 means everything \n"
+"(which is totally black)."
+msgstr ""
+"Maske für ein Schwarzweißbild.\n"
+"Nimmt Werte zwischen [0 ... 255] an.\n"
+"Legt fest, wie viel Details enthalten sind\n"
+"in der resultierenden Geometrie.\n"
+"0 bedeutet kein Detail und 255 bedeutet alles\n"
+"(das ist total schwarz)."
+
+#: flatcamTools/ToolImage.py:105
+msgid ""
+"Mask for RED color.\n"
+"Takes values between [0 ... 255].\n"
+"Decides the level of details to include\n"
+"in the resulting geometry."
+msgstr ""
+"Maske für rote Farbe.\n"
+"Nimmt Werte zwischen [0 ... 255] an.\n"
+"Legt fest, wie viel Details enthalten sind\n"
+"in der resultierenden Geometrie."
+
+#: flatcamTools/ToolImage.py:116
+msgid ""
+"Mask for GREEN color.\n"
+"Takes values between [0 ... 255].\n"
+"Decides the level of details to include\n"
+"in the resulting geometry."
+msgstr ""
+"Maske für GRÜNE Farbe.\n"
+"Nimmt Werte zwischen [0 ... 255] an.\n"
+"Legt fest, wie viel Details enthalten sind\n"
+"in der resultierenden Geometrie."
+
+#: flatcamTools/ToolImage.py:127
+msgid ""
+"Mask for BLUE color.\n"
+"Takes values between [0 ... 255].\n"
+"Decides the level of details to include\n"
+"in the resulting geometry."
+msgstr ""
+"Maske für BLAUE Farbe.\n"
+"Nimmt Werte zwischen [0 ... 255] an.\n"
+"Legt fest, wie viel Details enthalten sind\n"
+"in der resultierenden Geometrie."
+
+#: flatcamTools/ToolImage.py:139
+msgid "Import image"
+msgstr "Bild importieren"
+
+#: flatcamTools/ToolImage.py:141
+msgid "Open a image of raster type and then import it in FlatCAM."
+msgstr "Öffnen Sie ein Bild vom Raster-Typ und importieren Sie es in FlatCAM."
+
+#: flatcamTools/ToolImage.py:170
+msgid "Image Tool"
+msgstr "Bildwerkzeug"
+
+#: flatcamTools/ToolImage.py:200 flatcamTools/ToolImage.py:203
+msgid "Import IMAGE"
+msgstr "BILD importieren"
+
+#: flatcamTools/ToolMeasurement.py:26
+msgid "Measurement"
+msgstr "Messung"
+
+#: flatcamTools/ToolMeasurement.py:44
+msgid "Units:"
+msgstr "Einheiten:"
+
+#: flatcamTools/ToolMeasurement.py:45
+msgid "Those are the units in which the distance is measured."
+msgstr "Dies sind die Einheiten, in denen die Entfernung gemessen wird."
+
+#: flatcamTools/ToolMeasurement.py:49
+msgid "Start"
+msgstr "Start"
+
+#: flatcamTools/ToolMeasurement.py:49 flatcamTools/ToolMeasurement.py:52
+msgid "Coords"
+msgstr "Koordinaten"
+
+#: flatcamTools/ToolMeasurement.py:50 flatcamTools/ToolMeasurement.py:66
+msgid "This is measuring Start point coordinates."
+msgstr "Dies ist das Messen von Startpunktkoordinaten."
+
+#: flatcamTools/ToolMeasurement.py:52
+msgid "Stop"
+msgstr "Halt"
+
+#: flatcamTools/ToolMeasurement.py:53 flatcamTools/ToolMeasurement.py:70
+msgid "This is the measuring Stop point coordinates."
+msgstr "Dies ist die Messpunkt-Koordinate."
+
+#: flatcamTools/ToolMeasurement.py:56 flatcamTools/ToolMeasurement.py:74
+msgid "This is the distance measured over the X axis."
+msgstr "Dies ist der Abstand, der über die X-Achse gemessen wird."
+
+#: flatcamTools/ToolMeasurement.py:59 flatcamTools/ToolMeasurement.py:79
+msgid "This is the distance measured over the Y axis."
+msgstr "Dies ist die über die Y-Achse gemessene Entfernung."
+
+#: flatcamTools/ToolMeasurement.py:61
+msgid "DISTANCE"
+msgstr "ENTFERNUNG"
+
+#: flatcamTools/ToolMeasurement.py:62 flatcamTools/ToolMeasurement.py:84
+msgid "This is the point to point Euclidian distance."
+msgstr "Dies ist die Punkt-zu-Punkt-Euklidische Entfernung."
+
+#: flatcamTools/ToolMeasurement.py:86
+msgid "Measure"
+msgstr "Messen"
+
+#: flatcamTools/ToolMeasurement.py:126
+msgid "Meas. Tool"
+msgstr "Messgerät"
+
+#: flatcamTools/ToolMeasurement.py:221
+msgid "MEASURING: Click on the Start point ..."
+msgstr "MESSEN: Klicken Sie auf den Startpunkt ..."
+
+#: flatcamTools/ToolMeasurement.py:231
+msgid "Measurement Tool exit..."
+msgstr "Messwerkzeug beenden ..."
+
+#: flatcamTools/ToolMeasurement.py:258
+msgid "MEASURING: Click on the Destination point ..."
+msgstr "MESSEN: Klicken Sie auf den Zielpunkt ..."
+
+#: flatcamTools/ToolMeasurement.py:276
+#, python-brace-format
+msgid "MEASURING: Result D(x) = {d_x} | D(y) = {d_y} | Distance = {d_z}"
+msgstr "MESSEN: Ergebnis D (x) = {d_x} | D (y) = {d_y} | Abstand = {d_z}"
+
+#: flatcamTools/ToolMove.py:81
+msgid "MOVE: Click on the Start point ..."
+msgstr "Verschieben: Klicke auf den Startpunkt ..."
+
+#: flatcamTools/ToolMove.py:88
+msgid "[WARNING_NOTCL] MOVE action cancelled. No object(s) to move."
+msgstr ""
+"[WARNING_NOTCL] Bewegungsaktion abgebrochen. Keine Objekte zum Verschieben."
+
+#: flatcamTools/ToolMove.py:110
+msgid "MOVE: Click on the Destination point ..."
+msgstr "Verschieben: Klicken Sie auf den Zielpunkt ..."
+
+#: flatcamTools/ToolMove.py:128
+msgid "Moving ..."
+msgstr "Ziehen um ..."
+
+#: flatcamTools/ToolMove.py:135
+msgid "[WARNING_NOTCL] No object(s) selected."
+msgstr "[WARNING_NOTCL] Kein Objekt ausgewählt."
+
+#: flatcamTools/ToolMove.py:158
+#, python-format
+msgid "[ERROR_NOTCL] ToolMove.on_left_click() --> %s"
+msgstr "[ERROR_NOTCL] ToolMove.on_left_click() --> %s"
+
+#: flatcamTools/ToolMove.py:164
+#, python-format
+msgid "[success] %s object was moved ..."
+msgstr "[success] %s objekt wurde verschoben ..."
+
+#: flatcamTools/ToolMove.py:174
+msgid "[ERROR_NOTCL] ToolMove.on_left_click() --> Error when mouse left click."
+msgstr ""
+"[ERROR_NOTCL] ToolMove.on_left_click() --> Fehler beim Klicken mit der "
+"linken Maustaste."
+
+#: flatcamTools/ToolMove.py:202
+msgid "[WARNING_NOTCL] Move action cancelled."
+msgstr "[WARNING_NOTCL] Bewegungsaktion abgebrochen."
+
+#: flatcamTools/ToolMove.py:214
+msgid "[WARNING_NOTCL] Object(s) not selected"
+msgstr "[WARNING_NOTCL] Objekt (e) nicht ausgewählt"
+
+#: flatcamTools/ToolNonCopperClear.py:26
+msgid "Non-Copper Clearing"
+msgstr "Nicht-Kupfer-Clearing"
+
+#: flatcamTools/ToolNonCopperClear.py:64
+msgid "Gerber object to be cleared of excess copper.                        "
+msgstr "Gerber-Objekt, das von überschüssigem Kupfer befreit werden soll."
+
+#: flatcamTools/ToolNonCopperClear.py:74
+msgid ""
+"Tools pool from which the algorithm\n"
+"will pick the ones used for copper clearing."
+msgstr ""
+"Toolspool aus dem der Algorithmus\n"
+"wählt die für die Kupferreinigung verwendeten aus."
+
+#: flatcamTools/ToolNonCopperClear.py:89
+msgid ""
+"This is the Tool Number.\n"
+"Non copper clearing will start with the tool with the biggest \n"
+"diameter, continuing until there are no more tools.\n"
+"Only tools that create NCC clearing geometry will still be present\n"
+"in the resulting geometry. This is because with some tools\n"
+"this function will not be able to create painting geometry."
+msgstr ""
+"Dies ist die Werkzeugnummer.\n"
+"Das Nicht-Kupfer-Clearing beginnt mit dem Werkzeug mit dem größten\n"
+"Durchmesser, weiter, bis keine Werkzeuge mehr vorhanden sind.\n"
+"Es sind nur noch Werkzeuge vorhanden, die eine NCC-Clearing-Geometrie "
+"erstellen\n"
+"in der resultierenden Geometrie. Dies liegt daran, dass mit einigen Tools\n"
+"Diese Funktion kann keine Malgeometrie erstellen."
+
+#: flatcamTools/ToolNonCopperClear.py:101 flatcamTools/ToolPaint.py:98
+msgid ""
+"The Tool Type (TT) can be:<BR>- <B>Circular</B> with 1 ... 4 teeth -> it is "
+"informative only. Being circular, <BR>the cut width in material is exactly "
+"the tool diameter.<BR>- <B>Ball</B> -> informative only and make reference "
+"to the Ball type endmill.<BR>- <B>V-Shape</B> -> it will disable de Z-Cut "
+"parameter in the resulting geometry UI form and enable two additional UI "
+"form fields in the resulting geometry: V-Tip Dia and V-Tip Angle. Adjusting "
+"those two values will adjust the Z-Cut parameter such as the cut width into "
+"material will be equal with the value in the Tool Diameter column of this "
+"table.<BR>Choosing the <B>V-Shape</B> Tool Type automatically will select "
+"the Operation Type in the resulting geometry as Isolation."
+msgstr ""
+"Der Werkzeugtyp (TT) kann sein: <BR> - <B> Rund </B> mit 1 ... 4 Zähnen -> "
+"Es ist nur informativ. Die Schnittbreite des Materials ist kreisförmig und "
+"entspricht genau dem Werkzeugdurchmesser. <B> Kugel </B> -> dient nur der "
+"Information und nimmt Bezug auf das Schaftfräser der Kugel. <BR> - <B> V -"
+"Shape </B> -> Deaktiviert den Z-Cut-Parameter im resultierenden Geometrie-UI-"
+"Formular und aktiviert zwei zusätzliche UI-Formularfelder in der "
+"resultierenden Geometrie: V-Tip-Dia und V-Tip-Winkel. Durch das Anpassen "
+"dieser beiden Werte wird der Z-Cut-Parameter angepasst, z. B. wird die "
+"Schnittbreite in Material dem Wert in der Spalte \"Werkzeugdurchmesser\" "
+"dieser Tabelle entsprechen. <BR> Auswählen des <B> V-Shape </B> -"
+"Werkzeugtyps wählt automatisch den Operationstyp in der resultierenden "
+"Geometrie als Isolation aus."
+
+#: flatcamTools/ToolNonCopperClear.py:120 flatcamTools/ToolPaint.py:117
+msgid "Tool Dia"
+msgstr "Werkzeugdurchm"
+
+#: flatcamTools/ToolNonCopperClear.py:122
+msgid "Diameter for the new tool to add in the Tool Table"
+msgstr ""
+"Durchmesser für das neue Werkzeug, das in der Werkzeugtabelle hinzugefügt "
+"werden soll"
+
+#: flatcamTools/ToolNonCopperClear.py:148 flatcamTools/ToolPaint.py:145
+#: flatcamTools/ToolSolderPaste.py:123
+msgid ""
+"Delete a selection of tools in the Tool Table\n"
+"by first selecting a row(s) in the Tool Table."
+msgstr ""
+"Löschen Sie eine Auswahl von Werkzeugen in der Werkzeugtabelle\n"
+"indem Sie zuerst eine oder mehrere Zeilen in der Werkzeugtabelle auswählen."
+
+#: flatcamTools/ToolNonCopperClear.py:226
+msgid ""
+"If checked, use 'rest machining'.\n"
+"Basically it will clear copper outside PCB features,\n"
+"using the biggest tool and continue with the next tools,\n"
+"from bigger to smaller, to clear areas of copper that\n"
+"could not be cleared by previous tool, until there is\n"
+"no more copper to clear or there are no more tools.\n"
+"If not checked, use the standard algorithm."
+msgstr ""
+"Wenn aktiviert, verwenden Sie \"Restbearbeitung\".\n"
+"Grundsätzlich wird Kupfer außerhalb der PCB-Merkmale gelöscht.\n"
+"das größte Werkzeug verwenden und mit den nächsten Werkzeugen fortfahren,\n"
+"von größeren zu kleineren, um Kupferbereiche zu reinigen\n"
+"konnte nicht durch vorheriges Werkzeug gelöscht werden, bis es gibt\n"
+"kein kupfer mehr zum löschen oder es gibt keine werkzeuge mehr.\n"
+"Wenn nicht aktiviert, verwenden Sie den Standardalgorithmus."
+
+#: flatcamTools/ToolNonCopperClear.py:238
+msgid "Generate Geometry"
+msgstr "Geometrie erzeugen"
+
+#: flatcamTools/ToolNonCopperClear.py:484 flatcamTools/ToolPaint.py:543
+#: flatcamTools/ToolSolderPaste.py:760
+msgid "[WARNING_NOTCL] Please enter a tool diameter to add, in Float format."
+msgstr ""
+"[WARNING_NOTCL] Bitte geben Sie einen hinzuzufügenden Werkzeugdurchmesser im "
+"Float-Format an."
+
+#: flatcamTools/ToolNonCopperClear.py:512 flatcamTools/ToolPaint.py:567
+msgid "[WARNING_NOTCL] Adding tool cancelled. Tool already in Tool Table."
+msgstr ""
+"[WARNING_NOTCL] Das Hinzufügen des Tools wurde abgebrochen. Werkzeug bereits "
+"in der Werkzeugtabelle."
+
+#: flatcamTools/ToolNonCopperClear.py:517 flatcamTools/ToolPaint.py:572
+msgid "[success] New tool added to Tool Table."
+msgstr "[success] Neues Werkzeug zur Werkzeugtabelle hinzugefügt."
+
+#: flatcamTools/ToolNonCopperClear.py:559 flatcamTools/ToolPaint.py:615
+msgid "[success] Tool from Tool Table was edited."
+msgstr "[success] Werkzeug aus Werkzeugtabelle wurde bearbeitet."
+
+#: flatcamTools/ToolNonCopperClear.py:570 flatcamTools/ToolPaint.py:626
+#: flatcamTools/ToolSolderPaste.py:846
+msgid ""
+"[WARNING_NOTCL] Edit cancelled. New diameter value is already in the Tool "
+"Table."
+msgstr ""
+"[WARNING_NOTCL] Bearbeitung abgebrochen. Neuer Durchmesserwert befindet sich "
+"bereits in der Werkzeugtabelle."
+
+#: flatcamTools/ToolNonCopperClear.py:609 flatcamTools/ToolPaint.py:723
+msgid "[WARNING_NOTCL] Delete failed. Select a tool to delete."
+msgstr ""
+"[WARNING_NOTCL] Löschen fehlgeschlagen. Wählen Sie ein Werkzeug zum Löschen "
+"aus."
+
+#: flatcamTools/ToolNonCopperClear.py:614 flatcamTools/ToolPaint.py:728
+msgid "[success] Tool(s) deleted from Tool Table."
+msgstr "[success] Werkzeug(e) aus der Werkzeugtabelle gelöscht."
+
+#: flatcamTools/ToolNonCopperClear.py:632 flatcamTools/ToolPaint.py:747
+msgid ""
+"[ERROR_NOTCL] Overlap value must be between 0 (inclusive) and 1 (exclusive), "
+msgstr ""
+"[ERROR_NOTCL] Der Überlappungswert muss zwischen 0 (einschließlich) und 1 "
+"(exklusiv) liegen."
+
+#: flatcamTools/ToolNonCopperClear.py:672
+msgid "[ERROR_NOTCL] No Gerber file available."
+msgstr "[ERROR_NOTCL] Keine Gerber-Datei verfügbar."
+
+#: flatcamTools/ToolNonCopperClear.py:710
+#: flatcamTools/ToolNonCopperClear.py:832
+msgid "Clearing Non-Copper areas."
+msgstr "Nicht kupferne Bereiche entfernen."
+
+#: flatcamTools/ToolNonCopperClear.py:728
+#, python-format
+msgid "[success] Non-Copper Clearing with ToolDia = %s started."
+msgstr ""
+"[success] Nicht-Kupfer-Clearing mit Werkzeugdurchmesser = %s gestartet."
+
+#: flatcamTools/ToolNonCopperClear.py:797
+#, python-format
+msgid "[ERROR_NOTCL] NCCTool.clear_non_copper() --> %s"
+msgstr "[ERROR_NOTCL] NCCTool.clear_non_copper() --> %s"
+
+#: flatcamTools/ToolNonCopperClear.py:802
+msgid "[success] NCC Tool finished."
+msgstr "[success] NCC-Tool fertiggestellt."
+
+#: flatcamTools/ToolNonCopperClear.py:804
+msgid ""
+"[WARNING_NOTCL] NCC Tool finished but some PCB features could not be "
+"cleared. Check the result."
+msgstr ""
+"[WARNING_NOTCL] NCC-Tool fertiggestellt, einige PCB-Funktionen konnten "
+"jedoch nicht gelöscht werden. Überprüfen Sie das Ergebnis."
+
+#: flatcamTools/ToolNonCopperClear.py:850
+#, python-format
+msgid "[success] Non-Copper Rest Clearing with ToolDia = %s started."
+msgstr ""
+"[success] Nicht-Kupfer-Restklärung mit Werkzeugdurchmesser =%s gestartet."
+
+#: flatcamTools/ToolNonCopperClear.py:948
+#, python-format
+msgid "[ERROR_NOTCL] NCCTool.clear_non_copper_rest() --> %s"
+msgstr "[ERROR_NOTCL] NCCTool.clear_non_copper_rest() --> %s"
+
+#: flatcamTools/ToolNonCopperClear.py:956
+msgid ""
+"[ERROR_NOTCL] NCC Tool finished but could not clear the object with current "
+"settings."
+msgstr ""
+"[ERROR_NOTCL] NCC-Tool wurde beendet, das Objekt konnte jedoch nicht mit den "
+"aktuellen Einstellungen gelöscht werden."
+
+#: flatcamTools/ToolPaint.py:24
+msgid "Paint Area"
+msgstr "Paint Bereich"
+
+#: flatcamTools/ToolPaint.py:60
+msgid "Geometry:"
+msgstr "Geometrie:"
+
+#: flatcamTools/ToolPaint.py:62
+msgid "Geometry object to be painted.                        "
+msgstr "Geometrieobjekt, das gemalt werden soll."
+
+#: flatcamTools/ToolPaint.py:71
+msgid ""
+"Tools pool from which the algorithm\n"
+"will pick the ones used for painting."
+msgstr ""
+"Toolspool aus dem der Algorithmus\n"
+"wählt die zum Malen verwendeten aus."
+
+#: flatcamTools/ToolPaint.py:86
+msgid ""
+"This is the Tool Number.\n"
+"Painting will start with the tool with the biggest diameter,\n"
+"continuing until there are no more tools.\n"
+"Only tools that create painting geometry will still be present\n"
+"in the resulting geometry. This is because with some tools\n"
+"this function will not be able to create painting geometry."
+msgstr ""
+"Dies ist die Werkzeugnummer.\n"
+"Das Malen beginnt mit dem Werkzeug mit dem größten Durchmesser.\n"
+"fortsetzen, bis es keine Werkzeuge mehr gibt.\n"
+"Es sind nur noch Werkzeuge vorhanden, die eine Malgeometrie erstellen\n"
+"in der resultierenden Geometrie. Dies liegt daran, dass mit einigen Tools\n"
+"Diese Funktion kann keine Malgeometrie erstellen."
+
+#: flatcamTools/ToolPaint.py:119
+msgid "Diameter for the new tool."
+msgstr "Durchmesser für das neue Werkzeug."
+
+#: flatcamTools/ToolPaint.py:224
+msgid ""
+"If checked, use 'rest machining'.\n"
+"Basically it will clear copper outside PCB features,\n"
+"using the biggest tool and continue with the next tools,\n"
+"from bigger to smaller, to clear areas of copper that\n"
+"could not be cleared by previous tool, until there is\n"
+"no more copper to clear or there are no more tools.\n"
+"\n"
+"If not checked, use the standard algorithm."
+msgstr ""
+"Wenn aktiviert, verwenden Sie \"Restbearbeitung\".\n"
+"Grundsätzlich wird Kupfer außerhalb der PCB-Merkmale gelöscht.\n"
+"das größte Werkzeug verwenden und mit den nächsten Werkzeugen fortfahren,\n"
+"von größeren zu kleineren, um Kupferbereiche zu reinigen\n"
+"konnte nicht durch vorheriges Werkzeug gelöscht werden, bis es gibt\n"
+"kein kupfer mehr zum löschen oder es gibt keine werkzeuge mehr.\n"
+"\n"
+"Wenn nicht aktiviert, verwenden Sie den Standardalgorithmus."
+
+#: flatcamTools/ToolPaint.py:239
+msgid ""
+"How to select the polygons to paint.<BR>Options:<BR>- <B>Single</B>: left "
+"mouse click on the polygon to be painted.<BR>- <B>All</B>: paint all "
+"polygons."
+msgstr ""
+"So wählen Sie die zu lackierenden Polygone aus. <BR> Optionen: <BR> - <B> "
+"Single </B>: Klicken Sie mit der linken Maustaste auf das zu lackierende "
+"Polygon. <BR> - <B> Alles</B>: Paint alle Polygone."
+
+#: flatcamTools/ToolPaint.py:254
+msgid "Create Paint Geometry"
+msgstr "Farbgeometrie erstellen"
+
+#: flatcamTools/ToolPaint.py:256
+msgid ""
+"After clicking here, click inside<BR>the polygon you wish to be painted if "
+"<B>Single</B> is selected.<BR>If <B>All</B>  is selected then the Paint will "
+"start after click.<BR>A new Geometry object with the tool<BR>paths will be "
+"created."
+msgstr ""
+"Wenn Sie hier klicken, klicken Sie in <BR> das Polygon, das Sie zeichnen "
+"möchten, wenn <B> Single </B> ausgewählt ist. <BR> Wenn <B> Alles </B> "
+"ausgewählt ist, wird der Paint nach dem Klicken gestartet. <BR> Ein neues "
+"Geometrieobjekt mit den Werkzeugpfaden wird erstellt."
+
+#: flatcamTools/ToolPaint.py:732
+msgid "geometry_on_paint_button"
+msgstr "geometry_on_paint_button"
+
+#: flatcamTools/ToolPaint.py:751 flatcamTools/ToolPaint.py:786
+msgid "[WARNING_NOTCL] Click inside the desired polygon."
+msgstr "[WARNING_NOTCL] Klicken Sie in das gewünschte Polygon."
+
+#: flatcamTools/ToolPaint.py:773
+msgid "[ERROR_NOTCL] Can't do Paint on MultiGeo geometries ..."
+msgstr "[ERROR_NOTCL] \"Paint\" für MultiGeo-Geometrien nicht möglich ..."
+
+#: flatcamTools/ToolPaint.py:795 flatcamTools/ToolPaint.py:998
+msgid "Painting polygon..."
+msgstr "Polygon malen ..."
+
+#: flatcamTools/ToolPaint.py:846
+msgid "[WARNING] No polygon found."
+msgstr "[WARNING] Kein Polygon gefunden."
+
+#: flatcamTools/ToolPaint.py:849
+msgid "Painting polygon."
+msgstr "Polygon malen."
+
+#: flatcamTools/ToolPaint.py:891
+msgid "[ERROR_NOTCL] Geometry could not be painted completely"
+msgstr "[ERROR_NOTCL] Geometrie konnte nicht vollständig gezeichnet werden"
+
+#: flatcamTools/ToolPaint.py:917
+#, python-format
+msgid ""
+"[ERROR] Could not do Paint. Try a different combination of parameters. Or a "
+"different strategy of paint\n"
+"%s"
+msgstr ""
+"[ERROR] Paint konnte nicht ausgeführt werden. Versuchen Sie eine andere "
+"Kombination von Parametern. Oder eine andere Farbstrategie\n"
+"%s"
+
+#: flatcamTools/ToolPaint.py:959
+#, python-format
+msgid "[ERROR_NOTCL] PaintTool.paint_poly() --> %s"
+msgstr "[ERROR_NOTCL] PaintTool.paint_poly() --> %s"
+
+#: flatcamTools/ToolPaint.py:965 flatcamTools/ToolPaint.py:1258
+msgid "Polygon Paint started ..."
+msgstr "Polygonfarbe gestartet ..."
+
+#: flatcamTools/ToolPaint.py:1114 flatcamTools/ToolPaint.py:1203
+#, python-format
+msgid ""
+"[ERROR] Could not do Paint All. Try a different combination of parameters. "
+"Or a different Method of paint\n"
+"%s"
+msgstr ""
+"[ERROR] Paint Paint nicht möglich. Versuchen Sie eine andere Kombination von "
+"Parametern. Oder eine andere Farbmethode\n"
+"%s"
+
+#: flatcamTools/ToolPaint.py:1138
+msgid ""
+"[ERROR] There is no Painting Geometry in the file.\n"
+"Usually it means that the tool diameter is too big for the painted "
+"geometry.\n"
+"Change the painting parameters and try again."
+msgstr ""
+"[ERROR] Die Datei enthält keine Paint-Geometrie.\n"
+"Normalerweise bedeutet dies, dass der Werkzeugdurchmesser für die gemalte "
+"Geometrie zu groß ist.\n"
+"Ändern Sie die Malparameter und versuchen Sie es erneut."
+
+#: flatcamTools/ToolPaint.py:1147
+msgid "[success] Paint All Done."
+msgstr "[success] 'Paint' Sie alles fertig."
+
+#: flatcamTools/ToolPaint.py:1233
+msgid ""
+"[ERROR_NOTCL] There is no Painting Geometry in the file.\n"
+"Usually it means that the tool diameter is too big for the painted "
+"geometry.\n"
+"Change the painting parameters and try again."
+msgstr ""
+"[ERROR_NOTCL] Die Datei enthält keine 'Paint'-Geometrie.\n"
+"Normalerweise bedeutet dies, dass der Werkzeugdurchmesser für die gemalte "
+"Geometrie zu groß ist.\n"
+"Ändern Sie die Malparameter und versuchen Sie es erneut."
+
+#: flatcamTools/ToolPaint.py:1242
+msgid "[success] Paint All with Rest-Machining done."
+msgstr "[success] Paint All with Rest-Machining erledigt."
+
+#: flatcamTools/ToolPanelize.py:25
+msgid "Panelize PCB"
+msgstr "Panelize PCB"
+
+#: flatcamTools/ToolPanelize.py:58
+msgid ""
+"Specify the type of object to be panelized\n"
+"It can be of type: Gerber, Excellon or Geometry.\n"
+"The selection here decide the type of objects that will be\n"
+"in the Object combobox."
+msgstr ""
+"Geben Sie den Typ des Objekts an, für das ein Panel erstellt werden soll\n"
+"Es kann vom Typ sein: Gerber, Excellon oder Geometrie.\n"
+"Die Auswahl hier bestimmt den Objekttyp\n"
+"im Objekt-Kombinationsfeld."
+
+#: flatcamTools/ToolPanelize.py:73
+msgid ""
+"Object to be panelized. This means that it will\n"
+"be duplicated in an array of rows and columns."
+msgstr ""
+"Objekt, das in Panels gesetzt werden soll. Dies bedeutet, dass es wird\n"
+"in einem Array von Zeilen und Spalten dupliziert werden."
+
+#: flatcamTools/ToolPanelize.py:91
+msgid ""
+"Specify the type of object to be used as an container for\n"
+"panelization. It can be: Gerber or Geometry type.\n"
+"The selection here decide the type of objects that will be\n"
+"in the Box Object combobox."
+msgstr ""
+"Geben Sie den Objekttyp an, für den ein Container verwendet werden soll\n"
+"Panelisierung. Es kann sein: Gerber oder Geometrietyp.\n"
+"Die Auswahl hier bestimmt den Objekttyp\n"
+"im Kombinationsfeld Box-Objekt."
+
+#: flatcamTools/ToolPanelize.py:106
+msgid ""
+"The actual object that is used a container for the\n"
+" selected object that is to be panelized."
+msgstr ""
+"Das eigentliche Objekt, für das ein Container verwendet wird\n"
+"ausgewähltes Objekt, das in Panelisiert werden soll."
+
+#: flatcamTools/ToolPanelize.py:150
+msgid ""
+"Choose the type of object for the panel object:\n"
+"- Geometry\n"
+"- Gerber"
+msgstr ""
+"Wählen Sie den Objekttyp für das Panel-Objekt:\n"
+"- Geometrie\n"
+"- Gerber"
+
+#: flatcamTools/ToolPanelize.py:158
+msgid "Constrain panel within:"
+msgstr "Panel einschränken innerhalb:"
+
+#: flatcamTools/ToolPanelize.py:192
+msgid "Panelize Object"
+msgstr "Panelize Objekt"
+
+#: flatcamTools/ToolPanelize.py:194
+msgid ""
+"Panelize the specified object around the specified box.\n"
+"In other words it creates multiple copies of the source object,\n"
+"arranged in a 2D array of rows and columns."
+msgstr ""
+"Das angegebene Objekt um das angegebene Feld einteilen.\n"
+"Mit anderen Worten, es erstellt mehrere Kopien des Quellobjekts,\n"
+"in einem 2D-Array von Zeilen und Spalten angeordnet."
+
+#: flatcamTools/ToolPanelize.py:311
+#, python-format
+msgid "[WARNING]No object Box. Using instead %s"
+msgstr "[WARNING] Kein Objektfeld. Stattdessen verwenden %s"
+
+#: flatcamTools/ToolPanelize.py:392
+msgid ""
+"[ERROR_NOTCL] Columns or Rows are zero value. Change them to a positive "
+"integer."
+msgstr ""
+"[ERROR_NOTCL] Spalten oder Zeilen haben den Wert Null. Ändern Sie sie in "
+"eine positive ganze Zahl."
+
+#: flatcamTools/ToolPanelize.py:417 flatcamTools/ToolPanelize.py:526
+msgid "Generating panel ... Please wait."
+msgstr "Panel wird generiert ... Bitte warten Sie."
+
+#: flatcamTools/ToolPanelize.py:520
+msgid "[success] Panel done..."
+msgstr "[success] Panel fertig ..."
+
+#: flatcamTools/ToolPanelize.py:523
+#, python-brace-format
+msgid ""
+"[WARNING] Too big for the constrain area. Final panel has {col} columns and "
+"{row} rows"
+msgstr ""
+"[WARNING] Für den Constrain-Bereich zu groß. Das letzte Panel enthält {col} "
+"Spalten und {row} Zeilen"
+
+#: flatcamTools/ToolPanelize.py:531
+msgid "[success] Panel created successfully."
+msgstr "[success] Panel erfolgreich erstellt"
+
+#: flatcamTools/ToolProperties.py:103
+msgid "[ERROR_NOTCL] Properties Tool was not displayed. No object selected."
+msgstr ""
+"[ERROR_NOTCL] Eigenschaftenwerkzeug wurde nicht angezeigt. Kein Objekt "
+"ausgewählt"
+
+#: flatcamTools/ToolProperties.py:110
+msgid "[success] Object Properties are displayed."
+msgstr "[success] Objekteigenschaften werden angezeigt."
+
+#: flatcamTools/ToolProperties.py:111
+msgid "Properties Tool"
+msgstr "Eigenschaftenwerkzeug"
+
+#: flatcamTools/ToolShell.py:69
+msgid "...proccessing..."
+msgstr "...wird bearbeitet..."
+
+#: flatcamTools/ToolShell.py:71
+#, python-format
+msgid "...proccessing... [%s]"
+msgstr "...wird bearbeitet...[%s]"
+
+#: flatcamTools/ToolSolderPaste.py:37
+msgid "Solder Paste Tool"
+msgstr "Lötpaste-Werkzeug"
+
+#: flatcamTools/ToolSolderPaste.py:65
+msgid "Gerber Solder paste object.                        "
+msgstr "Gerber Lotpastenobjekt."
+
+#: flatcamTools/ToolSolderPaste.py:72
+msgid ""
+"Tools pool from which the algorithm\n"
+"will pick the ones used for dispensing solder paste."
+msgstr ""
+"Toolspool aus dem der Algorithmus\n"
+"wählt die für die Lotpaste verwendeten aus."
+
+#: flatcamTools/ToolSolderPaste.py:87
+msgid ""
+"This is the Tool Number.\n"
+"The solder dispensing will start with the tool with the biggest \n"
+"diameter, continuing until there are no more Nozzle tools.\n"
+"If there are no longer tools but there are still pads not covered\n"
+" with solder paste, the app will issue a warning message box."
+msgstr ""
+"Dies ist die Werkzeugnummer.\n"
+"Die Lotdosierung beginnt mit dem Werkzeug mit dem größten\n"
+"Durchmesser, weiter, bis keine Düsenwerkzeuge mehr vorhanden sind.\n"
+"Wenn keine Werkzeuge mehr vorhanden sind, sind aber noch keine Pads "
+"vorhanden\n"
+"Mit Lötpaste gibt die App eine Warnmeldung aus."
+
+#: flatcamTools/ToolSolderPaste.py:94
+msgid ""
+"Nozzle tool Diameter. It's value (in current FlatCAM units)\n"
+"is the width of the solder paste dispensed."
+msgstr ""
+"Düsenwerkzeug Durchmesser. Der Wert (in aktuellen FlatCAM-Einheiten)\n"
+"ist die Breite der Lotpaste."
+
+#: flatcamTools/ToolSolderPaste.py:101
+msgid "New Nozzle Tool"
+msgstr "Neues Düsenwerkzeug"
+
+#: flatcamTools/ToolSolderPaste.py:117
+msgid ""
+"Add a new nozzle tool to the Tool Table\n"
+"with the diameter specified above."
+msgstr ""
+"Fügen Sie der Werkzeugtabelle ein neues Düsenwerkzeug hinzu\n"
+"mit dem oben angegebenen Durchmesser."
+
+#: flatcamTools/ToolSolderPaste.py:129
+msgid "Generate solder paste dispensing geometry."
+msgstr "Generieren Sie Lotpastendispensiergeometrie."
+
+#: flatcamTools/ToolSolderPaste.py:142
+msgid "STEP 1"
+msgstr "SCHRITT 1"
+
+#: flatcamTools/ToolSolderPaste.py:144
+msgid ""
+"First step is to select a number of nozzle tools for usage\n"
+"and then optionally modify the GCode parameters bellow."
+msgstr ""
+"Zunächst müssen Sie eine Reihe von Düsenwerkzeugen auswählen\n"
+"und ändern Sie dann optional die GCode-Parameter."
+
+#: flatcamTools/ToolSolderPaste.py:147
+msgid ""
+"Select tools.\n"
+"Modify parameters."
+msgstr ""
+"Werkzeuge auswählen.\n"
+"Parameter ändern"
+
+#: flatcamTools/ToolSolderPaste.py:290
+msgid "Generate GCode"
+msgstr "GCode generieren"
+
+#: flatcamTools/ToolSolderPaste.py:292
+msgid ""
+"Generate GCode for Solder Paste dispensing\n"
+"on PCB pads."
+msgstr ""
+"Generieren Sie GCode für die Lotpastendosierung\n"
+"auf PCB-Pads."
+
+#: flatcamTools/ToolSolderPaste.py:308
+msgid "STEP 2:"
+msgstr "SCHRITT 2:"
+
+#: flatcamTools/ToolSolderPaste.py:310
+msgid ""
+"Second step is to create a solder paste dispensing\n"
+"geometry out of an Solder Paste Mask Gerber file."
+msgstr ""
+"Der zweite Schritt ist das Erstellen einer Lotpastendispensierung\n"
+"Geometrie aus einer Lotpastenmaske-Gerber-Datei."
+
+#: flatcamTools/ToolSolderPaste.py:326
+msgid "Geo Result:"
+msgstr "Geo-Ergebnis:"
+
+#: flatcamTools/ToolSolderPaste.py:328
+msgid ""
+"Geometry Solder Paste object.\n"
+"The name of the object has to end in:\n"
+"'_solderpaste' as a protection."
+msgstr ""
+"Geometrie Lötpaste Objekt einfügen.\n"
+"Der Name des Objekts muss auf enden:\n"
+"'_solderpaste' als Schutz."
+
+#: flatcamTools/ToolSolderPaste.py:337
+msgid "STEP 3:"
+msgstr "SCHRITT 3:"
+
+#: flatcamTools/ToolSolderPaste.py:339
+msgid ""
+"Third step is to select a solder paste dispensing geometry,\n"
+"and then generate a CNCJob object.\n"
+"\n"
+"REMEMBER: if you want to create a CNCJob with new parameters,\n"
+"first you need to generate a geometry with those new params,\n"
+"and only after that you can generate an updated CNCJob."
+msgstr ""
+"Der dritte Schritt ist die Auswahl einer Lotpastendosiergeometrie.\n"
+"und generieren Sie dann ein CNCJob-Objekt.\n"
+"\n"
+"HINWEIS: Wenn Sie einen CNCJob mit neuen Parametern erstellen möchten,\n"
+"Zuerst müssen Sie eine Geometrie mit diesen neuen Parametern generieren.\n"
+"und erst danach können Sie einen aktualisierten CNCJob erstellen."
+
+#: flatcamTools/ToolSolderPaste.py:359
+msgid "CNC Result:"
+msgstr "CNC-Ergebnis:"
+
+#: flatcamTools/ToolSolderPaste.py:361
+msgid ""
+"CNCJob Solder paste object.\n"
+"In order to enable the GCode save section,\n"
+"the name of the object has to end in:\n"
+"'_solderpaste' as a protection."
+msgstr ""
+"CNCJob Lotpastenobjekt.\n"
+"Um den GCode-Speicherbereich zu aktivieren,\n"
+"Der Name des Objekts muss auf enden:\n"
+"'_solderpaste' als Schutz."
+
+#: flatcamTools/ToolSolderPaste.py:371
+msgid "View GCode"
+msgstr "GCode anzeigen"
+
+#: flatcamTools/ToolSolderPaste.py:373
+msgid ""
+"View the generated GCode for Solder Paste dispensing\n"
+"on PCB pads."
+msgstr ""
+"Zeigen Sie den generierten GCode für die Lotpastendosierung an\n"
+"auf PCB-Pads."
+
+#: flatcamTools/ToolSolderPaste.py:377
+msgid "Save GCode"
+msgstr "Speichern Sie GCode"
+
+#: flatcamTools/ToolSolderPaste.py:379
+msgid ""
+"Save the generated GCode for Solder Paste dispensing\n"
+"on PCB pads, to a file."
+msgstr ""
+"Speichern Sie den generierten GCode für die Lotpastendosierung\n"
+"auf PCB-Pads zu einer Datei."
+
+#: flatcamTools/ToolSolderPaste.py:383
+msgid "STEP 4:"
+msgstr "SCHRITT 4:"
+
+#: flatcamTools/ToolSolderPaste.py:385
+msgid ""
+"Fourth step (and last) is to select a CNCJob made from \n"
+"a solder paste dispensing geometry, and then view/save it's GCode."
+msgstr ""
+"Vierter Schritt (und letzter Schritt) ist die Auswahl eines CNCJobs aus\n"
+"eine Lotpastendispensiergeometrie und dann den GCode anzeigen / speichern."
+
+#: flatcamTools/ToolSolderPaste.py:413
+msgid "Delete Object"
+msgstr "Objekt löschen"
+
+#: flatcamTools/ToolSolderPaste.py:788
+msgid ""
+"[WARNING_NOTCL] Adding Nozzle tool cancelled. Tool already in Tool Table."
+msgstr ""
+"[WARNING_NOTCL] Hinzufügen des Düsenwerkzeugs abgebrochen. Werkzeug bereits "
+"in der Werkzeugtabelle."
+
+#: flatcamTools/ToolSolderPaste.py:793
+msgid "[success] New Nozzle tool added to Tool Table."
+msgstr "[success] Neues Düsenwerkzeug zur Werkzeugtabelle hinzugefügt."
+
+#: flatcamTools/ToolSolderPaste.py:835
+msgid "[success] Nozzle tool from Tool Table was edited."
+msgstr "[success] Das Düsenwerkzeug aus der Werkzeugtabelle wurde bearbeitet."
+
+#: flatcamTools/ToolSolderPaste.py:891
+msgid "[WARNING_NOTCL] Delete failed. Select a Nozzle tool to delete."
+msgstr ""
+"[WARNING_NOTCL] Löschen fehlgeschlagen. Wählen Sie ein Düsenwerkzeug zum "
+"Löschen aus."
+
+#: flatcamTools/ToolSolderPaste.py:896
+msgid "[success] Nozzle tool(s) deleted from Tool Table."
+msgstr "[success] Düsenwerkzeug (e) aus der Werkzeugtabelle gelöscht."
+
+#: flatcamTools/ToolSolderPaste.py:951
+msgid "[WARNING_NOTCL] No SolderPaste mask Gerber object loaded."
+msgstr "[WARNING_NOTCL] Keine Lötpastenmaske Gerber-Objekt geladen."
+
+#: flatcamTools/ToolSolderPaste.py:968
+msgid "Creating Solder Paste dispensing geometry."
+msgstr "Erstellen einer Lotpastenspendergeometrie."
+
+#: flatcamTools/ToolSolderPaste.py:980
+msgid "[WARNING_NOTCL] No Nozzle tools in the tool table."
+msgstr "[WARNING_NOTCL] Nein Düsenwerkzeuge in der Werkzeugtabelle."
+
+#: flatcamTools/ToolSolderPaste.py:1109
+msgid "[success] Solder Paste geometry generated successfully..."
+msgstr "[success] Lotpastengeometrie erfolgreich generiert ..."
+
+#: flatcamTools/ToolSolderPaste.py:1115
+msgid ""
+"[WARNING_NOTCL] Some or all pads have no solder due of inadequate nozzle "
+"diameters..."
+msgstr ""
+"[WARNING_NOTCL] Einige oder alle Pads haben wegen unzureichender "
+"Düsendurchmesser keine Lötstellen ..."
+
+#: flatcamTools/ToolSolderPaste.py:1129
+msgid "Generating Solder Paste dispensing geometry..."
+msgstr "Lötpasten-Dosiergeometrie erzeugen ..."
+
+#: flatcamTools/ToolSolderPaste.py:1149
+msgid "[WARNING_NOTCL] There is no Geometry object available."
+msgstr "[WARNING_NOTCL] Es ist kein Geometrieobjekt verfügbar."
+
+#: flatcamTools/ToolSolderPaste.py:1153
+msgid ""
+"[WARNING_NOTCL] This Geometry can't be processed. NOT a solder_paste_tool "
+"geometry."
+msgstr ""
+"[WARNING_NOTCL] Diese Geometrie kann nicht verarbeitet werden. KEINE "
+"Geometrie \"Lötpaste_Tool\"."
+
+#: flatcamTools/ToolSolderPaste.py:1258
+#, python-format
+msgid "[success] ToolSolderPaste CNCjob created: %s"
+msgstr "[success] ToolSolderPaste CNCjob erstellt: %s"
+
+#: flatcamTools/ToolSolderPaste.py:1290 flatcamTools/ToolSolderPaste.py:1294
+#: flatcamTools/ToolSolderPaste.py:1345
+msgid ""
+"[WARNING_NOTCL] This CNCJob object can't be processed. NOT a "
+"solder_paste_tool CNCJob object."
+msgstr ""
+"[WARNING_NOTCL] Dieses CNCJob-Objekt kann nicht verarbeitet werden. KEIN "
+"lot_paste_tool CNCJob Objekt."
+
+#: flatcamTools/ToolSolderPaste.py:1317
+msgid "[ERROR_NOTCL] No Gcode in the object..."
+msgstr "[ERROR_NOTCL] Kein Gcode im Objekt ..."
+
+#: flatcamTools/ToolSolderPaste.py:1326
+#, python-format
+msgid "[ERROR] ToolSolderPaste.on_view_gcode() -->%s"
+msgstr "[ERROR] ToolSolderPaste.on_view_gcode() -->%s"
+
+#: flatcamTools/ToolSolderPaste.py:1355
+msgid "Export GCode ..."
+msgstr "GCode exportieren ..."
+
+#: flatcamTools/ToolSolderPaste.py:1393
+#, python-format
+msgid "[success] Solder paste dispenser GCode file saved to: %s"
+msgstr "[success] GCode-Datei für Lötpastendispenser in gespeichert: %s"
+
+#: flatcamTools/ToolTransform.py:23
+msgid "Object Transform"
+msgstr "Objekttransformation"
+
+#: flatcamTools/ToolTransform.py:84
+msgid ""
+"Rotate the selected object(s).\n"
+"The point of reference is the middle of\n"
+"the bounding box for all selected objects."
+msgstr ""
+"Drehen Sie die ausgewählten Objekte.\n"
+"Der Bezugspunkt ist die Mitte von\n"
+"der Begrenzungsrahmen für alle ausgewählten Objekte."
+
+#: flatcamTools/ToolTransform.py:120 flatcamTools/ToolTransform.py:138
+msgid ""
+"Skew/shear the selected object(s).\n"
+"The point of reference is the middle of\n"
+"the bounding box for all selected objects."
+msgstr ""
+"Schrägstellung / Scherung der ausgewählten Objekte.\n"
+"Der Bezugspunkt ist die Mitte von\n"
+"der Begrenzungsrahmen für alle ausgewählten Objekte."
+
+#: flatcamTools/ToolTransform.py:176 flatcamTools/ToolTransform.py:193
+msgid ""
+"Scale the selected object(s).\n"
+"The point of reference depends on \n"
+"the Scale reference checkbox state."
+msgstr ""
+"Skalieren Sie die ausgewählten Objekte.\n"
+"Der Bezugspunkt hängt von ab\n"
+"das Kontrollkästchen Skalenreferenz."
+
+#: flatcamTools/ToolTransform.py:202
+msgid ""
+"Scale the selected object(s)\n"
+"using the Scale Factor X for both axis."
+msgstr ""
+"Skalieren Sie die ausgewählten Objekte\n"
+"Verwenden des Skalierungsfaktors X für beide Achsen."
+
+#: flatcamTools/ToolTransform.py:250 flatcamTools/ToolTransform.py:267
+msgid ""
+"Offset the selected object(s).\n"
+"The point of reference is the middle of\n"
+"the bounding box for all selected objects.\n"
+msgstr ""
+"Versetzt die ausgewählten Objekte.\n"
+"Der Bezugspunkt ist die Mitte von\n"
+"der Begrenzungsrahmen für alle ausgewählten Objekte.\n"
+
+#: flatcamTools/ToolTransform.py:297 flatcamTools/ToolTransform.py:305
+msgid ""
+"Flip the selected object(s) over the X axis.\n"
+"Does not create a new object.\n"
+" "
+msgstr ""
+"Kippen Sie die ausgewählten Objekte über die X-Achse.\n"
+"Erstellt kein neues Objekt. "
+
+#: flatcamTools/ToolTransform.py:637
+msgid "[WARNING_NOTCL] No object selected. Please Select an object to rotate!"
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum "
+"Drehen aus!"
+
+#: flatcamTools/ToolTransform.py:665
+msgid "CNCJob objects can't be rotated."
+msgstr "CNCJob-Objekte können nicht gedreht werden."
+
+#: flatcamTools/ToolTransform.py:674
+msgid "[success] Rotate done ..."
+msgstr "[success] Drehen fertig ..."
+
+#: flatcamTools/ToolTransform.py:689
+msgid "[WARNING_NOTCL] No object selected. Please Select an object to flip!"
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum "
+"Umdrehen!"
+
+#: flatcamTools/ToolTransform.py:724
+msgid "CNCJob objects can't be mirrored/flipped."
+msgstr "CNCJob-Objekte können nicht gespiegelt / gespiegelt werden."
+
+#: flatcamTools/ToolTransform.py:759
+msgid ""
+"[WARNING_NOTCL] No object selected. Please Select an object to shear/skew!"
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum "
+"Scheren / Schrägstellen!"
+
+#: flatcamTools/ToolTransform.py:781
+msgid "CNCJob objects can't be skewed."
+msgstr "CNCJob-Objekte können nicht verzerrt werden."
+
+#: flatcamTools/ToolTransform.py:808
+msgid "[WARNING_NOTCL] No object selected. Please Select an object to scale!"
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt. Bitte wählen Sie ein zu skalierendes "
+"Objekt!"
+
+#: flatcamTools/ToolTransform.py:841
+msgid "CNCJob objects can't be scaled."
+msgstr "CNCJob-Objekte können nicht skaliert werden."
+
+#: flatcamTools/ToolTransform.py:861
+msgid "[WARNING_NOTCL] No object selected. Please Select an object to offset!"
+msgstr ""
+"[WARNING_NOTCL] Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum "
+"Offset aus!"
+
+#: flatcamTools/ToolTransform.py:882
+msgid "CNCJob objects can't be offseted."
+msgstr "CNCJob-Objekte können nicht versetzt werden."
+
+#~ msgid ""
+#~ "[ERROR_NOTCL] The aperture scale factor value is missing or wrong format."
+#~ msgstr ""
+#~ "[ERROR_NOTCL] Skalierungsfaktorwert für Öffnung fehlt oder falsches "
+#~ "Format."
+
+#~ msgid "[ERROR_NOTCL] The aperture buffer value is missing or wrong format."
+#~ msgstr ""
+#~ "[ERROR_NOTCL] Der Aperturepufferwert fehlt oder hat ein falsches Format."
+
+#~ msgid "[ERROR_NOTCL] Creation of Gerber failed."
+#~ msgstr "[ERROR_NOTCL] Erstellung von Gerber ist fehlgeschlagen."
+
+#~ msgid "[success] Created: %s"
+#~ msgstr "[success] Erstellt: %s"
+
+#, fuzzy
+#~ msgid ""
+#~ "<b>Editor Shortcut list</b><br>\n"
+#~ "            <br>\n"
+#~ "            <strong><span style=\"color:#0000ff\">GEOMETRY EDITOR</span></"
+#~ "strong><br>\n"
+#~ "    \n"
+#~ "            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+#~ "\"width:283px\">\n"
+#~ "                <tbody>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\" width=\"89\"><strong>A</"
+#~ "strong></td>\n"
+#~ "                        <td width=\"194\">&nbsp;Draw an Arc</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>B</strong></td>\n"
+#~ "                        <td>&nbsp;Buffer Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>C</strong></td>\n"
+#~ "                        <td>&nbsp;Copy Geo Item</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>E</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Intersection Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>I</strong></td>\n"
+#~ "                        <td>&nbsp;Paint Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>J</strong></td>\n"
+#~ "                        <td>&nbsp;Jump to Location (x, y)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>K</strong></td>\n"
+#~ "                        <td>&nbsp;Toggle Corner Snap</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>M</strong></td>\n"
+#~ "                        <td>&nbsp;Move Geo Item</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>N</strong></td>\n"
+#~ "                        <td>&nbsp;Draw a Polygon</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>O</strong></td>\n"
+#~ "                        <td>&nbsp;Draw a Circle</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>P</strong></td>\n"
+#~ "                        <td>&nbsp;Draw a Path</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>R</strong></td>\n"
+#~ "                        <td>&nbsp;Draw Rectangle</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>S</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Substraction Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>T</strong></td>\n"
+#~ "                        <td>&nbsp;Add Text Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>U</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Union Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>X</strong></td>\n"
+#~ "                        <td>&nbsp;Flip shape on X axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Y</strong></td>\n"
+#~ "                        <td>&nbsp;Flip shape on Y axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>SHIFT+X</strong></td>\n"
+#~ "                        <td>&nbsp;Skew shape on X axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>SHIFT+Y</strong></td>\n"
+#~ "                        <td>&nbsp;Skew shape on Y axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+#~ "                        <td>&nbsp;Editor Transformation Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ALT+X</strong></td>\n"
+#~ "                        <td>&nbsp;Offset shape on X axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ALT+Y</strong></td>\n"
+#~ "                        <td>&nbsp;Offset shape on Y axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+M</strong></td>\n"
+#~ "                        <td>&nbsp;Measurement Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+#~ "                        <td>&nbsp;Save Object and Exit Editor</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+X</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Cut Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Space</strong></td>\n"
+#~ "                        <td>&nbsp;Rotate Geometry</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ENTER</strong></td>\n"
+#~ "                        <td>&nbsp;Finish drawing for certain tools</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ESC</strong></td>\n"
+#~ "                        <td>&nbsp;Abort and return to Select</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Del</strong></td>\n"
+#~ "                        <td>&nbsp;Delete Shape</td>\n"
+#~ "                    </tr>\n"
+#~ "                </tbody>\n"
+#~ "            </table>\n"
+#~ "            <br>\n"
+#~ "            <br>\n"
+#~ "            <strong><span style=\"color:#ff0000\">EXCELLON EDITOR</span></"
+#~ "strong><br>\n"
+#~ "            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+#~ "\"width:283px\">\n"
+#~ "                <tbody>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\" width=\"89\"><strong>A</"
+#~ "strong></td>\n"
+#~ "                        <td width=\"194\">&nbsp;Add Drill Array</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>C</strong></td>\n"
+#~ "                        <td>&nbsp;Copy Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>D</strong></td>\n"
+#~ "                        <td>&nbsp;Add Drill</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>J</strong></td>\n"
+#~ "                        <td>&nbsp;Jump to Location (x, y)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>M</strong></td>\n"
+#~ "                        <td>&nbsp;Move Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>R</strong></td>\n"
+#~ "                        <td>&nbsp;Resize Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>T</strong></td>\n"
+#~ "                        <td>&nbsp;Add a new Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Del</strong></td>\n"
+#~ "                        <td>&nbsp;Delete Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Del</strong></td>\n"
+#~ "                        <td>&nbsp;Alternate: Delete Tool(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ESC</strong></td>\n"
+#~ "                        <td>&nbsp;Abort and return to Select</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+#~ "                        <td>&nbsp;Save Object and Exit Editor</td>\n"
+#~ "                    </tr>\n"
+#~ "                </tbody>\n"
+#~ "            </table>\n"
+#~ "                    "
+#~ msgstr ""
+#~ "<b>Editor Shortcut list</b><br>\n"
+#~ "            <br>\n"
+#~ "            <strong><span style=\"color:#0000ff\">GEOMETRY EDITOR</span></"
+#~ "strong><br>\n"
+#~ "    \n"
+#~ "            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+#~ "\"width:283px\">\n"
+#~ "                <tbody>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\" width=\"89\"><strong>A</"
+#~ "strong></td>\n"
+#~ "                        <td width=\"194\">&nbsp;Draw an Arc</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>B</strong></td>\n"
+#~ "                        <td>&nbsp;Buffer Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>C</strong></td>\n"
+#~ "                        <td>&nbsp;Copy Geo Item</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>E</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Intersection Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>I</strong></td>\n"
+#~ "                        <td>&nbsp;Paint Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>J</strong></td>\n"
+#~ "                        <td>&nbsp;Jump to Location (x, y)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>K</strong></td>\n"
+#~ "                        <td>&nbsp;Toggle Corner Snap</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>M</strong></td>\n"
+#~ "                        <td>&nbsp;Move Geo Item</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>N</strong></td>\n"
+#~ "                        <td>&nbsp;Draw a Polygon</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>O</strong></td>\n"
+#~ "                        <td>&nbsp;Draw a Circle</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>P</strong></td>\n"
+#~ "                        <td>&nbsp;Draw a Path</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>R</strong></td>\n"
+#~ "                        <td>&nbsp;Draw Rectangle</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>S</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Substraction Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>T</strong></td>\n"
+#~ "                        <td>&nbsp;Add Text Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>U</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Union Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>X</strong></td>\n"
+#~ "                        <td>&nbsp;Flip shape on X axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Y</strong></td>\n"
+#~ "                        <td>&nbsp;Flip shape on Y axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>SHIFT+X</strong></td>\n"
+#~ "                        <td>&nbsp;Skew shape on X axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>SHIFT+Y</strong></td>\n"
+#~ "                        <td>&nbsp;Skew shape on Y axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ALT+R</strong></td>\n"
+#~ "                        <td>&nbsp;Editor Transformation Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ALT+X</strong></td>\n"
+#~ "                        <td>&nbsp;Offset shape on X axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ALT+Y</strong></td>\n"
+#~ "                        <td>&nbsp;Offset shape on Y axis</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+M</strong></td>\n"
+#~ "                        <td>&nbsp;Measurement Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+#~ "                        <td>&nbsp;Save Object and Exit Editor</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+X</strong></td>\n"
+#~ "                        <td>&nbsp;Polygon Cut Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Space</strong></td>\n"
+#~ "                        <td>&nbsp;Rotate Geometry</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ENTER</strong></td>\n"
+#~ "                        <td>&nbsp;Finish drawing for certain tools</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ESC</strong></td>\n"
+#~ "                        <td>&nbsp;Abort and return to Select</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Del</strong></td>\n"
+#~ "                        <td>&nbsp;Delete Shape</td>\n"
+#~ "                    </tr>\n"
+#~ "                </tbody>\n"
+#~ "            </table>\n"
+#~ "            <br>\n"
+#~ "            <br>\n"
+#~ "            <strong><span style=\"color:#ff0000\">EXCELLON EDITOR</span></"
+#~ "strong><br>\n"
+#~ "            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style="
+#~ "\"width:283px\">\n"
+#~ "                <tbody>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\" width=\"89\"><strong>A</"
+#~ "strong></td>\n"
+#~ "                        <td width=\"194\">&nbsp;Add Drill Array</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>C</strong></td>\n"
+#~ "                        <td>&nbsp;Copy Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>D</strong></td>\n"
+#~ "                        <td>&nbsp;Add Drill</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>J</strong></td>\n"
+#~ "                        <td>&nbsp;Jump to Location (x, y)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>M</strong></td>\n"
+#~ "                        <td>&nbsp;Move Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>R</strong></td>\n"
+#~ "                        <td>&nbsp;Resize Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>T</strong></td>\n"
+#~ "                        <td>&nbsp;Add a new Tool</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Del</strong></td>\n"
+#~ "                        <td>&nbsp;Delete Drill(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>Del</strong></td>\n"
+#~ "                        <td>&nbsp;Alternate: Delete Tool(s)</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\">&nbsp;</td>\n"
+#~ "                        <td>&nbsp;</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>ESC</strong></td>\n"
+#~ "                        <td>&nbsp;Abort and return to Select</td>\n"
+#~ "                    </tr>\n"
+#~ "                    <tr height=\"20\">\n"
+#~ "                        <td height=\"20\"><strong>CTRL+S</strong></td>\n"
+#~ "                        <td>&nbsp;Save Object and Exit Editor</td>\n"
+#~ "                    </tr>\n"
+#~ "                </tbody>\n"
+#~ "            </table>\n"
+#~ "                    "
+
+#~ msgid "Save && Close Edit"
+#~ msgstr "Speichern && Schließen Sie die Bearbeitung"
+
+#~ msgid "[success] GUI settings deleted ..."
+#~ msgstr "[success] GUI settings deleted ..."
+
+#~ msgid "Scale Factor:"
+#~ msgstr "Scale Factor:"
+
+#~ msgid "Perform scaling operation on the selected apertures."
+#~ msgstr "Perform scaling operation on the selected apertures."
+
+#~ msgid "Buffer Factor:"
+#~ msgstr "Buffer Factor:"
+
+#~ msgid "Perform buffer operation on the selected apertures."
+#~ msgstr "Perform buffer operation on the selected apertures."
+
+#~ msgid "<b>Generate new Gerber Object:</b>"
+#~ msgstr "<b>Generate new Gerber Object:</b>"
+
+#~ msgid "Will generate a new Gerber object from the changed apertures."
+#~ msgstr "Will generate a new Gerber object from the changed apertures."
+
+#~ msgid ""
+#~ "Will generate a new Gerber object from the changed apertures.\n"
+#~ "This new object can then be isolated etc."
+#~ msgstr ""
+#~ "Will generate a new Gerber object from the changed apertures.\n"
+#~ "This new object can then be isolated etc."
+
+#~ msgid "[ERROR_NOTCL]Could not load defaults file."
+#~ msgstr "[ERROR_NOTCL] Standarddatei konnte nicht geladen werden."
+
+#~ msgid "[ERROR_NOTCL] Failed to parse project file: %s"
+#~ msgstr "[ERROR_NOTCL] Fehler beim Parsen der Projektdatei: %s"
+
+#~ msgid "[ERROR_NOTCL]Wrong value format entered, use a number."
+#~ msgstr ""
+#~ "[ERROR_NOTCL] Falsches Wertformat eingegeben, verwenden Sie eine Zahl."
+
+#~ msgid "[ERROR_NOTCL]Cancelled. Empty file, it has no geometry..."
+#~ msgstr "[ERROR_NOTCL] Abgebrochen. Leere Datei hat keine Geometrie ..."
+
+#~ msgid "[WARNING_NOTCL]Export Machine Code cancelled ..."
+#~ msgstr "[WARNING_NOTCL] Maschinencode wurde abgebrochen ..."
+
+#~ msgid "[success]Flip on the Y axis done ..."
+#~ msgstr "[success]Flip on the Y axis done ..."
+
+#~ msgid "[success]Flip on the X axis done ..."
+#~ msgstr "[success]Flip on the X axis done ..."
+
+#~ msgid "[success]Skew on the %s axis done ..."
+#~ msgstr "[success]Skew on the %s axis done ..."
+
+#~ msgid "[success]Offset on the %s axis done ..."
+#~ msgstr "[success]Offset on the %s axis done ..."
+
+#~ msgid ""
+#~ "How much (fraction) of the tool width to overlap each tool pass.\n"
+#~ "Example:\n"
+#~ "A value here of 0.25 means 25\\% from the tool diameter found above.\n"
+#~ "\n"
+#~ "Adjust the value starting with lower values\n"
+#~ "and increasing it if areas that should be painted are still \n"
+#~ "not painted.\n"
+#~ "Lower values = faster processing, faster execution on PCB.\n"
+#~ "Higher values = slow processing and slow execution on CNC\n"
+#~ "due of too many paths."
+#~ msgstr ""
+#~ "How much (fraction) of the tool width to overlap each tool pass.\n"
+#~ "Example:\n"
+#~ "A value here of 0.25 means 25\\% from the tool diameter found above.\n"
+#~ "\n"
+#~ "Adjust the value starting with lower values\n"
+#~ "and increasing it if areas that should be painted are still \n"
+#~ "not painted.\n"
+#~ "Lower values = faster processing, faster execution on PCB.\n"
+#~ "Higher values = slow processing and slow execution on CNC\n"
+#~ "due of too many paths."
+
+#~ msgid "z_cut = Z coord for Toolchange"
+#~ msgstr "z_cut = Z coord for Toolchange"
+
+#~ msgid "z_move = Z coord for Toolchange"
+#~ msgstr "z_move = Z coord for Toolchange"
+
+#~ msgid "%s/Project_%s"
+#~ msgstr "%s/Project_%s"
+
+#~ msgid "tool_tab"
+#~ msgstr "tool_tab"

BIN
locale/en/LC_MESSAGES/strings.mo


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


BIN
locale/ro/LC_MESSAGES/strings.mo


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


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


BIN
share/aperture16.png


BIN
share/aperture32.png


BIN
share/padarray32.png


BIN
share/scale32.png


BIN
share/script_new16.png


BIN
share/script_open16.png


BIN
share/track32.png


+ 1 - 1
tclCommands/TclCommand.py

@@ -314,7 +314,7 @@ class TclCommandSignaled(TclCommand):
 
 
         This class is  child of  TclCommand and is used for commands  which create  new objects
-        it handles  all neccessary stuff about blocking and passing exeptions
+        it handles  all necessary stuff about blocking and passing exceptions
     """
 
     @abc.abstractmethod

+ 1 - 1
tclCommands/TclCommandCutout.py

@@ -121,6 +121,6 @@ class TclCommandCutout(TclCommand):
 
         try:
             obj.app.new_object("geometry", name + "_cutout", geo_init_me)
-            self.app.inform.emit("[success]Rectangular-form Cutout operation finished.")
+            self.app.inform.emit("[success] Rectangular-form Cutout operation finished.")
         except Exception as e:
             return "Operation failed: %s" % str(e)

+ 3 - 3
tclCommands/TclCommandGeoCutout.py

@@ -155,8 +155,8 @@ class TclCommandGeoCutout(TclCommandSignaled):
             return "Could not retrieve object: %s" % name
 
         if 0 in {dia}:
-            self.app.inform.emit("[WARNING]Tool Diameter is zero value. Change it to a positive integer.")
-            return "Tool Diameter is zero value. Change it to a positive integer."
+            self.app.inform.emit("[WARNING]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."
 
         if gaps not in ['lr', 'tb', '2lr', '2tb', 4, 8]:
             self.app.inform.emit("[WARNING]Gaps value can be only one of: 'lr', 'tb', '2lr', '2tb', 4 or 8. "
@@ -220,7 +220,7 @@ class TclCommandGeoCutout(TclCommandSignaled):
                                    ymax + gapsize)
 
             cutout_obj.plot()
-            self.app.inform.emit("[success]Any-form Cutout operation finished.")
+            self.app.inform.emit("[success] Any-form Cutout operation finished.")
         elif isinstance(cutout_obj, FlatCAMGerber):
 
             def geo_init(geo_obj, app_obj):

+ 1 - 1
tclCommands/TclCommandOpenGerber.py

@@ -89,4 +89,4 @@ class TclCommandOpenGerber(TclCommandSignaled):
             self.app.progress.emit(100)
 
             # GUI feedback
-            self.app.inform.emit("[success]Opened: " + filename)
+            self.app.inform.emit("[success] Opened: " + filename)

+ 2 - 2
tclCommands/TclCommandPanelize.py

@@ -276,7 +276,7 @@ class TclCommandPanelize(TclCommand):
             def job_thread(app_obj):
                 try:
                     panelize_2()
-                    self.app.inform.emit("[success]Panel created successfully.")
+                    self.app.inform.emit("[success] Panel created successfully.")
                 except Exception as e:
                     proc.done()
                     log.debug(str(e))
@@ -287,4 +287,4 @@ class TclCommandPanelize(TclCommand):
             self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
         else:
             panelize_2()
-            self.app.inform.emit("[success]Panel created successfully.")
+            self.app.inform.emit("[success] Panel created successfully.")

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