diff --git a/FlatCAMDB.py b/AppDatabase.py similarity index 92% rename from FlatCAMDB.py rename to AppDatabase.py index eb270ee3..ea90213a 100644 --- a/FlatCAMDB.py +++ b/AppDatabase.py @@ -1,5 +1,5 @@ from PyQt5 import QtGui, QtCore, QtWidgets -from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \ +from AppGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \ FCTree, RadioSet, FCFileSaveDialog from camlib import to_dict @@ -8,8 +8,10 @@ import json from copy import deepcopy from datetime import datetime +import math + import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -655,7 +657,7 @@ class ToolsDB(QtWidgets.QWidget): l_save=str(self.app.get_last_save_folder()), n=_("Tools_Database"), date=date), - filter=filter__) + ext_filter=filter__) filename = str(filename) @@ -1030,6 +1032,7 @@ class ToolsDB2(QtWidgets.QWidget): self.advanced_box.setTitle(_("Advanced Geo Parameters")) self.advanced_box.setFixedWidth(250) + # NCC TOOL BOX self.ncc_box = QtWidgets.QGroupBox() self.ncc_box.setStyleSheet(""" QGroupBox @@ -1042,6 +1045,7 @@ class ToolsDB2(QtWidgets.QWidget): self.ncc_box.setTitle(_("NCC Parameters")) self.ncc_box.setFixedWidth(250) + # PAINT TOOL BOX self.paint_box = QtWidgets.QGroupBox() self.paint_box.setStyleSheet(""" QGroupBox @@ -1054,10 +1058,24 @@ class ToolsDB2(QtWidgets.QWidget): self.paint_box.setTitle(_("Paint Parameters")) self.paint_box.setFixedWidth(250) + # ISOLATION TOOL BOX + self.iso_box = QtWidgets.QGroupBox() + self.iso_box.setStyleSheet(""" + QGroupBox + { + font-size: 16px; + font-weight: bold; + } + """) + self.iso_vlay = QtWidgets.QVBoxLayout() + self.iso_box.setTitle(_("Isolation Parameters")) + self.iso_box.setFixedWidth(250) + self.basic_box.setLayout(self.basic_vlay) self.advanced_box.setLayout(self.advanced_vlay) self.ncc_box.setLayout(self.ncc_vlay) self.paint_box.setLayout(self.paint_vlay) + self.iso_box.setLayout(self.iso_vlay) geo_vlay = QtWidgets.QVBoxLayout() geo_vlay.addWidget(self.basic_box) @@ -1067,6 +1085,7 @@ class ToolsDB2(QtWidgets.QWidget): tools_vlay = QtWidgets.QVBoxLayout() tools_vlay.addWidget(self.ncc_box) tools_vlay.addWidget(self.paint_box) + tools_vlay.addWidget(self.iso_box) tools_vlay.addStretch() param_hlay.addLayout(geo_vlay) @@ -1478,7 +1497,7 @@ class ToolsDB2(QtWidgets.QWidget): self.ncc_method_combo = FCComboBox() self.ncc_method_combo.addItems( - [_("Standard"), _("Seed"), _("Lines")] + [_("Standard"), _("Seed"), _("Lines"), _("Combo")] ) self.ncc_method_combo.setObjectName("gdb_n_method") @@ -1621,6 +1640,101 @@ class ToolsDB2(QtWidgets.QWidget): self.grid3.addWidget(self.pathconnect_cb, 10, 0) self.grid3.addWidget(self.paintcontour_cb, 10, 1) + # ########################################################################### + # ############### Paint UI form ############################################# + # ########################################################################### + + self.grid4 = QtWidgets.QGridLayout() + self.iso_vlay.addLayout(self.grid4) + self.grid4.setColumnStretch(0, 0) + self.grid4.setColumnStretch(1, 1) + self.iso_vlay.addStretch() + + # Passes + passlabel = QtWidgets.QLabel('%s:' % _('Passes')) + passlabel.setToolTip( + _("Width of the isolation gap in\n" + "number (integer) of tool widths.") + ) + self.passes_entry = FCSpinner() + self.passes_entry.set_range(1, 999) + self.passes_entry.setObjectName("gdb_i_passes") + + self.grid4.addWidget(passlabel, 0, 0) + self.grid4.addWidget(self.passes_entry, 0, 1) + + # Overlap Entry + overlabel = QtWidgets.QLabel('%s:' % _('Overlap')) + overlabel.setToolTip( + _("How much (percentage) of the tool width to overlap each tool pass.") + ) + self.iso_overlap_entry = FCDoubleSpinner(suffix='%') + self.iso_overlap_entry.set_precision(self.decimals) + self.iso_overlap_entry.setWrapping(True) + self.iso_overlap_entry.set_range(0.0000, 99.9999) + self.iso_overlap_entry.setSingleStep(0.1) + self.iso_overlap_entry.setObjectName("gdb_i_overlap") + + self.grid4.addWidget(overlabel, 2, 0) + self.grid4.addWidget(self.iso_overlap_entry, 2, 1) + + # Milling Type Radio Button + self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) + self.milling_type_label.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, + {'label': _('Conventional'), 'value': 'cv'}]) + self.milling_type_radio.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + self.milling_type_radio.setObjectName("gdb_i_milling_type") + + self.grid4.addWidget(self.milling_type_label, 4, 0) + self.grid4.addWidget(self.milling_type_radio, 4, 1) + + # Follow + self.follow_label = QtWidgets.QLabel('%s:' % _('Follow')) + self.follow_label.setToolTip( + _("Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace.") + ) + + self.follow_cb = FCCheckBox() + self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace.")) + self.follow_cb.setObjectName("gdb_i_follow") + + self.grid4.addWidget(self.follow_label, 6, 0) + self.grid4.addWidget(self.follow_cb, 6, 1) + + # Isolation Type + self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type')) + self.iso_type_label.setToolTip( + _("Choose how the isolation will be executed:\n" + "- 'Full' -> complete isolation of polygons\n" + "- 'Ext' -> will isolate only on the outside\n" + "- 'Int' -> will isolate only on the inside\n" + "'Exterior' isolation is almost always possible\n" + "(with the right tool) but 'Interior'\n" + "isolation can be done only when there is an opening\n" + "inside of the polygon (e.g polygon is a 'doughnut' shape).") + ) + self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'}, + {'label': _('Ext'), 'value': 'ext'}, + {'label': _('Int'), 'value': 'int'}]) + self.iso_type_radio.setObjectName("gdb_i_iso_type") + + self.grid4.addWidget(self.iso_type_label, 8, 0) + self.grid4.addWidget(self.iso_type_radio, 8, 1) + # #################################################################### # #################################################################### # GUI for the lower part of the window @@ -1743,6 +1857,13 @@ class ToolsDB2(QtWidgets.QWidget): "tools_paintmethod": self.paintmethod_combo, "tools_pathconnect": self.pathconnect_cb, "tools_paintcontour": self.paintcontour_cb, + + # Isolation + "tools_iso_passes": self.passes_entry, + "tools_iso_overlap": self.iso_overlap_entry, + "tools_iso_milling_type": self.milling_type_radio, + "tools_iso_follow": self.follow_cb, + "tools_iso_isotype": self.iso_type_radio } self.name2option = { @@ -1787,6 +1908,13 @@ class ToolsDB2(QtWidgets.QWidget): 'gdb_p_method': "tools_paintmethod", 'gdb_p_connect': "tools_pathconnect", 'gdb_p_contour': "tools_paintcontour", + + # Isolation + "gdb_i_passes": "tools_iso_passes", + "gdb_i_overlap": "tools_iso_overlap", + "gdb_i_milling_type": "tools_iso_milling_type", + "gdb_i_follow": "tools_iso_follow", + "gdb_i_iso_type": "tools_iso_isotype" } self.current_toolid = None @@ -1939,21 +2067,23 @@ class ToolsDB2(QtWidgets.QWidget): if self.db_tool_dict: self.storage_to_form(self.db_tool_dict['1']) - # Enable GUI + # Enable AppGUI self.basic_box.setEnabled(True) self.advanced_box.setEnabled(True) self.ncc_box.setEnabled(True) self.paint_box.setEnabled(True) + self.iso_box.setEnabled(True) self.tree_widget.setCurrentItem(self.tree_widget.topLevelItem(0)) # self.tree_widget.setFocus() else: - # Disable GUI + # Disable AppGUI self.basic_box.setEnabled(False) self.advanced_box.setEnabled(False) self.ncc_box.setEnabled(False) self.paint_box.setEnabled(False) + self.iso_box.setEnabled(False) else: self.storage_to_form(self.db_tool_dict[str(self.current_toolid)]) @@ -2006,6 +2136,13 @@ class ToolsDB2(QtWidgets.QWidget): "tools_paintmethod": self.app.defaults["tools_paintmethod"], "tools_pathconnect": self.app.defaults["tools_pathconnect"], "tools_paintcontour": self.app.defaults["tools_paintcontour"], + + # Isolation + "tools_iso_passes": int(self.app.defaults["tools_iso_passes"]), + "tools_iso_overlap": float(self.app.defaults["tools_iso_overlap"]), + "tools_iso_milling_type": self.app.defaults["tools_iso_milling_type"], + "tools_iso_follow": self.app.defaults["tools_iso_follow"], + "tools_iso_isotype": self.app.defaults["tools_iso_isotype"], }) dict_elem = {} @@ -2117,7 +2254,7 @@ class ToolsDB2(QtWidgets.QWidget): l_save=str(self.app.get_last_save_folder()), n=_("Tools_Database"), date=date), - filter=filter__) + ext_filter=filter__) filename = str(filename) @@ -2218,6 +2355,18 @@ class ToolsDB2(QtWidgets.QWidget): self.app.tools_db_changed_flag = False self.on_save_tools_db() + def on_calculate_tooldia(self): + if self.shape_combo.get_value() == 'V': + tip_dia = float(self.vdia_entry.get_value()) + half_tip_angle = float(self.vangle_entry.get_value()) / 2.0 + cut_z = float(self.cutz_entry.get_value()) + cut_z = -cut_z if cut_z < 0 else cut_z + + # calculated tool diameter so the cut_z parameter is obeyed + tool_dia = tip_dia + (2 * cut_z * math.tan(math.radians(half_tip_angle))) + + self.dia_entry.set_value(tool_dia) + def ui_connect(self): # make sure that we don't make multiple connections to the widgets self.ui_disconnect() @@ -2247,12 +2396,40 @@ class ToolsDB2(QtWidgets.QWidget): if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner): wdg.valueChanged.connect(self.update_storage) + # connect the calculate tooldia method to the controls + # if the tool shape is 'V' the tool dia will be calculated to obey Cut Z parameter + self.shape_combo.currentIndexChanged.connect(self.on_calculate_tooldia) + self.cutz_entry.valueChanged.connect(self.on_calculate_tooldia) + self.vdia_entry.valueChanged.connect(self.on_calculate_tooldia) + self.vangle_entry.valueChanged.connect(self.on_calculate_tooldia) + + def ui_disconnect(self): try: self.name_entry.editingFinished.disconnect(self.update_tree_name) except (TypeError, AttributeError): pass + try: + self.shape_combo.currentIndexChanged.disconnect(self.on_calculate_tooldia) + except (TypeError, AttributeError): + pass + + try: + self.cutz_entry.valueChanged.disconnect(self.on_calculate_tooldia) + except (TypeError, AttributeError): + pass + + try: + self.vdia_entry.valueChanged.disconnect(self.on_calculate_tooldia) + except (TypeError, AttributeError): + pass + + try: + self.vangle_entry.valueChanged.disconnect(self.on_calculate_tooldia) + except (TypeError, AttributeError): + pass + for key in self.form_fields: wdg = self.form_fields[key] @@ -2398,6 +2575,18 @@ class ToolsDB2(QtWidgets.QWidget): elif wdg_name == "gdb_p_contour": self.db_tool_dict[tool_id]['data']['tools_paintcontour'] = val + # Isolation Tool + elif wdg_name == "gdb_i_passes": + self.db_tool_dict[tool_id]['data']['tools_iso_passes'] = val + elif wdg_name == "gdb_i_overlap": + self.db_tool_dict[tool_id]['data']['tools_iso_overlap'] = val + elif wdg_name == "gdb_i_milling_type": + self.db_tool_dict[tool_id]['data']['tools_iso_milling_type'] = val + elif wdg_name == "gdb_i_follow": + self.db_tool_dict[tool_id]['data']['tools_iso_follow'] = val + elif wdg_name == "gdb_i_iso_type": + self.db_tool_dict[tool_id]['data']['tools_iso_isotype'] = val + self.callback_app() def on_tool_requested_from_app(self): diff --git a/flatcamEditors/FlatCAMExcEditor.py b/AppEditors/FlatCAMExcEditor.py similarity index 98% rename from flatcamEditors/FlatCAMExcEditor.py rename to AppEditors/FlatCAMExcEditor.py index 49dc5eb5..c5d56218 100644 --- a/flatcamEditors/FlatCAMExcEditor.py +++ b/AppEditors/FlatCAMExcEditor.py @@ -9,9 +9,9 @@ from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5.QtCore import Qt, QSettings from camlib import distance, arc, FlatCAMRTreeStorage -from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, RadioSet, FCSpinner -from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor -from flatcamParsers.ParseExcellon import Excellon +from AppGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, RadioSet, FCSpinner +from AppEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor +from AppParsers.ParseExcellon import Excellon from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point import shapely.affinity as affinity @@ -26,7 +26,7 @@ import logging from copy import deepcopy import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -2123,7 +2123,7 @@ class FlatCAMExcEditor(QtCore.QObject): else: self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1) else: - from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_exc_editor') self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_exc_editor') @@ -2239,7 +2239,7 @@ class FlatCAMExcEditor(QtCore.QObject): # store the status of the editor so the Delete at object level will not work until the edit is finished self.editor_active = False - log.debug("Initialization of the FlatCAM Excellon Editor is finished ...") + log.debug("Initialization of the Excellon Editor is finished ...") def pool_recreated(self, pool): self.shapes.pool = pool @@ -2312,7 +2312,7 @@ class FlatCAMExcEditor(QtCore.QObject): tool_dia = float('%.*f' % (self.decimals, v['C'])) self.tool2tooldia[int(k)] = tool_dia - # Init GUI + # Init AppGUI self.addtool_entry.set_value(float(self.app.defaults['excellon_editor_newdia'])) self.drill_array_size_entry.set_value(int(self.app.defaults['excellon_editor_array_size'])) self.drill_axis_radio.set_value(self.app.defaults['excellon_editor_lin_dir']) @@ -2819,10 +2819,8 @@ class FlatCAMExcEditor(QtCore.QObject): 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.snap_magnet.setVisible(True) self.app.ui.exc_editor_menu.setDisabled(False) self.app.ui.exc_editor_menu.menuAction().setVisible(True) @@ -2832,12 +2830,11 @@ class FlatCAMExcEditor(QtCore.QObject): self.app.ui.exc_edit_toolbar.setDisabled(False) self.app.ui.exc_edit_toolbar.setVisible(True) - # self.app.ui.snap_toolbar.setDisabled(False) + # self.app.ui.status_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() - self.app.ui.on_grid_snap_triggered(state=True) self.app.ui.popmenu_disable.setVisible(False) self.app.ui.cmenu_newmenu.menuAction().setVisible(False) @@ -2869,30 +2866,8 @@ class FlatCAMExcEditor(QtCore.QObject): 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) - else: - # 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) + self.app.ui.corner_snap_btn.setVisible(False) + self.app.ui.snap_magnet.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 \ @@ -3068,7 +3043,7 @@ class FlatCAMExcEditor(QtCore.QObject): self.set_ui() - # now that we hava data, create the GUI interface and add it to the Tool Tab + # now that we hava data, create the AppGUI interface and add it to the Tool Tab self.build_ui(first_run=True) # we activate this after the initial build as we don't need to see the tool been populated @@ -3361,15 +3336,17 @@ class FlatCAMExcEditor(QtCore.QObject): with self.app.proc_container.new(_("Creating Excellon.")): try: - edited_obj = self.app.new_object("excellon", outname, obj_init) + edited_obj = self.app.app_obj.new_object("excellon", outname, obj_init) edited_obj.source_file = self.app.export_excellon(obj_name=edited_obj.options['name'], local_use=edited_obj, filename=None, use_thread=False) except Exception as e: + self.deactivate() log.error("Error on Edited object creation: %s" % str(e)) return + self.deactivate() self.app.inform.emit('[success] %s' % _("Excellon editing finished.")) def on_tool_select(self, tool): @@ -3463,8 +3440,8 @@ class FlatCAMExcEditor(QtCore.QObject): self.pos = (self.pos[0], self.pos[1]) if event.button == 1: - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (0, 0)) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (0, 0)) # Selection with left mouse button if self.active_tool is not None and event.button == 1: @@ -3801,18 +3778,22 @@ class FlatCAMExcEditor(QtCore.QObject): 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("    X: %.4f   " - "Y: %.4f" % (x, y)) - if self.pos is None: self.pos = (0, 0) self.app.dx = x - self.pos[0] self.app.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("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + # # update the position label in the infobar since the APP mouse event handlers are disconnected + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (x, y)) + # # update the reference position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, x, units, y, units) # ## Utility geometry (animated) self.update_utility_geometry(data=(x, y)) @@ -4045,7 +4026,7 @@ class FlatCAMExcEditor(QtCore.QObject): def select_tool(self, toolname): """ - Selects a drawing tool. Impacts the object and GUI. + Selects a drawing tool. Impacts the object and AppGUI. :param toolname: Name of the tool. :return: None diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/AppEditors/FlatCAMGeoEditor.py similarity index 98% rename from flatcamEditors/FlatCAMGeoEditor.py rename to AppEditors/FlatCAMGeoEditor.py index 1f10c01f..51bb53a6 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/AppEditors/FlatCAMGeoEditor.py @@ -15,11 +15,10 @@ from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5.QtCore import Qt, QSettings from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage -from FlatCAMTool import FlatCAMTool -from flatcamGUI.ObjectUI import RadioSet -from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \ - FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog, FCTree -from flatcamParsers.ParseFont import * +from AppTool import AppTool +from AppGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \ + FCDoubleSpinner, FCButton, FCInputDialog, FCTree +from AppParsers.ParseFont import * from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon from shapely.ops import cascaded_union, unary_union, linemerge @@ -34,7 +33,7 @@ from rtree import index as rtindex from copy import deepcopy # from vispy.io import read_png import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -42,7 +41,7 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class BufferSelectionTool(FlatCAMTool): +class BufferSelectionTool(AppTool): """ Simple input for buffer distance. """ @@ -50,7 +49,7 @@ class BufferSelectionTool(FlatCAMTool): toolName = "Buffer Selection" def __init__(self, app, draw_app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.draw_app = draw_app self.decimals = app.decimals @@ -118,12 +117,12 @@ class BufferSelectionTool(FlatCAMTool): self.buffer_int_button.clicked.connect(self.on_buffer_int) self.buffer_ext_button.clicked.connect(self.on_buffer_ext) - # Init GUI + # Init AppGUI self.buffer_distance_entry.set_value(0.01) def run(self): self.app.defaults.report_usage("Geo Editor ToolBuffer()") - FlatCAMTool.run(self) + AppTool.run(self) # if the splitter us hidden, display it if self.app.ui.splitter.sizes()[0] == 0: @@ -187,7 +186,7 @@ class BufferSelectionTool(FlatCAMTool): self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab) -class TextInputTool(FlatCAMTool): +class TextInputTool(AppTool): """ Simple input for buffer distance. """ @@ -195,7 +194,7 @@ class TextInputTool(FlatCAMTool): toolName = "Text Input Tool" def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.text_path = [] @@ -340,7 +339,7 @@ class TextInputTool(FlatCAMTool): def run(self): self.app.defaults.report_usage("Geo Editor TextInputTool()") - FlatCAMTool.run(self) + AppTool.run(self) # if the splitter us hidden, display it if self.app.ui.splitter.sizes()[0] == 0: @@ -405,7 +404,7 @@ class TextInputTool(FlatCAMTool): self.app.ui.notebook.setTabText(2, _("Tool")) -class PaintOptionsTool(FlatCAMTool): +class PaintOptionsTool(AppTool): """ Inputs to specify how to paint the selected polygons. """ @@ -413,7 +412,7 @@ class PaintOptionsTool(FlatCAMTool): toolName = "Paint Tool" def __init__(self, app, fcdraw): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.fcdraw = fcdraw @@ -538,7 +537,7 @@ class PaintOptionsTool(FlatCAMTool): def run(self): self.app.defaults.report_usage("Geo Editor ToolPaint()") - FlatCAMTool.run(self) + AppTool.run(self) # if the splitter us hidden, display it if self.app.ui.splitter.sizes()[0] == 0: @@ -547,7 +546,7 @@ class PaintOptionsTool(FlatCAMTool): self.app.ui.notebook.setTabText(2, _("Paint Tool")) def set_tool_ui(self): - # Init GUI + # Init AppGUI if self.app.defaults["tools_painttooldia"]: self.painttooldia_entry.set_value(self.app.defaults["tools_painttooldia"]) else: @@ -599,7 +598,7 @@ class PaintOptionsTool(FlatCAMTool): self.app.ui.splitter.setSizes([0, 1]) -class TransformEditorTool(FlatCAMTool): +class TransformEditorTool(AppTool): """ Inputs to specify how to paint the selected polygons. """ @@ -612,7 +611,7 @@ class TransformEditorTool(FlatCAMTool): offsetName = _("Offset") def __init__(self, app, draw_app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.draw_app = draw_app @@ -933,7 +932,7 @@ class TransformEditorTool(FlatCAMTool): "the 'y' in (x, y) will be used when using Flip on Y.") ) self.flip_ref_label.setFixedWidth(50) - self.flip_ref_entry = FCEntry("(0, 0)") + self.flip_ref_entry = FCEntry("0, 0") self.flip_ref_button = FCButton() self.flip_ref_button.set_value(_("Add")) @@ -981,7 +980,7 @@ class TransformEditorTool(FlatCAMTool): def run(self): self.app.defaults.report_usage("Geo Editor Transform Tool()") - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() # if the splitter us hidden, display it @@ -991,7 +990,7 @@ class TransformEditorTool(FlatCAMTool): 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) + AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs) def set_tool_ui(self): # Initialize form @@ -1048,7 +1047,7 @@ class TransformEditorTool(FlatCAMTool): 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)) + self.flip_ref_entry.set_value("0, 0") def template(self): if not self.draw_app.selected: @@ -3383,7 +3382,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.shapes = self.app.plotcanvas.new_shape_collection(layers=1) self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1) else: - from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_geo_editor') self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_geo_editor') @@ -3467,22 +3466,32 @@ class FlatCAMGeoEditor(QtCore.QObject): :return: """ try: - self.options[opt] = float(entry.text()) + text_value = entry.text() + if ',' in text_value: + text_value = text_value.replace(',', '.') + self.options[opt] = float(text_value) except Exception as e: + entry.set_value(self.app.defaults[opt]) log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e)) return - def gridx_changed(goption, gentry): + def grid_changed(goption, gentry): """ - :param goption: String. Can be either 'global_gridx' or 'global_gridy' - :param gentry: A GUI element which text value is read and used + :param goption: String. Can be either 'global_gridx' or 'global_gridy' + :param gentry: A GUI element which text value is read and used :return: """ + if goption not in ['global_gridx', 'global_gridy']: + return + entry2option(opt=goption, entry=gentry) # if the grid link is checked copy the value in the GridX field to GridY try: - val = float(gentry.get_value()) + text_value = gentry.text() + if ',' in text_value: + text_value = text_value.replace(',', '.') + val = float(text_value) except ValueError: return @@ -3491,7 +3500,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator()) self.app.ui.grid_gap_x_entry.textChanged.connect( - lambda: gridx_changed("global_gridx", self.app.ui.grid_gap_x_entry)) + lambda: grid_changed("global_gridx", self.app.ui.grid_gap_x_entry)) self.app.ui.grid_gap_y_entry.setValidator(QtGui.QDoubleValidator()) self.app.ui.grid_gap_y_entry.textChanged.connect( @@ -3542,7 +3551,7 @@ class FlatCAMGeoEditor(QtCore.QObject): # store the status of the editor so the Delete at object level will not work until the edit is finished self.editor_active = False - log.debug("Initialization of the FlatCAM Geometry Editor is finished ...") + log.debug("Initialization of the Geometry Editor is finished ...") def pool_recreated(self, pool): self.shapes.pool = pool @@ -3559,14 +3568,14 @@ class FlatCAMGeoEditor(QtCore.QObject): # Remove anything else in the GUI Selected Tab self.app.ui.selected_scroll_area.takeWidget() - # Put ourselves in the GUI Selected Tab + # Put ourselves in the AppGUI Selected Tab self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget) # Switch notebook to Selected page self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) def build_ui(self): """ - Build the GUI in the Selected Tab for this editor + Build the AppGUI in the Selected Tab for this editor :return: """ @@ -3644,10 +3653,8 @@ class FlatCAMGeoEditor(QtCore.QObject): 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.snap_magnet.setVisible(True) self.app.ui.geo_editor_menu.setDisabled(False) self.app.ui.geo_editor_menu.menuAction().setVisible(True) @@ -3658,7 +3665,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.geo_edit_toolbar.setDisabled(False) self.app.ui.geo_edit_toolbar.setVisible(True) - self.app.ui.snap_toolbar.setDisabled(False) + self.app.ui.status_toolbar.setDisabled(False) self.app.ui.popmenu_disable.setVisible(False) self.app.ui.cmenu_newmenu.menuAction().setVisible(False) @@ -3675,7 +3682,7 @@ class FlatCAMGeoEditor(QtCore.QObject): self.item_selected.connect(self.on_geo_elem_selected) - # ## GUI Events + # ## AppGUI Events self.tw.itemSelectionChanged.connect(self.on_tree_selection_change) # self.tw.keyPressed.connect(self.app.ui.keyPressEvent) # self.tw.customContextMenuRequested.connect(self.on_menu_request) @@ -3703,27 +3710,8 @@ class FlatCAMGeoEditor(QtCore.QObject): self.app.ui.geo_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.geo_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) - else: - # self.app.ui.geo_edit_toolbar.setVisible(True) - - self.app.ui.snap_max_dist_entry.setEnabled(False) - self.app.ui.corner_snap_btn.setEnabled(False) - else: - # self.app.ui.geo_edit_toolbar.setVisible(False) - - self.app.ui.snap_magnet.setVisible(False) - self.app.ui.corner_snap_btn.setVisible(False) - self.app.ui.snap_max_dist_entry.setEnabled(False) - self.app.ui.corner_snap_btn.setEnabled(False) + self.app.ui.corner_snap_btn.setVisible(False) + self.app.ui.snap_magnet.setVisible(False) # set the Editor Toolbar visibility to what was before entering in the Editor self.app.ui.geo_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \ @@ -3757,7 +3745,7 @@ class FlatCAMGeoEditor(QtCore.QObject): pass try: - # ## GUI Events + # ## AppGUI Events self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change) # self.tw.keyPressed.connect(self.app.ui.keyPressEvent) # self.tw.customContextMenuRequested.connect(self.on_menu_request) @@ -4100,7 +4088,6 @@ class FlatCAMGeoEditor(QtCore.QObject): # start with GRID toolbar activated if self.app.ui.grid_snap_btn.isChecked() is False: self.app.ui.grid_snap_btn.trigger() - self.app.ui.on_grid_snap_triggered(state=True) def on_buffer_tool(self): buff_tool = BufferSelectionTool(self.app, self) @@ -4148,9 +4135,11 @@ class FlatCAMGeoEditor(QtCore.QObject): # make sure that the cursor shape is enabled/disabled, too if self.options['grid_snap'] is True: + self.app.inform[str, bool].emit(_("Grid Snap enabled."), False) self.app.app_cursor.enabled = True else: self.app.app_cursor.enabled = False + self.app.inform[str, bool].emit(_("Grid Snap disabled."), False) def on_canvas_click(self, event): """ @@ -4173,8 +4162,8 @@ class FlatCAMGeoEditor(QtCore.QObject): self.pos = (self.pos[0], self.pos[1]) if event.button == 1: - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (0, 0)) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (0, 0)) modifiers = QtWidgets.QApplication.keyboardModifiers() # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard @@ -4261,18 +4250,23 @@ class FlatCAMGeoEditor(QtCore.QObject): self.snap_y = y self.app.mouse = [x, y] - # update the position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (x, y)) - if self.pos is None: self.pos = (0, 0) self.app.dx = x - self.pos[0] self.app.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("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + # # update the position label in the infobar since the APP mouse event handlers are disconnected + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (x, y)) + # + # # update the reference position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, x, units, y, units) if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser): pass @@ -4665,7 +4659,7 @@ class FlatCAMGeoEditor(QtCore.QObject): def select_tool(self, toolname): """ - Selects a drawing tool. Impacts the object and GUI. + Selects a drawing tool. Impacts the object and AppGUI. :param toolname: Name of the tool. :return: None @@ -4750,8 +4744,8 @@ class FlatCAMGeoEditor(QtCore.QObject): Transfers the geometry tool shape buffer to the selected geometry object. The geometry already in the object are removed. - :param fcgeometry: GeometryObject - :return: None + :param fcgeometry: GeometryObject + :return: None """ if self.multigeo_tool: fcgeometry.tools[self.multigeo_tool]['solid_geometry'] = [] @@ -4776,6 +4770,8 @@ class FlatCAMGeoEditor(QtCore.QObject): new_geo = linemerge(new_geo) fcgeometry.solid_geometry.append(new_geo) + self.deactivate() + def update_options(self, obj): if self.paint_tooldia: obj.options['cnctooldia'] = deepcopy(str(self.paint_tooldia)) diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/AppEditors/FlatCAMGrbEditor.py similarity index 98% rename from flatcamEditors/FlatCAMGrbEditor.py rename to AppEditors/FlatCAMGrbEditor.py index 9aa85c78..70f386fa 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/AppEditors/FlatCAMGrbEditor.py @@ -14,15 +14,13 @@ import shapely.affinity as affinity from vispy.geometry import Rect -import threading -import time from copy import copy, deepcopy import logging from camlib import distance, arc, three_point_circle -from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \ +from AppGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \ EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox -from FlatCAMTool import FlatCAMTool +from AppTool import AppTool import numpy as np from numpy.linalg import norm as numpy_norm @@ -32,7 +30,7 @@ import math # import pngcanvas import traceback import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -1086,15 +1084,6 @@ class FCRegion(FCShapeTool): self.draw_app.app.inform.emit('[success] %s' % _("Done.")) - def clean_up(self): - self.draw_app.selected = [] - self.draw_app.apertures_table.clearSelection() - self.draw_app.plot_all() - try: - self.draw_app.app.jump_signal.disconnect() - except (TypeError, AttributeError): - pass - def on_key(self, key): # Jump to coords if key == QtCore.Qt.Key_J or key == 'J': @@ -1160,16 +1149,36 @@ class FCRegion(FCShapeTool): return msg + def clean_up(self): + self.draw_app.selected = [] + self.draw_app.apertures_table.clearSelection() + self.draw_app.plot_all() + try: + self.draw_app.app.jump_signal.disconnect() + except (TypeError, AttributeError): + pass -class FCTrack(FCRegion): + +class FCTrack(FCShapeTool): """ Resulting type: Polygon """ def __init__(self, draw_app): - FCRegion.__init__(self, draw_app) + DrawTool.__init__(self, draw_app) self.name = 'track' self.draw_app = draw_app + self.steps_per_circle = self.draw_app.app.defaults["gerber_circle_steps"] + + 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.gridx_size = float(self.draw_app.app.ui.grid_gap_x_entry.get_value()) + self.gridy_size = float(self.draw_app.app.ui.grid_gap_y_entry.get_value()) + + self.temp_points = [] + + self. final_click = False try: QtGui.QGuiApplication.restoreOverrideCursor() except Exception as e: @@ -1183,52 +1192,23 @@ class FCTrack(FCRegion): self.draw_app.app.inform.emit(_('Track Mode 1: 45 degrees ...')) - def make(self): - new_geo_el = {} - if len(self.temp_points) == 1: - new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, - resolution=int(self.steps_per_circle / 4)) - new_geo_el['follow'] = Point(self.temp_points) - else: - new_geo_el['solid'] = (LineString(self.temp_points).buffer( - self.buf_val, resolution=int(self.steps_per_circle / 4))).buffer(0) - new_geo_el['follow'] = LineString(self.temp_points) - - self.geometry = DrawToolShape(new_geo_el) - - self.draw_app.in_action = False - self.complete = True - - self.draw_app.app.jump_signal.disconnect() - - self.draw_app.app.inform.emit('[success] %s' % _("Done.")) - - def clean_up(self): - self.draw_app.selected = [] - self.draw_app.apertures_table.clearSelection() - self.draw_app.plot_all() - try: - self.draw_app.app.jump_signal.disconnect() - except (TypeError, AttributeError): - pass - def click(self, point): self.draw_app.in_action = True - try: - if point != self.points[-1]: - self.points.append(point) - except IndexError: + + if not self.points: self.points.append(point) + elif point != self.points[-1]: + self.points.append(point) + else: + return new_geo_el = {} if len(self.temp_points) == 1: - new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, - resolution=int(self.steps_per_circle / 4)) + new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle)) new_geo_el['follow'] = Point(self.temp_points) else: - new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, - resolution=int(self.steps_per_circle / 4)) + new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle)) new_geo_el['follow'] = LineString(self.temp_points) self.draw_app.add_gerber_shape(DrawToolShape(new_geo_el), @@ -1241,23 +1221,25 @@ class FCTrack(FCRegion): return "" + def update_grid_info(self): + self.gridx_size = float(self.draw_app.app.ui.grid_gap_x_entry.get_value()) + self.gridy_size = float(self.draw_app.app.ui.grid_gap_y_entry.get_value()) + def utility_geometry(self, data=None): self.update_grid_info() new_geo_el = {} - if len(self.points) == 0: - new_geo_el['solid'] = Point(data).buffer(self.buf_val, - resolution=int(self.steps_per_circle / 4)) - + if not self.points: + new_geo_el['solid'] = Point(data).buffer(self.buf_val, int(self.steps_per_circle)) return DrawToolUtilityShape(new_geo_el) - elif len(self.points) > 0: - - self.temp_points = [self.points[-1]] + else: old_x = self.points[-1][0] old_y = self.points[-1][1] x = data[0] y = data[1] + self.temp_points = [self.points[-1]] + mx = abs(round((x - old_x) / self.gridx_size)) my = abs(round((y - old_y) / self.gridy_size)) @@ -1305,14 +1287,30 @@ class FCTrack(FCRegion): self.temp_points.append(data) if len(self.temp_points) == 1: - new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, - resolution=int(self.steps_per_circle / 4)) + new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle)) return DrawToolUtilityShape(new_geo_el) - new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, - resolution=int(self.steps_per_circle / 4)) + new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle)) return DrawToolUtilityShape(new_geo_el) + def make(self): + new_geo_el = {} + if len(self.temp_points) == 1: + new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle)) + new_geo_el['follow'] = Point(self.temp_points) + else: + new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle)) + new_geo_el['solid'] = new_geo_el['solid'].buffer(0) # try to clean the geometry + new_geo_el['follow'] = LineString(self.temp_points) + + self.geometry = DrawToolShape(new_geo_el) + + self.draw_app.in_action = False + self.complete = True + + self.draw_app.app.jump_signal.disconnect() + self.draw_app.app.inform.emit('[success] %s' % _("Done.")) + def on_key(self, key): if key == 'Backspace' or key == QtCore.Qt.Key_Backspace: if len(self.points) > 0: @@ -1405,6 +1403,15 @@ class FCTrack(FCRegion): return msg + def clean_up(self): + self.draw_app.selected = [] + self.draw_app.apertures_table.clearSelection() + self.draw_app.plot_all() + try: + self.draw_app.app.jump_signal.disconnect() + except (TypeError, AttributeError): + pass + class FCDisc(FCShapeTool): """ @@ -2955,7 +2962,7 @@ class FlatCAMGrbEditor(QtCore.QObject): # this var will store the state of the toolbar before starting the editor self.toolbar_old_state = False - # Init GUI + # Init AppGUI self.apdim_lbl.hide() self.apdim_entry.hide() self.gerber_obj = None @@ -2967,7 +2974,7 @@ class FlatCAMGrbEditor(QtCore.QObject): self.tool_shape = self.canvas.new_shape_collection(layers=1) self.ma_annotation = self.canvas.new_text_group() else: - from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor') self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor') self.ma_annotation = ShapeCollectionLegacy( @@ -3110,7 +3117,7 @@ class FlatCAMGrbEditor(QtCore.QObject): self.complete = True self.set_ui() - log.debug("Initialization of the FlatCAM Gerber Editor is finished ...") + log.debug("Initialization of the Gerber Editor is finished ...") def pool_recreated(self, pool): self.shapes.pool = pool @@ -3139,7 +3146,7 @@ class FlatCAMGrbEditor(QtCore.QObject): tt_aperture = self.sorted_apcode[i] self.tid2apcode[i + 1] = tt_aperture - # Init GUI + # Init AppGUI self.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"]) self.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"]) @@ -3685,10 +3692,8 @@ class FlatCAMGrbEditor(QtCore.QObject): 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.snap_magnet.setVisible(True) self.app.ui.grb_editor_menu.setDisabled(False) self.app.ui.grb_editor_menu.menuAction().setVisible(True) @@ -3698,12 +3703,11 @@ class FlatCAMGrbEditor(QtCore.QObject): self.app.ui.grb_edit_toolbar.setDisabled(False) self.app.ui.grb_edit_toolbar.setVisible(True) - # self.app.ui.snap_toolbar.setDisabled(False) + # self.app.ui.status_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() - self.app.ui.on_grid_snap_triggered(state=True) # adjust the visibility of some of the canvas context menu self.app.ui.popmenu_edit.setVisible(False) @@ -3736,29 +3740,8 @@ class FlatCAMGrbEditor(QtCore.QObject): 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) - else: - # 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) + self.app.ui.corner_snap_btn.setVisible(False) + self.app.ui.snap_magnet.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 \ @@ -4210,7 +4193,7 @@ class FlatCAMGrbEditor(QtCore.QObject): def on_multiprocessing_finished(self): self.app.proc_container.update_view_text(' %s' % _("Setting up the UI")) - self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI")) + self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the AppGUI")) self.set_ui() self.build_ui(first_run=True) self.plot_all() @@ -4245,6 +4228,7 @@ class FlatCAMGrbEditor(QtCore.QObject): new_grb_name = self.edited_obj_name + "_edit" self.app.worker_task.emit({'fcn': self.new_edited_gerber, 'params': [new_grb_name, self.storage_dict]}) + # self.new_edited_gerber(new_grb_name, self.storage_dict) @staticmethod def update_options(obj): @@ -4266,9 +4250,10 @@ class FlatCAMGrbEditor(QtCore.QObject): """ 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 - :param aperture_storage: a dictionary that holds all the objects geometry + :param outname: Name of the resulting object. None causes the name to be that of the file. + :type outname: str + :param aperture_storage: a dictionary that holds all the objects geometry + :type aperture_storage: dict :return: None """ @@ -4360,26 +4345,27 @@ class FlatCAMGrbEditor(QtCore.QObject): self.app.inform.emit('[ERROR_NOTCL] %s' % _("There are no Aperture definitions in the file. Aborting Gerber creation.")) except Exception: - msg = '[ERROR] %s' % \ - _("An internal error has occurred. See shell.\n") + msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n") msg += traceback.format_exc() app_obj.inform.emit(msg) raise + grb_obj.source_file = self.app.export_gerber(obj_name=out_name, filename=None, local_use=grb_obj, use_thread=False) with self.app.proc_container.new(_("Creating Gerber.")): try: - self.app.new_object("gerber", outname, obj_init) + self.app.app_obj.new_object("gerber", outname, obj_init) except Exception as e: log.error("Error on Edited object creation: %s" % str(e)) # make sure to clean the previous results self.results = [] return - self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished.")) # make sure to clean the previous results self.results = [] + self.deactivate_grb_editor() + self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished.")) def on_tool_select(self, tool): """ @@ -4537,8 +4523,8 @@ class FlatCAMGrbEditor(QtCore.QObject): self.pos = (self.pos[0], self.pos[1]) if event.button == 1: - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (0, 0)) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (0, 0)) # Selection with left mouse button if self.active_tool is not None: @@ -4550,8 +4536,7 @@ class FlatCAMGrbEditor(QtCore.QObject): self.app.defaults["global_point_clipboard_format"] % (self.decimals, self.pos[0], self.decimals, self.pos[1]) ) - self.app.inform.emit('[success] %s' % - _("Coordinates copied to clipboard.")) + self.app.inform.emit('[success] %s' % _("Coordinates copied to clipboard.")) return # Dispatch event to active_tool @@ -4562,6 +4547,7 @@ class FlatCAMGrbEditor(QtCore.QObject): 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() @@ -4569,6 +4555,7 @@ class FlatCAMGrbEditor(QtCore.QObject): 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: @@ -4629,12 +4616,14 @@ class FlatCAMGrbEditor(QtCore.QObject): # 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 isinstance(self.active_tool, FCTrack): + self.active_tool.make() + else: + 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] %s' % - _("Done.")) + self.app.inform.emit('[success] %s' % _("Done.")) # MS: always return to the Select Tool if modifier key is not pressed # else return to the current tool but not for FCTrack @@ -4774,18 +4763,23 @@ class FlatCAMGrbEditor(QtCore.QObject): self.app.mouse = [x, y] - # update the position label in the infobar since the APP mouse event handlers are disconnected - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (x, y)) - if self.pos is None: self.pos = (0, 0) self.app.dx = x - self.pos[0] self.app.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("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + # # update the position label in the infobar since the APP mouse event handlers are disconnected + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (x, y)) + # + # # update the reference position label in the infobar since the APP mouse event handlers are disconnected + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, x, units, y, units) self.update_utility_geometry(data=(x, y)) @@ -5032,7 +5026,7 @@ class FlatCAMGrbEditor(QtCore.QObject): def select_tool(self, toolname): """ - Selects a drawing tool. Impacts the object and GUI. + Selects a drawing tool. Impacts the object and AppGUI. :param toolname: Name of the tool. :return: None @@ -5298,7 +5292,7 @@ class FlatCAMGrbEditor(QtCore.QObject): self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) -class TransformEditorTool(FlatCAMTool): +class TransformEditorTool(AppTool): """ Inputs to specify how to paint the selected polygons. """ @@ -5311,7 +5305,7 @@ class TransformEditorTool(FlatCAMTool): offsetName = _("Offset") def __init__(self, app, draw_app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.draw_app = draw_app @@ -5633,7 +5627,7 @@ class TransformEditorTool(FlatCAMTool): "the 'y' in (x, y) will be used when using Flip on Y.") ) self.flip_ref_label.setMinimumWidth(50) - self.flip_ref_entry = EvalEntry2("(0, 0)") + self.flip_ref_entry = FCEntry() self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) # self.flip_ref_entry.setFixedWidth(60) @@ -5697,13 +5691,13 @@ class TransformEditorTool(FlatCAMTool): except AttributeError: pass - FlatCAMTool.run(self) + AppTool.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) + AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs) def set_tool_ui(self): # Initialize form @@ -5760,7 +5754,7 @@ class TransformEditorTool(FlatCAMTool): 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)) + self.flip_ref_entry.set_value("0, 0") def template(self): if not self.draw_app.selected: diff --git a/flatcamEditors/FlatCAMTextEditor.py b/AppEditors/FlatCAMTextEditor.py similarity index 98% rename from flatcamEditors/FlatCAMTextEditor.py rename to AppEditors/FlatCAMTextEditor.py index 92641258..21c8aa7e 100644 --- a/flatcamEditors/FlatCAMTextEditor.py +++ b/AppEditors/FlatCAMTextEditor.py @@ -5,7 +5,7 @@ # MIT Licence # # ########################################################## -from flatcamGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber +from AppGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui from reportlab.platypus import SimpleDocTemplate, Paragraph @@ -15,7 +15,7 @@ from reportlab.lib.units import inch, mm # from io import StringIO import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -214,10 +214,10 @@ class TextEditor(QtWidgets.QWidget): filename = str(FCFileSaveDialog.get_saved_filename( caption=_("Export Code ..."), directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name), - filter=_filter_ + ext_filter=_filter_ )[0]) except TypeError: - filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), filter=_filter_)[0]) + filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)[0]) if filename == "": self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled.")) diff --git a/flatcamEditors/__init__.py b/AppEditors/__init__.py similarity index 100% rename from flatcamEditors/__init__.py rename to AppEditors/__init__.py diff --git a/flatcamGUI/GUIElements.py b/AppGUI/GUIElements.py similarity index 90% rename from flatcamGUI/GUIElements.py rename to AppGUI/GUIElements.py index 4771c5e6..a7829719 100644 --- a/flatcamGUI/GUIElements.py +++ b/AppGUI/GUIElements.py @@ -20,9 +20,10 @@ from copy import copy import re import logging import html +import sys import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins log = logging.getLogger('base') @@ -569,9 +570,13 @@ class FCEntry3(FCEntry): class EvalEntry(QtWidgets.QLineEdit): - def __init__(self, parent=None): + def __init__(self, border_color=None, parent=None): super(EvalEntry, self).__init__(parent) self.readyToEdit = True + + if border_color: + self.setStyleSheet("QLineEdit {border: 1px solid %s;}" % border_color) + self.editingFinished.connect(self.on_edit_finished) def on_edit_finished(self): @@ -598,7 +603,6 @@ class EvalEntry(QtWidgets.QLineEdit): def get_value(self): raw = str(self.text()).strip(' ') - evaled = 0.0 try: evaled = eval(raw) except Exception as e: @@ -638,7 +642,7 @@ class EvalEntry2(QtWidgets.QLineEdit): def get_value(self): raw = str(self.text()).strip(' ') - evaled = 0.0 + try: evaled = eval(raw) except Exception as e: @@ -655,6 +659,30 @@ class EvalEntry2(QtWidgets.QLineEdit): return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height()) +class NumericalEvalEntry(EvalEntry): + """ + Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,% + """ + def __init__(self, border_color=None): + super().__init__(border_color=border_color) + + regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s]*") + validator = QtGui.QRegExpValidator(regex, self) + self.setValidator(validator) + + +class NumericalEvalTupleEntry(FCEntry): + """ + Will evaluate the input and return a value. Accepts only float numbers and formulas using the operators: /,*,+,-,% + """ + def __init__(self, border_color=None): + super().__init__(border_color=border_color) + + regex = QtCore.QRegExp("[0-9\/\*\+\-\%\.\s\,]*") + validator = QtGui.QRegExpValidator(regex, self) + self.setValidator(validator) + + class FCSpinner(QtWidgets.QSpinBox): returnPressed = QtCore.pyqtSignal() @@ -683,6 +711,8 @@ class FCSpinner(QtWidgets.QSpinBox): self.setAlignment(align_val) self.prev_readyToEdit = True + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred) + self.setSizePolicy(sizePolicy) def eventFilter(self, object, event): if event.type() == QtCore.QEvent.MouseButtonPress and self.prev_readyToEdit is True: @@ -815,6 +845,8 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): self.setAlignment(align_val) self.prev_readyToEdit = True + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred) + self.setSizePolicy(sizePolicy) def on_edit_finished(self): self.clearFocus() @@ -1336,7 +1368,8 @@ class FCComboBox(QtWidgets.QComboBox): return str(self.currentText()) def set_value(self, val): - self.setCurrentIndex(self.findText(str(val))) + idx = self.findText(str(val)) + self.setCurrentIndex(idx) @property def is_last(self): @@ -1480,9 +1513,11 @@ class FCDetachableTab(QtWidgets.QTabWidget): From here: https://stackoverflow.com/questions/47267195/in-pyqt4-is-it-possible-to-detach-tabs-from-a-qtabwidget """ + tab_detached = QtCore.pyqtSignal(str) + tab_attached = QtCore.pyqtSignal(str) def __init__(self, protect=None, protect_by_name=None, parent=None): - super().__init__() + super().__init__(parent=parent) self.tabBar = self.FCTabBar(self) self.tabBar.onMoveTabSignal.connect(self.moveTab) @@ -1619,7 +1654,7 @@ class FCDetachableTab(QtWidgets.QTabWidget): self.insertTab(toIndex, widget, icon, text) self.setCurrentIndex(toIndex) - @pyqtSlot(int, QtCore.QPoint) + # @pyqtSlot(int, QtCore.QPoint) def detachTab(self, index, point): """ Detach the tab by removing it's contents and placing them in @@ -1656,6 +1691,8 @@ class FCDetachableTab(QtWidgets.QTabWidget): # Create a reference to maintain access to the detached tab self.detachedTabs[name] = detachedTab + self.tab_detached.emit(name) + def attachTab(self, contentWidget, name, icon, insertAt=None): """ Re-attach the tab by removing the content from the DetachedTab window, @@ -1668,11 +1705,11 @@ class FCDetachableTab(QtWidgets.QTabWidget): :return: """ + old_name = name + # Make the content widget a child of this widget contentWidget.setParent(self) - # Remove the reference - del self.detachedTabs[name] # make sure that we strip the 'FlatCAM' part of the detached name otherwise the tab name will be too long name = name.partition(' ')[2] @@ -1712,6 +1749,9 @@ class FCDetachableTab(QtWidgets.QTabWidget): else: index = self.insertTab(insert_index, contentWidget, icon, name) + obj_name = contentWidget.objectName() + self.tab_attached.emit(obj_name) + # on reattaching the tab if protect is true then the closure button is not added if self.protect_tab is True: self.protectTab(index) @@ -1727,6 +1767,14 @@ class FCDetachableTab(QtWidgets.QTabWidget): if index > -1: self.setCurrentIndex(insert_index) if self.use_old_index else self.setCurrentIndex(index) + # Remove the reference + # Unix-like OS's crash with segmentation fault after this. FOr whatever reason, they loose reference + if sys.platform == 'win32': + try: + del self.detachedTabs[old_name] + except KeyError: + pass + def removeTabByName(self, name): """ Remove the tab with the given name, even if it is detached @@ -2179,11 +2227,14 @@ class OptionalHideInputSection: """ Associates the a checkbox with a set of inputs. - :param cb: Checkbox that enables the optional inputs. - :param optinputs: List of widgets that are optional. - :param logic: When True the logic is normal, when False the logic is in reverse - It means that for logic=True, when the checkbox is checked the widgets are Enabled, and - for logic=False, when the checkbox is checked the widgets are Disabled + :param cb: Checkbox that enables the optional inputs. + :type cb: QtWidgets.QCheckBox + :param optinputs: List of widgets that are optional. + :type optinputs: list + :param logic: When True the logic is normal, when False the logic is in reverse + It means that for logic=True, when the checkbox is checked the widgets are Enabled, and + for logic=False, when the checkbox is checked the widgets are Disabled + :type logic: bool :return: """ assert isinstance(cb, FCCheckBox), \ @@ -2215,6 +2266,7 @@ class OptionalHideInputSection: class FCTable(QtWidgets.QTableWidget): drag_drop_sig = QtCore.pyqtSignal() + lost_focus = QtCore.pyqtSignal() def __init__(self, drag_drop=False, protected_rows=None, parent=None): super(FCTable, self).__init__(parent) @@ -2278,6 +2330,10 @@ class FCTable(QtWidgets.QTableWidget): else: QtWidgets.QTableWidget.mousePressEvent(self, event) + def focusOutEvent(self, event): + self.lost_focus.emit() + super().focusOutEvent(event) + def setupContextMenu(self): self.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu) @@ -2449,7 +2505,8 @@ class SpinBoxDelegate(QtWidgets.QItemDelegate): def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) - def setDecimals(self, spinbox, digits): + @staticmethod + def setDecimals(spinbox, digits): spinbox.setDecimals(digits) @@ -2497,7 +2554,7 @@ class DialogBoxRadio(QtWidgets.QDialog): :param title: string with the window title :param label: string with the message inside the dialog box """ - super(DialogBoxRadio, self).__init__() + super(DialogBoxRadio, self).__init__(parent=parent) if initial_text is None: self.location = str((0, 0)) else: @@ -2740,9 +2797,12 @@ class MyCompleter(QCompleter): insertText = QtCore.pyqtSignal(str) def __init__(self, parent=None): - QCompleter.__init__(self) + QCompleter.__init__(self, parent=parent) self.setCompletionMode(QCompleter.PopupCompletion) self.highlighted.connect(self.setHighlighted) + + self.lastSelected = '' + # self.popup().installEventFilter(self) # def eventFilter(self, obj, event): @@ -2898,9 +2958,9 @@ class FCFileSaveDialog(QtWidgets.QFileDialog): super(FCFileSaveDialog, self).__init__(*args) @staticmethod - def get_saved_filename(parent=None, caption='', directory='', filter='', initialFilter=''): + def get_saved_filename(parent=None, caption='', directory='', ext_filter='', initialFilter=''): filename, _filter = QtWidgets.QFileDialog.getSaveFileName(parent=parent, caption=caption, - directory=directory, filter=filter, + directory=directory, filter=ext_filter, initialFilter=initialFilter) filename = str(filename) @@ -2916,6 +2976,225 @@ class FCFileSaveDialog(QtWidgets.QFileDialog): return filename, _filter +class FCDock(QtWidgets.QDockWidget): + + def __init__(self, *args, **kwargs): + super(FCDock, self).__init__(*args) + self.close_callback = kwargs["close_callback"] if "close_callback" in kwargs else None + + def closeEvent(self, event: QtGui.QCloseEvent) -> None: + self.close_callback() + super().closeEvent(event) + + def show(self) -> None: + if self.isFloating(): + self.setFloating(False) + super().show() + + +class FlatCAMActivityView(QtWidgets.QWidget): + """ + This class create and control the activity icon displayed in the App status bar + """ + + def __init__(self, app, parent=None): + super().__init__(parent=parent) + + self.app = app + + if self.app.defaults["global_activity_icon"] == "Ball green": + icon = self.app.resource_location + '/active_2_static.png' + movie = self.app.resource_location + "/active_2.gif" + elif self.app.defaults["global_activity_icon"] == "Ball black": + icon = self.app.resource_location + '/active_static.png' + movie = self.app.resource_location + "/active.gif" + elif self.app.defaults["global_activity_icon"] == "Arrow green": + icon = self.app.resource_location + '/active_3_static.png' + movie = self.app.resource_location + "/active_3.gif" + elif self.app.defaults["global_activity_icon"] == "Eclipse green": + icon = self.app.resource_location + '/active_4_static.png' + movie = self.app.resource_location + "/active_4.gif" + else: + icon = self.app.resource_location + '/active_static.png' + movie = self.app.resource_location + "/active.gif" + + self.setMinimumWidth(200) + self.movie_path = movie + self.icon_path = icon + + self.icon = FCLabel(self) + self.icon.setGeometry(0, 0, 16, 12) + self.movie = QtGui.QMovie(self.movie_path) + + self.icon.setMovie(self.movie) + # self.movie.start() + + layout = QtWidgets.QHBoxLayout() + layout.setContentsMargins(5, 0, 5, 0) + layout.setAlignment(QtCore.Qt.AlignLeft) + self.setLayout(layout) + + layout.addWidget(self.icon) + self.text = QtWidgets.QLabel(self) + self.text.setText(_("Idle.")) + self.icon.setPixmap(QtGui.QPixmap(self.icon_path)) + + layout.addWidget(self.text) + + self.icon.clicked.connect(self.app.on_toolbar_replot) + + def set_idle(self): + self.movie.stop() + self.text.setText(_("Idle.")) + + def set_busy(self, msg, no_movie=None): + if no_movie is not True: + self.icon.setMovie(self.movie) + self.movie.start() + self.text.setText(msg) + + +class FlatCAMInfoBar(QtWidgets.QWidget): + """ + This class create a place to display the App messages in the Status Bar + """ + + def __init__(self, parent=None, app=None): + super(FlatCAMInfoBar, self).__init__(parent=parent) + + self.app = app + + self.icon = QtWidgets.QLabel(self) + self.icon.setGeometry(0, 0, 12, 12) + self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png') + self.icon.setPixmap(self.pmap) + + self.lock_pmaps = False + + layout = QtWidgets.QHBoxLayout() + layout.setContentsMargins(5, 0, 5, 0) + self.setLayout(layout) + + layout.addWidget(self.icon) + + self.text = QtWidgets.QLabel(self) + self.text.setText(_("Application started ...")) + self.text.setToolTip(_("Hello!")) + + layout.addWidget(self.text) + layout.addStretch() + + def set_text_(self, text, color=None): + self.text.setText(text) + self.text.setToolTip(text) + if color: + self.text.setStyleSheet('color: %s' % str(color)) + + def set_status(self, text, level="info"): + level = str(level) + + if self.lock_pmaps is not True: + self.pmap.fill() + if level == "ERROR" or level == "ERROR_NOTCL": + self.pmap = QtGui.QPixmap(self.app.resource_location + '/redlight12.png') + elif level.lower() == "success": + self.pmap = QtGui.QPixmap(self.app.resource_location + '/greenlight12.png') + elif level == "WARNING" or level == "WARNING_NOTCL": + self.pmap = QtGui.QPixmap(self.app.resource_location + '/yellowlight12.png') + elif level.lower() == "selected": + self.pmap = QtGui.QPixmap(self.app.resource_location + '/bluelight12.png') + else: + self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png') + + try: + self.set_text_(text) + self.icon.setPixmap(self.pmap) + except Exception as e: + log.debug("FlatCAMInfoBar.set_status() --> %s" % str(e)) + + +class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon): + """ + This class create the Sys Tray icon for the app + """ + + def __init__(self, app, icon, headless=None, parent=None): + # QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) + super().__init__(icon, parent=parent) + self.app = app + + menu = QtWidgets.QMenu(parent) + + menu_runscript = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/script14.png'), + '%s' % _('Run Script ...'), self) + menu_runscript.setToolTip( + _("Will run the opened Tcl Script thus\n" + "enabling the automation of certain\n" + "functions of FlatCAM.") + ) + menu.addAction(menu_runscript) + + menu.addSeparator() + + if headless is None: + self.menu_open = menu.addMenu(QtGui.QIcon(self.app.resource_location + '/folder32_bis.png'), _('Open')) + + # Open Project ... + menu_openproject = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/folder16.png'), + _('Open Project ...'), self) + self.menu_open.addAction(menu_openproject) + self.menu_open.addSeparator() + + # Open Gerber ... + menu_opengerber = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/flatcam_icon24.png'), + _('Open &Gerber ...\tCtrl+G'), self) + self.menu_open.addAction(menu_opengerber) + + # Open Excellon ... + menu_openexcellon = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'), + _('Open &Excellon ...\tCtrl+E'), self) + self.menu_open.addAction(menu_openexcellon) + + # Open G-Code ... + menu_opengcode = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/code.png'), + _('Open G-&Code ...'), self) + self.menu_open.addAction(menu_opengcode) + + self.menu_open.addSeparator() + + menu_openproject.triggered.connect(self.app.on_file_openproject) + menu_opengerber.triggered.connect(self.app.on_fileopengerber) + menu_openexcellon.triggered.connect(self.app.on_fileopenexcellon) + menu_opengcode.triggered.connect(self.app.on_fileopengcode) + + exitAction = menu.addAction(_("Exit")) + exitAction.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png')) + self.setContextMenu(menu) + + menu_runscript.triggered.connect(lambda: self.app.on_filerunscript( + silent=True if self.app.cmd_line_headless == 1 else False)) + + exitAction.triggered.connect(self.app.final_save) + + +def message_dialog(title, message, kind="info", parent=None): + """ + Builds and show a custom QMessageBox to be used in FlatCAM. + + :param title: title of the QMessageBox + :param message: message to be displayed + :param kind: type of QMessageBox; will display a specific icon. + :param parent: parent + :return: None + """ + icon = {"info": QtWidgets.QMessageBox.Information, + "warning": QtWidgets.QMessageBox.Warning, + "error": QtWidgets.QMessageBox.Critical}[str(kind)] + dlg = QtWidgets.QMessageBox(icon, title, message, parent=parent) + dlg.setText(message) + dlg.exec_() + + def rreplace(s, old, new, occurrence): """ Credits go here: diff --git a/flatcamGUI/FlatCAMGUI.py b/AppGUI/MainGUI.py similarity index 89% rename from flatcamGUI/FlatCAMGUI.py rename to AppGUI/MainGUI.py index b6d20c35..6423ae8f 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/AppGUI/MainGUI.py @@ -12,28 +12,28 @@ # ########################################################## import platform -from flatcamGUI.GUIElements import * -from flatcamGUI.preferences import settings -from flatcamGUI.preferences.cncjob.CNCJobPreferencesUI import CNCJobPreferencesUI -from flatcamGUI.preferences.excellon.ExcellonPreferencesUI import ExcellonPreferencesUI -from flatcamGUI.preferences.general.GeneralPreferencesUI import GeneralPreferencesUI -from flatcamGUI.preferences.geometry.GeometryPreferencesUI import GeometryPreferencesUI -from flatcamGUI.preferences.gerber.GerberPreferencesUI import GerberPreferencesUI -from flatcamEditors.FlatCAMGeoEditor import FCShapeTool +from AppGUI.GUIElements import * +from AppGUI.preferences import settings +from AppGUI.preferences.cncjob.CNCJobPreferencesUI import CNCJobPreferencesUI +from AppGUI.preferences.excellon.ExcellonPreferencesUI import ExcellonPreferencesUI +from AppGUI.preferences.general.GeneralPreferencesUI import GeneralPreferencesUI +from AppGUI.preferences.geometry.GeometryPreferencesUI import GeometryPreferencesUI +from AppGUI.preferences.gerber.GerberPreferencesUI import GerberPreferencesUI +from AppEditors.FlatCAMGeoEditor import FCShapeTool from matplotlib.backend_bases import KeyEvent as mpl_key_event import webbrowser -from flatcamGUI.preferences.tools.Tools2PreferencesUI import Tools2PreferencesUI -from flatcamGUI.preferences.tools.ToolsPreferencesUI import ToolsPreferencesUI -from flatcamGUI.preferences.utilities.UtilPreferencesUI import UtilPreferencesUI -from flatcamObjects.ObjectCollection import KeySensitiveListView +from AppGUI.preferences.tools.Tools2PreferencesUI import Tools2PreferencesUI +from AppGUI.preferences.tools.ToolsPreferencesUI import ToolsPreferencesUI +from AppGUI.preferences.utilities.UtilPreferencesUI import UtilPreferencesUI +from AppObjects.ObjectCollection import KeySensitiveListView import subprocess import os import sys import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -41,13 +41,13 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class FlatCAMGUI(QtWidgets.QMainWindow): +class MainGUI(QtWidgets.QMainWindow): # Emitted when persistent window geometry needs to be retained geom_update = QtCore.pyqtSignal(int, int, int, int, int, name='geomUpdate') final_save = QtCore.pyqtSignal(name='saveBeforeExit') def __init__(self, app): - super(FlatCAMGUI, self).__init__() + super(MainGUI, self).__init__() self.app = app self.decimals = self.app.decimals @@ -58,17 +58,6 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ############ BUILDING THE GUI IS EXECUTED HERE ######################## # ####################################################################### - # ####################################################################### - # ####################### TCL Shell DOCK ################################ - # ####################################################################### - self.shell_dock = QtWidgets.QDockWidget("FlatCAM TCL Shell") - self.shell_dock.setObjectName('Shell_DockWidget') - self.shell_dock.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) - self.shell_dock.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable | - QtWidgets.QDockWidget.DockWidgetFloatable | - QtWidgets.QDockWidget.DockWidgetClosable) - self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.shell_dock) - # ####################################################################### # ###################### Menu BUILDING ################################## # ####################################################################### @@ -200,9 +189,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufilerunscript = QtWidgets.QAction( QtGui.QIcon(self.app.resource_location + '/script16.png'), '%s\tShift+S' % _('Run Script ...'), self) self.menufilerunscript.setToolTip( - _("Will run the opened Tcl Script thus\n" - "enabling the automation of certain\n" - "functions of FlatCAM.") + _("Will run the opened Tcl Script thus\n" + "enabling the automation of certain\n" + "functions of FlatCAM.") ) self.menufile_scripting.addAction(self.menufilenewscript) self.menufile_scripting.addAction(self.menufileopenscript) @@ -265,9 +254,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menufileexportexcellon = QtWidgets.QAction( QtGui.QIcon(self.app.resource_location + '/drill32.png'), _('Export &Excellon ...'), self) self.menufileexportexcellon.setToolTip( - _("Will export an Excellon Object as Excellon file,\n" - "the coordinates format, the file units and zeros\n" - "are set in Preferences -> Excellon Export.") + _("Will export an Excellon Object as Excellon file,\n" + "the coordinates format, the file units and zeros\n" + "are set in Preferences -> Excellon Export.") ) self.menufileexport.addAction(self.menufileexportexcellon) @@ -344,16 +333,16 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menuedit_convertjoin = self.menuedit_convert.addAction( QtGui.QIcon(self.app.resource_location + '/join16.png'), _('&Join Geo/Gerber/Exc -> Geo')) self.menuedit_convertjoin.setToolTip( - _("Merge a selection of objects, which can be of type:\n" - "- Gerber\n" - "- Excellon\n" - "- Geometry\n" - "into a new combo Geometry object.") + _("Merge a selection of objects, which can be of type:\n" + "- Gerber\n" + "- Excellon\n" + "- Geometry\n" + "into a new combo Geometry object.") ) self.menuedit_convertjoinexc = self.menuedit_convert.addAction( QtGui.QIcon(self.app.resource_location + '/join16.png'), _('Join Excellon(s) -> Excellon')) self.menuedit_convertjoinexc.setToolTip( - _("Merge a selection of Excellon objects into a new combo Excellon object.") + _("Merge a selection of Excellon objects into a new combo Excellon object.") ) self.menuedit_convertjoingrb = self.menuedit_convert.addAction( QtGui.QIcon(self.app.resource_location + '/join16.png'), _('Join Gerber(s) -> Gerber')) @@ -365,14 +354,14 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menuedit_convert_sg2mg = self.menuedit_convert.addAction( QtGui.QIcon(self.app.resource_location + '/convert24.png'), _('Convert Single to MultiGeo')) self.menuedit_convert_sg2mg.setToolTip( - _("Will convert a Geometry object from single_geometry type\n" - "to a multi_geometry type.") + _("Will convert a Geometry object from single_geometry type\n" + "to a multi_geometry type.") ) self.menuedit_convert_mg2sg = self.menuedit_convert.addAction( QtGui.QIcon(self.app.resource_location + '/convert24.png'), _('Convert Multi to SingleGeo')) self.menuedit_convert_mg2sg.setToolTip( - _("Will convert a Geometry object from multi_geometry type\n" - "to a single_geometry type.") + _("Will convert a Geometry object from multi_geometry type\n" + "to a single_geometry type.") ) # Separator self.menuedit_convert.addSeparator() @@ -488,11 +477,13 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.menuview_toggle_grid = self.menuview.addAction( QtGui.QIcon(self.app.resource_location + '/grid32.png'), _("&Toggle Grid Snap\tG")) self.menuview_toggle_grid_lines = self.menuview.addAction( - QtGui.QIcon(self.app.resource_location + '/grid32.png'), _("&Toggle Grid Lines\tAlt+G")) + QtGui.QIcon(self.app.resource_location + '/grid_lines32.png'), _("&Toggle Grid Lines\tAlt+G")) self.menuview_toggle_axis = self.menuview.addAction( QtGui.QIcon(self.app.resource_location + '/axis32.png'), _("&Toggle Axis\tShift+G")) self.menuview_toggle_workspace = self.menuview.addAction( QtGui.QIcon(self.app.resource_location + '/workspace24.png'), _("Toggle Workspace\tShift+W")) + self.menuview_toggle_hud = self.menuview.addAction( + QtGui.QIcon(self.app.resource_location + '/hud_32.png'), _("Toggle HUD\tAlt+M")) # ######################################################################## # ########################## Objects # ################################### @@ -541,6 +532,12 @@ class FlatCAMGUI(QtWidgets.QMainWindow): QtGui.QIcon(self.app.resource_location + '/shortcuts24.png'), _('Shortcuts List\tF3')) self.menuhelp_videohelp = self.menuhelp.addAction( QtGui.QIcon(self.app.resource_location + '/youtube32.png'), _('YouTube Channel\tF4')) + + self.menuhelp.addSeparator() + + self.menuhelp_readme = self.menuhelp.addAction( + QtGui.QIcon(self.app.resource_location + '/warning.png'), _('ReadMe?')) + self.menuhelp_about = self.menuhelp.addAction( QtGui.QIcon(self.app.resource_location + '/about32.png'), _('About FlatCAM')) @@ -767,7 +764,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.setCentralWidget(self.splitter) # self.notebook = QtWidgets.QTabWidget() - self.notebook = FCDetachableTab(protect=True) + self.notebook = FCDetachableTab(protect=True, parent=self) self.notebook.setTabsClosable(False) self.notebook.useOldIndex(True) @@ -787,9 +784,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.toolbarfile.setObjectName('File_TB') self.addToolBar(self.toolbarfile) - self.toolbargeo = QtWidgets.QToolBar(_('Edit Toolbar')) - self.toolbargeo.setObjectName('Edit_TB') - self.addToolBar(self.toolbargeo) + self.toolbaredit = QtWidgets.QToolBar(_('Edit Toolbar')) + self.toolbaredit.setObjectName('Edit_TB') + self.addToolBar(self.toolbaredit) self.toolbarview = QtWidgets.QToolBar(_('View Toolbar')) self.toolbarview.setObjectName('View_TB') @@ -817,17 +814,15 @@ class FlatCAMGUI(QtWidgets.QMainWindow): 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) - - flat_settings = QSettings("Open Source", "FlatCAM") - if flat_settings.contains("layout"): - layout = flat_settings.value('layout', type=str) - if layout == 'compact': - self.removeToolBar(self.snap_toolbar) - self.snap_toolbar.setMaximumHeight(30) - self.splitter_left.addWidget(self.snap_toolbar) + self.status_toolbar = QtWidgets.QToolBar(_('Grid Toolbar')) + self.status_toolbar.setObjectName('Snap_TB') + # self.addToolBar(self.status_toolbar) + self.status_toolbar.setStyleSheet( + """ + QToolBar { padding: 0; } + QToolBar QToolButton { padding: -2; margin: -2; } + """ + ) # ######################################################################## # ########################## File Toolbar# ############################### @@ -845,37 +840,30 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ######################################################################## # ########################## Edit Toolbar# ############################### # ######################################################################## - self.newgeo_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/new_file_geo32.png'), _("New Blank Geometry")) - self.newgrb_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/new_file_grb32.png'), _("New Blank Gerber")) - self.newexc_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/new_file_exc32.png'), _("New Blank Excellon")) - self.toolbargeo.addSeparator() - self.editgeo_btn = self.toolbargeo.addAction( + self.editgeo_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/edit_file32.png'), _("Editor")) - self.update_obj_btn = self.toolbargeo.addAction( + self.update_obj_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/close_edit_file32.png'), _("Save Object and close the Editor") ) - self.toolbargeo.addSeparator() - self.copy_btn = self.toolbargeo.addAction( + self.toolbaredit.addSeparator() + self.copy_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/copy_file32.png'), _("Copy")) - self.delete_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/delete_file32.png'), _("&Delete")) - self.toolbargeo.addSeparator() - self.distance_btn = self.toolbargeo.addAction( + self.delete_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("&Delete")) + self.toolbaredit.addSeparator() + self.distance_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/distance32.png'), _("Distance Tool")) - self.distance_min_btn = self.toolbargeo.addAction( + self.distance_min_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/distance_min32.png'), _("Distance Min Tool")) - self.origin_btn = self.toolbargeo.addAction( + self.origin_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/origin32.png'), _('Set Origin')) - self.move2origin_btn = self.toolbargeo.addAction( + self.move2origin_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/origin2_32.png'), _('Move to Origin')) - self.jmp_btn = self.toolbargeo.addAction( + self.jmp_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location')) - self.locate_btn = self.toolbargeo.addAction( + self.locate_btn = self.toolbaredit.addAction( QtGui.QIcon(self.app.resource_location + '/locate32.png'), _('Locate in Object')) # ######################################################################## @@ -922,6 +910,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow): QtGui.QIcon(self.app.resource_location + '/ncc16.png'), _("NCC Tool")) self.paint_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/paint20_1.png'), _("Paint Tool")) + self.isolation_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/iso_16.png'), _("Isolation Tool")) self.toolbartools.addSeparator() self.panelize_btn = self.toolbartools.addAction( @@ -956,6 +946,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): QtGui.QIcon(self.app.resource_location + '/punch32.png'), _("Punch Gerber Tool")) self.invert_btn = self.toolbartools.addAction( QtGui.QIcon(self.app.resource_location + '/invert32.png'), _("Invert Gerber Tool")) + self.corners_tool_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/corners_32.png'), _("Corner Markers Tool")) + self.etch_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/etch_32.png'), _("Etch Compensation Tool")) # ######################################################################## # ########################## Excellon Editor Toolbar# #################### @@ -1083,34 +1077,60 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ######################################################################## # Snap GRID toolbar is always active to facilitate usage of measurements done on GRID - self.grid_snap_btn = self.snap_toolbar.addAction( + self.grid_snap_btn = self.status_toolbar.addAction( QtGui.QIcon(self.app.resource_location + '/grid32.png'), _('Snap to grid')) self.grid_gap_x_entry = FCEntry2() self.grid_gap_x_entry.setMaximumWidth(70) self.grid_gap_x_entry.setToolTip(_("Grid X snapping distance")) - self.snap_toolbar.addWidget(self.grid_gap_x_entry) + self.status_toolbar.addWidget(self.grid_gap_x_entry) + + self.status_toolbar.addWidget(QtWidgets.QLabel(" ")) + self.grid_gap_link_cb = FCCheckBox() + self.grid_gap_link_cb.setToolTip(_("When active, value on Grid_X\n" + "is copied to the Grid_Y value.")) + self.status_toolbar.addWidget(self.grid_gap_link_cb) + self.status_toolbar.addWidget(QtWidgets.QLabel(" ")) self.grid_gap_y_entry = FCEntry2() self.grid_gap_y_entry.setMaximumWidth(70) self.grid_gap_y_entry.setToolTip(_("Grid Y snapping distance")) - self.snap_toolbar.addWidget(self.grid_gap_y_entry) + self.status_toolbar.addWidget(self.grid_gap_y_entry) - self.grid_space_label = QtWidgets.QLabel(" ") - self.snap_toolbar.addWidget(self.grid_space_label) - self.grid_gap_link_cb = FCCheckBox() - self.grid_gap_link_cb.setToolTip(_("When active, value on Grid_X\n" - "is copied to the Grid_Y value.")) - self.snap_toolbar.addWidget(self.grid_gap_link_cb) + self.status_toolbar.addWidget(QtWidgets.QLabel(" ")) + self.axis_status_label = FCLabel() + self.axis_status_label.setToolTip(_("Toggle the display of axis on canvas")) + self.axis_status_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/axis16.png')) + self.status_toolbar.addWidget(self.axis_status_label) + self.status_toolbar.addWidget(QtWidgets.QLabel(" ")) + + self.shell_status_label = FCLabel() + self.shell_status_label.setToolTip(_("Command Line")) + self.shell_status_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/shell20.png')) + self.status_toolbar.addWidget(self.shell_status_label) self.ois_grid = OptionalInputSection(self.grid_gap_link_cb, [self.grid_gap_y_entry], logic=False) - self.corner_snap_btn = self.snap_toolbar.addAction( + self.corner_snap_btn = self.status_toolbar.addAction( QtGui.QIcon(self.app.resource_location + '/corner32.png'), _('Snap to corner')) self.snap_max_dist_entry = FCEntry() self.snap_max_dist_entry.setMaximumWidth(70) self.snap_max_dist_entry.setToolTip(_("Max. magnet distance")) - self.snap_magnet = self.snap_toolbar.addWidget(self.snap_max_dist_entry) + self.snap_magnet = self.status_toolbar.addWidget(self.snap_max_dist_entry) + + self.corner_snap_btn.setVisible(False) + self.snap_magnet.setVisible(False) + + # ####################################################################### + # ####################### TCL Shell DOCK ################################ + # ####################################################################### + self.shell_dock = FCDock("FlatCAM TCL Shell", close_callback=self.toggle_shell_ui) + self.shell_dock.setObjectName('Shell_DockWidget') + self.shell_dock.setAllowedAreas(QtCore.Qt.AllDockWidgetAreas) + self.shell_dock.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable | + QtWidgets.QDockWidget.DockWidgetFloatable | + QtWidgets.QDockWidget.DockWidgetClosable) + self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.shell_dock) # ######################################################################## # ########################## Notebook # ################################## @@ -1174,7 +1194,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ######################################################################## # ########################## PLOT AREA Tab # ############################# # ######################################################################## - self.plot_tab_area = FCDetachableTab2(protect=False, protect_by_name=[_('Plot Area')]) + self.plot_tab_area = FCDetachableTab2(protect=False, protect_by_name=[_('Plot Area')], parent=self) self.plot_tab_area.useOldIndex(True) self.right_lay.addWidget(self.plot_tab_area) @@ -1353,17 +1373,2331 @@ class FlatCAMGUI(QtWidgets.QMainWindow): # ######################################################################## # #################### SHORTCUT LIST AREA Tab # ########################## # ######################################################################## - self.shortcuts_tab = QtWidgets.QWidget() + self.shortcuts_tab = ShortcutsTab() + + # ######################################################################## + # ########################## PLOT AREA CONTEXT MENU # ################### + # ######################################################################## + self.popMenu = FCMenu() + + self.popmenu_disable = self.popMenu.addAction( + QtGui.QIcon(self.app.resource_location + '/disable32.png'), _("Toggle Visibility")) + self.popmenu_panel_toggle = self.popMenu.addAction( + QtGui.QIcon(self.app.resource_location + '/notebook16.png'), _("Toggle Panel")) + + self.popMenu.addSeparator() + self.cmenu_newmenu = self.popMenu.addMenu( + QtGui.QIcon(self.app.resource_location + '/file32.png'), _("New")) + self.popmenu_new_geo = self.cmenu_newmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/new_file_geo16.png'), _("Geometry")) + self.popmenu_new_grb = self.cmenu_newmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/new_file_grb16.png'), "Gerber") + self.popmenu_new_exc = self.cmenu_newmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/new_file_exc16.png'), _("Excellon")) + self.cmenu_newmenu.addSeparator() + self.popmenu_new_prj = self.cmenu_newmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/file16.png'), _("Project")) + self.popMenu.addSeparator() + + self.cmenu_gridmenu = self.popMenu.addMenu( + QtGui.QIcon(self.app.resource_location + '/grid32_menu.png'), _("Grids")) + + self.cmenu_viewmenu = self.popMenu.addMenu( + QtGui.QIcon(self.app.resource_location + '/view64.png'), _("View")) + self.zoomfit = self.cmenu_viewmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/zoom_fit32.png'), _("Zoom Fit")) + self.clearplot = self.cmenu_viewmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/clear_plot32.png'), _("Clear Plot")) + self.replot = self.cmenu_viewmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/replot32.png'), _("Replot")) + self.popMenu.addSeparator() + + self.g_editor_cmenu = self.popMenu.addMenu( + QtGui.QIcon(self.app.resource_location + '/draw32.png'), _("Geo Editor")) + self.draw_line = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/path32.png'), _("Path")) + self.draw_rect = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/rectangle32.png'), _("Rectangle")) + self.g_editor_cmenu.addSeparator() + self.draw_circle = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/circle32.png'), _("Circle")) + self.draw_poly = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _("Polygon")) + self.draw_arc = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/arc32.png'), _("Arc")) + self.g_editor_cmenu.addSeparator() + + self.draw_text = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/text32.png'), _("Text")) + self.draw_buffer = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _("Buffer")) + self.draw_paint = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/paint20_1.png'), _("Paint")) + self.draw_eraser = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _("Eraser")) + self.g_editor_cmenu.addSeparator() + + self.draw_union = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/union32.png'), _("Union")) + self.draw_intersect = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/intersection32.png'), _("Intersection")) + self.draw_substract = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/subtract32.png'), _("Subtraction")) + self.draw_cut = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/cutpath32.png'), _("Cut")) + self.draw_transform = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) + + self.g_editor_cmenu.addSeparator() + self.draw_move = self.g_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move")) + + self.grb_editor_cmenu = self.popMenu.addMenu( + QtGui.QIcon(self.app.resource_location + '/draw32.png'), _("Gerber Editor")) + self.grb_draw_pad = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/aperture32.png'), _("Pad")) + self.grb_draw_pad_array = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/padarray32.png'), _("Pad Array")) + self.grb_editor_cmenu.addSeparator() + + self.grb_draw_track = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/track32.png'), _("Track")) + self.grb_draw_region = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _("Region")) + self.grb_draw_poligonize = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/poligonize32.png'), _("Poligonize")) + self.grb_draw_semidisc = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/semidisc32.png'), _("SemiDisc")) + self.grb_draw_disc = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/disc32.png'), _("Disc")) + self.grb_editor_cmenu.addSeparator() + + self.grb_draw_buffer = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _("Buffer")) + self.grb_draw_scale = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/scale32.png'), _("Scale")) + self.grb_draw_markarea = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/markarea32.png'), _("Mark Area")) + self.grb_draw_eraser = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _("Eraser")) + self.grb_editor_cmenu.addSeparator() + + self.grb_draw_transformations = self.grb_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) + + self.e_editor_cmenu = self.popMenu.addMenu( + QtGui.QIcon(self.app.resource_location + '/drill32.png'), _("Exc Editor")) + self.drill = self.e_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/drill32.png'), _("Add Drill")) + self.drill_array = self.e_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/addarray32.png'), _("Add Drill Array")) + self.e_editor_cmenu.addSeparator() + self.slot = self.e_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/slot26.png'), _("Add Slot")) + self.slot_array = self.e_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/slot_array26.png'), _("Add Slot Array")) + self.e_editor_cmenu.addSeparator() + self.drill_resize = self.e_editor_cmenu.addAction( + QtGui.QIcon(self.app.resource_location + '/resize16.png'), _("Resize Drill")) + + self.popMenu.addSeparator() + self.popmenu_copy = self.popMenu.addAction(QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy")) + self.popmenu_delete = self.popMenu.addAction( + QtGui.QIcon(self.app.resource_location + '/delete32.png'), _("Delete")) + self.popmenu_edit = self.popMenu.addAction( + QtGui.QIcon(self.app.resource_location + '/edit32.png'), _("Edit")) + self.popmenu_save = self.popMenu.addAction( + QtGui.QIcon(self.app.resource_location + '/floppy32.png'), _("Close Editor")) + self.popmenu_save.setVisible(False) + self.popMenu.addSeparator() + + self.popmenu_move = self.popMenu.addAction( + QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move")) + self.popmenu_properties = self.popMenu.addAction( + QtGui.QIcon(self.app.resource_location + '/properties32.png'), _("Properties")) + + # ######################################################################## + # ########################## INFO BAR # ################################## + # ######################################################################## + self.infobar = self.statusBar() + self.fcinfo = FlatCAMInfoBar(app=self.app) + self.infobar.addWidget(self.fcinfo, stretch=1) + + # self.rel_position_label = QtWidgets.QLabel( + # "Dx: 0.0000   Dy: 0.0000    ") + # self.rel_position_label.setMinimumWidth(110) + # self.rel_position_label.setToolTip(_("Relative measurement.\nReference is last click position")) + # self.infobar.addWidget(self.rel_position_label) + # + self.position_label = QtWidgets.QLabel(" X: 0.0000   Y: 0.0000 ") + self.position_label.setMinimumWidth(110) + self.position_label.setToolTip(_("Absolute measurement.\n" + "Reference is (X=0, Y= 0) position")) + self.infobar.addWidget(self.position_label) + + self.status_toolbar.setMaximumHeight(24) + self.infobar.addWidget(self.status_toolbar) + + self.hud_label = FCLabel("H") + self.hud_label.setToolTip(_("HUD (Heads up display)")) + self.hud_label.setMargin(2) + self.infobar.addWidget(self.hud_label) + + self.wplace_label = FCLabel("A4") + self.wplace_label.setToolTip(_("Draw a delimiting rectangle on canvas.\n" + "The purpose is to illustrate the limits for our work.") + ) + self.wplace_label.setMargin(2) + self.infobar.addWidget(self.wplace_label) + + self.units_label = QtWidgets.QLabel("[mm]") + self.units_label.setToolTip(_("Application units")) + self.units_label.setMargin(2) + self.infobar.addWidget(self.units_label) + + # disabled + # self.progress_bar = QtWidgets.QProgressBar() + # self.progress_bar.setMinimum(0) + # self.progress_bar.setMaximum(100) + # infobar.addWidget(self.progress_bar) + + # ######################################################################## + # ########################## SET GUI Elements # ########################## + # ######################################################################## + self.app_icon = QtGui.QIcon() + self.app_icon.addFile(self.app.resource_location + '/flatcam_icon16.png', QtCore.QSize(16, 16)) + self.app_icon.addFile(self.app.resource_location + '/flatcam_icon24.png', QtCore.QSize(24, 24)) + self.app_icon.addFile(self.app.resource_location + '/flatcam_icon32.png', QtCore.QSize(32, 32)) + self.app_icon.addFile(self.app.resource_location + '/flatcam_icon48.png', QtCore.QSize(48, 48)) + self.app_icon.addFile(self.app.resource_location + '/flatcam_icon128.png', QtCore.QSize(128, 128)) + self.app_icon.addFile(self.app.resource_location + '/flatcam_icon256.png', QtCore.QSize(256, 256)) + self.setWindowIcon(self.app_icon) + + self.setGeometry(100, 100, 1024, 650) + self.setWindowTitle('FlatCAM %s %s - %s' % + (self.app.version, + ('BETA' if self.app.beta else ''), + platform.architecture()[0]) + ) + + self.filename = "" + self.units = "" + self.setAcceptDrops(True) + + # ######################################################################## + # ########################## Build GUI # ################################# + # ######################################################################## + self.grid_snap_btn.setCheckable(True) + self.corner_snap_btn.setCheckable(True) + self.update_obj_btn.setEnabled(False) + # start with GRID activated + self.grid_snap_btn.trigger() + + self.g_editor_cmenu.menuAction().setVisible(False) + self.grb_editor_cmenu.menuAction().setVisible(False) + self.e_editor_cmenu.menuAction().setVisible(False) + + # ######################################################################## + # ######################## BUILD PREFERENCES ############################# + # ######################################################################## + self.general_defaults_form = GeneralPreferencesUI(decimals=self.decimals) + self.gerber_defaults_form = GerberPreferencesUI(decimals=self.decimals) + self.excellon_defaults_form = ExcellonPreferencesUI(decimals=self.decimals) + self.geometry_defaults_form = GeometryPreferencesUI(decimals=self.decimals) + self.cncjob_defaults_form = CNCJobPreferencesUI(decimals=self.decimals) + self.tools_defaults_form = ToolsPreferencesUI(decimals=self.decimals) + self.tools2_defaults_form = Tools2PreferencesUI(decimals=self.decimals) + self.util_defaults_form = UtilPreferencesUI(decimals=self.decimals) + + QtWidgets.qApp.installEventFilter(self) + + # ######################################################################## + # ################## RESTORE THE TOOLBAR STATE from file ################# + # ######################################################################## + flat_settings = QSettings("Open Source", "FlatCAM") + if flat_settings.contains("saved_gui_state"): + saved_gui_state = flat_settings.value('saved_gui_state') + self.restoreState(saved_gui_state) + log.debug("MainGUI.__init__() --> UI state restored from QSettings.") + + self.corner_snap_btn.setVisible(False) + self.snap_magnet.setVisible(False) + + if flat_settings.contains("layout"): + layout = flat_settings.value('layout', type=str) + self.exc_edit_toolbar.setDisabled(True) + self.geo_edit_toolbar.setDisabled(True) + self.grb_edit_toolbar.setDisabled(True) + + log.debug("MainGUI.__init__() --> UI layout restored from QSettings. Layout = %s" % str(layout)) + else: + self.exc_edit_toolbar.setDisabled(True) + self.geo_edit_toolbar.setDisabled(True) + self.grb_edit_toolbar.setDisabled(True) + + flat_settings.setValue('layout', "standard") + # This will write the setting to the platform specific storage. + del flat_settings + log.debug("MainGUI.__init__() --> UI layout restored from defaults. QSettings set to 'standard'") + + # construct the Toolbar Lock menu entry to the context menu of the QMainWindow + self.lock_action = QtWidgets.QAction() + self.lock_action.setText(_("Lock Toolbars")) + self.lock_action.setCheckable(True) + + qsettings = QSettings("Open Source", "FlatCAM") + if qsettings.contains("toolbar_lock"): + lock_val = settings.value('toolbar_lock') + if lock_val == 'true': + lock_state = True + self.lock_action.setChecked(True) + else: + + lock_state = False + self.lock_action.setChecked(False) + else: + lock_state = False + qsettings.setValue('toolbar_lock', lock_state) + + # This will write the setting to the platform specific storage. + del qsettings + + self.lock_toolbar(lock=lock_state) + + self.lock_action.triggered[bool].connect(self.lock_toolbar) + + self.pref_open_button.clicked.connect(self.on_preferences_open_folder) + self.clear_btn.clicked.connect(self.on_gui_clear) + + self.wplace_label.clicked.connect(self.app.on_workspace_toggle) + self.shell_status_label.clicked.connect(self.toggle_shell_ui) + + # to be used in the future + # self.plot_tab_area.tab_attached.connect(lambda x: print(x)) + # self.plot_tab_area.tab_detached.connect(lambda x: print(x)) + + # restore the toolbar view + self.restore_toolbar_view() + + # restore the GUI geometry + self.restore_main_win_geom() + + # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + # %%%%%%%%%%%%%%%%% GUI Building FINISHED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + # Variable to store the status of the fullscreen event + self.toggle_fscreen = False + self.x_pos = None + self.y_pos = None + self.width = None + self.height = None + + self.geom_update[int, int, int, int, int].connect(self.save_geometry) + self.final_save.connect(self.app.final_save) + + def save_geometry(self, x, y, width, height, notebook_width): + """ + Will save the application geometry and positions in the defaults dicitionary to be restored at the next + launch of the application. + + :param x: X position of the main window + :param y: Y position of the main window + :param width: width of the main window + :param height: height of the main window + :param notebook_width: the notebook width is adjustable so it get saved here, too. + + :return: None + """ + self.app.defaults["global_def_win_x"] = x + self.app.defaults["global_def_win_y"] = y + self.app.defaults["global_def_win_w"] = width + self.app.defaults["global_def_win_h"] = height + self.app.defaults["global_def_notebook_width"] = notebook_width + self.app.preferencesUiManager.save_defaults() + + def restore_main_win_geom(self): + try: + self.setGeometry(self.app.defaults["global_def_win_x"], + self.app.defaults["global_def_win_y"], + self.app.defaults["global_def_win_w"], + self.app.defaults["global_def_win_h"]) + self.splitter.setSizes([self.app.defaults["global_def_notebook_width"], 0]) + except KeyError as e: + log.debug("AppGUI.MainGUI.restore_main_win_geom() --> %s" % str(e)) + + def restore_toolbar_view(self): + """ + Some toolbars may be hidden by user and here we restore the state of the toolbars visibility that + was saved in the defaults dictionary. + + :return: None + """ + tb = self.app.defaults["global_toolbar_view"] + + if tb & 1: + self.toolbarfile.setVisible(True) + else: + self.toolbarfile.setVisible(False) + + if tb & 2: + self.toolbaredit.setVisible(True) + else: + self.toolbaredit.setVisible(False) + + if tb & 4: + self.toolbarview.setVisible(True) + else: + self.toolbarview.setVisible(False) + + if tb & 8: + self.toolbartools.setVisible(True) + else: + self.toolbartools.setVisible(False) + + if tb & 16: + self.exc_edit_toolbar.setVisible(True) + else: + self.exc_edit_toolbar.setVisible(False) + + if tb & 32: + self.geo_edit_toolbar.setVisible(True) + else: + self.geo_edit_toolbar.setVisible(False) + + if tb & 64: + self.grb_edit_toolbar.setVisible(True) + else: + self.grb_edit_toolbar.setVisible(False) + + # if tb & 128: + # self.ui.status_toolbar.setVisible(True) + # else: + # self.ui.status_toolbar.setVisible(False) + + # Grid Toolbar is always active now + self.status_toolbar.setVisible(True) + + if tb & 256: + self.toolbarshell.setVisible(True) + else: + self.toolbarshell.setVisible(False) + + def eventFilter(self, obj, event): + """ + Filter the ToolTips display based on a Preferences setting + + :param obj: + :param event: QT event to filter + :return: + """ + if self.app.defaults["global_toggle_tooltips"] is False: + if event.type() == QtCore.QEvent.ToolTip: + return True + else: + return False + + return False + + def on_preferences_open_folder(self): + """ + Will open an Explorer window set to the folder path where the FlatCAM preferences files are usually saved. + + :return: None + """ + + if sys.platform == 'win32': + subprocess.Popen('explorer %s' % self.app.data_path) + elif sys.platform == 'darwin': + os.system('open "%s"' % self.app.data_path) + else: + subprocess.Popen(['xdg-open', self.app.data_path]) + self.app.inform.emit('[success] %s' % _("FlatCAM Preferences Folder opened.")) + + def on_gui_clear(self): + theme_settings = QtCore.QSettings("Open Source", "FlatCAM") + theme_settings.setValue('theme', 'white') + + del theme_settings + + resource_loc = self.app.resource_location + + msgbox = QtWidgets.QMessageBox() + msgbox.setText(_("Are you sure you want to delete the GUI Settings? \n")) + msgbox.setWindowTitle(_("Clear GUI Settings")) + msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/trash32.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + + bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) + bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) + + msgbox.setDefaultButton(bt_no) + msgbox.exec_() + response = msgbox.clickedButton() + + if response == bt_yes: + qsettings = QSettings("Open Source", "FlatCAM") + for key in qsettings.allKeys(): + qsettings.remove(key) + # This will write the setting to the platform specific storage. + del qsettings + + def populate_toolbars(self): + """ + Will populate the App Toolbars with their actions + + :return: None + """ + + # ######################################################################## + # ## File Toolbar # ## + # ######################################################################## + self.file_open_gerber_btn = self.toolbarfile.addAction( + QtGui.QIcon(self.app.resource_location + '/flatcam_icon32.png'), _("Open Gerber")) + self.file_open_excellon_btn = self.toolbarfile.addAction( + QtGui.QIcon(self.app.resource_location + '/drill32.png'), _("Open Excellon")) + self.toolbarfile.addSeparator() + self.file_open_btn = self.toolbarfile.addAction( + QtGui.QIcon(self.app.resource_location + '/folder32.png'), _("Open project")) + self.file_save_btn = self.toolbarfile.addAction( + QtGui.QIcon(self.app.resource_location + '/project_save32.png'), _("Save project")) + + # ######################################################################## + # ## Edit Toolbar # ## + # ######################################################################## + self.editgeo_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/edit32.png'), _("Editor")) + self.update_obj_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/close_edit_file32.png'), + _("Save Object and close the Editor") + ) + + self.toolbaredit.addSeparator() + self.copy_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/copy_file32.png'), _("Copy")) + self.delete_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("&Delete")) + self.toolbaredit.addSeparator() + self.distance_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/distance32.png'), _("Distance Tool")) + self.distance_min_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/distance_min32.png'), _("Distance Min Tool")) + self.origin_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/origin32.png'), _('Set Origin')) + self.move2origin_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/origin2_32.png'), _('Move to Origin')) + self.jmp_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location')) + self.locate_btn = self.toolbaredit.addAction( + QtGui.QIcon(self.app.resource_location + '/locate32.png'), _('Locate in Object')) + + # ######################################################################## + # ########################## View Toolbar# ############################### + # ######################################################################## + self.replot_btn = self.toolbarview.addAction( + QtGui.QIcon(self.app.resource_location + '/replot32.png'), _("&Replot")) + self.clear_plot_btn = self.toolbarview.addAction( + QtGui.QIcon(self.app.resource_location + '/clear_plot32.png'), _("&Clear plot")) + self.zoom_in_btn = self.toolbarview.addAction( + QtGui.QIcon(self.app.resource_location + '/zoom_in32.png'), _("Zoom In")) + self.zoom_out_btn = self.toolbarview.addAction( + QtGui.QIcon(self.app.resource_location + '/zoom_out32.png'), _("Zoom Out")) + self.zoom_fit_btn = self.toolbarview.addAction( + QtGui.QIcon(self.app.resource_location + '/zoom_fit32.png'), _("Zoom Fit")) + + # ######################################################################## + # ########################## Shell Toolbar# ############################## + # ######################################################################## + self.shell_btn = self.toolbarshell.addAction( + QtGui.QIcon(self.app.resource_location + '/shell32.png'), _("&Command Line")) + self.new_script_btn = self.toolbarshell.addAction( + QtGui.QIcon(self.app.resource_location + '/script_new24.png'), _('New Script ...')) + self.open_script_btn = self.toolbarshell.addAction( + QtGui.QIcon(self.app.resource_location + '/open_script32.png'), _('Open Script ...')) + self.run_script_btn = self.toolbarshell.addAction( + QtGui.QIcon(self.app.resource_location + '/script16.png'), _('Run Script ...')) + + # ######################################################################### + # ######################### Tools Toolbar ################################# + # ######################################################################### + self.dblsided_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/doubleside32.png'), _("2Sided Tool")) + self.align_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/align32.png'), _("Align Objects Tool")) + self.extract_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/extract_drill32.png'), _("Extract Drills Tool")) + + self.cutout_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/cut16_bis.png'), _("&Cutout Tool")) + self.ncc_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/ncc16.png'), _("NCC Tool")) + self.paint_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/paint20_1.png'), _("Paint Tool")) + self.isolation_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/iso_16.png'), _("Isolation Tool")) + self.toolbartools.addSeparator() + + self.panelize_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/panelize32.png'), _("Panel Tool")) + self.film_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/film16.png'), _("Film Tool")) + self.solder_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/solderpastebis32.png'), _("SolderPaste Tool")) + self.sub_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/sub32.png'), _("Subtract Tool")) + self.rules_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/rules32.png'), _("Rules Tool")) + self.optimal_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'), _("Optimal Tool")) + + self.toolbartools.addSeparator() + + self.calculators_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/calculator24.png'), _("Calculators Tool")) + self.transform_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transform Tool")) + self.qrcode_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/qrcode32.png'), _("QRCode Tool")) + self.copperfill_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/copperfill32.png'), _("Copper Thieving Tool")) + + self.fiducials_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/fiducials_32.png'), _("Fiducials Tool")) + self.cal_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/calibrate_32.png'), _("Calibration Tool")) + self.punch_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/punch32.png'), _("Punch Gerber Tool")) + self.invert_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/invert32.png'), _("Invert Gerber Tool")) + self.corners_tool_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/corners_32.png'), _("Corner Markers Tool")) + self.etch_btn = self.toolbartools.addAction( + QtGui.QIcon(self.app.resource_location + '/etch_32.png'), _("Etch Compensation Tool")) + + # ######################################################################## + # ## Excellon Editor Toolbar # ## + # ######################################################################## + self.select_drill_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/pointer32.png'), _("Select")) + self.add_drill_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/plus16.png'), _('Add Drill Hole')) + self.add_drill_array_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/addarray16.png'), _('Add Drill Hole Array')) + self.resize_drill_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/resize16.png'), _('Resize Drill')) + self.add_slot_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/slot26.png'), _('Add Slot')) + self.add_slot_array_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/slot_array26.png'), _('Add Slot Array')) + self.exc_edit_toolbar.addSeparator() + + self.copy_drill_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/copy32.png'), _('Copy Drill')) + self.delete_drill_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete Drill")) + + self.exc_edit_toolbar.addSeparator() + self.move_drill_btn = self.exc_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move Drill")) + + # ######################################################################## + # ## Geometry Editor Toolbar # ## + # ######################################################################## + self.geo_select_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/pointer32.png'), _("Select 'Esc'")) + self.geo_add_circle_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/circle32.png'), _('Add Circle')) + self.geo_add_arc_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/arc32.png'), _('Add Arc')) + self.geo_add_rectangle_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/rectangle32.png'), _('Add Rectangle')) + + self.geo_edit_toolbar.addSeparator() + self.geo_add_path_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/path32.png'), _('Add Path')) + self.geo_add_polygon_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _('Add Polygon')) + self.geo_edit_toolbar.addSeparator() + self.geo_add_text_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/text32.png'), _('Add Text')) + self.geo_add_buffer_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _('Add Buffer')) + self.geo_add_paint_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/paint20_1.png'), _('Paint Shape')) + self.geo_eraser_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _('Eraser')) + + self.geo_edit_toolbar.addSeparator() + self.geo_union_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/union32.png'), _('Polygon Union')) + self.geo_explode_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/explode32.png'), _('Polygon Explode')) + + self.geo_intersection_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/intersection32.png'), _('Polygon Intersection')) + self.geo_subtract_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/subtract32.png'), _('Polygon Subtraction')) + + self.geo_edit_toolbar.addSeparator() + self.geo_cutpath_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/cutpath32.png'), _('Cut Path')) + self.geo_copy_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy Objects")) + self.geo_delete_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete Shape")) + self.geo_transform_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) + + self.geo_edit_toolbar.addSeparator() + self.geo_move_btn = self.geo_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move Objects")) + + # ######################################################################## + # ## Gerber Editor Toolbar # ## + # ######################################################################## + self.grb_select_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/pointer32.png'), _("Select")) + self.grb_add_pad_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/aperture32.png'), _("Add Pad")) + self.add_pad_ar_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/padarray32.png'), _('Add Pad Array')) + self.grb_add_track_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/track32.png'), _("Add Track")) + self.grb_add_region_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _("Add Region")) + self.grb_convert_poly_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/poligonize32.png'), _("Poligonize")) + + self.grb_add_semidisc_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/semidisc32.png'), _("SemiDisc")) + self.grb_add_disc_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/disc32.png'), _("Disc")) + self.grb_edit_toolbar.addSeparator() + + self.aperture_buffer_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _('Buffer')) + self.aperture_scale_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/scale32.png'), _('Scale')) + self.aperture_markarea_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/markarea32.png'), _('Mark Area')) + self.aperture_eraser_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _('Eraser')) + + self.grb_edit_toolbar.addSeparator() + self.aperture_copy_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy")) + self.aperture_delete_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete")) + self.grb_transform_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) + self.grb_edit_toolbar.addSeparator() + self.aperture_move_btn = self.grb_edit_toolbar.addAction( + QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move")) + + self.corner_snap_btn.setVisible(False) + self.snap_magnet.setVisible(False) + + qsettings = QSettings("Open Source", "FlatCAM") + if qsettings.contains("layout"): + layout = qsettings.value('layout', type=str) + + # on 'minimal' layout only some toolbars are active + if layout != 'minimal': + 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(True) + self.grb_edit_toolbar.setDisabled(True) + + def keyPressEvent(self, event): + """ + Key event handler for the entire app. + Some of the key events are also treated locally in the FlatCAM editors + + :param event: QT event + :return: + """ + modifiers = QtWidgets.QApplication.keyboardModifiers() + active = self.app.collection.get_active() + selected = self.app.collection.get_selected() + names_list = self.app.collection.get_names() + + matplotlib_key_flag = False + + # events out of the self.app.collection view (it's about Project Tab) are of type int + if type(event) is int: + key = event + # events from the GUI are of type QKeyEvent + elif type(event) == QtGui.QKeyEvent: + key = event.key() + elif isinstance(event, mpl_key_event): # MatPlotLib key events are trickier to interpret than the rest + matplotlib_key_flag = True + + key = event.key + key = QtGui.QKeySequence(key) + + # check for modifiers + key_string = key.toString().lower() + if '+' in key_string: + mod, __, key_text = key_string.rpartition('+') + if mod.lower() == 'ctrl': + modifiers = QtCore.Qt.ControlModifier + elif mod.lower() == 'alt': + modifiers = QtCore.Qt.AltModifier + elif mod.lower() == 'shift': + modifiers = QtCore.Qt.ShiftModifier + else: + modifiers = QtCore.Qt.NoModifier + key = QtGui.QKeySequence(key_text) + + # events from Vispy are of type KeyEvent + else: + key = event.key + + if self.app.call_source == 'app': + # CTRL + ALT + if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier: + if key == QtCore.Qt.Key_X: + self.app.abort_all_tasks() + return + # CTRL + SHIFT + if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier: + if key == QtCore.Qt.Key_S: + self.app.on_file_saveprojectas() + return + # CTRL + elif modifiers == QtCore.Qt.ControlModifier: + # Select All + if key == QtCore.Qt.Key_A: + self.app.on_selectall() + + # Copy an FlatCAM object + if key == QtCore.Qt.Key_C: + widget_name = self.plot_tab_area.currentWidget().objectName() + if widget_name == 'database_tab': + # Tools DB saved, update flag + self.app.tools_db_changed_flag = True + self.app.tools_db_tab.on_tool_copy() + return + + self.app.on_copy_command() + + # Copy an FlatCAM object + if key == QtCore.Qt.Key_D: + self.app.on_tools_database() + + # Open Excellon file + if key == QtCore.Qt.Key_E: + self.app.on_fileopenexcellon(signal=None) + + # Open Gerber file + if key == QtCore.Qt.Key_G: + widget_name = self.plot_tab_area.currentWidget().objectName() + if 'editor' in widget_name.lower(): + self.app.goto_text_line() + else: + self.app.on_fileopengerber(signal=None) + + # Distance Tool + if key == QtCore.Qt.Key_M: + self.app.distance_tool.run() + + # Create New Project + if key == QtCore.Qt.Key_N: + self.app.on_file_new_click() + + # Open Project + if key == QtCore.Qt.Key_O: + self.app.on_file_openproject(signal=None) + + # Open Project + if key == QtCore.Qt.Key_P: + self.app.on_file_save_objects_pdf(use_thread=True) + + # PDF Import + if key == QtCore.Qt.Key_Q: + self.app.pdf_tool.run() + + # Save Project + if key == QtCore.Qt.Key_S: + widget_name = self.plot_tab_area.currentWidget().objectName() + if widget_name == 'preferences_tab': + self.app.preferencesUiManager.on_save_button(save_to_file=False) + return + + if widget_name == 'database_tab': + # Tools DB saved, update flag + self.app.tools_db_changed_flag = False + self.app.tools_db_tab.on_save_tools_db() + return + + self.app.on_file_saveproject() + + # Toggle Plot Area + if key == QtCore.Qt.Key_F10 or key == 'F10': + self.on_toggle_plotarea() + + return + # SHIFT + elif modifiers == QtCore.Qt.ShiftModifier: + + # Copy Object Name + if key == QtCore.Qt.Key_C: + self.app.on_copy_name() + + # Toggle Code Editor + if key == QtCore.Qt.Key_E: + self.app.on_toggle_code_editor() + + # Toggle axis + if key == QtCore.Qt.Key_G: + self.app.plotcanvas.on_toggle_axis() + + # Toggle HUD (Heads-Up Display) + if key == QtCore.Qt.Key_H: + self.app.plotcanvas.on_toggle_hud() + # Locate in Object + if key == QtCore.Qt.Key_J: + self.app.on_locate(obj=self.app.collection.get_active()) + + # Run Distance Minimum Tool + if key == QtCore.Qt.Key_M: + self.app.distance_min_tool.run() + return + + # Open Preferences Window + if key == QtCore.Qt.Key_P: + self.app.on_preferences() + return + + # Rotate Object by 90 degree CCW + if key == QtCore.Qt.Key_R: + self.app.on_rotate(silent=True, preset=-float(self.app.defaults['tools_transform_rotate'])) + return + + # Run a Script + if key == QtCore.Qt.Key_S: + self.app.on_filerunscript() + return + + # Toggle Workspace + if key == QtCore.Qt.Key_W: + self.app.on_workspace_toggle() + return + + # Skew on X axis + if key == QtCore.Qt.Key_X: + self.app.on_skewx() + return + + # Skew on Y axis + if key == QtCore.Qt.Key_Y: + self.app.on_skewy() + return + # ALT + elif modifiers == QtCore.Qt.AltModifier: + # Eanble all plots + if key == Qt.Key_1: + self.app.enable_all_plots() + + # Disable all plots + if key == Qt.Key_2: + self.app.disable_all_plots() + + # Disable all other plots + if key == Qt.Key_3: + self.app.disable_other_plots() + + # Align in Object Tool + if key == QtCore.Qt.Key_A: + self.app.align_objects_tool.run(toggle=True) + + # Calculator Tool + if key == QtCore.Qt.Key_C: + self.app.calculator_tool.run(toggle=True) + + # 2-Sided PCB Tool + if key == QtCore.Qt.Key_D: + self.app.dblsidedtool.run(toggle=True) + return + + # Extract Drills Tool + if key == QtCore.Qt.Key_E: + # self.app.cal_exc_tool.run(toggle=True) + self.app.edrills_tool.run(toggle=True) + return + + # Fiducials Tool + if key == QtCore.Qt.Key_F: + self.app.fiducial_tool.run(toggle=True) + return + + # Toggle Grid lines + if key == QtCore.Qt.Key_G: + self.app.plotcanvas.on_toggle_grid_lines() + return + + # Punch Gerber Tool + if key == QtCore.Qt.Key_H: + self.app.punch_tool.run(toggle=True) + + # Isolation Tool + if key == QtCore.Qt.Key_I: + self.app.isolation_tool.run(toggle=True) + + # Copper Thieving Tool + if key == QtCore.Qt.Key_J: + self.app.copper_thieving_tool.run(toggle=True) + return + + # Solder Paste Dispensing Tool + if key == QtCore.Qt.Key_K: + self.app.paste_tool.run(toggle=True) + return + + # Film Tool + if key == QtCore.Qt.Key_L: + self.app.film_tool.run(toggle=True) + return + + # Corner Markers Tool + if key == QtCore.Qt.Key_M: + self.app.corners_tool.run(toggle=True) + return + + # Non-Copper Clear Tool + if key == QtCore.Qt.Key_N: + self.app.ncclear_tool.run(toggle=True) + return + + # Optimal Tool + if key == QtCore.Qt.Key_O: + self.app.optimal_tool.run(toggle=True) + return + + # Paint Tool + if key == QtCore.Qt.Key_P: + self.app.paint_tool.run(toggle=True) + return + + # QRCode Tool + if key == QtCore.Qt.Key_Q: + self.app.qrcode_tool.run() + return + + # Rules Tool + if key == QtCore.Qt.Key_R: + self.app.rules_tool.run(toggle=True) + return + + # View Source Object Content + if key == QtCore.Qt.Key_S: + self.app.on_view_source() + return + + # Transformation Tool + if key == QtCore.Qt.Key_T: + self.app.transform_tool.run(toggle=True) + return + + # Substract Tool + if key == QtCore.Qt.Key_W: + self.app.sub_tool.run(toggle=True) + return + + # Cutout Tool + if key == QtCore.Qt.Key_X: + self.app.cutout_tool.run(toggle=True) + return + + # Panelize Tool + if key == QtCore.Qt.Key_Z: + self.app.panelize_tool.run(toggle=True) + return + + # Toggle Fullscreen + if key == QtCore.Qt.Key_F10 or key == 'F10': + self.on_fullscreen() + return + # NO MODIFIER + elif modifiers == QtCore.Qt.NoModifier: + # Open Manual + if key == QtCore.Qt.Key_F1 or key == 'F1': + webbrowser.open(self.app.manual_url) + + # Show shortcut list + if key == QtCore.Qt.Key_F3 or key == 'F3': + self.app.on_shortcut_list() + + # Open Video Help + if key == QtCore.Qt.Key_F4 or key == 'F4': + webbrowser.open(self.app.video_url) + + # Open Video Help + if key == QtCore.Qt.Key_F5 or key == 'F5': + self.app.plot_all() + + # Switch to Project Tab + if key == QtCore.Qt.Key_1: + self.app.on_select_tab('project') + + # Switch to Selected Tab + if key == QtCore.Qt.Key_2: + self.app.on_select_tab('selected') + + # Switch to Tool Tab + if key == QtCore.Qt.Key_3: + self.app.on_select_tab('tool') + + # Delete from PyQt + # It's meant to make a difference between delete objects and delete tools in + # Geometry Selected tool table + if key == QtCore.Qt.Key_Delete and matplotlib_key_flag is False: + widget_name = self.plot_tab_area.currentWidget().objectName() + if widget_name == 'database_tab': + # Tools DB saved, update flag + self.app.tools_db_changed_flag = True + self.app.tools_db_tab.on_tool_delete() + return + + self.app.on_delete_keypress() + + # Delete from canvas + if key == 'Delete': + # Delete via the application to + # ensure cleanup of the AppGUI + if active: + active.app.on_delete() + + # 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 + if self.toggle_fscreen is True: + self.on_fullscreen(disable=True) + + # try to disconnect the slot from Set Origin + try: + self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_set_zero_click) + except TypeError: + pass + self.app.inform.emit("") + + # Space = Toggle Active/Inactive + if key == QtCore.Qt.Key_Space: + for select in selected: + select.ui.plot_cb.toggle() + self.app.collection.update_view() + self.app.delete_selection_shape() + + # Select the object in the Tree above the current one + if key == QtCore.Qt.Key_Up: + # make sure it works only for the Project Tab who is an instance of KeySensitiveListView + focused_wdg = QtWidgets.QApplication.focusWidget() + if isinstance(focused_wdg, KeySensitiveListView): + self.app.collection.set_all_inactive() + if active is None: + return + active_name = active.options['name'] + active_index = names_list.index(active_name) + if active_index == 0: + self.app.collection.set_active(names_list[-1]) + else: + self.app.collection.set_active(names_list[active_index - 1]) + + # Select the object in the Tree below the current one + if key == QtCore.Qt.Key_Down: + # make sure it works only for the Project Tab who is an instance of KeySensitiveListView + focused_wdg = QtWidgets.QApplication.focusWidget() + if isinstance(focused_wdg, KeySensitiveListView): + self.app.collection.set_all_inactive() + if active is None: + return + active_name = active.options['name'] + active_index = names_list.index(active_name) + if active_index == len(names_list) - 1: + self.app.collection.set_active(names_list[0]) + else: + self.app.collection.set_active(names_list[active_index + 1]) + + # New Geometry + if key == QtCore.Qt.Key_B: + self.app.app_obj.new_gerber_object() + + # New Geometry + if key == QtCore.Qt.Key_D: + self.app.new_document_object() + + # Copy Object Name + if key == QtCore.Qt.Key_E: + self.app.object2editor() + + # Grid toggle + if key == QtCore.Qt.Key_G: + self.app.ui.grid_snap_btn.trigger() + + # Jump to coords + if key == QtCore.Qt.Key_J: + self.app.on_jump_to() + + # New Excellon + if key == QtCore.Qt.Key_L: + self.app.app_obj.new_excellon_object() + + # Move tool toggle + if key == QtCore.Qt.Key_M: + self.app.move_tool.toggle() + + # New Geometry + if key == QtCore.Qt.Key_N: + self.app.app_obj.new_geometry_object() + + # Set Origin + if key == QtCore.Qt.Key_O: + self.app.on_set_origin() + return + + # Properties Tool + if key == QtCore.Qt.Key_P: + self.app.properties_tool.run() + return + + # Change Units + if key == QtCore.Qt.Key_Q: + # if self.app.defaults["units"] == 'MM': + # self.app.ui.general_defaults_form.general_app_group.units_radio.set_value("IN") + # else: + # self.app.ui.general_defaults_form.general_app_group.units_radio.set_value("MM") + # self.app.on_toggle_units(no_pref=True) + self.app.on_toggle_units_click() + + # Rotate Object by 90 degree CW + if key == QtCore.Qt.Key_R: + self.app.on_rotate(silent=True, preset=self.app.defaults['tools_transform_rotate']) + + # Shell toggle + if key == QtCore.Qt.Key_S: + self.toggle_shell_ui() + + # Add a Tool from shortcut + if key == QtCore.Qt.Key_T: + widget_name = self.plot_tab_area.currentWidget().objectName() + if widget_name == 'database_tab': + # Tools DB saved, update flag + self.app.tools_db_changed_flag = True + self.app.tools_db_tab.on_tool_add() + return + + self.app.on_tool_add_keypress() + + # Zoom Fit + if key == QtCore.Qt.Key_V: + self.app.on_zoom_fit() + + # Mirror on X the selected object(s) + if key == QtCore.Qt.Key_X: + self.app.on_flipx() + + # Mirror on Y the selected object(s) + if key == QtCore.Qt.Key_Y: + self.app.on_flipy() + + # Zoom In + if key == QtCore.Qt.Key_Equal: + self.app.plotcanvas.zoom(1 / self.app.defaults['global_zoom_ratio'], self.app.mouse) + + # Zoom Out + if key == QtCore.Qt.Key_Minus: + self.app.plotcanvas.zoom(self.app.defaults['global_zoom_ratio'], self.app.mouse) + + # toggle display of Notebook area + if key == QtCore.Qt.Key_QuoteLeft: + self.on_toggle_notebook() + + return + elif self.app.call_source == 'geo_editor': + # CTRL + 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.distance_tool.run() + return + + # Cut Action Tool + if key == QtCore.Qt.Key_X or key == 'X': + if self.app.geo_editor.get_selected() is not None: + self.app.geo_editor.cutpath() + else: + msg = _('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.') + + messagebox = QtWidgets.QMessageBox() + messagebox.setText(msg) + messagebox.setWindowTitle(_("Warning")) + messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) + messagebox.setIcon(QtWidgets.QMessageBox.Question) + + messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) + messagebox.exec_() + return + # SHIFT + elif modifiers == QtCore.Qt.ShiftModifier: + # Run Distance Minimum Tool + if key == QtCore.Qt.Key_M or key == 'M': + self.app.distance_min_tool.run() + return + + # Skew on X axis + if key == QtCore.Qt.Key_X or key == 'X': + self.app.geo_editor.transform_tool.on_skewx_key() + return + + # Skew on Y axis + if key == QtCore.Qt.Key_Y or key == 'Y': + self.app.geo_editor.transform_tool.on_skewy_key() + return + # ALT + elif modifiers == QtCore.Qt.AltModifier: + + # Transformation Tool + if key == QtCore.Qt.Key_R or key == 'R': + self.app.geo_editor.select_tool('transform') + return + + # Offset on X axis + if key == QtCore.Qt.Key_X or key == 'X': + self.app.geo_editor.transform_tool.on_offx_key() + return + + # Offset on Y axis + if key == QtCore.Qt.Key_Y or key == 'Y': + self.app.geo_editor.transform_tool.on_offy_key() + return + # NO MODIFIER + elif modifiers == QtCore.Qt.NoModifier: + # toggle display of Notebook area + if key == QtCore.Qt.Key_QuoteLeft or key == '`': + self.on_toggle_notebook() + + # Finish the current action. Use with tools that do not + # complete automatically, like a polygon or path. + if key == QtCore.Qt.Key_Enter or key == 'Enter': + if isinstance(self.app.geo_editor.active_tool, FCShapeTool): + if self.app.geo_editor.active_tool.name == 'rotate': + self.app.geo_editor.active_tool.make() + + if self.app.geo_editor.active_tool.complete: + self.app.geo_editor.on_shape_complete() + self.app.inform.emit('[success] %s' % _("Done.")) + # automatically make the selection tool active after completing current action + self.app.geo_editor.select_tool('select') + return + else: + self.app.geo_editor.active_tool.click( + self.app.geo_editor.snap(self.app.geo_editor.x, self.app.geo_editor.y)) + + self.app.geo_editor.active_tool.make() + + if self.app.geo_editor.active_tool.complete: + self.app.geo_editor.on_shape_complete() + self.app.inform.emit('[success] %s' % _("Done.")) + # automatically make the selection tool active after completing current action + self.app.geo_editor.select_tool('select') + + # Abort the current action + if key == QtCore.Qt.Key_Escape or key == 'Escape': + # self.on_tool_select("select") + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled.")) + + self.app.geo_editor.delete_utility_geometry() + + self.app.geo_editor.active_tool.clean_up() + + self.app.geo_editor.select_tool('select') + + # hide the notebook + self.app.ui.splitter.setSizes([0, 1]) + return + + # Delete selected object + if key == QtCore.Qt.Key_Delete or key == 'Delete': + self.app.geo_editor.delete_selected() + self.app.geo_editor.replot() + + # Rotate + if key == QtCore.Qt.Key_Space or key == 'Space': + self.app.geo_editor.transform_tool.on_rotate_key() + + # Zoom Out + if key == QtCore.Qt.Key_Minus or key == '-': + self.app.plotcanvas.zoom(1 / self.app.defaults['global_zoom_ratio'], + [self.app.geo_editor.snap_x, self.app.geo_editor.snap_y]) + + # Zoom In + if key == QtCore.Qt.Key_Equal or key == '=': + self.app.plotcanvas.zoom(self.app.defaults['global_zoom_ratio'], + [self.app.geo_editor.snap_x, self.app.geo_editor.snap_y]) + + # Switch to Project Tab + if key == QtCore.Qt.Key_1 or key == '1': + self.app.on_select_tab('project') + + # Switch to Selected Tab + if key == QtCore.Qt.Key_2 or key == '2': + self.app.on_select_tab('selected') + + # Switch to Tool Tab + if key == QtCore.Qt.Key_3 or key == '3': + self.app.on_select_tab('tool') + + # Grid Snap + if key == QtCore.Qt.Key_G or key == 'G': + self.app.ui.grid_snap_btn.trigger() + + # make sure that the cursor shape is enabled/disabled, too + if self.app.geo_editor.options['grid_snap'] is True: + self.app.app_cursor.enabled = True + else: + self.app.app_cursor.enabled = False + + # Corner Snap + if key == QtCore.Qt.Key_K or key == 'K': + self.app.geo_editor.on_corner_snap() + + if key == QtCore.Qt.Key_V or key == 'V': + self.app.on_zoom_fit() + + # we do this so we can reuse the following keys while inside a Tool + # the above keys are general enough so were left outside + if self.app.geo_editor.active_tool is not None and self.geo_select_btn.isChecked() is False: + response = self.app.geo_editor.active_tool.on_key(key=key) + if response is not None: + self.app.inform.emit(response) + else: + # Arc Tool + if key == QtCore.Qt.Key_A or key == 'A': + self.app.geo_editor.select_tool('arc') + + # Buffer + if key == QtCore.Qt.Key_B or key == 'B': + self.app.geo_editor.select_tool('buffer') + + # Copy + if key == QtCore.Qt.Key_C or key == 'C': + self.app.geo_editor.on_copy_click() + + # Substract Tool + if key == QtCore.Qt.Key_E or key == 'E': + if self.app.geo_editor.get_selected() is not None: + self.app.geo_editor.intersection() + else: + msg = _("Please select geometry items \n" + "on which to perform Intersection Tool.") + + messagebox = QtWidgets.QMessageBox() + messagebox.setText(msg) + messagebox.setWindowTitle(_("Warning")) + messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) + messagebox.setIcon(QtWidgets.QMessageBox.Warning) + + messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) + messagebox.exec_() + + # Paint + if key == QtCore.Qt.Key_I or key == 'I': + self.app.geo_editor.select_tool('paint') + + # Jump to coords + if key == QtCore.Qt.Key_J or key == 'J': + self.app.on_jump_to() + + # Move + if key == QtCore.Qt.Key_M or key == 'M': + self.app.geo_editor.on_move_click() + + # Polygon Tool + if key == QtCore.Qt.Key_N or key == 'N': + self.app.geo_editor.select_tool('polygon') + + # Circle Tool + if key == QtCore.Qt.Key_O or key == 'O': + self.app.geo_editor.select_tool('circle') + + # Path Tool + if key == QtCore.Qt.Key_P or key == 'P': + self.app.geo_editor.select_tool('path') + + # Rectangle Tool + if key == QtCore.Qt.Key_R or key == 'R': + self.app.geo_editor.select_tool('rectangle') + + # Substract Tool + if key == QtCore.Qt.Key_S or key == 'S': + if self.app.geo_editor.get_selected() is not None: + self.app.geo_editor.subtract() + else: + msg = _( + "Please select geometry items \n" + "on which to perform Substraction Tool.") + + messagebox = QtWidgets.QMessageBox() + messagebox.setText(msg) + messagebox.setWindowTitle(_("Warning")) + messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) + messagebox.setIcon(QtWidgets.QMessageBox.Warning) + + messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) + messagebox.exec_() + + # Add Text Tool + if key == QtCore.Qt.Key_T or key == 'T': + self.app.geo_editor.select_tool('text') + + # Substract Tool + if key == QtCore.Qt.Key_U or key == 'U': + if self.app.geo_editor.get_selected() is not None: + self.app.geo_editor.union() + else: + msg = _("Please select geometry items \n" + "on which to perform union.") + + messagebox = QtWidgets.QMessageBox() + messagebox.setText(msg) + messagebox.setWindowTitle(_("Warning")) + messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) + messagebox.setIcon(QtWidgets.QMessageBox.Warning) + + messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) + messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) + messagebox.exec_() + + # Flip on X axis + if key == QtCore.Qt.Key_X or key == 'X': + self.app.geo_editor.transform_tool.on_flipx() + return + + # Flip on Y axis + if key == QtCore.Qt.Key_Y or key == 'Y': + self.app.geo_editor.transform_tool.on_flipy() + return + + # Show Shortcut list + if key == 'F3': + self.app.on_shortcut_list() + elif self.app.call_source == 'grb_editor': + # CTRL + if modifiers == QtCore.Qt.ControlModifier: + # Eraser Tool + if key == QtCore.Qt.Key_E or key == 'E': + self.app.grb_editor.on_eraser() + return + + # 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.distance_tool.run() + return + # SHIFT + elif modifiers == QtCore.Qt.ShiftModifier: + # Run Distance Minimum Tool + if key == QtCore.Qt.Key_M or key == 'M': + self.app.distance_min_tool.run() + return + # ALT + elif modifiers == QtCore.Qt.AltModifier: + # Mark Area Tool + if key == QtCore.Qt.Key_A or key == 'A': + self.app.grb_editor.on_markarea() + return + + # Poligonize Tool + if key == QtCore.Qt.Key_N or key == 'N': + self.app.grb_editor.on_poligonize() + return + # Transformation Tool + if key == QtCore.Qt.Key_R or key == 'R': + self.app.grb_editor.on_transform() + return + # NO MODIFIER + 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] %s' % _("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] %s' % _("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['global_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['global_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.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 + + # we do this so we can reuse the following keys while inside a Tool + # the above keys are general enough so were left outside + if self.app.grb_editor.active_tool is not None and self.grb_select_btn.isChecked() is False: + response = self.app.grb_editor.active_tool.on_key(key=key) + if response is not None: + self.app.inform.emit(response) + else: + # 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] %s' % _("Cancelled. Nothing selected to copy.")) + return + + # Add Disc Tool + if key == QtCore.Qt.Key_D or key == 'D': + self.app.grb_editor.launched_from_shortcuts = True + self.app.grb_editor.select_tool('disc') + return + + # Add SemiDisc Tool + if key == QtCore.Qt.Key_E or key == 'E': + self.app.grb_editor.launched_from_shortcuts = True + self.app.grb_editor.select_tool('semidisc') + 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] %s' % _("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.grb_editor.on_zoom_fit() + return + + # 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': + # CTRL + 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.distance_tool.run() + return + # SHIFT + elif modifiers == QtCore.Qt.ShiftModifier: + # Run Distance Minimum Tool + if key == QtCore.Qt.Key_M or key == 'M': + self.app.distance_min_tool.run() + return + # ALT + elif modifiers == QtCore.Qt.AltModifier: + pass + # NO MODIFIER + elif modifiers == QtCore.Qt.NoModifier: + # Abort the current action + if key == QtCore.Qt.Key_Escape or key == 'Escape': + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled.")) + + self.app.exc_editor.delete_utility_geometry() + + self.app.exc_editor.active_tool.clean_up() + + self.app.exc_editor.select_tool('drill_select') + return + + # Delete selected object if delete key event comes out of canvas + if key == 'Delete': + self.app.exc_editor.launched_from_shortcuts = True + if self.app.exc_editor.selected: + self.app.exc_editor.delete_selected() + self.app.exc_editor.replot() + else: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Nothing selected to delete.")) + return + + # Delete tools in tools table if delete key event comes from the Selected Tab + if key == QtCore.Qt.Key_Delete: + self.app.exc_editor.launched_from_shortcuts = True + self.app.exc_editor.on_tool_delete() + return + + if key == QtCore.Qt.Key_Minus or key == '-': + self.app.exc_editor.launched_from_shortcuts = True + self.app.plotcanvas.zoom(1 / self.app.defaults['global_zoom_ratio'], + [self.app.exc_editor.snap_x, self.app.exc_editor.snap_y]) + return + + if key == QtCore.Qt.Key_Equal or key == '=': + self.app.exc_editor.launched_from_shortcuts = True + self.app.plotcanvas.zoom(self.app.defaults['global_zoom_ratio'], + [self.app.exc_editor.snap_x, self.app.exc_editor.snap_y]) + return + + # toggle display of Notebook area + if key == QtCore.Qt.Key_QuoteLeft or key == '`': + self.app.exc_editor.launched_from_shortcuts = True + self.on_toggle_notebook() + return + + # Switch to Project Tab + if key == QtCore.Qt.Key_1 or key == '1': + self.app.exc_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.exc_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.exc_editor.launched_from_shortcuts = True + self.app.on_select_tab('tool') + return + + # Grid Snap + if key == QtCore.Qt.Key_G or key == 'G': + self.app.exc_editor.launched_from_shortcuts = True + # make sure that the cursor shape is enabled/disabled, too + if self.app.exc_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 + + # Corner Snap + if key == QtCore.Qt.Key_K or key == 'K': + self.app.exc_editor.launched_from_shortcuts = True + self.app.ui.corner_snap_btn.trigger() + return + + # Zoom Fit + if key == QtCore.Qt.Key_V or key == 'V': + self.app.exc_editor.launched_from_shortcuts = True + self.app.on_zoom_fit() + return + + # Add Slot Hole Tool + if key == QtCore.Qt.Key_W or key == 'W': + self.app.exc_editor.launched_from_shortcuts = True + self.app.inform.emit(_("Click on target point.")) + self.app.ui.add_slot_btn.setChecked(True) + + self.app.exc_editor.x = self.app.mouse[0] + self.app.exc_editor.y = self.app.mouse[1] + + self.app.exc_editor.select_tool('slot_add') + return + + # Propagate to tool + + # Show Shortcut list + if key == QtCore.Qt.Key_F3 or key == 'F3': + self.app.on_shortcut_list() + return + + # we do this so we can reuse the following keys while inside a Tool + # the above keys are general enough so were left outside + if self.app.exc_editor.active_tool is not None and self.select_drill_btn.isChecked() is False: + response = self.app.exc_editor.active_tool.on_key(key=key) + if response is not None: + self.app.inform.emit(response) + else: + # Add Array of Drill Hole Tool + if key == QtCore.Qt.Key_A or key == 'A': + self.app.exc_editor.launched_from_shortcuts = True + self.app.inform.emit("Click on target point.") + self.app.ui.add_drill_array_btn.setChecked(True) + + self.app.exc_editor.x = self.app.mouse[0] + self.app.exc_editor.y = self.app.mouse[1] + + self.app.exc_editor.select_tool('drill_array') + return + + # Copy + if key == QtCore.Qt.Key_C or key == 'C': + self.app.exc_editor.launched_from_shortcuts = True + if self.app.exc_editor.selected: + self.app.inform.emit(_("Click on target point.")) + self.app.ui.copy_drill_btn.setChecked(True) + self.app.exc_editor.on_tool_select('drill_copy') + 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] %s' % _("Cancelled. Nothing selected to copy.")) + return + + # Add Drill Hole Tool + if key == QtCore.Qt.Key_D or key == 'D': + self.app.exc_editor.launched_from_shortcuts = True + self.app.inform.emit(_("Click on target point.")) + self.app.ui.add_drill_btn.setChecked(True) + + self.app.exc_editor.x = self.app.mouse[0] + self.app.exc_editor.y = self.app.mouse[1] + + self.app.exc_editor.select_tool('drill_add') + return + + # Jump to coords + if key == QtCore.Qt.Key_J or key == 'J': + self.app.on_jump_to() + + # Move + if key == QtCore.Qt.Key_M or key == 'M': + self.app.exc_editor.launched_from_shortcuts = True + if self.app.exc_editor.selected: + self.app.inform.emit(_("Click on target point.")) + self.app.ui.move_drill_btn.setChecked(True) + self.app.exc_editor.on_tool_select('drill_move') + 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] %s' % _("Cancelled. Nothing selected to move.")) + return + + # Add Array of Slots Hole Tool + if key == QtCore.Qt.Key_Q or key == 'Q': + self.app.exc_editor.launched_from_shortcuts = True + self.app.inform.emit("Click on target point.") + self.app.ui.add_slot_array_btn.setChecked(True) + + self.app.exc_editor.x = self.app.mouse[0] + self.app.exc_editor.y = self.app.mouse[1] + + self.app.exc_editor.select_tool('slot_array') + return + + # Resize Tool + if key == QtCore.Qt.Key_R or key == 'R': + self.app.exc_editor.launched_from_shortcuts = True + self.app.exc_editor.select_tool('drill_resize') + return + + # Add Tool + if key == QtCore.Qt.Key_T or key == 'T': + self.app.exc_editor.launched_from_shortcuts = True + # ## Current application units in Upper Case + self.units = self.general_defaults_form.general_app_group.units_radio.get_value().upper() + tool_add_popup = FCInputDialog(title=_("New Tool ..."), + text='%s:' % _('Enter a Tool Diameter'), + min=0.0000, max=99.9999, decimals=4) + tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png')) + + val, ok = tool_add_popup.get_value() + if ok: + self.app.exc_editor.on_tool_add(tooldia=val) + formated_val = '%.*f' % (self.decimals, float(val)) + self.app.inform.emit( + '[success] %s: %s %s' % (_("Added new tool with dia"), formated_val, str(self.units)) + ) + else: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding Tool cancelled ...")) + return + elif self.app.call_source == 'measurement': + if modifiers == QtCore.Qt.ControlModifier: + pass + elif modifiers == QtCore.Qt.AltModifier: + pass + elif modifiers == QtCore.Qt.ShiftModifier: + pass + # NO MODIFIER + elif modifiers == QtCore.Qt.NoModifier: + if key == QtCore.Qt.Key_Escape or key == 'Escape': + # abort the measurement action + self.app.distance_tool.deactivate_measure_tool() + self.app.inform.emit(_("Distance Tool exit...")) + return + + if key == QtCore.Qt.Key_G or key == 'G': + 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() + elif self.app.call_source == 'qrcode_tool': + # CTRL + ALT + if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier: + if key == QtCore.Qt.Key_X: + self.app.abort_all_tasks() + return + + elif modifiers == QtCore.Qt.ControlModifier: + pass + elif modifiers == QtCore.Qt.ShiftModifier: + pass + elif modifiers == QtCore.Qt.AltModifier: + pass + # NO MODIFIER + elif modifiers == QtCore.Qt.NoModifier: + # Escape = Deselect All + if key == QtCore.Qt.Key_Escape or key == 'Escape': + self.app.qrcode_tool.on_exit() + + # Grid toggle + if key == QtCore.Qt.Key_G: + self.app.ui.grid_snap_btn.trigger() + + # Jump to coords + if key == QtCore.Qt.Key_J: + self.app.on_jump_to() + elif self.app.call_source == 'copper_thieving_tool': + # CTRL + ALT + if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier: + if key == QtCore.Qt.Key_X: + self.app.abort_all_tasks() + return + elif modifiers == QtCore.Qt.ControlModifier: + pass + elif modifiers == QtCore.Qt.ShiftModifier: + pass + elif modifiers == QtCore.Qt.AltModifier: + pass + # NO MODIFIER + elif modifiers == QtCore.Qt.NoModifier: + # Escape = Deselect All + if key == QtCore.Qt.Key_Escape or key == 'Escape': + self.app.copperfill_tool.on_exit() + + # Grid toggle + if key == QtCore.Qt.Key_G: + self.app.ui.grid_snap_btn.trigger() + + # Jump to coords + if key == QtCore.Qt.Key_J: + self.app.on_jump_to() + elif self.app.call_source == 'geometry': + if modifiers == QtCore.Qt.ControlModifier: + pass + elif modifiers == QtCore.Qt.AltModifier: + pass + elif modifiers == QtCore.Qt.ShiftModifier: + pass + # NO MODIFIER + elif modifiers == QtCore.Qt.NoModifier: + if key == QtCore.Qt.Key_Escape or key == 'Escape': + sel_obj = self.app.collection.get_active() + assert sel_obj.kind == 'geometry' or sel_obj.kind == 'excellon', \ + "Expected a Geometry or Excellon Object, got %s" % type(sel_obj) + + sel_obj.area_disconnect() + return + + if key == QtCore.Qt.Key_G or key == 'G': + 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() + + def createPopupMenu(self): + menu = super().createPopupMenu() + + menu.addSeparator() + menu.addAction(self.lock_action) + return menu + + def lock_toolbar(self, lock=False): + """ + Used to (un)lock the toolbars of the app. + + :param lock: boolean, will lock all toolbars in place when set True + :return: None + """ + + if lock: + for widget in self.children(): + if isinstance(widget, QtWidgets.QToolBar): + widget.setMovable(False) + else: + for widget in self.children(): + if isinstance(widget, QtWidgets.QToolBar): + widget.setMovable(True) + + def dragEnterEvent(self, event): + if event.mimeData().hasUrls: + event.accept() + else: + event.ignore() + + def dragMoveEvent(self, event): + if event.mimeData().hasUrls: + event.accept() + else: + event.ignore() + + def dropEvent(self, event): + if event.mimeData().hasUrls: + event.setDropAction(QtCore.Qt.CopyAction) + event.accept() + for url in event.mimeData().urls(): + self.filename = str(url.toLocalFile()) + + if self.filename == "": + self.app.inform.emit("Cancelled.") + else: + 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 extension in self.app.exc_list: + self.app.worker_task.emit({'fcn': self.app.open_excellon, + 'params': [self.filename]}) + else: + event.ignore() + + if extension in self.app.gcode_list: + self.app.worker_task.emit({'fcn': self.app.open_gcode, + 'params': [self.filename]}) + else: + event.ignore() + + 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 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 extension in self.app.pdf_list: + self.app.pdf_tool.periodic_check(1000) + self.app.worker_task.emit({'fcn': self.app.pdf_tool.open_pdf, + 'params': [self.filename]}) + + if extension in self.app.prj_list: + # self.app.open_project() is not Thread Safe + self.app.open_project(self.filename) + + if extension in self.app.conf_list: + self.app.open_config_file(self.filename) + else: + event.ignore() + else: + event.ignore() + + def closeEvent(self, event): + if self.app.save_in_progress: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Application is saving the project. Please wait ...")) + else: + 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]) + + self.final_save.emit() + event.ignore() + + def on_fullscreen(self, disable=False): + """ + + :param disable: + :return: + """ + flags = self.windowFlags() + if self.toggle_fscreen is False and disable is False: + # self.ui.showFullScreen() + self.setWindowFlags(flags | Qt.FramelessWindowHint) + a = self.geometry() + self.x_pos = a.x() + self.y_pos = a.y() + self.width = a.width() + self.height = a.height() + + # set new geometry to full desktop rect + # Subtracting and adding the pixels below it's hack to bypass a bug in Qt5 and OpenGL that made that a + # window drawn with OpenGL in fullscreen will not show any other windows on top which means that menus and + # everything else will not work without this hack. This happen in Windows. + # https://bugreports.qt.io/browse/QTBUG-41309 + desktop = QtWidgets.QApplication.desktop() + screen = desktop.screenNumber(QtGui.QCursor.pos()) + + rec = desktop.screenGeometry(screen) + x = rec.x() - 1 + y = rec.y() - 1 + h = rec.height() + 2 + w = rec.width() + 2 + self.setGeometry(x, y, w, h) + self.show() + + # hide all Toolbars + for tb in self.findChildren(QtWidgets.QToolBar): + tb.setVisible(False) + self.status_toolbar.setVisible(True) # This Toolbar is always visible so restore it + + self.splitter.setSizes([0, 1]) + self.toggle_fscreen = True + elif self.toggle_fscreen is True or disable is True: + self.setWindowFlags(flags & ~Qt.FramelessWindowHint) + self.setGeometry(self.x_pos, self.y_pos, self.width, self.height) + self.showNormal() + self.restore_toolbar_view() + self.toggle_fscreen = False + + def on_toggle_plotarea(self): + """ + + :return: + """ + try: + name = self.plot_tab_area.widget(0).objectName() + except AttributeError: + self.plot_tab_area.addTab(self.plot_tab, "Plot Area") + # remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON + self.plot_tab_area.protectTab(0) + return + + if name != 'plotarea_tab': + self.plot_tab_area.insertTab(0, self.plot_tab, "Plot Area") + # remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON + self.plot_tab_area.protectTab(0) + else: + self.plot_tab_area.closeTab(0) + + def on_toggle_notebook(self): + """ + + :return: + """ + if self.splitter.sizes()[0] == 0: + self.splitter.setSizes([1, 1]) + self.menu_toggle_nb.setChecked(True) + else: + self.splitter.setSizes([0, 1]) + self.menu_toggle_nb.setChecked(False) + + def on_toggle_grid(self): + """ + + :return: + """ + self.grid_snap_btn.trigger() + + def toggle_shell_ui(self): + """ + Toggle shell dock: if is visible close it, if it is closed then open it + + :return: None + """ + + if self.shell_dock.isVisible(): + self.shell_dock.hide() + self.app.plotcanvas.native.setFocus() + self.shell_status_label.setStyleSheet("") + self.app.inform[str, bool].emit(_("Shell disabled."), False) + else: + self.shell_dock.show() + self.shell_status_label.setStyleSheet(""" + QLabel + { + color: black; + background-color: lightcoral; + } + """) + self.app.inform[str, bool].emit(_("Shell enabled."), False) + + # I want to take the focus and give it to the Tcl Shell when the Tcl Shell is run + # self.shell._edit.setFocus() + QtCore.QTimer.singleShot(0, lambda: self.shell_dock.widget()._edit.setFocus()) + + # HACK - simulate a mouse click - alternative + # no_km = QtCore.Qt.KeyboardModifier(QtCore.Qt.NoModifier) # no KB modifier + # pos = QtCore.QPoint((self.shell._edit.width() - 40), (self.shell._edit.height() - 2)) + # e = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, + # no_km) + # QtWidgets.qApp.sendEvent(self.shell._edit, e) + # f = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonRelease, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, + # no_km) + # QtWidgets.qApp.sendEvent(self.shell._edit, f) + + +class ShortcutsTab(QtWidgets.QWidget): + + def __init__(self): + super(ShortcutsTab, self).__init__() + self.sh_tab_layout = QtWidgets.QVBoxLayout() self.sh_tab_layout.setContentsMargins(2, 2, 2, 2) - self.shortcuts_tab.setLayout(self.sh_tab_layout) + self.setLayout(self.sh_tab_layout) self.sh_hlay = QtWidgets.QHBoxLayout() - self.sh_title = QtWidgets.QTextEdit( - _('Shortcut Key List')) + + self.sh_title = QtWidgets.QTextEdit(_('Shortcut Key List')) self.sh_title.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) self.sh_title.setFrameStyle(QtWidgets.QFrame.NoFrame) self.sh_title.setMaximumHeight(30) + font = self.sh_title.font() font.setPointSize(12) self.sh_title.setFont(font) @@ -1372,7 +3706,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.sh_tab_layout.addLayout(self.sh_hlay) self.app_sh_msg = ( - '''General Shortcut list
+ '''%s
@@ -1591,6 +3925,16 @@ class FlatCAMGUI(QtWidgets.QMainWindow): + + + + + + + + + + @@ -1611,6 +3955,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow): + + + + @@ -1636,7 +3984,19 @@ class FlatCAMGUI(QtWidgets.QMainWindow): - + + + + + + + + + + + + + @@ -1715,50 +4075,53 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
Alt+E  %s
Alt+F %s
Alt+G %s
Alt+H  %sAlt+L  %s
Alt+M %s
Alt+N  %s %s
Alt+UAlt+T %s
Alt+W %s
Alt+X %s
Alt+Z  %s
''' % - ( - _("SHOW SHORTCUT LIST"), _("Switch to Project Tab"), _("Switch to Selected Tab"), - _("Switch to Tool Tab"), - _("New Gerber"), _("Edit Object (if selected)"), _("Grid On/Off"), _("Jump to Coordinates"), - _("New Excellon"), _("Move Obj"), _("New Geometry"), _("Set Origin"), _("Change Units"), - _("Open Properties Tool"), _("Rotate by 90 degree CW"), _("Shell Toggle"), - _("Add a Tool (when in Geometry Selected Tab or in Tools NCC or Tools Paint)"), _("Zoom Fit"), - _("Flip on X_axis"), _("Flip on Y_axis"), _("Zoom Out"), _("Zoom In"), + ( + _("General Shortcut list"), + _("SHOW SHORTCUT LIST"), _("Switch to Project Tab"), _("Switch to Selected Tab"), + _("Switch to Tool Tab"), + _("New Gerber"), _("Edit Object (if selected)"), _("Grid On/Off"), _("Jump to Coordinates"), + _("New Excellon"), _("Move Obj"), _("New Geometry"), _("Set Origin"), _("Change Units"), + _("Open Properties Tool"), _("Rotate by 90 degree CW"), _("Shell Toggle"), + _("Add a Tool (when in Geometry Selected Tab or in Tools NCC or Tools Paint)"), _("Zoom Fit"), + _("Flip on X_axis"), _("Flip on Y_axis"), _("Zoom Out"), _("Zoom In"), - # CTRL section - _("Select All"), _("Copy Obj"), _("Open Tools Database"), - _("Open Excellon File"), _("Open Gerber File"), _("Distance Tool"), _("New Project"), - _("Open Project"), _("Print (PDF)"), _("PDF Import Tool"), _("Save Project"), _("Toggle Plot Area"), + # CTRL section + _("Select All"), _("Copy Obj"), _("Open Tools Database"), + _("Open Excellon File"), _("Open Gerber File"), _("Distance Tool"), _("New Project"), + _("Open Project"), _("Print (PDF)"), _("PDF Import Tool"), _("Save Project"), _("Toggle Plot Area"), - # SHIFT section - _("Copy Obj_Name"), - _("Toggle Code Editor"), _("Toggle the axis"), _("Locate in Object"), _("Distance Minimum Tool"), - _("Open Preferences Window"), - _("Rotate by 90 degree CCW"), _("Run a Script"), _("Toggle the workspace"), _("Skew on X axis"), - _("Skew on Y axis"), + # SHIFT section + _("Copy Obj_Name"), + _("Toggle Code Editor"), _("Toggle the axis"), _("Locate in Object"), _("Distance Minimum Tool"), + _("Open Preferences Window"), + _("Rotate by 90 degree CCW"), _("Run a Script"), _("Toggle the workspace"), _("Skew on X axis"), + _("Skew on Y axis"), - # ALT section - _("Align Objects Tool"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"), - _("Punch Gerber Tool"), _("Extract Drills Tool"), _("Fiducials Tool"), - _("Solder Paste Dispensing Tool"), - _("Film PCB Tool"), _("Non-Copper Clearing Tool"), _("Optimal Tool"), - _("Paint Area Tool"), _("QRCode Tool"), _("Rules Check Tool"), - _("View File Source"), - _("Cutout PCB Tool"), _("Enable all Plots"), _("Disable all Plots"), _("Disable Non-selected Plots"), - _("Toggle Full Screen"), + # ALT section + _("Align Objects Tool"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Extract Drills Tool"), + _("Fiducials Tool"), _("Toggle Grid Lines"), + _("Punch Gerber Tool"), _("Isolation Tool"), _("Copper Thieving Tool"), + _("Solder Paste Dispensing Tool"), + _("Film PCB Tool"), _("Corner Markers Tool"), _("Non-Copper Clearing Tool"), _("Optimal Tool"), + _("Paint Area Tool"), _("QRCode Tool"), _("Rules Check Tool"), + _("View File Source"), _("Transformations Tool"), + _("Subtract Tool"), _("Cutout PCB Tool"), _("Panelize PCB"), + _("Enable all Plots"), _("Disable all Plots"), _("Disable Non-selected Plots"), + _("Toggle Full Screen"), - # CTRL + ALT section - _("Abort current task (gracefully)"), + # CTRL + ALT section + _("Abort current task (gracefully)"), - # CTRL + SHIFT section - _("Save Project As"), - _("Paste Special. Will convert a Windows path style to the one required in Tcl Shell"), + # CTRL + SHIFT section + _("Save Project As"), + _("Paste Special. Will convert a Windows path style to the one required in Tcl Shell"), - # F keys section - _("Open Online Manual"), - _("Open Online Tutorials"), _("Refresh Plots"), _("Delete Object"), _("Alternate: Delete Tool"), - _("(left to Key_1)Toggle Notebook Area (Left Side)"), _("En(Dis)able Obj Plot"), - _("Deselects all objects") - ) + # F keys section + _("Open Online Manual"), + _("Open Online Tutorials"), _("Refresh Plots"), _("Delete Object"), _("Alternate: Delete Tool"), + _("(left to Key_1)Toggle Notebook Area (Left Side)"), _("En(Dis)able Obj Plot"), + _("Deselects all objects") + ) ) self.sh_app = QtWidgets.QTextEdit() @@ -2152,2308 +4515,4 @@ class FlatCAMGUI(QtWidgets.QMainWindow): self.sh_editor.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) self.sh_hlay.addWidget(self.sh_editor) - # ######################################################################## - # ########################## PLOT AREA CONTEXT MENU # ################### - # ######################################################################## - self.popMenu = FCMenu() - - self.popmenu_disable = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/disable32.png'), _("Toggle Visibility")) - self.popmenu_panel_toggle = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/notebook16.png'), _("Toggle Panel")) - - self.popMenu.addSeparator() - self.cmenu_newmenu = self.popMenu.addMenu( - QtGui.QIcon(self.app.resource_location + '/file32.png'), _("New")) - self.popmenu_new_geo = self.cmenu_newmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/new_geo32_bis.png'), _("Geometry")) - self.popmenu_new_grb = self.cmenu_newmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/flatcam_icon32.png'), "Gerber") - self.popmenu_new_exc = self.cmenu_newmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/new_exc32.png'), _("Excellon")) - self.cmenu_newmenu.addSeparator() - self.popmenu_new_prj = self.cmenu_newmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/file16.png'), _("Project")) - self.popMenu.addSeparator() - - self.cmenu_gridmenu = self.popMenu.addMenu( - QtGui.QIcon(self.app.resource_location + '/grid32_menu.png'), _("Grids")) - - self.cmenu_viewmenu = self.popMenu.addMenu( - QtGui.QIcon(self.app.resource_location + '/view64.png'), _("View")) - self.zoomfit = self.cmenu_viewmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/zoom_fit32.png'), _("Zoom Fit")) - self.clearplot = self.cmenu_viewmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/clear_plot32.png'), _("Clear Plot")) - self.replot = self.cmenu_viewmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/replot32.png'), _("Replot")) - self.popMenu.addSeparator() - - self.g_editor_cmenu = self.popMenu.addMenu( - QtGui.QIcon(self.app.resource_location + '/draw32.png'), _("Geo Editor")) - self.draw_line = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/path32.png'), _("Path")) - self.draw_rect = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/rectangle32.png'), _("Rectangle")) - self.g_editor_cmenu.addSeparator() - self.draw_circle = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/circle32.png'), _("Circle")) - self.draw_poly = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _("Polygon")) - self.draw_arc = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/arc32.png'), _("Arc")) - self.g_editor_cmenu.addSeparator() - - self.draw_text = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/text32.png'), _("Text")) - self.draw_buffer = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _("Buffer")) - self.draw_paint = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/paint20_1.png'), _("Paint")) - self.draw_eraser = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _("Eraser")) - self.g_editor_cmenu.addSeparator() - - self.draw_union = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/union32.png'), _("Union")) - self.draw_intersect = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/intersection32.png'), _("Intersection")) - self.draw_substract = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/subtract32.png'), _("Subtraction")) - self.draw_cut = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/cutpath32.png'), _("Cut")) - self.draw_transform = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) - - self.g_editor_cmenu.addSeparator() - self.draw_move = self.g_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move")) - - self.grb_editor_cmenu = self.popMenu.addMenu( - QtGui.QIcon(self.app.resource_location + '/draw32.png'), _("Gerber Editor")) - self.grb_draw_pad = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/aperture32.png'), _("Pad")) - self.grb_draw_pad_array = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/padarray32.png'), _("Pad Array")) - self.grb_editor_cmenu.addSeparator() - - self.grb_draw_track = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/track32.png'), _("Track")) - self.grb_draw_region = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _("Region")) - self.grb_draw_poligonize = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/poligonize32.png'), _("Poligonize")) - self.grb_draw_semidisc = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/semidisc32.png'), _("SemiDisc")) - self.grb_draw_disc = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/disc32.png'), _("Disc")) - self.grb_editor_cmenu.addSeparator() - - self.grb_draw_buffer = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _("Buffer")) - self.grb_draw_scale = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/scale32.png'), _("Scale")) - self.grb_draw_markarea = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/markarea32.png'), _("Mark Area")) - self.grb_draw_eraser = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _("Eraser")) - self.grb_editor_cmenu.addSeparator() - - self.grb_draw_transformations = self.grb_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) - - self.e_editor_cmenu = self.popMenu.addMenu( - QtGui.QIcon(self.app.resource_location + '/drill32.png'), _("Exc Editor")) - self.drill = self.e_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/drill32.png'), _("Add Drill")) - self.drill_array = self.e_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/addarray32.png'), _("Add Drill Array")) - self.e_editor_cmenu.addSeparator() - self.slot = self.e_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/slot26.png'), _("Add Slot")) - self.slot_array = self.e_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/slot_array26.png'), _("Add Slot Array")) - self.e_editor_cmenu.addSeparator() - self.drill_resize = self.e_editor_cmenu.addAction( - QtGui.QIcon(self.app.resource_location + '/resize16.png'), _("Resize Drill")) - - self.popMenu.addSeparator() - self.popmenu_copy = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy")) - self.popmenu_delete = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/delete32.png'), _("Delete")) - self.popmenu_edit = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/edit32.png'), _("Edit")) - self.popmenu_save = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/floppy32.png'), _("Close Editor")) - self.popmenu_save.setVisible(False) - self.popMenu.addSeparator() - - self.popmenu_move = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move")) - self.popmenu_properties = self.popMenu.addAction( - QtGui.QIcon(self.app.resource_location + '/properties32.png'), _("Properties")) - - # ######################################################################## - # ########################## INFO BAR # ################################## - # ######################################################################## - self.infobar = self.statusBar() - self.fcinfo = FlatCAMInfoBar(app=self.app) - self.infobar.addWidget(self.fcinfo, stretch=1) - - self.snap_infobar_label = FCLabel() - self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_16.png')) - self.infobar.addWidget(self.snap_infobar_label) - - self.rel_position_label = QtWidgets.QLabel( - "Dx: 0.0000   Dy: 0.0000    ") - self.rel_position_label.setMinimumWidth(110) - self.rel_position_label.setToolTip(_("Relative measurement.\nReference is last click position")) - self.infobar.addWidget(self.rel_position_label) - - self.position_label = QtWidgets.QLabel( - "    X: 0.0000   Y: 0.0000") - self.position_label.setMinimumWidth(110) - self.position_label.setToolTip(_("Absolute measurement.\nReference is (X=0, Y= 0) position")) - self.infobar.addWidget(self.position_label) - - self.units_label = QtWidgets.QLabel("[in]") - self.units_label.setMargin(2) - self.infobar.addWidget(self.units_label) - - # disabled - # self.progress_bar = QtWidgets.QProgressBar() - # self.progress_bar.setMinimum(0) - # self.progress_bar.setMaximum(100) - # infobar.addWidget(self.progress_bar) - - # ######################################################################## - # ########################## SET GUI Elements # ########################## - # ######################################################################## - self.app_icon = QtGui.QIcon() - self.app_icon.addFile(self.app.resource_location + '/flatcam_icon16.png', QtCore.QSize(16, 16)) - self.app_icon.addFile(self.app.resource_location + '/flatcam_icon24.png', QtCore.QSize(24, 24)) - self.app_icon.addFile(self.app.resource_location + '/flatcam_icon32.png', QtCore.QSize(32, 32)) - self.app_icon.addFile(self.app.resource_location + '/flatcam_icon48.png', QtCore.QSize(48, 48)) - self.app_icon.addFile(self.app.resource_location + '/flatcam_icon128.png', QtCore.QSize(128, 128)) - self.app_icon.addFile(self.app.resource_location + '/flatcam_icon256.png', QtCore.QSize(256, 256)) - self.setWindowIcon(self.app_icon) - - self.setGeometry(100, 100, 1024, 650) - self.setWindowTitle('FlatCAM %s %s - %s' % - (self.app.version, - ('BETA' if self.app.beta else ''), - platform.architecture()[0]) - ) - - self.filename = "" - self.units = "" - self.setAcceptDrops(True) - - # ######################################################################## - # ########################## Build GUI # ################################# - # ######################################################################## - self.grid_snap_btn.setCheckable(True) - self.corner_snap_btn.setCheckable(True) - self.update_obj_btn.setEnabled(False) - # start with GRID activated - self.grid_snap_btn.trigger() - - self.g_editor_cmenu.menuAction().setVisible(False) - self.grb_editor_cmenu.menuAction().setVisible(False) - self.e_editor_cmenu.menuAction().setVisible(False) - - # ######################################################################## - # ######################## BUILD PREFERENCES ############################# - # ######################################################################## - self.general_defaults_form = GeneralPreferencesUI(decimals=self.decimals) - self.gerber_defaults_form = GerberPreferencesUI(decimals=self.decimals) - self.excellon_defaults_form = ExcellonPreferencesUI(decimals=self.decimals) - self.geometry_defaults_form = GeometryPreferencesUI(decimals=self.decimals) - self.cncjob_defaults_form = CNCJobPreferencesUI(decimals=self.decimals) - self.tools_defaults_form = ToolsPreferencesUI(decimals=self.decimals) - self.tools2_defaults_form = Tools2PreferencesUI(decimals=self.decimals) - self.util_defaults_form = UtilPreferencesUI(decimals=self.decimals) - - QtWidgets.qApp.installEventFilter(self) - - # ######################################################################## - # ################## RESTORE THE TOOLBAR STATE from file ################# - # ######################################################################## - flat_settings = QSettings("Open Source", "FlatCAM") - if flat_settings.contains("saved_gui_state"): - saved_gui_state = flat_settings.value('saved_gui_state') - self.restoreState(saved_gui_state) - log.debug("FlatCAMGUI.__init__() --> UI state restored from QSettings.") - - if flat_settings.contains("layout"): - layout = flat_settings.value('layout', type=str) - self.exc_edit_toolbar.setDisabled(True) - self.geo_edit_toolbar.setDisabled(True) - self.grb_edit_toolbar.setDisabled(True) - - if layout == 'standard': - self.corner_snap_btn.setVisible(False) - self.snap_magnet.setVisible(False) - else: - 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. Layout = %s" % str(layout)) - else: - self.exc_edit_toolbar.setDisabled(True) - self.geo_edit_toolbar.setDisabled(True) - self.grb_edit_toolbar.setDisabled(True) - - self.corner_snap_btn.setVisible(False) - self.snap_magnet.setVisible(False) - - flat_settings.setValue('layout', "standard") - # This will write the setting to the platform specific storage. - del flat_settings - log.debug("FlatCAMGUI.__init__() --> UI layout restored from defaults. QSettings set to 'standard'") - - # construct the Toolbar Lock menu entry to the context menu of the QMainWindow - self.lock_action = QtWidgets.QAction() - self.lock_action.setText(_("Lock Toolbars")) - self.lock_action.setCheckable(True) - - qsettings = QSettings("Open Source", "FlatCAM") - if qsettings.contains("toolbar_lock"): - lock_val = settings.value('toolbar_lock') - if lock_val == 'true': - lock_state = True - self.lock_action.setChecked(True) - else: - - lock_state = False - self.lock_action.setChecked(False) - else: - lock_state = False - qsettings.setValue('toolbar_lock', lock_state) - - # This will write the setting to the platform specific storage. - del qsettings - - self.lock_toolbar(lock=lock_state) - self.on_grid_snap_triggered(state=True) - - self.lock_action.triggered[bool].connect(self.lock_toolbar) - - self.pref_open_button.clicked.connect(self.on_preferences_open_folder) - self.clear_btn.clicked.connect(self.on_gui_clear) - self.grid_snap_btn.triggered.connect(self.on_grid_snap_triggered) - self.snap_infobar_label.clicked.connect(self.on_grid_icon_snap_clicked) - - # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - # %%%%%%%%%%%%%%%%% GUI Building FINISHED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - def on_grid_snap_triggered(self, state): - """ - - :param state: A parameter with the state of the grid, boolean - - :return: - """ - if state: - self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_filled_16.png')) - else: - self.snap_infobar_label.setPixmap(QtGui.QPixmap(self.app.resource_location + '/snap_16.png')) - - self.snap_infobar_label.clicked_state = state - - def on_grid_icon_snap_clicked(self): - """ - Slot called by clicking a GUI element, in this case a FCLabel - - :return: - """ - if isinstance(self.sender(), FCLabel): - self.grid_snap_btn.trigger() - - def eventFilter(self, obj, event): - """ - Filter the ToolTips display based on a Preferences setting - - :param obj: - :param event: QT event to filter - :return: - """ - if self.app.defaults["global_toggle_tooltips"] is False: - if event.type() == QtCore.QEvent.ToolTip: - return True - else: - return False - - return False - - def on_preferences_open_folder(self): - """ - Will open an Explorer window set to the folder path where the FlatCAM preferences files are usually saved. - - :return: None - """ - - if sys.platform == 'win32': - subprocess.Popen('explorer %s' % self.app.data_path) - elif sys.platform == 'darwin': - os.system('open "%s"' % self.app.data_path) - else: - subprocess.Popen(['xdg-open', self.app.data_path]) - self.app.inform.emit('[success] %s' % _("FlatCAM Preferences Folder opened.")) - - def on_gui_clear(self): - theme_settings = QtCore.QSettings("Open Source", "FlatCAM") - theme_settings.setValue('theme', 'white') - - del theme_settings - - resource_loc = self.app.resource_location - - msgbox = QtWidgets.QMessageBox() - msgbox.setText(_("Are you sure you want to delete the GUI Settings? \n")) - msgbox.setWindowTitle(_("Clear GUI Settings")) - msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/trash32.png')) - bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) - bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) - - msgbox.setDefaultButton(bt_no) - msgbox.exec_() - response = msgbox.clickedButton() - - if response == bt_yes: - qsettings = QSettings("Open Source", "FlatCAM") - for key in qsettings.allKeys(): - qsettings.remove(key) - # This will write the setting to the platform specific storage. - del qsettings - - def populate_toolbars(self): - """ - Will populate the App Toolbars with their actions - - :return: None - """ - - # ######################################################################## - # ## File Toolbar # ## - # ######################################################################## - self.file_open_gerber_btn = self.toolbarfile.addAction( - QtGui.QIcon(self.app.resource_location + '/flatcam_icon32.png'), _("Open Gerber")) - self.file_open_excellon_btn = self.toolbarfile.addAction( - QtGui.QIcon(self.app.resource_location + '/drill32.png'), _("Open Excellon")) - self.toolbarfile.addSeparator() - self.file_open_btn = self.toolbarfile.addAction( - QtGui.QIcon(self.app.resource_location + '/folder32.png'), _("Open project")) - self.file_save_btn = self.toolbarfile.addAction( - QtGui.QIcon(self.app.resource_location + '/project_save32.png'), _("Save project")) - - # ######################################################################## - # ## Edit Toolbar # ## - # ######################################################################## - self.newgeo_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/new_file_geo32.png'), _("New Blank Geometry")) - self.newgrb_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/new_file_grb32.png'), _("New Blank Gerber")) - self.newexc_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/new_file_exc32.png'), _("New Blank Excellon")) - self.toolbargeo.addSeparator() - self.editgeo_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/edit32.png'), _("Editor")) - self.update_obj_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/close_edit_file32.png'), - _("Save Object and close the Editor") - ) - - self.toolbargeo.addSeparator() - self.copy_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/copy_file32.png'), _("Copy")) - self.delete_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/delete_file32.png'), _("&Delete")) - self.toolbargeo.addSeparator() - self.distance_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/distance32.png'), _("Distance Tool")) - self.distance_min_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/distance_min32.png'), _("Distance Min Tool")) - self.origin_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/origin32.png'), _('Set Origin')) - self.jmp_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location')) - self.locate_btn = self.toolbargeo.addAction( - QtGui.QIcon(self.app.resource_location + '/locate32.png'), _('Locate in Object')) - - # ######################################################################## - # ########################## View Toolbar# ############################### - # ######################################################################## - self.replot_btn = self.toolbarview.addAction( - QtGui.QIcon(self.app.resource_location + '/replot32.png'), _("&Replot")) - self.clear_plot_btn = self.toolbarview.addAction( - QtGui.QIcon(self.app.resource_location + '/clear_plot32.png'), _("&Clear plot")) - self.zoom_in_btn = self.toolbarview.addAction( - QtGui.QIcon(self.app.resource_location + '/zoom_in32.png'), _("Zoom In")) - self.zoom_out_btn = self.toolbarview.addAction( - QtGui.QIcon(self.app.resource_location + '/zoom_out32.png'), _("Zoom Out")) - self.zoom_fit_btn = self.toolbarview.addAction( - QtGui.QIcon(self.app.resource_location + '/zoom_fit32.png'), _("Zoom Fit")) - - # ######################################################################## - # ########################## Shell Toolbar# ############################## - # ######################################################################## - self.shell_btn = self.toolbarshell.addAction( - QtGui.QIcon(self.app.resource_location + '/shell32.png'), _("&Command Line")) - self.new_script_btn = self.toolbarshell.addAction( - QtGui.QIcon(self.app.resource_location + '/script_new24.png'), _('New Script ...')) - self.open_script_btn = self.toolbarshell.addAction( - QtGui.QIcon(self.app.resource_location + '/open_script32.png'), _('Open Script ...')) - self.run_script_btn = self.toolbarshell.addAction( - QtGui.QIcon(self.app.resource_location + '/script16.png'), _('Run Script ...')) - - # ######################################################################### - # ######################### Tools Toolbar ################################# - # ######################################################################### - self.dblsided_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/doubleside32.png'), _("2Sided Tool")) - self.align_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/align32.png'), _("Align Objects Tool")) - self.extract_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/extract_drill32.png'), _("Extract Drills Tool")) - - self.cutout_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/cut16_bis.png'), _("&Cutout Tool")) - self.ncc_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/ncc16.png'), _("NCC Tool")) - self.paint_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/paint20_1.png'), _("Paint Tool")) - self.toolbartools.addSeparator() - - self.panelize_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/panelize32.png'), _("Panel Tool")) - self.film_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/film16.png'), _("Film Tool")) - self.solder_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/solderpastebis32.png'), _("SolderPaste Tool")) - self.sub_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/sub32.png'), _("Subtract Tool")) - self.rules_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/rules32.png'), _("Rules Tool")) - self.optimal_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'), _("Optimal Tool")) - - self.toolbartools.addSeparator() - - self.calculators_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/calculator24.png'), _("Calculators Tool")) - self.transform_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transform Tool")) - self.qrcode_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/qrcode32.png'), _("QRCode Tool")) - self.copperfill_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/copperfill32.png'), _("Copper Thieving Tool")) - - self.fiducials_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/fiducials_32.png'), _("Fiducials Tool")) - self.cal_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/calibrate_32.png'), _("Calibration Tool")) - self.punch_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/punch32.png'), _("Punch Gerber Tool")) - self.invert_btn = self.toolbartools.addAction( - QtGui.QIcon(self.app.resource_location + '/invert32.png'), _("Invert Gerber Tool")) - - # ######################################################################## - # ## Excellon Editor Toolbar # ## - # ######################################################################## - self.select_drill_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/pointer32.png'), _("Select")) - self.add_drill_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/plus16.png'), _('Add Drill Hole')) - self.add_drill_array_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/addarray16.png'), _('Add Drill Hole Array')) - self.resize_drill_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/resize16.png'), _('Resize Drill')) - self.add_slot_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/slot26.png'), _('Add Slot')) - self.add_slot_array_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/slot_array26.png'), _('Add Slot Array')) - self.exc_edit_toolbar.addSeparator() - - self.copy_drill_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/copy32.png'), _('Copy Drill')) - self.delete_drill_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete Drill")) - - self.exc_edit_toolbar.addSeparator() - self.move_drill_btn = self.exc_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move Drill")) - - # ######################################################################## - # ## Geometry Editor Toolbar # ## - # ######################################################################## - self.geo_select_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/pointer32.png'), _("Select 'Esc'")) - self.geo_add_circle_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/circle32.png'), _('Add Circle')) - self.geo_add_arc_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/arc32.png'), _('Add Arc')) - self.geo_add_rectangle_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/rectangle32.png'), _('Add Rectangle')) - - self.geo_edit_toolbar.addSeparator() - self.geo_add_path_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/path32.png'), _('Add Path')) - self.geo_add_polygon_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _('Add Polygon')) - self.geo_edit_toolbar.addSeparator() - self.geo_add_text_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/text32.png'), _('Add Text')) - self.geo_add_buffer_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _('Add Buffer')) - self.geo_add_paint_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/paint20_1.png'), _('Paint Shape')) - self.geo_eraser_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _('Eraser')) - - self.geo_edit_toolbar.addSeparator() - self.geo_union_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/union32.png'), _('Polygon Union')) - self.geo_explode_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/explode32.png'), _('Polygon Explode')) - - self.geo_intersection_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/intersection32.png'), _('Polygon Intersection')) - self.geo_subtract_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/subtract32.png'), _('Polygon Subtraction')) - - self.geo_edit_toolbar.addSeparator() - self.geo_cutpath_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/cutpath32.png'), _('Cut Path')) - self.geo_copy_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy Objects")) - self.geo_delete_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete Shape")) - self.geo_transform_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) - - self.geo_edit_toolbar.addSeparator() - self.geo_move_btn = self.geo_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move Objects")) - - # ######################################################################## - # ## Gerber Editor Toolbar # ## - # ######################################################################## - self.grb_select_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/pointer32.png'), _("Select")) - self.grb_add_pad_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/aperture32.png'), _("Add Pad")) - self.add_pad_ar_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/padarray32.png'), _('Add Pad Array')) - self.grb_add_track_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/track32.png'), _("Add Track")) - self.grb_add_region_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/polygon32.png'), _("Add Region")) - self.grb_convert_poly_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/poligonize32.png'), _("Poligonize")) - - self.grb_add_semidisc_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/semidisc32.png'), _("SemiDisc")) - self.grb_add_disc_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/disc32.png'), _("Disc")) - self.grb_edit_toolbar.addSeparator() - - self.aperture_buffer_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/buffer16-2.png'), _('Buffer')) - self.aperture_scale_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/scale32.png'), _('Scale')) - self.aperture_markarea_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/markarea32.png'), _('Mark Area')) - self.aperture_eraser_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/eraser26.png'), _('Eraser')) - - self.grb_edit_toolbar.addSeparator() - self.aperture_copy_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/copy32.png'), _("Copy")) - self.aperture_delete_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/trash32.png'), _("Delete")) - self.grb_transform_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/transform.png'), _("Transformations")) - self.grb_edit_toolbar.addSeparator() - self.aperture_move_btn = self.grb_edit_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/move32.png'), _("Move")) - - # ######################################################################## - # ## Snap Toolbar # ## - # ######################################################################## - - # Snap GRID toolbar is always active to facilitate usage of measurements done on GRID - # self.addToolBar(self.snap_toolbar) - self.grid_snap_btn = self.snap_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/grid32.png'), _('Snap to grid')) - self.grid_gap_x_entry = FCEntry2() - self.grid_gap_x_entry.setMaximumWidth(70) - self.grid_gap_x_entry.setToolTip(_("Grid X snapping distance")) - self.snap_toolbar.addWidget(self.grid_gap_x_entry) - - self.grid_gap_y_entry = FCEntry2() - self.grid_gap_y_entry.setMaximumWidth(70) - self.grid_gap_y_entry.setToolTip(_("Grid Y snapping distance")) - self.snap_toolbar.addWidget(self.grid_gap_y_entry) - - self.grid_space_label = QtWidgets.QLabel(" ") - self.snap_toolbar.addWidget(self.grid_space_label) - self.grid_gap_link_cb = FCCheckBox() - self.grid_gap_link_cb.setToolTip(_("When active, value on Grid_X\n" - "is copied to the Grid_Y value.")) - self.snap_toolbar.addWidget(self.grid_gap_link_cb) - - self.ois_grid = OptionalInputSection(self.grid_gap_link_cb, [self.grid_gap_y_entry], logic=False) - - self.corner_snap_btn = self.snap_toolbar.addAction( - QtGui.QIcon(self.app.resource_location + '/corner32.png'), _('Snap to corner')) - - self.snap_max_dist_entry = FCEntry() - self.snap_max_dist_entry.setMaximumWidth(70) - self.snap_max_dist_entry.setToolTip(_("Max. magnet distance")) - self.snap_magnet = self.snap_toolbar.addWidget(self.snap_max_dist_entry) - - self.grid_snap_btn.setCheckable(True) - self.corner_snap_btn.setCheckable(True) - self.update_obj_btn.setEnabled(False) - # start with GRID activated - self.grid_snap_btn.trigger() - - qsettings = QSettings("Open Source", "FlatCAM") - if qsettings.contains("layout"): - layout = qsettings.value('layout', type=str) - - if layout == 'standard': - self.corner_snap_btn.setVisible(False) - self.snap_magnet.setVisible(False) - else: - self.corner_snap_btn.setVisible(True) - self.snap_magnet.setVisible(True) - self.corner_snap_btn.setDisabled(True) - self.snap_magnet.setDisabled(True) - - # on 'minimal' layout only some toolbars are active - if layout != 'minimal': - 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(True) - self.grb_edit_toolbar.setDisabled(True) - - def keyPressEvent(self, event): - """ - Key event handler for the entire app. - Some of the key events are also treated locally in the FlatCAM editors - - :param event: QT event - :return: - """ - modifiers = QtWidgets.QApplication.keyboardModifiers() - active = self.app.collection.get_active() - selected = self.app.collection.get_selected() - names_list = self.app.collection.get_names() - - matplotlib_key_flag = False - - # events out of the self.app.collection view (it's about Project Tab) are of type int - if type(event) is int: - key = event - # events from the GUI are of type QKeyEvent - elif type(event) == QtGui.QKeyEvent: - key = event.key() - elif isinstance(event, mpl_key_event): # MatPlotLib key events are trickier to interpret than the rest - matplotlib_key_flag = True - - key = event.key - key = QtGui.QKeySequence(key) - - # check for modifiers - key_string = key.toString().lower() - if '+' in key_string: - mod, __, key_text = key_string.rpartition('+') - if mod.lower() == 'ctrl': - modifiers = QtCore.Qt.ControlModifier - elif mod.lower() == 'alt': - modifiers = QtCore.Qt.AltModifier - elif mod.lower() == 'shift': - modifiers = QtCore.Qt.ShiftModifier - else: - modifiers = QtCore.Qt.NoModifier - key = QtGui.QKeySequence(key_text) - - # events from Vispy are of type KeyEvent - else: - key = event.key - - if self.app.call_source == 'app': - # CTRL + ALT - if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier: - if key == QtCore.Qt.Key_X: - self.app.abort_all_tasks() - return - # CTRL + SHIFT - if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier: - if key == QtCore.Qt.Key_S: - self.app.on_file_saveprojectas() - return - # CTRL - elif modifiers == QtCore.Qt.ControlModifier: - # Select All - if key == QtCore.Qt.Key_A: - self.app.on_selectall() - - # Copy an FlatCAM object - if key == QtCore.Qt.Key_C: - widget_name = self.plot_tab_area.currentWidget().objectName() - if widget_name == 'database_tab': - # Tools DB saved, update flag - self.app.tools_db_changed_flag = True - self.app.tools_db_tab.on_tool_copy() - return - - self.app.on_copy_command() - - # Copy an FlatCAM object - if key == QtCore.Qt.Key_D: - self.app.on_tools_database() - - # Open Excellon file - if key == QtCore.Qt.Key_E: - self.app.on_fileopenexcellon(signal=None) - - # Open Gerber file - if key == QtCore.Qt.Key_G: - widget_name = self.plot_tab_area.currentWidget().objectName() - if 'editor' in widget_name.lower(): - self.app.goto_text_line() - else: - self.app.on_fileopengerber(signal=None) - - # Distance Tool - if key == QtCore.Qt.Key_M: - self.app.distance_tool.run() - - # Create New Project - if key == QtCore.Qt.Key_N: - self.app.on_file_new_click() - - # Open Project - if key == QtCore.Qt.Key_O: - self.app.on_file_openproject() - - # Open Project - if key == QtCore.Qt.Key_P: - self.app.on_file_save_objects_pdf(use_thread=True) - - # PDF Import - if key == QtCore.Qt.Key_Q: - self.app.pdf_tool.run() - - # Save Project - if key == QtCore.Qt.Key_S: - widget_name = self.plot_tab_area.currentWidget().objectName() - if widget_name == 'preferences_tab': - self.app.preferencesUiManager.on_save_button(save_to_file=False) - return - - if widget_name == 'database_tab': - # Tools DB saved, update flag - self.app.tools_db_changed_flag = False - self.app.tools_db_tab.on_save_tools_db() - return - - self.app.on_file_saveproject() - - # Toggle Plot Area - if key == QtCore.Qt.Key_F10 or key == 'F10': - self.app.on_toggle_plotarea() - - return - # SHIFT - elif modifiers == QtCore.Qt.ShiftModifier: - - # Copy Object Name - if key == QtCore.Qt.Key_C: - self.app.on_copy_name() - - # Toggle Code Editor - if key == QtCore.Qt.Key_E: - self.app.on_toggle_code_editor() - - # Toggle axis - if key == QtCore.Qt.Key_G: - self.app.on_toggle_axis() - - # Locate in Object - if key == QtCore.Qt.Key_J: - self.app.on_locate(obj=self.app.collection.get_active()) - - # Run Distance Minimum Tool - if key == QtCore.Qt.Key_M: - self.app.distance_min_tool.run() - return - - # Open Preferences Window - if key == QtCore.Qt.Key_P: - self.app.on_preferences() - return - - # Rotate Object by 90 degree CCW - if key == QtCore.Qt.Key_R: - self.app.on_rotate(silent=True, preset=-float(self.app.defaults['tools_transform_rotate'])) - return - - # Run a Script - if key == QtCore.Qt.Key_S: - self.app.on_filerunscript() - return - - # Toggle Workspace - if key == QtCore.Qt.Key_W: - self.app.on_workspace_toggle() - return - - # Skew on X axis - if key == QtCore.Qt.Key_X: - self.app.on_skewx() - return - - # Skew on Y axis - if key == QtCore.Qt.Key_Y: - self.app.on_skewy() - return - # ALT - elif modifiers == QtCore.Qt.AltModifier: - # Eanble all plots - if key == Qt.Key_1: - self.app.enable_all_plots() - - # Disable all plots - if key == Qt.Key_2: - self.app.disable_all_plots() - - # Disable all other plots - if key == Qt.Key_3: - self.app.disable_other_plots() - - # Align in Object Tool - if key == QtCore.Qt.Key_A: - self.app.align_objects_tool.run(toggle=True) - - # Calculator Tool - if key == QtCore.Qt.Key_C: - self.app.calculator_tool.run(toggle=True) - - # 2-Sided PCB Tool - if key == QtCore.Qt.Key_D: - self.app.dblsidedtool.run(toggle=True) - return - - # Calibration Tool - if key == QtCore.Qt.Key_E: - self.app.cal_exc_tool.run(toggle=True) - return - - # Copper Thieving Tool - if key == QtCore.Qt.Key_F: - self.app.copper_thieving_tool.run(toggle=True) - return - - # Toggle Grid lines - if key == QtCore.Qt.Key_G: - self.app.on_toggle_grid_lines() - return - - # Align in Object Tool - if key == QtCore.Qt.Key_H: - self.app.punch_tool.run(toggle=True) - - # Extract Drills Tool - if key == QtCore.Qt.Key_I: - self.app.edrills_tool.run(toggle=True) - - # Fiducials Tool - if key == QtCore.Qt.Key_J: - self.app.fiducial_tool.run(toggle=True) - return - - # Solder Paste Dispensing Tool - if key == QtCore.Qt.Key_K: - self.app.paste_tool.run(toggle=True) - return - - # Film Tool - if key == QtCore.Qt.Key_L: - self.app.film_tool.run(toggle=True) - return - - # Non-Copper Clear Tool - if key == QtCore.Qt.Key_N: - self.app.ncclear_tool.run(toggle=True) - return - - # Optimal Tool - if key == QtCore.Qt.Key_O: - self.app.optimal_tool.run(toggle=True) - return - - # Paint Tool - if key == QtCore.Qt.Key_P: - self.app.paint_tool.run(toggle=True) - return - - # QRCode Tool - if key == QtCore.Qt.Key_Q: - self.app.qrcode_tool.run() - return - - # Rules Tool - if key == QtCore.Qt.Key_R: - self.app.rules_tool.run(toggle=True) - return - - # View Source Object Content - if key == QtCore.Qt.Key_S: - self.app.on_view_source() - return - - # Transformation Tool - if key == QtCore.Qt.Key_T: - self.app.transform_tool.run(toggle=True) - return - - # Substract Tool - if key == QtCore.Qt.Key_W: - self.app.sub_tool.run(toggle=True) - return - - # Cutout Tool - if key == QtCore.Qt.Key_X: - self.app.cutout_tool.run(toggle=True) - return - - # Panelize Tool - if key == QtCore.Qt.Key_Z: - self.app.panelize_tool.run(toggle=True) - return - - # Toggle Fullscreen - if key == QtCore.Qt.Key_F10 or key == 'F10': - self.app.on_fullscreen() - return - # NO MODIFIER - elif modifiers == QtCore.Qt.NoModifier: - # Open Manual - if key == QtCore.Qt.Key_F1 or key == 'F1': - webbrowser.open(self.app.manual_url) - - # Show shortcut list - if key == QtCore.Qt.Key_F3 or key == 'F3': - self.app.on_shortcut_list() - - # Open Video Help - if key == QtCore.Qt.Key_F4 or key == 'F4': - webbrowser.open(self.app.video_url) - - # Open Video Help - if key == QtCore.Qt.Key_F5 or key == 'F5': - self.app.plot_all() - - # Switch to Project Tab - if key == QtCore.Qt.Key_1: - self.app.on_select_tab('project') - - # Switch to Selected Tab - if key == QtCore.Qt.Key_2: - self.app.on_select_tab('selected') - - # Switch to Tool Tab - if key == QtCore.Qt.Key_3: - self.app.on_select_tab('tool') - - # Delete from PyQt - # It's meant to make a difference between delete objects and delete tools in - # Geometry Selected tool table - if key == QtCore.Qt.Key_Delete and matplotlib_key_flag is False: - widget_name = self.plot_tab_area.currentWidget().objectName() - if widget_name == 'database_tab': - # Tools DB saved, update flag - self.app.tools_db_changed_flag = True - self.app.tools_db_tab.on_tool_delete() - return - - self.app.on_delete_keypress() - - # Delete from canvas - if key == 'Delete': - # Delete via the application to - # ensure cleanup of the GUI - if active: - active.app.on_delete() - - # 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 - if self.app.toggle_fscreen is True: - self.app.on_fullscreen(disable=True) - - # try to disconnect the slot from Set Origin - try: - self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_set_zero_click) - except TypeError: - pass - self.app.inform.emit("") - - # Space = Toggle Active/Inactive - if key == QtCore.Qt.Key_Space: - for select in selected: - select.ui.plot_cb.toggle() - self.app.collection.update_view() - self.app.delete_selection_shape() - - # Select the object in the Tree above the current one - if key == QtCore.Qt.Key_Up: - # make sure it works only for the Project Tab who is an instance of KeySensitiveListView - focused_wdg = QtWidgets.QApplication.focusWidget() - if isinstance(focused_wdg, KeySensitiveListView): - self.app.collection.set_all_inactive() - if active is None: - return - active_name = active.options['name'] - active_index = names_list.index(active_name) - if active_index == 0: - self.app.collection.set_active(names_list[-1]) - else: - self.app.collection.set_active(names_list[active_index-1]) - - # Select the object in the Tree below the current one - if key == QtCore.Qt.Key_Down: - # make sure it works only for the Project Tab who is an instance of KeySensitiveListView - focused_wdg = QtWidgets.QApplication.focusWidget() - if isinstance(focused_wdg, KeySensitiveListView): - self.app.collection.set_all_inactive() - if active is None: - return - active_name = active.options['name'] - active_index = names_list.index(active_name) - if active_index == len(names_list) - 1: - self.app.collection.set_active(names_list[0]) - else: - self.app.collection.set_active(names_list[active_index+1]) - - # New Geometry - if key == QtCore.Qt.Key_B: - self.app.new_gerber_object() - - # New Geometry - if key == QtCore.Qt.Key_D: - self.app.new_document_object() - - # Copy Object Name - if key == QtCore.Qt.Key_E: - self.app.object2editor() - - # Grid toggle - if key == QtCore.Qt.Key_G: - self.app.ui.grid_snap_btn.trigger() - - # Jump to coords - if key == QtCore.Qt.Key_J: - self.app.on_jump_to() - - # New Excellon - if key == QtCore.Qt.Key_L: - self.app.new_excellon_object() - - # Move tool toggle - if key == QtCore.Qt.Key_M: - self.app.move_tool.toggle() - - # New Geometry - if key == QtCore.Qt.Key_N: - self.app.new_geometry_object() - - # Set Origin - if key == QtCore.Qt.Key_O: - self.app.on_set_origin() - return - - # Properties Tool - if key == QtCore.Qt.Key_P: - self.app.properties_tool.run() - return - - # Change Units - if key == QtCore.Qt.Key_Q: - # if self.app.defaults["units"] == 'MM': - # self.app.ui.general_defaults_form.general_app_group.units_radio.set_value("IN") - # else: - # self.app.ui.general_defaults_form.general_app_group.units_radio.set_value("MM") - # self.app.on_toggle_units(no_pref=True) - self.app.on_toggle_units_click() - - # Rotate Object by 90 degree CW - if key == QtCore.Qt.Key_R: - self.app.on_rotate(silent=True, preset=self.app.defaults['tools_transform_rotate']) - - # Shell toggle - if key == QtCore.Qt.Key_S: - self.app.toggle_shell() - - # Add a Tool from shortcut - if key == QtCore.Qt.Key_T: - widget_name = self.plot_tab_area.currentWidget().objectName() - if widget_name == 'database_tab': - # Tools DB saved, update flag - self.app.tools_db_changed_flag = True - self.app.tools_db_tab.on_tool_add() - return - - self.app.on_tool_add_keypress() - - # Zoom Fit - if key == QtCore.Qt.Key_V: - self.app.on_zoom_fit(None) - - # Mirror on X the selected object(s) - if key == QtCore.Qt.Key_X: - self.app.on_flipx() - - # Mirror on Y the selected object(s) - if key == QtCore.Qt.Key_Y: - self.app.on_flipy() - - # Zoom In - if key == QtCore.Qt.Key_Equal: - self.app.plotcanvas.zoom(1 / self.app.defaults['global_zoom_ratio'], self.app.mouse) - - # Zoom Out - if key == QtCore.Qt.Key_Minus: - self.app.plotcanvas.zoom(self.app.defaults['global_zoom_ratio'], self.app.mouse) - - # toggle display of Notebook area - if key == QtCore.Qt.Key_QuoteLeft: - self.app.on_toggle_notebook() - - return - elif self.app.call_source == 'geo_editor': - # CTRL - 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.distance_tool.run() - return - - # Cut Action Tool - if key == QtCore.Qt.Key_X or key == 'X': - if self.app.geo_editor.get_selected() is not None: - self.app.geo_editor.cutpath() - else: - msg = _('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.') - - messagebox = QtWidgets.QMessageBox() - messagebox.setText(msg) - messagebox.setWindowTitle(_("Warning")) - messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) - messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) - messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) - messagebox.exec_() - return - # SHIFT - elif modifiers == QtCore.Qt.ShiftModifier: - # Run Distance Minimum Tool - if key == QtCore.Qt.Key_M or key == 'M': - self.app.distance_min_tool.run() - return - - # Skew on X axis - if key == QtCore.Qt.Key_X or key == 'X': - self.app.geo_editor.transform_tool.on_skewx_key() - return - - # Skew on Y axis - if key == QtCore.Qt.Key_Y or key == 'Y': - self.app.geo_editor.transform_tool.on_skewy_key() - return - # ALT - elif modifiers == QtCore.Qt.AltModifier: - - # Transformation Tool - if key == QtCore.Qt.Key_R or key == 'R': - self.app.geo_editor.select_tool('transform') - return - - # Offset on X axis - if key == QtCore.Qt.Key_X or key == 'X': - self.app.geo_editor.transform_tool.on_offx_key() - return - - # Offset on Y axis - if key == QtCore.Qt.Key_Y or key == 'Y': - self.app.geo_editor.transform_tool.on_offy_key() - return - # NO MODIFIER - elif modifiers == QtCore.Qt.NoModifier: - # toggle display of Notebook area - if key == QtCore.Qt.Key_QuoteLeft or key == '`': - self.app.on_toggle_notebook() - - # Finish the current action. Use with tools that do not - # complete automatically, like a polygon or path. - if key == QtCore.Qt.Key_Enter or key == 'Enter': - if isinstance(self.app.geo_editor.active_tool, FCShapeTool): - if self.app.geo_editor.active_tool.name == 'rotate': - self.app.geo_editor.active_tool.make() - - if self.app.geo_editor.active_tool.complete: - self.app.geo_editor.on_shape_complete() - self.app.inform.emit('[success] %s' % _("Done.")) - # automatically make the selection tool active after completing current action - self.app.geo_editor.select_tool('select') - return - else: - self.app.geo_editor.active_tool.click( - self.app.geo_editor.snap(self.app.geo_editor.x, self.app.geo_editor.y)) - - self.app.geo_editor.active_tool.make() - - if self.app.geo_editor.active_tool.complete: - self.app.geo_editor.on_shape_complete() - self.app.inform.emit('[success] %s' % _("Done.")) - # automatically make the selection tool active after completing current action - self.app.geo_editor.select_tool('select') - - # Abort the current action - if key == QtCore.Qt.Key_Escape or key == 'Escape': - # self.on_tool_select("select") - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled.")) - - self.app.geo_editor.delete_utility_geometry() - - self.app.geo_editor.active_tool.clean_up() - - self.app.geo_editor.select_tool('select') - - # hide the notebook - self.app.ui.splitter.setSizes([0, 1]) - return - - # Delete selected object - if key == QtCore.Qt.Key_Delete or key == 'Delete': - self.app.geo_editor.delete_selected() - self.app.geo_editor.replot() - - # Rotate - if key == QtCore.Qt.Key_Space or key == 'Space': - self.app.geo_editor.transform_tool.on_rotate_key() - - # Zoom Out - if key == QtCore.Qt.Key_Minus or key == '-': - self.app.plotcanvas.zoom(1 / self.app.defaults['global_zoom_ratio'], - [self.app.geo_editor.snap_x, self.app.geo_editor.snap_y]) - - # Zoom In - if key == QtCore.Qt.Key_Equal or key == '=': - self.app.plotcanvas.zoom(self.app.defaults['global_zoom_ratio'], - [self.app.geo_editor.snap_x, self.app.geo_editor.snap_y]) - - # Switch to Project Tab - if key == QtCore.Qt.Key_1 or key == '1': - self.app.on_select_tab('project') - - # Switch to Selected Tab - if key == QtCore.Qt.Key_2 or key == '2': - self.app.on_select_tab('selected') - - # Switch to Tool Tab - if key == QtCore.Qt.Key_3 or key == '3': - self.app.on_select_tab('tool') - - # Grid Snap - if key == QtCore.Qt.Key_G or key == 'G': - self.app.ui.grid_snap_btn.trigger() - - # make sure that the cursor shape is enabled/disabled, too - if self.app.geo_editor.options['grid_snap'] is True: - self.app.app_cursor.enabled = True - else: - self.app.app_cursor.enabled = False - - # Corner Snap - if key == QtCore.Qt.Key_K or key == 'K': - self.app.geo_editor.on_corner_snap() - - if key == QtCore.Qt.Key_V or key == 'V': - self.app.on_zoom_fit(None) - - # we do this so we can reuse the following keys while inside a Tool - # the above keys are general enough so were left outside - if self.app.geo_editor.active_tool is not None and self.geo_select_btn.isChecked() is False: - response = self.app.geo_editor.active_tool.on_key(key=key) - if response is not None: - self.app.inform.emit(response) - else: - # Arc Tool - if key == QtCore.Qt.Key_A or key == 'A': - self.app.geo_editor.select_tool('arc') - - # Buffer - if key == QtCore.Qt.Key_B or key == 'B': - self.app.geo_editor.select_tool('buffer') - - # Copy - if key == QtCore.Qt.Key_C or key == 'C': - self.app.geo_editor.on_copy_click() - - # Substract Tool - if key == QtCore.Qt.Key_E or key == 'E': - if self.app.geo_editor.get_selected() is not None: - self.app.geo_editor.intersection() - else: - msg = _("Please select geometry items \n" - "on which to perform Intersection Tool.") - - messagebox = QtWidgets.QMessageBox() - messagebox.setText(msg) - messagebox.setWindowTitle(_("Warning")) - messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) - messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) - messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) - messagebox.exec_() - - # Paint - if key == QtCore.Qt.Key_I or key == 'I': - self.app.geo_editor.select_tool('paint') - - # Jump to coords - if key == QtCore.Qt.Key_J or key == 'J': - self.app.on_jump_to() - - # Move - if key == QtCore.Qt.Key_M or key == 'M': - self.app.geo_editor.on_move_click() - - # Polygon Tool - if key == QtCore.Qt.Key_N or key == 'N': - self.app.geo_editor.select_tool('polygon') - - # Circle Tool - if key == QtCore.Qt.Key_O or key == 'O': - self.app.geo_editor.select_tool('circle') - - # Path Tool - if key == QtCore.Qt.Key_P or key == 'P': - self.app.geo_editor.select_tool('path') - - # Rectangle Tool - if key == QtCore.Qt.Key_R or key == 'R': - self.app.geo_editor.select_tool('rectangle') - - # Substract Tool - if key == QtCore.Qt.Key_S or key == 'S': - if self.app.geo_editor.get_selected() is not None: - self.app.geo_editor.subtract() - else: - msg = _( - "Please select geometry items \n" - "on which to perform Substraction Tool.") - - messagebox = QtWidgets.QMessageBox() - messagebox.setText(msg) - messagebox.setWindowTitle(_("Warning")) - messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) - messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) - messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) - messagebox.exec_() - - # Add Text Tool - if key == QtCore.Qt.Key_T or key == 'T': - self.app.geo_editor.select_tool('text') - - # Substract Tool - if key == QtCore.Qt.Key_U or key == 'U': - if self.app.geo_editor.get_selected() is not None: - self.app.geo_editor.union() - else: - msg = _("Please select geometry items \n" - "on which to perform union.") - - messagebox = QtWidgets.QMessageBox() - messagebox.setText(msg) - messagebox.setWindowTitle(_("Warning")) - messagebox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/warning.png')) - messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok) - messagebox.setDefaultButton(QtWidgets.QMessageBox.Ok) - messagebox.exec_() - - # Flip on X axis - if key == QtCore.Qt.Key_X or key == 'X': - self.app.geo_editor.transform_tool.on_flipx() - return - - # Flip on Y axis - if key == QtCore.Qt.Key_Y or key == 'Y': - self.app.geo_editor.transform_tool.on_flipy() - return - - # Show Shortcut list - if key == 'F3': - self.app.on_shortcut_list() - elif self.app.call_source == 'grb_editor': - # CTRL - if modifiers == QtCore.Qt.ControlModifier: - # Eraser Tool - if key == QtCore.Qt.Key_E or key == 'E': - self.app.grb_editor.on_eraser() - return - - # 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.distance_tool.run() - return - # SHIFT - elif modifiers == QtCore.Qt.ShiftModifier: - # Run Distance Minimum Tool - if key == QtCore.Qt.Key_M or key == 'M': - self.app.distance_min_tool.run() - return - # ALT - elif modifiers == QtCore.Qt.AltModifier: - # Mark Area Tool - if key == QtCore.Qt.Key_A or key == 'A': - self.app.grb_editor.on_markarea() - return - - # Poligonize Tool - if key == QtCore.Qt.Key_N or key == 'N': - self.app.grb_editor.on_poligonize() - return - # Transformation Tool - if key == QtCore.Qt.Key_R or key == 'R': - self.app.grb_editor.on_transform() - return - # NO MODIFIER - 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] %s' % _("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] %s' % _("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['global_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['global_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 - - # we do this so we can reuse the following keys while inside a Tool - # the above keys are general enough so were left outside - if self.app.grb_editor.active_tool is not None and self.grb_select_btn.isChecked() is False: - response = self.app.grb_editor.active_tool.on_key(key=key) - if response is not None: - self.app.inform.emit(response) - else: - # 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] %s' % _("Cancelled. Nothing selected to copy.")) - return - - # Add Disc Tool - if key == QtCore.Qt.Key_D or key == 'D': - self.app.grb_editor.launched_from_shortcuts = True - self.app.grb_editor.select_tool('disc') - return - - # Add SemiDisc Tool - if key == QtCore.Qt.Key_E or key == 'E': - self.app.grb_editor.launched_from_shortcuts = True - self.app.grb_editor.select_tool('semidisc') - 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] %s' % _("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.grb_editor.on_zoom_fit() - return - - # 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': - # CTRL - 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.distance_tool.run() - return - # SHIFT - elif modifiers == QtCore.Qt.ShiftModifier: - # Run Distance Minimum Tool - if key == QtCore.Qt.Key_M or key == 'M': - self.app.distance_min_tool.run() - return - # ALT - elif modifiers == QtCore.Qt.AltModifier: - pass - # NO MODIFIER - elif modifiers == QtCore.Qt.NoModifier: - # Abort the current action - if key == QtCore.Qt.Key_Escape or key == 'Escape': - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled.")) - - self.app.exc_editor.delete_utility_geometry() - - self.app.exc_editor.active_tool.clean_up() - - self.app.exc_editor.select_tool('drill_select') - return - - # Delete selected object if delete key event comes out of canvas - if key == 'Delete': - self.app.exc_editor.launched_from_shortcuts = True - if self.app.exc_editor.selected: - self.app.exc_editor.delete_selected() - self.app.exc_editor.replot() - else: - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Nothing selected to delete.")) - return - - # Delete tools in tools table if delete key event comes from the Selected Tab - if key == QtCore.Qt.Key_Delete: - self.app.exc_editor.launched_from_shortcuts = True - self.app.exc_editor.on_tool_delete() - return - - if key == QtCore.Qt.Key_Minus or key == '-': - self.app.exc_editor.launched_from_shortcuts = True - self.app.plotcanvas.zoom(1 / self.app.defaults['global_zoom_ratio'], - [self.app.exc_editor.snap_x, self.app.exc_editor.snap_y]) - return - - if key == QtCore.Qt.Key_Equal or key == '=': - self.app.exc_editor.launched_from_shortcuts = True - self.app.plotcanvas.zoom(self.app.defaults['global_zoom_ratio'], - [self.app.exc_editor.snap_x, self.app.exc_editor.snap_y]) - return - - # toggle display of Notebook area - if key == QtCore.Qt.Key_QuoteLeft or key == '`': - self.app.exc_editor.launched_from_shortcuts = True - self.app.on_toggle_notebook() - return - - # Switch to Project Tab - if key == QtCore.Qt.Key_1 or key == '1': - self.app.exc_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.exc_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.exc_editor.launched_from_shortcuts = True - self.app.on_select_tab('tool') - return - - # Grid Snap - if key == QtCore.Qt.Key_G or key == 'G': - self.app.exc_editor.launched_from_shortcuts = True - # make sure that the cursor shape is enabled/disabled, too - if self.app.exc_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 - - # Corner Snap - if key == QtCore.Qt.Key_K or key == 'K': - self.app.exc_editor.launched_from_shortcuts = True - self.app.ui.corner_snap_btn.trigger() - return - - # Zoom Fit - if key == QtCore.Qt.Key_V or key == 'V': - self.app.exc_editor.launched_from_shortcuts = True - self.app.on_zoom_fit(None) - return - - # Add Slot Hole Tool - if key == QtCore.Qt.Key_W or key == 'W': - self.app.exc_editor.launched_from_shortcuts = True - self.app.inform.emit(_("Click on target point.")) - self.app.ui.add_slot_btn.setChecked(True) - - self.app.exc_editor.x = self.app.mouse[0] - self.app.exc_editor.y = self.app.mouse[1] - - self.app.exc_editor.select_tool('slot_add') - return - - # Propagate to tool - - # Show Shortcut list - if key == QtCore.Qt.Key_F3 or key == 'F3': - self.app.on_shortcut_list() - return - - # we do this so we can reuse the following keys while inside a Tool - # the above keys are general enough so were left outside - if self.app.exc_editor.active_tool is not None and self.select_drill_btn.isChecked() is False: - response = self.app.exc_editor.active_tool.on_key(key=key) - if response is not None: - self.app.inform.emit(response) - else: - # Add Array of Drill Hole Tool - if key == QtCore.Qt.Key_A or key == 'A': - self.app.exc_editor.launched_from_shortcuts = True - self.app.inform.emit("Click on target point.") - self.app.ui.add_drill_array_btn.setChecked(True) - - self.app.exc_editor.x = self.app.mouse[0] - self.app.exc_editor.y = self.app.mouse[1] - - self.app.exc_editor.select_tool('drill_array') - return - - # Copy - if key == QtCore.Qt.Key_C or key == 'C': - self.app.exc_editor.launched_from_shortcuts = True - if self.app.exc_editor.selected: - self.app.inform.emit(_("Click on target point.")) - self.app.ui.copy_drill_btn.setChecked(True) - self.app.exc_editor.on_tool_select('drill_copy') - 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] %s' % _("Cancelled. Nothing selected to copy.")) - return - - # Add Drill Hole Tool - if key == QtCore.Qt.Key_D or key == 'D': - self.app.exc_editor.launched_from_shortcuts = True - self.app.inform.emit(_("Click on target point.")) - self.app.ui.add_drill_btn.setChecked(True) - - self.app.exc_editor.x = self.app.mouse[0] - self.app.exc_editor.y = self.app.mouse[1] - - self.app.exc_editor.select_tool('drill_add') - return - - # Jump to coords - if key == QtCore.Qt.Key_J or key == 'J': - self.app.on_jump_to() - - # Move - if key == QtCore.Qt.Key_M or key == 'M': - self.app.exc_editor.launched_from_shortcuts = True - if self.app.exc_editor.selected: - self.app.inform.emit(_("Click on target point.")) - self.app.ui.move_drill_btn.setChecked(True) - self.app.exc_editor.on_tool_select('drill_move') - 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] %s' % _("Cancelled. Nothing selected to move.")) - return - - # Add Array of Slots Hole Tool - if key == QtCore.Qt.Key_Q or key == 'Q': - self.app.exc_editor.launched_from_shortcuts = True - self.app.inform.emit("Click on target point.") - self.app.ui.add_slot_array_btn.setChecked(True) - - self.app.exc_editor.x = self.app.mouse[0] - self.app.exc_editor.y = self.app.mouse[1] - - self.app.exc_editor.select_tool('slot_array') - return - - # Resize Tool - if key == QtCore.Qt.Key_R or key == 'R': - self.app.exc_editor.launched_from_shortcuts = True - self.app.exc_editor.select_tool('drill_resize') - return - - # Add Tool - if key == QtCore.Qt.Key_T or key == 'T': - self.app.exc_editor.launched_from_shortcuts = True - # ## Current application units in Upper Case - self.units = self.general_defaults_form.general_app_group.units_radio.get_value().upper() - tool_add_popup = FCInputDialog(title=_("New Tool ..."), - text='%s:' % _('Enter a Tool Diameter'), - min=0.0000, max=99.9999, decimals=4) - tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png')) - - val, ok = tool_add_popup.get_value() - if ok: - self.app.exc_editor.on_tool_add(tooldia=val) - formated_val = '%.*f' % (self.decimals, float(val)) - self.app.inform.emit( - '[success] %s: %s %s' % (_("Added new tool with dia"), formated_val, str(self.units)) - ) - else: - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding Tool cancelled ...")) - return - elif self.app.call_source == 'measurement': - if modifiers == QtCore.Qt.ControlModifier: - pass - elif modifiers == QtCore.Qt.AltModifier: - pass - elif modifiers == QtCore.Qt.ShiftModifier: - pass - # NO MODIFIER - elif modifiers == QtCore.Qt.NoModifier: - if key == QtCore.Qt.Key_Escape or key == 'Escape': - # abort the measurement action - self.app.distance_tool.deactivate_measure_tool() - self.app.inform.emit(_("Distance Tool exit...")) - return - - if key == QtCore.Qt.Key_G or key == 'G': - 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() - elif self.app.call_source == 'qrcode_tool': - # CTRL + ALT - if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier: - if key == QtCore.Qt.Key_X: - self.app.abort_all_tasks() - return - - elif modifiers == QtCore.Qt.ControlModifier: - pass - elif modifiers == QtCore.Qt.ShiftModifier: - pass - elif modifiers == QtCore.Qt.AltModifier: - pass - # NO MODIFIER - elif modifiers == QtCore.Qt.NoModifier: - # Escape = Deselect All - if key == QtCore.Qt.Key_Escape or key == 'Escape': - self.app.qrcode_tool.on_exit() - - # Grid toggle - if key == QtCore.Qt.Key_G: - self.app.ui.grid_snap_btn.trigger() - - # Jump to coords - if key == QtCore.Qt.Key_J: - self.app.on_jump_to() - elif self.app.call_source == 'copper_thieving_tool': - # CTRL + ALT - if modifiers == QtCore.Qt.ControlModifier | QtCore.Qt.AltModifier: - if key == QtCore.Qt.Key_X: - self.app.abort_all_tasks() - return - elif modifiers == QtCore.Qt.ControlModifier: - pass - elif modifiers == QtCore.Qt.ShiftModifier: - pass - elif modifiers == QtCore.Qt.AltModifier: - pass - # NO MODIFIER - elif modifiers == QtCore.Qt.NoModifier: - # Escape = Deselect All - if key == QtCore.Qt.Key_Escape or key == 'Escape': - self.app.copperfill_tool.on_exit() - - # Grid toggle - if key == QtCore.Qt.Key_G: - self.app.ui.grid_snap_btn.trigger() - - # Jump to coords - if key == QtCore.Qt.Key_J: - self.app.on_jump_to() - elif self.app.call_source == 'geometry': - if modifiers == QtCore.Qt.ControlModifier: - pass - elif modifiers == QtCore.Qt.AltModifier: - pass - elif modifiers == QtCore.Qt.ShiftModifier: - pass - # NO MODIFIER - elif modifiers == QtCore.Qt.NoModifier: - if key == QtCore.Qt.Key_Escape or key == 'Escape': - sel_obj = self.app.collection.get_active() - assert sel_obj.kind == 'geometry', "Expected a Geometry Object, got %s" % type(sel_obj) - - sel_obj.area_disconnect() - return - - if key == QtCore.Qt.Key_G or key == 'G': - 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() - - def createPopupMenu(self): - menu = super().createPopupMenu() - - menu.addSeparator() - menu.addAction(self.lock_action) - return menu - - def lock_toolbar(self, lock=False): - """ - Used to (un)lock the toolbars of the app. - - :param lock: boolean, will lock all toolbars in place when set True - :return: None - """ - - if lock: - for widget in self.children(): - if isinstance(widget, QtWidgets.QToolBar): - widget.setMovable(False) - else: - for widget in self.children(): - if isinstance(widget, QtWidgets.QToolBar): - widget.setMovable(True) - - def dragEnterEvent(self, event): - if event.mimeData().hasUrls: - event.accept() - else: - event.ignore() - - def dragMoveEvent(self, event): - if event.mimeData().hasUrls: - event.accept() - else: - event.ignore() - - def dropEvent(self, event): - if event.mimeData().hasUrls: - event.setDropAction(QtCore.Qt.CopyAction) - event.accept() - for url in event.mimeData().urls(): - self.filename = str(url.toLocalFile()) - - if self.filename == "": - self.app.inform.emit("Cancelled.") - else: - 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 extension in self.app.exc_list: - self.app.worker_task.emit({'fcn': self.app.open_excellon, - 'params': [self.filename]}) - else: - event.ignore() - - if extension in self.app.gcode_list: - self.app.worker_task.emit({'fcn': self.app.open_gcode, - 'params': [self.filename]}) - else: - event.ignore() - - 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 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 extension in self.app.pdf_list: - self.app.pdf_tool.periodic_check(1000) - self.app.worker_task.emit({'fcn': self.app.pdf_tool.open_pdf, - 'params': [self.filename]}) - - if extension in self.app.prj_list: - # self.app.open_project() is not Thread Safe - self.app.open_project(self.filename) - - if extension in self.app.conf_list: - self.app.open_config_file(self.filename) - else: - event.ignore() - else: - event.ignore() - - def closeEvent(self, event): - if self.app.save_in_progress: - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Application is saving the project. Please wait ...")) - else: - 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]) - - self.final_save.emit() - event.ignore() - - -class FlatCAMActivityView(QtWidgets.QWidget): - """ - This class create and control the activity icon displayed in the App status bar - """ - - def __init__(self, app, parent=None): - super().__init__(parent=parent) - - self.app = app - - if self.app.defaults["global_activity_icon"] == "Ball green": - icon = self.app.resource_location + '/active_2_static.png' - movie = self.app.resource_location + "/active_2.gif" - elif self.app.defaults["global_activity_icon"] == "Ball black": - icon = self.app.resource_location + '/active_static.png' - movie = self.app.resource_location + "/active.gif" - elif self.app.defaults["global_activity_icon"] == "Arrow green": - icon = self.app.resource_location + '/active_3_static.png' - movie = self.app.resource_location + "/active_3.gif" - elif self.app.defaults["global_activity_icon"] == "Eclipse green": - icon = self.app.resource_location + '/active_4_static.png' - movie = self.app.resource_location + "/active_4.gif" - else: - icon = self.app.resource_location + '/active_static.png' - movie = self.app.resource_location + "/active.gif" - - self.setMinimumWidth(200) - self.movie_path = movie - self.icon_path = icon - - self.icon = QtWidgets.QLabel(self) - self.icon.setGeometry(0, 0, 16, 12) - self.movie = QtGui.QMovie(self.movie_path) - - self.icon.setMovie(self.movie) - # self.movie.start() - - layout = QtWidgets.QHBoxLayout() - layout.setContentsMargins(5, 0, 5, 0) - layout.setAlignment(QtCore.Qt.AlignLeft) - self.setLayout(layout) - - layout.addWidget(self.icon) - self.text = QtWidgets.QLabel(self) - self.text.setText(_("Idle.")) - self.icon.setPixmap(QtGui.QPixmap(self.icon_path)) - - layout.addWidget(self.text) - - def set_idle(self): - self.movie.stop() - self.text.setText(_("Idle.")) - - def set_busy(self, msg, no_movie=None): - if no_movie is not True: - self.icon.setMovie(self.movie) - self.movie.start() - self.text.setText(msg) - - -class FlatCAMInfoBar(QtWidgets.QWidget): - """ - This class create a place to display the App messages in the Status Bar - """ - - def __init__(self, parent=None, app=None): - super(FlatCAMInfoBar, self).__init__(parent=parent) - - self.app = app - - self.icon = QtWidgets.QLabel(self) - self.icon.setGeometry(0, 0, 12, 12) - self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png') - self.icon.setPixmap(self.pmap) - - layout = QtWidgets.QHBoxLayout() - layout.setContentsMargins(5, 0, 5, 0) - self.setLayout(layout) - - layout.addWidget(self.icon) - - self.text = QtWidgets.QLabel(self) - self.text.setText(_("Application started ...")) - self.text.setToolTip(_("Hello!")) - - layout.addWidget(self.text) - - layout.addStretch() - - def set_text_(self, text, color=None): - self.text.setText(text) - self.text.setToolTip(text) - if color: - self.text.setStyleSheet('color: %s' % str(color)) - - def set_status(self, text, level="info"): - level = str(level) - - self.pmap.fill() - if level == "ERROR" or level == "ERROR_NOTCL": - self.pmap = QtGui.QPixmap(self.app.resource_location + '/redlight12.png') - elif level.lower() == "success": - self.pmap = QtGui.QPixmap(self.app.resource_location + '/greenlight12.png') - elif level == "WARNING" or level == "WARNING_NOTCL": - self.pmap = QtGui.QPixmap(self.app.resource_location + '/yellowlight12.png') - elif level.lower() == "selected": - self.pmap = QtGui.QPixmap(self.app.resource_location + '/bluelight12.png') - else: - self.pmap = QtGui.QPixmap(self.app.resource_location + '/graylight12.png') - - try: - self.set_text_(text) - self.icon.setPixmap(self.pmap) - except Exception as e: - log.debug("FlatCAMInfoBar.set_status() --> %s" % str(e)) - - -class FlatCAMSystemTray(QtWidgets.QSystemTrayIcon): - """ - This class create the Sys Tray icon for the app - """ - - def __init__(self, app, icon, headless=None, parent=None): - # QtWidgets.QSystemTrayIcon.__init__(self, icon, parent) - super().__init__(icon, parent=parent) - self.app = app - - menu = QtWidgets.QMenu(parent) - - menu_runscript = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/script14.png'), - '%s' % _('Run Script ...'), self) - menu_runscript.setToolTip( - _("Will run the opened Tcl Script thus\n" - "enabling the automation of certain\n" - "functions of FlatCAM.") - ) - menu.addAction(menu_runscript) - - menu.addSeparator() - - if headless is None: - self.menu_open = menu.addMenu(QtGui.QIcon(self.app.resource_location + '/folder32_bis.png'), _('Open')) - - # Open Project ... - menu_openproject = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/folder16.png'), - _('Open Project ...'), self) - self.menu_open.addAction(menu_openproject) - self.menu_open.addSeparator() - - # Open Gerber ... - menu_opengerber = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/flatcam_icon24.png'), - _('Open &Gerber ...\tCtrl+G'), self) - self.menu_open.addAction(menu_opengerber) - - # Open Excellon ... - menu_openexcellon = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/open_excellon32.png'), - _('Open &Excellon ...\tCtrl+E'), self) - self.menu_open.addAction(menu_openexcellon) - - # Open G-Code ... - menu_opengcode = QtWidgets.QAction(QtGui.QIcon(self.app.resource_location + '/code.png'), - _('Open G-&Code ...'), self) - self.menu_open.addAction(menu_opengcode) - - self.menu_open.addSeparator() - - menu_openproject.triggered.connect(self.app.on_file_openproject) - menu_opengerber.triggered.connect(self.app.on_fileopengerber) - menu_openexcellon.triggered.connect(self.app.on_fileopenexcellon) - menu_opengcode.triggered.connect(self.app.on_fileopengcode) - - exitAction = menu.addAction(_("Exit")) - exitAction.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png')) - self.setContextMenu(menu) - - menu_runscript.triggered.connect(lambda: self.app.on_filerunscript( - silent=True if self.app.cmd_line_headless == 1 else False)) - - exitAction.triggered.connect(self.app.final_save) - # end of file diff --git a/flatcamGUI/ObjectUI.py b/AppGUI/ObjectUI.py similarity index 85% rename from flatcamGUI/ObjectUI.py rename to AppGUI/ObjectUI.py index c5dacb42..4c1f7abd 100644 --- a/flatcamGUI/ObjectUI.py +++ b/AppGUI/ObjectUI.py @@ -11,11 +11,11 @@ # Date: 3/10/2019 # # ########################################################## -from flatcamGUI.GUIElements import * +from AppGUI.GUIElements import * import sys import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -114,7 +114,7 @@ class ObjectUI(QtWidgets.QWidget): self.common_grid.addWidget(self.transform_label, 2, 0, 1, 2) # ### Scale #### - self.scale_entry = FCEntry() + self.scale_entry = NumericalEvalEntry(border_color='#0069A9') self.scale_entry.set_value(1.0) self.scale_entry.setToolTip( _("Factor by which to multiply\n" @@ -132,7 +132,7 @@ class ObjectUI(QtWidgets.QWidget): self.common_grid.addWidget(self.scale_button, 3, 1) # ### Offset #### - self.offsetvector_entry = EvalEntry2() + self.offsetvector_entry = NumericalEvalTupleEntry(border_color='#0069A9') self.offsetvector_entry.setText("(0.0, 0.0)") self.offsetvector_entry.setToolTip( _("Amount by which to move the object\n" @@ -153,17 +153,20 @@ class ObjectUI(QtWidgets.QWidget): def confirmation_message(self, accepted, minval, maxval): if accepted is False: - self.app.inform.emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % - (_("Edited value is out of range"), self.decimals, minval, self.decimals, maxval)) + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"), + self.decimals, + minval, + self.decimals, + maxval), False) else: - self.app.inform.emit('[success] %s' % _("Edited value is within limits.")) + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) def confirmation_message_int(self, accepted, minval, maxval): if accepted is False: - self.app.inform.emit('[WARNING_NOTCL] %s: [%d, %d]' % - (_("Edited value is out of range"), minval, maxval)) + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' % + (_("Edited value is out of range"), minval, maxval), False) else: - self.app.inform.emit('[success] %s' % _("Edited value is within limits.")) + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) class GerberObjectUI(ObjectUI): @@ -205,27 +208,37 @@ class GerberObjectUI(ObjectUI): self.multicolored_cb.setMinimumWidth(55) grid0.addWidget(self.multicolored_cb, 0, 2) - # Plot CB - self.plot_cb = FCCheckBox('%s' % _("Plot")) - # self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft) - self.plot_cb.setToolTip(_("Plot (show) this object.")) - - grid0.addWidget(self.plot_cb, 1, 0, 1, 2) - # ## Object name self.name_hlay = QtWidgets.QHBoxLayout() - self.custom_box.addLayout(self.name_hlay) + grid0.addLayout(self.name_hlay, 1, 0, 1, 3) + name_label = QtWidgets.QLabel("%s:" % _("Name")) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus) self.name_hlay.addWidget(name_label) self.name_hlay.addWidget(self.name_entry) + # Plot CB + self.plot_lbl = FCLabel('%s:' % _("Plot")) + self.plot_lbl.setToolTip(_("Plot (show) this object.")) + self.plot_cb = FCCheckBox() + + grid0.addWidget(self.plot_lbl, 2, 0) + grid0.addWidget(self.plot_cb, 2, 1) + + # generate follow + self.follow_cb = FCCheckBox('%s' % _("Follow")) + self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace.")) + self.follow_cb.setMinimumWidth(55) + grid0.addWidget(self.follow_cb, 2, 2) + hlay_plot = QtWidgets.QHBoxLayout() self.custom_box.addLayout(hlay_plot) # ### Gerber Apertures #### - self.apertures_table_label = QtWidgets.QLabel('%s:' % _('Apertures')) + self.apertures_table_label = QtWidgets.QLabel('%s:' % _('Apertures')) self.apertures_table_label.setToolTip( _("Apertures Table for the Gerber Object.") ) @@ -282,254 +295,7 @@ class GerberObjectUI(ObjectUI): # start with apertures table hidden self.apertures_table.setVisible(False) - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - self.custom_box.addWidget(separator_line) - - # Isolation Routing - self.isolation_routing_label = QtWidgets.QLabel("%s" % _("Isolation Routing")) - self.isolation_routing_label.setToolTip( - _("Create a Geometry object with\n" - "toolpaths to cut outside polygons.") - ) - self.custom_box.addWidget(self.isolation_routing_label) - - # ########################################### - # ########## NEW GRID ####################### - # ########################################### - - grid1 = QtWidgets.QGridLayout() - self.custom_box.addLayout(grid1) - grid1.setColumnStretch(0, 0) - grid1.setColumnStretch(1, 1) - grid1.setColumnStretch(2, 1) - - # Tool Type - self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type')) - self.tool_type_label.setToolTip( - _("Choose which tool to use for Gerber isolation:\n" - "'Circular' or 'V-shape'.\n" - "When the 'V-shape' is selected then the tool\n" - "diameter will depend on the chosen cut depth.") - ) - self.tool_type_radio = RadioSet([{'label': _('Circular'), 'value': 'circular'}, - {'label': _('V-Shape'), 'value': 'v'}]) - - grid1.addWidget(self.tool_type_label, 0, 0) - grid1.addWidget(self.tool_type_radio, 0, 1, 1, 2) - - # Tip Dia - self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia')) - self.tipdialabel.setToolTip( - _("The tip diameter for V-Shape Tool") - ) - self.tipdia_spinner = FCDoubleSpinner(callback=self.confirmation_message) - self.tipdia_spinner.set_range(-99.9999, 99.9999) - self.tipdia_spinner.set_precision(self.decimals) - self.tipdia_spinner.setSingleStep(0.1) - self.tipdia_spinner.setWrapping(True) - grid1.addWidget(self.tipdialabel, 1, 0) - grid1.addWidget(self.tipdia_spinner, 1, 1, 1, 2) - - # Tip Angle - self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle')) - self.tipanglelabel.setToolTip( - _("The tip angle for V-Shape Tool.\n" - "In degree.") - ) - self.tipangle_spinner = FCDoubleSpinner(callback=self.confirmation_message) - self.tipangle_spinner.set_range(1, 180) - self.tipangle_spinner.set_precision(self.decimals) - self.tipangle_spinner.setSingleStep(5) - self.tipangle_spinner.setWrapping(True) - grid1.addWidget(self.tipanglelabel, 2, 0) - grid1.addWidget(self.tipangle_spinner, 2, 1, 1, 2) - - # Cut Z - self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z')) - self.cutzlabel.setToolTip( - _("Cutting depth (negative)\n" - "below the copper surface.") - ) - self.cutz_spinner = FCDoubleSpinner(callback=self.confirmation_message) - self.cutz_spinner.set_range(-9999.9999, 0.0000) - self.cutz_spinner.set_precision(self.decimals) - self.cutz_spinner.setSingleStep(0.1) - self.cutz_spinner.setWrapping(True) - grid1.addWidget(self.cutzlabel, 3, 0) - grid1.addWidget(self.cutz_spinner, 3, 1, 1, 2) - - # Tool diameter - tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia')) - tdlabel.setToolTip( - _("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.") - ) - tdlabel.setMinimumWidth(90) - self.iso_tool_dia_entry = FCDoubleSpinner(callback=self.confirmation_message) - self.iso_tool_dia_entry.set_range(-9999.9999, 9999.9999) - self.iso_tool_dia_entry.set_precision(self.decimals) - self.iso_tool_dia_entry.setSingleStep(0.1) - - grid1.addWidget(tdlabel, 4, 0) - grid1.addWidget(self.iso_tool_dia_entry, 4, 1, 1, 2) - - # Number of Passes - passlabel = QtWidgets.QLabel('%s:' % _('# Passes')) - passlabel.setToolTip( - _("Width of the isolation gap in\n" - "number (integer) of tool widths.") - ) - passlabel.setMinimumWidth(90) - self.iso_width_entry = FCSpinner(callback=self.confirmation_message_int) - self.iso_width_entry.set_range(1, 999) - - grid1.addWidget(passlabel, 5, 0) - grid1.addWidget(self.iso_width_entry, 5, 1, 1, 2) - - # Pass overlap - overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap')) - overlabel.setToolTip( - _("How much (percentage) of the tool width to overlap each tool pass.") - ) - overlabel.setMinimumWidth(90) - self.iso_overlap_entry = FCDoubleSpinner(suffix='%', callback=self.confirmation_message) - self.iso_overlap_entry.set_precision(self.decimals) - self.iso_overlap_entry.setWrapping(True) - self.iso_overlap_entry.set_range(0.0000, 99.9999) - self.iso_overlap_entry.setSingleStep(0.1) - grid1.addWidget(overlabel, 6, 0) - grid1.addWidget(self.iso_overlap_entry, 6, 1, 1, 2) - - # Milling Type Radio Button - self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) - self.milling_type_label.setToolTip( - _("Milling type:\n" - "- climb / best for precision milling and to reduce tool usage\n" - "- conventional / useful when there is no backlash compensation") - ) - self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, - {'label': _('Conventional'), 'value': 'cv'}]) - grid1.addWidget(self.milling_type_label, 7, 0) - grid1.addWidget(self.milling_type_radio, 7, 1, 1, 2) - - # combine all passes CB - self.combine_passes_cb = FCCheckBox(label=_('Combine')) - self.combine_passes_cb.setToolTip( - _("Combine all passes into one object") - ) - - # generate follow - self.follow_cb = FCCheckBox(label=_('"Follow"')) - self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n" - "This means that it will cut through\n" - "the middle of the trace.")) - grid1.addWidget(self.combine_passes_cb, 8, 0) - - # avoid an area from isolation - self.except_cb = FCCheckBox(label=_('Except')) - grid1.addWidget(self.follow_cb, 8, 1) - - self.except_cb.setToolTip(_("When the isolation geometry is generated,\n" - "by checking this, the area of the object below\n" - "will be subtracted from the isolation geometry.")) - grid1.addWidget(self.except_cb, 8, 2) - - # ## Form Layout - form_layout = QtWidgets.QFormLayout() - grid1.addLayout(form_layout, 9, 0, 1, 3) - - # ################################################ - # ##### Type of object to be excepted ############ - # ################################################ - self.type_obj_combo = FCComboBox() - self.type_obj_combo.addItems([_("Gerber"), _("Geometry")]) - - # we get rid of item1 ("Excellon") as it is not suitable - # self.type_obj_combo.view().setRowHidden(1, True) - self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.resource_loc + "/flatcam_icon16.png")) - self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.resource_loc + "/geometry16.png")) - - self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Obj Type")) - self.type_obj_combo_label.setToolTip( - _("Specify the type of object to be excepted from isolation.\n" - "It can be of type: Gerber or Geometry.\n" - "What is selected here will dictate the kind\n" - "of objects that will populate the 'Object' combobox.") - ) - # self.type_obj_combo_label.setMinimumWidth(60) - form_layout.addRow(self.type_obj_combo_label, self.type_obj_combo) - - # ################################################ - # ##### The object to be excepted ################ - # ################################################ - self.obj_combo = FCComboBox() - - self.obj_label = QtWidgets.QLabel('%s:' % _("Object")) - self.obj_label.setToolTip(_("Object whose area will be removed from isolation geometry.")) - - form_layout.addRow(self.obj_label, self.obj_combo) - - # ---------------------------------------------- # - # --------- Isolation scope -------------------- # - # ---------------------------------------------- # - self.iso_scope_label = QtWidgets.QLabel('%s:' % _('Scope')) - self.iso_scope_label.setToolTip( - _("Isolation scope. Choose what to isolate:\n" - "- 'All' -> Isolate all the polygons in the object\n" - "- 'Selection' -> Isolate a selection of polygons.") - ) - self.iso_scope_radio = RadioSet([{'label': _('All'), 'value': 'all'}, - {'label': _('Selection'), 'value': 'single'}]) - - grid1.addWidget(self.iso_scope_label, 10, 0) - grid1.addWidget(self.iso_scope_radio, 10, 1, 1, 2) - - # ---------------------------------------------- # - # --------- Isolation type -------------------- # - # ---------------------------------------------- # - self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type')) - self.iso_type_label.setToolTip( - _("Choose how the isolation will be executed:\n" - "- 'Full' -> complete isolation of polygons\n" - "- 'Ext' -> will isolate only on the outside\n" - "- 'Int' -> will isolate only on the inside\n" - "'Exterior' isolation is almost always possible\n" - "(with the right tool) but 'Interior'\n" - "isolation can be done only when there is an opening\n" - "inside of the polygon (e.g polygon is a 'doughnut' shape).") - ) - self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'}, - {'label': _('Ext'), 'value': 'ext'}, - {'label': _('Int'), 'value': 'int'}]) - - grid1.addWidget(self.iso_type_label, 11, 0) - grid1.addWidget(self.iso_type_radio, 11, 1, 1, 2) - - self.generate_iso_button = QtWidgets.QPushButton("%s" % _("Generate Isolation Geometry")) - self.generate_iso_button.setStyleSheet(""" - QPushButton - { - font-weight: bold; - } - """) - self.generate_iso_button.setToolTip( - _("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.") - ) - grid1.addWidget(self.generate_iso_button, 12, 0, 1, 3) - + # Buffer Geometry self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry')) self.create_buffer_button.setToolTip( _("This button is shown only when the Gerber file\n" @@ -537,19 +303,12 @@ class GerberObjectUI(ObjectUI): "Clicking this will create the buffered geometry\n" "required for isolation.") ) - grid1.addWidget(self.create_buffer_button, 13, 0, 1, 3) + self.custom_box.addWidget(self.create_buffer_button) - self.ohis_iso = OptionalHideInputSection( - self.except_cb, - [self.type_obj_combo, self.type_obj_combo_label, self.obj_combo, self.obj_label], - logic=True - ) - - separator_line2 = QtWidgets.QFrame() - separator_line2.setFrameShape(QtWidgets.QFrame.HLine) - separator_line2.setFrameShadow(QtWidgets.QFrame.Sunken) - grid1.addWidget(separator_line2, 14, 0, 1, 3) - # grid1.addWidget(QtWidgets.QLabel(''), 15, 0) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.custom_box.addWidget(separator_line) # ########################################### # ########## NEW GRID ####################### @@ -563,14 +322,21 @@ class GerberObjectUI(ObjectUI): self.tool_lbl = QtWidgets.QLabel('%s' % _("TOOLS")) grid2.addWidget(self.tool_lbl, 0, 0, 1, 2) - # ## Clear non-copper regions - self.clearcopper_label = QtWidgets.QLabel("%s" % _("Clear N-copper")) - self.clearcopper_label.setToolTip( + # Isolation Tool - will create isolation paths around the copper features + self.iso_button = QtWidgets.QPushButton(_('Isolation Routing')) + self.iso_button.setToolTip( _("Create a Geometry object with\n" - "toolpaths to cut all non-copper regions.") + "toolpaths to cut around polygons.") ) - self.clearcopper_label.setMinimumWidth(90) + self.iso_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + grid2.addWidget(self.iso_button, 1, 0, 1, 2) + # ## Clear non-copper regions self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool')) self.generate_ncc_button.setToolTip( _("Create the Geometry Object\n" @@ -582,17 +348,9 @@ class GerberObjectUI(ObjectUI): font-weight: bold; } """) - grid2.addWidget(self.clearcopper_label, 1, 0) - grid2.addWidget(self.generate_ncc_button, 1, 1) + grid2.addWidget(self.generate_ncc_button, 2, 0, 1, 2) # ## Board cutout - self.board_cutout_label = QtWidgets.QLabel("%s" % _("Board cutout")) - self.board_cutout_label.setToolTip( - _("Create toolpaths to cut around\n" - "the PCB and separate it from\n" - "the original board.") - ) - self.generate_cutout_button = QtWidgets.QPushButton(_('Cutout Tool')) self.generate_cutout_button.setToolTip( _("Generate the geometry for\n" @@ -604,13 +362,12 @@ class GerberObjectUI(ObjectUI): font-weight: bold; } """) - grid2.addWidget(self.board_cutout_label, 2, 0) - grid2.addWidget(self.generate_cutout_button, 2, 1) + grid2.addWidget(self.generate_cutout_button, 3, 0, 1, 2) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid2.addWidget(separator_line, 3, 0, 1, 2) + grid2.addWidget(separator_line, 4, 0, 1, 2) # ## Non-copper regions self.noncopper_label = QtWidgets.QLabel("%s" % _("Non-copper regions")) @@ -622,7 +379,7 @@ class GerberObjectUI(ObjectUI): "copper from a specified region.") ) - grid2.addWidget(self.noncopper_label, 4, 0, 1, 2) + grid2.addWidget(self.noncopper_label, 5, 0, 1, 2) # Margin bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin')) @@ -638,8 +395,8 @@ class GerberObjectUI(ObjectUI): self.noncopper_margin_entry.set_precision(self.decimals) self.noncopper_margin_entry.setSingleStep(0.1) - grid2.addWidget(bmlabel, 5, 0) - grid2.addWidget(self.noncopper_margin_entry, 5, 1) + grid2.addWidget(bmlabel, 6, 0) + grid2.addWidget(self.noncopper_margin_entry, 6, 1) # Rounded corners self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo")) @@ -649,13 +406,13 @@ class GerberObjectUI(ObjectUI): self.noncopper_rounded_cb.setMinimumWidth(90) self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo')) - grid2.addWidget(self.noncopper_rounded_cb, 6, 0) - grid2.addWidget(self.generate_noncopper_button, 6, 1) + grid2.addWidget(self.noncopper_rounded_cb, 7, 0) + grid2.addWidget(self.generate_noncopper_button, 7, 1) separator_line1 = QtWidgets.QFrame() separator_line1.setFrameShape(QtWidgets.QFrame.HLine) separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken) - grid2.addWidget(separator_line1, 7, 0, 1, 2) + grid2.addWidget(separator_line1, 8, 0, 1, 2) # ## Bounding box self.boundingbox_label = QtWidgets.QLabel('%s' % _('Bounding Box')) @@ -664,7 +421,7 @@ class GerberObjectUI(ObjectUI): "Square shape.") ) - grid2.addWidget(self.boundingbox_label, 8, 0, 1, 2) + grid2.addWidget(self.boundingbox_label, 9, 0, 1, 2) bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin')) bbmargin.setToolTip( @@ -677,8 +434,8 @@ class GerberObjectUI(ObjectUI): self.bbmargin_entry.set_precision(self.decimals) self.bbmargin_entry.setSingleStep(0.1) - grid2.addWidget(bbmargin, 9, 0) - grid2.addWidget(self.bbmargin_entry, 9, 1) + grid2.addWidget(bbmargin, 10, 0) + grid2.addWidget(self.bbmargin_entry, 10, 1) self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo")) self.bbrounded_cb.setToolTip( @@ -693,8 +450,8 @@ class GerberObjectUI(ObjectUI): self.generate_bb_button.setToolTip( _("Generate the Geometry object.") ) - grid2.addWidget(self.bbrounded_cb, 10, 0) - grid2.addWidget(self.generate_bb_button, 10, 1) + grid2.addWidget(self.bbrounded_cb, 11, 0) + grid2.addWidget(self.generate_bb_button, 11, 1) class ExcellonObjectUI(ObjectUI): @@ -723,22 +480,38 @@ class ExcellonObjectUI(ObjectUI): parent=parent, app=self.app) - # ### Plot options #### - hlay_plot = QtWidgets.QHBoxLayout() - self.custom_box.addLayout(hlay_plot) + # Plot options + grid_h = QtWidgets.QGridLayout() + grid_h.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + self.custom_box.addLayout(grid_h) + grid_h.setColumnStretch(0, 0) + grid_h.setColumnStretch(1, 1) self.plot_options_label = QtWidgets.QLabel("%s:" % _("Plot Options")) + self.plot_options_label.setMinimumWidth(90) + + grid_h.addWidget(self.plot_options_label, 0, 0) + + # Solid CB self.solid_cb = FCCheckBox(label=_('Solid')) self.solid_cb.setToolTip( _("Solid circles.") ) - hlay_plot.addWidget(self.plot_options_label) - hlay_plot.addStretch() - hlay_plot.addWidget(self.solid_cb) + self.solid_cb.setMinimumWidth(50) + grid_h.addWidget(self.solid_cb, 0, 1) + + # Multicolored CB + self.multicolored_cb = FCCheckBox(label=_('Multi-Color')) + self.multicolored_cb.setToolTip( + _("Draw polygons in different colors.") + ) + self.multicolored_cb.setMinimumWidth(55) + grid_h.addWidget(self.multicolored_cb, 0, 2) # ## Object name self.name_hlay = QtWidgets.QHBoxLayout() - self.custom_box.addLayout(self.name_hlay) + grid_h.addLayout(self.name_hlay, 1, 0, 1, 3) + name_label = QtWidgets.QLabel("%s:" % _("Name")) self.name_entry = FCEntry() self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus) @@ -1292,10 +1065,112 @@ class ExcellonObjectUI(ObjectUI): self.grid5.addWidget(pp_geo_label, 16, 0) self.grid5.addWidget(self.pp_geo_name_cb, 16, 1) + # ------------------------------------------------------------------------------------------------------------ + # ------------------------- EXCLUSION AREAS ------------------------------------------------------------------ + # ------------------------------------------------------------------------------------------------------------ + + # Exclusion Areas + self.exclusion_cb = FCCheckBox('%s' % _("Add exclusion areas")) + self.exclusion_cb.setToolTip( + _( + "Include exclusion areas.\n" + "In those areas the travel of the tools\n" + "is forbidden." + ) + ) + self.grid5.addWidget(self.exclusion_cb, 20, 0, 1, 2) + + self.exclusion_frame = QtWidgets.QFrame() + self.exclusion_frame.setContentsMargins(0, 0, 0, 0) + self.grid5.addWidget(self.exclusion_frame, 22, 0, 1, 2) + + self.exclusion_box = QtWidgets.QVBoxLayout() + self.exclusion_box.setContentsMargins(0, 0, 0, 0) + self.exclusion_frame.setLayout(self.exclusion_box) + + self.exclusion_table = FCTable() + self.exclusion_box.addWidget(self.exclusion_table) + self.exclusion_table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) + + self.exclusion_table.setColumnCount(4) + self.exclusion_table.setColumnWidth(0, 20) + self.exclusion_table.setHorizontalHeaderLabels(['#', _('Object'), _('Strategy'), _('Over Z')]) + + self.exclusion_table.horizontalHeaderItem(0).setToolTip(_("This is the Area ID.")) + self.exclusion_table.horizontalHeaderItem(1).setToolTip( + _("Type of the object where the exclusion area was added.")) + self.exclusion_table.horizontalHeaderItem(2).setToolTip( + _("The strategy used for exclusion area. Go around the exclusion areas or over it.")) + self.exclusion_table.horizontalHeaderItem(3).setToolTip( + _("If the strategy is to go over the area then this is the height at which the tool will go to avoid the " + "exclusion area.")) + + self.exclusion_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + + grid_a1 = QtWidgets.QGridLayout() + grid_a1.setColumnStretch(0, 0) + grid_a1.setColumnStretch(1, 1) + self.exclusion_box.addLayout(grid_a1) + + # Chose Strategy + self.strategy_label = FCLabel('%s:' % _("Strategy")) + self.strategy_label.setToolTip(_("The strategy followed when encountering an exclusion area.\n" + "Can be:\n" + "- Over -> when encountering the area, the tool will go to a set height\n" + "- Around -> will avoid the exclusion area by going around the area")) + self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'}, + {'label': _('Around'), 'value': 'around'}]) + + grid_a1.addWidget(self.strategy_label, 1, 0) + grid_a1.addWidget(self.strategy_radio, 1, 1) + + # Over Z + self.over_z_label = FCLabel('%s:' % _("Over Z")) + self.over_z_label.setToolTip(_("The height Z to which the tool will rise in order to avoid\n" + "an interdiction area.")) + self.over_z_entry = FCDoubleSpinner() + self.over_z_entry.set_range(0.000, 9999.9999) + self.over_z_entry.set_precision(self.decimals) + + grid_a1.addWidget(self.over_z_label, 2, 0) + grid_a1.addWidget(self.over_z_entry, 2, 1) + + # Button Add Area + self.add_area_button = QtWidgets.QPushButton(_('Add area:')) + self.add_area_button.setToolTip(_("Add an Exclusion Area.")) + + # Area Selection shape + self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, + {'label': _("Polygon"), 'value': 'polygon'}]) + self.area_shape_radio.setToolTip( + _("The kind of selection shape used for area selection.") + ) + + grid_a1.addWidget(self.add_area_button, 4, 0) + grid_a1.addWidget(self.area_shape_radio, 4, 1) + + h_lay_1 = QtWidgets.QHBoxLayout() + self.exclusion_box.addLayout(h_lay_1) + + # Button Delete All Areas + self.delete_area_button = QtWidgets.QPushButton(_('Delete All')) + self.delete_area_button.setToolTip(_("Delete all exclusion areas.")) + + # Button Delete Selected Areas + self.delete_sel_area_button = QtWidgets.QPushButton(_('Delete Selected')) + self.delete_sel_area_button.setToolTip(_("Delete all exclusion areas that are selected in the table.")) + + h_lay_1.addWidget(self.delete_area_button) + h_lay_1.addWidget(self.delete_sel_area_button) + + self.ois_exclusion_exc = OptionalHideInputSection(self.exclusion_cb, [self.exclusion_frame]) + # -------------------------- EXCLUSION AREAS END ------------------------------------------------------------- + # ------------------------------------------------------------------------------------------------------------ + separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - self.grid5.addWidget(separator_line, 17, 0, 1, 2) + self.grid5.addWidget(separator_line, 25, 0, 1, 2) # ################################################################# # ################# GRID LAYOUT 6 ############################### @@ -1312,7 +1187,7 @@ class ExcellonObjectUI(ObjectUI): "for custom selection of tools." )) - self.grid6.addWidget(QtWidgets.QLabel(''), 1, 0, 1, 3) + # self.grid6.addWidget(QtWidgets.QLabel(''), 1, 0, 1, 3) self.grid6.addWidget(warning_lbl, 2, 0, 1, 3) self.generate_cnc_button = QtWidgets.QPushButton(_('Generate CNCJob object')) @@ -1423,12 +1298,28 @@ class GeometryObjectUI(ObjectUI): ) # Plot options + grid_header = QtWidgets.QGridLayout() + grid_header.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) + self.custom_box.addLayout(grid_header) + grid_header.setColumnStretch(0, 0) + grid_header.setColumnStretch(1, 1) + self.plot_options_label = QtWidgets.QLabel("%s:" % _("Plot Options")) - self.custom_box.addWidget(self.plot_options_label) + self.plot_options_label.setMinimumWidth(90) + + grid_header.addWidget(self.plot_options_label, 0, 0) + + # Multicolored CB + self.multicolored_cb = FCCheckBox(label=_('Multi-Color')) + self.multicolored_cb.setToolTip( + _("Draw polygons in different colors.") + ) + self.multicolored_cb.setMinimumWidth(55) + grid_header.addWidget(self.multicolored_cb, 0, 2) # ## Object name self.name_hlay = QtWidgets.QHBoxLayout() - self.custom_box.addLayout(self.name_hlay) + grid_header.addLayout(self.name_hlay, 1, 0, 1, 3) name_label = QtWidgets.QLabel("%s:" % _("Name")) self.name_entry = FCEntry() @@ -1592,19 +1483,24 @@ class GeometryObjectUI(ObjectUI): grid1.addWidget(self.addtool_entry_lbl, 3, 0) grid1.addWidget(self.addtool_entry, 3, 1) + bhlay = QtWidgets.QHBoxLayout() + self.addtool_btn = QtWidgets.QPushButton(_('Add')) self.addtool_btn.setToolTip( _("Add a new tool to the Tool Table\n" - "with the specified diameter.") + "with the diameter specified above.") ) - grid1.addWidget(self.addtool_btn, 4, 0, 1, 2) self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add from DB')) self.addtool_from_db_btn.setToolTip( _("Add a new tool to the Tool Table\n" "from the Tool DataBase.") ) - grid1.addWidget(self.addtool_from_db_btn, 7, 0, 1, 2) + + bhlay.addWidget(self.addtool_btn) + bhlay.addWidget(self.addtool_from_db_btn) + + grid1.addLayout(bhlay, 5, 0, 1, 2) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) @@ -1652,10 +1548,10 @@ class GeometryObjectUI(ObjectUI): # ################# GRID LAYOUT 3 ############################### # ################################################################# - grid3 = QtWidgets.QGridLayout() - grid3.setColumnStretch(0, 0) - grid3.setColumnStretch(1, 1) - self.geo_param_box.addLayout(grid3) + self.grid3 = QtWidgets.QGridLayout() + self.grid3.setColumnStretch(0, 0) + self.grid3.setColumnStretch(1, 1) + self.geo_param_box.addLayout(self.grid3) # ### Tools Data ## ## self.tool_data_label = QtWidgets.QLabel( @@ -1666,7 +1562,7 @@ class GeometryObjectUI(ObjectUI): "Each tool store it's own set of such data." ) ) - grid3.addWidget(self.tool_data_label, 0, 0, 1, 2) + self.grid3.addWidget(self.tool_data_label, 0, 0, 1, 2) # Tip Dia self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia')) @@ -1680,8 +1576,8 @@ class GeometryObjectUI(ObjectUI): self.tipdia_entry.set_range(0.00001, 9999.9999) self.tipdia_entry.setSingleStep(0.1) - grid3.addWidget(self.tipdialabel, 1, 0) - grid3.addWidget(self.tipdia_entry, 1, 1) + self.grid3.addWidget(self.tipdialabel, 1, 0) + self.grid3.addWidget(self.tipdia_entry, 1, 1) # Tip Angle self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle')) @@ -1696,8 +1592,8 @@ class GeometryObjectUI(ObjectUI): self.tipangle_entry.set_range(1.0, 180.0) self.tipangle_entry.setSingleStep(1) - grid3.addWidget(self.tipanglelabel, 2, 0) - grid3.addWidget(self.tipangle_entry, 2, 1) + self.grid3.addWidget(self.tipanglelabel, 2, 0) + self.grid3.addWidget(self.tipangle_entry, 2, 1) # Cut Z self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z')) @@ -1717,8 +1613,8 @@ class GeometryObjectUI(ObjectUI): self.cutz_entry.setSingleStep(0.1) - grid3.addWidget(self.cutzlabel, 3, 0) - grid3.addWidget(self.cutz_entry, 3, 1) + self.grid3.addWidget(self.cutzlabel, 3, 0) + self.grid3.addWidget(self.cutz_entry, 3, 1) # Multi-pass self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth")) @@ -1743,8 +1639,8 @@ class GeometryObjectUI(ObjectUI): ) self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry]) - grid3.addWidget(self.mpass_cb, 4, 0) - grid3.addWidget(self.maxdepth_entry, 4, 1) + self.grid3.addWidget(self.mpass_cb, 4, 0) + self.grid3.addWidget(self.maxdepth_entry, 4, 1) # Travel Z self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z')) @@ -1762,8 +1658,8 @@ class GeometryObjectUI(ObjectUI): self.travelz_entry.setSingleStep(0.1) - grid3.addWidget(self.travelzlabel, 5, 0) - grid3.addWidget(self.travelz_entry, 5, 1) + self.grid3.addWidget(self.travelzlabel, 5, 0) + self.grid3.addWidget(self.travelz_entry, 5, 1) # Feedrate X-Y self.frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y')) @@ -1776,8 +1672,8 @@ class GeometryObjectUI(ObjectUI): self.cncfeedrate_entry.set_range(0, 99999.9999) self.cncfeedrate_entry.setSingleStep(0.1) - grid3.addWidget(self.frlabel, 10, 0) - grid3.addWidget(self.cncfeedrate_entry, 10, 1) + self.grid3.addWidget(self.frlabel, 10, 0) + self.grid3.addWidget(self.cncfeedrate_entry, 10, 1) # Feedrate Z (Plunge) self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z')) @@ -1791,8 +1687,8 @@ class GeometryObjectUI(ObjectUI): self.feedrate_z_entry.set_range(0, 99999.9999) self.feedrate_z_entry.setSingleStep(0.1) - grid3.addWidget(self.frzlabel, 11, 0) - grid3.addWidget(self.feedrate_z_entry, 11, 1) + self.grid3.addWidget(self.frzlabel, 11, 0) + self.grid3.addWidget(self.feedrate_z_entry, 11, 1) # Feedrate rapids self.fr_rapidlabel = QtWidgets.QLabel('%s:' % _('Feedrate Rapids')) @@ -1808,8 +1704,8 @@ class GeometryObjectUI(ObjectUI): self.feedrate_rapid_entry.set_range(0, 99999.9999) self.feedrate_rapid_entry.setSingleStep(0.1) - grid3.addWidget(self.fr_rapidlabel, 12, 0) - grid3.addWidget(self.feedrate_rapid_entry, 12, 1) + self.grid3.addWidget(self.fr_rapidlabel, 12, 0) + self.grid3.addWidget(self.feedrate_rapid_entry, 12, 1) # default values is to hide self.fr_rapidlabel.hide() self.feedrate_rapid_entry.hide() @@ -1834,8 +1730,8 @@ class GeometryObjectUI(ObjectUI): "meet with last cut, we generate an\n" "extended cut over the first cut section.") ) - grid3.addWidget(self.extracut_cb, 13, 0) - grid3.addWidget(self.e_cut_entry, 13, 1) + self.grid3.addWidget(self.extracut_cb, 13, 0) + self.grid3.addWidget(self.e_cut_entry, 13, 1) # Spindlespeed self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed')) @@ -1850,8 +1746,8 @@ class GeometryObjectUI(ObjectUI): self.cncspindlespeed_entry.set_range(0, 1000000) self.cncspindlespeed_entry.set_step(100) - grid3.addWidget(self.spindle_label, 14, 0) - grid3.addWidget(self.cncspindlespeed_entry, 14, 1) + self.grid3.addWidget(self.spindle_label, 14, 0) + self.grid3.addWidget(self.cncspindlespeed_entry, 14, 1) # Dwell self.dwell_cb = FCCheckBox('%s:' % _('Dwell')) @@ -1871,8 +1767,8 @@ class GeometryObjectUI(ObjectUI): ) self.ois_dwell_geo = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry]) - grid3.addWidget(self.dwell_cb, 15, 0) - grid3.addWidget(self.dwelltime_entry, 15, 1) + self.grid3.addWidget(self.dwell_cb, 15, 0) + self.grid3.addWidget(self.dwelltime_entry, 15, 1) # Probe depth self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth")) @@ -1885,8 +1781,8 @@ class GeometryObjectUI(ObjectUI): self.pdepth_entry.set_range(-9999.9999, 9999.9999) self.pdepth_entry.setSingleStep(0.1) - grid3.addWidget(self.pdepth_label, 17, 0) - grid3.addWidget(self.pdepth_entry, 17, 1) + self.grid3.addWidget(self.pdepth_label, 17, 0) + self.grid3.addWidget(self.pdepth_entry, 17, 1) self.pdepth_label.hide() self.pdepth_entry.setVisible(False) @@ -1901,8 +1797,8 @@ class GeometryObjectUI(ObjectUI): self.feedrate_probe_entry.set_range(0.0, 9999.9999) self.feedrate_probe_entry.setSingleStep(0.1) - grid3.addWidget(self.feedrate_probe_label, 18, 0) - grid3.addWidget(self.feedrate_probe_entry, 18, 1) + self.grid3.addWidget(self.feedrate_probe_label, 18, 0) + self.grid3.addWidget(self.feedrate_probe_entry, 18, 1) self.feedrate_probe_label.hide() self.feedrate_probe_entry.setVisible(False) @@ -2024,8 +1920,12 @@ class GeometryObjectUI(ObjectUI): # grid4.addWidget(QtWidgets.QLabel(''), 12, 0, 1, 2) + # ------------------------------------------------------------------------------------------------------------ + # ------------------------- EXCLUSION AREAS ------------------------------------------------------------------ + # ------------------------------------------------------------------------------------------------------------ + # Exclusion Areas - self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas")) + self.exclusion_cb = FCCheckBox('%s' % _("Add exclusion areas")) self.exclusion_cb.setToolTip( _( "Include exclusion areas.\n" @@ -2035,9 +1935,6 @@ class GeometryObjectUI(ObjectUI): ) grid4.addWidget(self.exclusion_cb, 12, 0, 1, 2) - # ------------------------------------------------------------------------------------------------------------ - # ------------------------- EXCLUSION AREAS ------------------------------------------------------------------ - # ------------------------------------------------------------------------------------------------------------ self.exclusion_frame = QtWidgets.QFrame() self.exclusion_frame.setContentsMargins(0, 0, 0, 0) grid4.addWidget(self.exclusion_frame, 14, 0, 1, 2) @@ -2046,35 +1943,29 @@ class GeometryObjectUI(ObjectUI): self.exclusion_box.setContentsMargins(0, 0, 0, 0) self.exclusion_frame.setLayout(self.exclusion_box) - h_lay = QtWidgets.QHBoxLayout() - self.exclusion_box.addLayout(h_lay) + self.exclusion_table = FCTable() + self.exclusion_box.addWidget(self.exclusion_table) + self.exclusion_table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents) - # Button Add Area - self.add_area_button = QtWidgets.QPushButton(_('Add area')) - self.add_area_button.setToolTip(_("Add an Exclusion Area.")) - h_lay.addWidget(self.add_area_button) + self.exclusion_table.setColumnCount(4) + self.exclusion_table.setColumnWidth(0, 20) + self.exclusion_table.setHorizontalHeaderLabels(['#', _('Object'), _('Strategy'), _('Over Z')]) - # Button Delete Area - self.delete_area_button = QtWidgets.QPushButton(_('Clear areas')) - self.delete_area_button.setToolTip(_("Delete all exclusion areas.")) - h_lay.addWidget(self.delete_area_button) + self.exclusion_table.horizontalHeaderItem(0).setToolTip(_("This is the Area ID.")) + self.exclusion_table.horizontalHeaderItem(1).setToolTip( + _("Type of the object where the exclusion area was added.")) + self.exclusion_table.horizontalHeaderItem(2).setToolTip( + _("The strategy used for exclusion area. Go around the exclusion areas or over it.")) + self.exclusion_table.horizontalHeaderItem(3).setToolTip( + _("If the strategy is to go over the area then this is the height at which the tool will go to avoid the " + "exclusion area.")) - grid_l = QtWidgets.QGridLayout() - grid_l.setColumnStretch(0, 0) - grid_l.setColumnStretch(1, 1) - self.exclusion_box.addLayout(grid_l) + self.exclusion_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) - # Area Selection shape - self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape")) - self.area_shape_label.setToolTip( - _("The kind of selection shape used for area selection.") - ) - - self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, - {'label': _("Polygon"), 'value': 'polygon'}]) - - grid_l.addWidget(self.area_shape_label, 0, 0) - grid_l.addWidget(self.area_shape_radio, 0, 1) + grid_a1 = QtWidgets.QGridLayout() + grid_a1.setColumnStretch(0, 0) + grid_a1.setColumnStretch(1, 1) + self.exclusion_box.addLayout(grid_a1) # Chose Strategy self.strategy_label = FCLabel('%s:' % _("Strategy")) @@ -2085,8 +1976,8 @@ class GeometryObjectUI(ObjectUI): self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'}, {'label': _('Around'), 'value': 'around'}]) - grid_l.addWidget(self.strategy_label, 1, 0) - grid_l.addWidget(self.strategy_radio, 1, 1) + grid_a1.addWidget(self.strategy_label, 1, 0) + grid_a1.addWidget(self.strategy_radio, 1, 1) # Over Z self.over_z_label = FCLabel('%s:' % _("Over Z")) @@ -2096,12 +1987,45 @@ class GeometryObjectUI(ObjectUI): self.over_z_entry.set_range(0.000, 9999.9999) self.over_z_entry.set_precision(self.decimals) - grid_l.addWidget(self.over_z_label, 2, 0) - grid_l.addWidget(self.over_z_entry, 2, 1) + grid_a1.addWidget(self.over_z_label, 2, 0) + grid_a1.addWidget(self.over_z_entry, 2, 1) + # Button Add Area + self.add_area_button = QtWidgets.QPushButton(_('Add area:')) + self.add_area_button.setToolTip(_("Add an Exclusion Area.")) + + # Area Selection shape + self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, + {'label': _("Polygon"), 'value': 'polygon'}]) + self.area_shape_radio.setToolTip( + _("The kind of selection shape used for area selection.") + ) + + grid_a1.addWidget(self.add_area_button, 4, 0) + grid_a1.addWidget(self.area_shape_radio, 4, 1) + + h_lay_1 = QtWidgets.QHBoxLayout() + self.exclusion_box.addLayout(h_lay_1) + + # Button Delete All Areas + self.delete_area_button = QtWidgets.QPushButton(_('Delete All')) + self.delete_area_button.setToolTip(_("Delete all exclusion areas.")) + + # Button Delete Selected Areas + self.delete_sel_area_button = QtWidgets.QPushButton(_('Delete Selected')) + self.delete_sel_area_button.setToolTip(_("Delete all exclusion areas that are selected in the table.")) + + h_lay_1.addWidget(self.delete_area_button) + h_lay_1.addWidget(self.delete_sel_area_button) + + self.ois_exclusion_geo = OptionalHideInputSection(self.exclusion_cb, [self.exclusion_frame]) # -------------------------- EXCLUSION AREAS END ------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------ - self.ois_exclusion_geo = OptionalInputSection(self.exclusion_cb, [self.exclusion_frame]) + + separator_line2 = QtWidgets.QFrame() + separator_line2.setFrameShape(QtWidgets.QFrame.HLine) + separator_line2.setFrameShadow(QtWidgets.QFrame.Sunken) + grid4.addWidget(separator_line2, 15, 0, 1, 2) warning_lbl = QtWidgets.QLabel( _( @@ -2109,7 +2033,7 @@ class GeometryObjectUI(ObjectUI): "Click the # header to select all, or Ctrl + LMB\n" "for custom selection of tools." )) - grid4.addWidget(warning_lbl, 15, 0, 1, 2) + grid4.addWidget(warning_lbl, 16, 0, 1, 2) # Button self.generate_cnc_button = QtWidgets.QPushButton(_('Generate CNCJob object')) diff --git a/flatcamGUI/PlotCanvas.py b/AppGUI/PlotCanvas.py similarity index 69% rename from flatcamGUI/PlotCanvas.py rename to AppGUI/PlotCanvas.py index de7a7028..6f7cdc9f 100644 --- a/flatcamGUI/PlotCanvas.py +++ b/AppGUI/PlotCanvas.py @@ -8,13 +8,21 @@ from PyQt5 import QtCore import logging -from flatcamGUI.VisPyCanvas import VisPyCanvas, Color -from flatcamGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor -from vispy.scene.visuals import InfiniteLine, Line +from AppGUI.VisPyCanvas import VisPyCanvas, Color +from AppGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor +from vispy.scene.visuals import InfiniteLine, Line, Rectangle, Text + +import gettext +import AppTranslation as fcTranslate +import builtins import numpy as np from vispy.geometry import Rect +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + log = logging.getLogger('base') @@ -54,8 +62,12 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): if theme == 'white': self.line_color = (0.3, 0.0, 0.0, 1.0) + self.rect_hud_color = Color('#0000FF10') + self.text_hud_color = 'black' else: self.line_color = (0.4, 0.4, 0.4, 1.0) + self.rect_hud_color = Color('#80808040') + self.text_hud_color = 'white' # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node, # which might decrease performance @@ -129,11 +141,6 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 0.8), vertical=False, parent=self.view.scene) - # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area - # all CNC have a limited workspace - if self.fcapp.defaults['global_workspace'] is True: - self.draw_workspace(workspace_size=self.fcapp.defaults["global_workspaceT"]) - self.line_parent = None if self.fcapp.defaults["global_cursor_color_enabled"]: c_color = Color(self.fcapp.defaults["global_cursor_color"]).rgba @@ -146,13 +153,61 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False, parent=self.line_parent) + # font size + qsettings = QtCore.QSettings("Open Source", "FlatCAM") + if qsettings.contains("hud_font_size"): + fsize = qsettings.value('hud_font_size', type=int) + else: + fsize = 8 + + # units + units = self.fcapp.defaults["units"].lower() + + # coordinates and anchors + height = fsize * 11 # 90. Constant 11 is something that works + width = height * 2 # width is double the height = it is something that works + center_x = (width / 2) + 5 + center_y = (height / 2) + 5 + + # text + self.text_hud = Text('', color=self.text_hud_color, pos=(10, center_y), method='gpu', anchor_x='left', + parent=None) + self.text_hud.font_size = fsize + self.text_hud.text = 'Dx:\t%s [%s]\nDy:\t%s [%s]\n\nX: \t%s [%s]\nY: \t%s [%s]' % \ + ('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units) + + # rectangle + self.rect_hud = Rectangle(center=(center_x, center_y), width=width, height=height, radius=[5, 5, 5, 5], + border_color=self.rect_hud_color, color=self.rect_hud_color, parent=None) + self.rect_hud.set_gl_state(depth_test=False) + + # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area + # all CNC have a limited workspace + if self.fcapp.defaults['global_workspace'] is True: + self.draw_workspace(workspace_size=self.fcapp.defaults["global_workspaceT"]) + + # HUD Display + self.hud_enabled = False + + # enable the HUD if it is activated in FlatCAM Preferences + if self.fcapp.defaults['global_hud'] is True: + self.on_toggle_hud(state=True) + + # Axis Display + self.axis_enabled = True + + # enable Axis + self.on_toggle_axis(state=True) + + # enable Grid lines + self.grid_lines_enabled = True + self.shape_collections = [] self.shape_collection = self.new_shape_collection() self.fcapp.pool_recreated.connect(self.on_pool_recreated) self.text_collection = self.new_text_collection() - # TODO: Should be setting to show/hide CNC job annotations (global or per object) self.text_collection.enabled = True self.c = None @@ -163,6 +218,76 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.graph_event_connect('mouse_wheel', self.on_mouse_scroll) + def on_toggle_axis(self, signal=None, state=None): + if state is None: + state = not self.axis_enabled + + if state: + self.axis_enabled = True + self.v_line.parent = self.view.scene + self.h_line.parent = self.view.scene + self.fcapp.ui.axis_status_label.setStyleSheet(""" + QLabel + { + color: black; + background-color: peachpuff; + } + """) + self.fcapp.inform[str, bool].emit(_("Axis enabled."), False) + else: + self.axis_enabled = False + self.v_line.parent = None + self.h_line.parent = None + self.fcapp.ui.axis_status_label.setStyleSheet("") + self.fcapp.inform[str, bool].emit(_("Axis disabled."), False) + + def on_toggle_hud(self, signal=None, state=None): + if state is None: + state = not self.hud_enabled + + if state: + self.hud_enabled = True + self.rect_hud.parent = self.view + self.text_hud.parent = self.view + self.fcapp.defaults['global_hud'] = True + self.fcapp.ui.hud_label.setStyleSheet(""" + QLabel + { + color: black; + background-color: lightblue; + } + """) + self.fcapp.inform[str, bool].emit(_("HUD enabled."), False) + + else: + self.hud_enabled = False + self.rect_hud.parent = None + self.text_hud.parent = None + self.fcapp.defaults['global_hud'] = False + self.fcapp.ui.hud_label.setStyleSheet("") + self.fcapp.inform[str, bool].emit(_("HUD disabled."), False) + + def on_toggle_grid_lines(self): + state = not self.grid_lines_enabled + + if state: + self.grid_lines_enabled = True + self.grid.parent = self.view.scene + self.fcapp.inform[str, bool].emit(_("Grid enabled."), False) + else: + self.grid_lines_enabled = False + self.grid.parent = None + self.fcapp.inform[str, bool].emit(_("Grid disabled."), False) + + # HACK: enabling/disabling the cursor seams to somehow update the shapes on screen + # - perhaps is a bug in VisPy implementation + if self.fcapp.grid_status(): + self.fcapp.app_cursor.enabled = False + self.fcapp.app_cursor.enabled = True + else: + self.fcapp.app_cursor.enabled = True + self.fcapp.app_cursor.enabled = False + def draw_workspace(self, workspace_size): """ Draw a rectangular shape on canvas to specify our valid workspace. @@ -183,17 +308,30 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): a = np.array([(0, 0), (dims[0], 0), (dims[0], dims[1]), (0, dims[1])]) - if not self.workspace_line: - self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7), - antialias=True, method='agg', parent=self.view.scene) - else: - self.workspace_line.parent = self.view.scene + # if not self.workspace_line: + # self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7), + # antialias=True, method='agg', parent=self.view.scene) + # else: + # self.workspace_line.parent = self.view.scene + self.workspace_line = Line(pos=np.array((a[0], a[1], a[2], a[3], a[0])), color=(0.70, 0.3, 0.3, 0.7), + antialias=True, method='agg', parent=self.view.scene) + + self.fcapp.ui.wplace_label.set_value(workspace_size[:3]) + self.fcapp.ui.wplace_label.setToolTip(workspace_size) + self.fcapp.ui.wplace_label.setStyleSheet(""" + QLabel + { + color: black; + background-color: lightgreen; + } + """) def delete_workspace(self): try: self.workspace_line.parent = None except Exception: pass + self.fcapp.ui.wplace_label.setStyleSheet("") # redraw the workspace lines on the plot by re adding them to the parent view.scene def restore_workspace(self): @@ -287,6 +425,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): def on_mouse_scroll(self, event): # key modifiers modifiers = event.modifiers + pan_delta_x = self.fcapp.defaults["global_gridx"] pan_delta_y = self.fcapp.defaults["global_gridy"] curr_pos = event.pos diff --git a/flatcamGUI/PlotCanvasLegacy.py b/AppGUI/PlotCanvasLegacy.py similarity index 84% rename from flatcamGUI/PlotCanvasLegacy.py rename to AppGUI/PlotCanvasLegacy.py index df9c8231..47cd24c0 100644 --- a/flatcamGUI/PlotCanvasLegacy.py +++ b/AppGUI/PlotCanvasLegacy.py @@ -19,8 +19,10 @@ from shapely.geometry import Polygon, LineString, LinearRing from copy import deepcopy import logging +import numpy as np + import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins # Prevent conflict with Qt5 and above. @@ -29,13 +31,13 @@ mpl_use("Qt5Agg") from matplotlib.figure import Figure from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.lines import Line2D +from matplotlib.offsetbox import AnchoredText # from matplotlib.widgets import Cursor fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext - log = logging.getLogger('base') @@ -45,9 +47,9 @@ class CanvasCache(QtCore.QObject): Case story #1: 1) No objects in the project. - 2) Object is created (new_object() emits object_created(obj)). + 2) Object is created (app_obj.new_object() emits object_created(obj)). on_object_created() adds (i) object to collection and emits - (ii) new_object_available() then calls (iii) object.plot() + (ii) app_obj.new_object_available() then calls (iii) object.plot() 3) object.plot() creates axes if necessary on app.collection.figure. Then plots on it. 4) Plots on a cache-size canvas (in background). @@ -113,7 +115,7 @@ class CanvasCache(QtCore.QObject): # Continue to update the cache. - # def on_new_object_available(self): + # def on_app_obj.new_object_available(self): # # log.debug("A new object is available. Should plot it!") @@ -147,9 +149,13 @@ class PlotCanvasLegacy(QtCore.QObject): if self.app.defaults['global_theme'] == 'white': theme_color = '#FFFFFF' tick_color = '#000000' + self.rect_hud_color = '#0000FF10' + self.text_hud_color = '#000000' else: theme_color = '#000000' tick_color = '#FFFFFF' + self.rect_hud_color = '#80808040' + self.text_hud_color = '#FFFFFF' # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node, # which might decrease performance @@ -295,14 +301,163 @@ class PlotCanvasLegacy(QtCore.QObject): # signal is the mouse is dragging self.is_dragging = False + self.mouse_press_pos = None + # signal if there is a doubleclick self.is_dblclk = False + self.hud_enabled = False + self.text_hud = self.Thud(plotcanvas=self) + + # enable Grid lines + self.grid_lines_enabled = True + # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area # all CNC have a limited workspace if self.app.defaults['global_workspace'] is True: self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"]) + if self.app.defaults['global_hud'] is True: + self.on_toggle_hud(state=True) + + # Axis Display + self.axis_enabled = True + + # enable Axis + self.on_toggle_axis(state=True) + + def on_toggle_axis(self, signal=None, state=None): + if state is None: + state = not self.axis_enabled + + if state: + self.axis_enabled = True + if self.h_line not in self.axes.lines and self.v_line not in self.axes.lines: + self.h_line = self.axes.axhline(color=(0.70, 0.3, 0.3), linewidth=2) + self.v_line = self.axes.axvline(color=(0.70, 0.3, 0.3), linewidth=2) + self.app.ui.axis_status_label.setStyleSheet(""" + QLabel + { + color: black; + background-color: peachpuff; + } + """) + self.app.inform[str, bool].emit(_("Axis enabled."), False) + else: + self.axis_enabled = False + if self.h_line in self.axes.lines and self.v_line in self.axes.lines: + self.axes.lines.remove(self.h_line) + self.axes.lines.remove(self.v_line) + self.app.ui.axis_status_label.setStyleSheet("") + self.app.inform[str, bool].emit(_("Axis disabled."), False) + + self.canvas.draw() + + def on_toggle_hud(self, signal=None, state=None): + if state is None: + state = not self.hud_enabled + + if state: + self.hud_enabled = True + self.text_hud.add_artist() + self.app.defaults['global_hud'] = True + + self.app.ui.hud_label.setStyleSheet(""" + QLabel + { + color: black; + background-color: lightblue; + } + """) + self.app.inform[str, bool].emit(_("HUD enabled."), False) + else: + self.hud_enabled = False + self.text_hud.remove_artist() + self.app.defaults['global_hud'] = False + self.app.ui.hud_label.setStyleSheet("") + self.app.inform[str, bool].emit(_("HUD disabled."), False) + + self.canvas.draw() + + class Thud(QtCore.QObject): + text_changed = QtCore.pyqtSignal(str) + + def __init__(self, plotcanvas): + super().__init__() + + self.p = plotcanvas + units = self.p.app.defaults['units'] + self._text = 'Dx: %s [%s]\nDy: %s [%s]\n\nX: %s [%s]\nY: %s [%s]' % \ + ('0.0000', units, '0.0000', units, '0.0000', units, '0.0000', units) + + # set font size + qsettings = QtCore.QSettings("Open Source", "FlatCAM") + if qsettings.contains("hud_font_size"): + # I multiply with 2.5 because this seems to be the difference between the value taken by the VisPy (3D) + # and Matplotlib (Legacy2D FlatCAM graphic engine) + fsize = int(qsettings.value('hud_font_size', type=int) * 2.5) + else: + fsize = 20 + + self.hud_holder = AnchoredText(self._text, prop=dict(size=fsize), frameon=True, loc='upper left') + self.hud_holder.patch.set_boxstyle("round,pad=0.,rounding_size=0.2") + + fc_color = self.p.rect_hud_color[:-2] + fc_alpha = int(self.p.rect_hud_color[-2:], 16) / 255 + text_color = self.p.text_hud_color + + self.hud_holder.patch.set_facecolor(fc_color) + self.hud_holder.patch.set_alpha(fc_alpha) + self.hud_holder.patch.set_edgecolor((0, 0, 0, 0)) + + self. hud_holder.txt._text.set_color(color=text_color) + self.text_changed.connect(self.on_text_changed) + + @property + def text(self): + return self._text + + @text.setter + def text(self, val): + self.text_changed.emit(val) + self._text = val + + def on_text_changed(self, txt): + try: + txt = txt.replace('\t', ' ') + self.hud_holder.txt.set_text(txt) + self.p.canvas.draw() + except Exception: + pass + + def add_artist(self): + if self.hud_holder not in self.p.axes.artists: + self.p.axes.add_artist(self.hud_holder) + + def remove_artist(self): + if self.hud_holder in self.p.axes.artists: + self.p.axes.artists.remove(self.hud_holder) + + def on_toggle_grid_lines(self): + state = not self.grid_lines_enabled + + if state: + self.grid_lines_enabled = True + self.axes.grid(True) + try: + self.canvas.draw() + except IndexError: + pass + self.app.inform[str, bool].emit(_("Grid enabled."), False) + else: + self.grid_lines_enabled = False + self.axes.grid(False) + try: + self.canvas.draw() + except IndexError: + pass + self.app.inform[str, bool].emit(_("Grid disabled."), False) + def draw_workspace(self, workspace_size): """ Draw a rectangular shape on canvas to specify our valid workspace. @@ -329,12 +484,23 @@ class PlotCanvasLegacy(QtCore.QObject): self.axes.add_line(self.workspace_line) self.canvas.draw() + self.app.ui.wplace_label.set_value(workspace_size[:3]) + self.app.ui.wplace_label.setToolTip(workspace_size) + self.fcapp.ui.wplace_label.setStyleSheet(""" + QLabel + { + color: black; + background-color: lightgreen; + } + """) + def delete_workspace(self): try: self.axes.lines.remove(self.workspace_line) self.canvas.draw() except Exception: pass + self.fcapp.ui.wplace_label.setStyleSheet("") def graph_event_connect(self, event_name, callback): """ @@ -423,7 +589,7 @@ class PlotCanvasLegacy(QtCore.QObject): if self.big_cursor is False: try: - x, y = self.app.geo_editor.snap(x_pos, y_pos) + x, y = self.snap(x_pos, y_pos) # Pointer (snapped) # The size of the cursor is multiplied by 1.65 because that value made the cursor similar with the @@ -456,7 +622,7 @@ class PlotCanvasLegacy(QtCore.QObject): pass self.canvas.draw_idle() - self.canvas.blit(self.axes.bbox) + self.canvas.blit(self.axes.bbox) def clear_cursor(self, state): if state is True: @@ -781,6 +947,7 @@ class PlotCanvasLegacy(QtCore.QObject): def on_mouse_press(self, event): self.is_dragging = True + self.mouse_press_pos = (event.x, event.y) # Check for middle mouse button press if self.app.defaults["global_pan_button"] == '2': @@ -806,7 +973,11 @@ class PlotCanvasLegacy(QtCore.QObject): def on_mouse_release(self, event): - self.is_dragging = False + mouse_release_pos = (event.x, event.y) + delta = 0.05 + + if abs(self.distance(self.mouse_press_pos, mouse_release_pos)) < delta: + self.is_dragging = False # Check for middle mouse button release to complete pan procedure # Check for middle mouse button press @@ -858,7 +1029,7 @@ class PlotCanvasLegacy(QtCore.QObject): self.canvas.draw_idle() # #### Temporary place-holder for cached update ##### - self.update_screen_request.emit([0, 0, 0, 0, 0]) + # self.update_screen_request.emit([0, 0, 0, 0, 0]) if self.app.defaults["global_cursor_color_enabled"] is True: self.draw_cursor(x_pos=x, y_pos=y, color=self.app.cursor_color_3D) @@ -910,6 +1081,59 @@ class PlotCanvasLegacy(QtCore.QObject): return width / xpx, height / ypx + def snap(self, x, y): + """ + Adjusts coordinates to snap settings. + + :param x: Input coordinate X + :param y: Input coordinate Y + :return: Snapped (x, y) + """ + + snap_x, snap_y = (x, y) + snap_distance = np.Inf + + # ### Grid snap + if self.app.grid_status(): + if self.app.defaults["global_gridx"] != 0: + try: + snap_x_ = round(x / float(self.app.defaults["global_gridx"])) * \ + float(self.app.defaults["global_gridx"]) + except TypeError: + snap_x_ = x + else: + snap_x_ = x + + # If the Grid_gap_linked on Grid Toolbar is checked then the snap distance on GridY entry will be ignored + # and it will use the snap distance from GridX entry + if self.app.ui.grid_gap_link_cb.isChecked(): + if self.app.defaults["global_gridx"] != 0: + try: + snap_y_ = round(y / float(self.app.defaults["global_gridx"])) * \ + float(self.app.defaults["global_gridx"]) + except TypeError: + snap_y_ = y + else: + snap_y_ = y + else: + if self.app.defaults["global_gridy"] != 0: + try: + snap_y_ = round(y / float(self.app.defaults["global_gridy"])) * \ + float(self.app.defaults["global_gridy"]) + except TypeError: + snap_y_ = y + else: + snap_y_ = y + nearest_grid_distance = self.distance((x, y), (snap_x_, snap_y_)) + if nearest_grid_distance < snap_distance: + snap_x, snap_y = (snap_x_, snap_y_) + + return snap_x, snap_y + + @staticmethod + def distance(pt1, pt2): + return np.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) + class FakeCursor(QtCore.QObject): """ @@ -948,10 +1172,10 @@ class ShapeCollectionLegacy: """ :param obj: This is the object to which the shapes collection is attached and for - which it will have to draw shapes + which it will have to draw shapes :param app: This is the FLatCAM.App usually, needed because we have to access attributes there :param name: This is the name given to the Matplotlib axes; it needs to be unique due of - Matplotlib requurements + Matplotlib requurements :param annotation_job: Make this True if the job needed is just for annotation :param linewidth: THe width of the line (outline where is the case) """ diff --git a/flatcamGUI/VisPyCanvas.py b/AppGUI/VisPyCanvas.py similarity index 99% rename from flatcamGUI/VisPyCanvas.py rename to AppGUI/VisPyCanvas.py index 7d7efe13..aa55675f 100644 --- a/flatcamGUI/VisPyCanvas.py +++ b/AppGUI/VisPyCanvas.py @@ -13,6 +13,7 @@ import numpy as np import vispy.scene as scene from vispy.scene.cameras.base_camera import BaseCamera +# from vispy.scene.widgets import Widget as VisPyWidget from vispy.color import Color import time diff --git a/flatcamGUI/VisPyData/data/fonts/opensans-regular.ttf b/AppGUI/VisPyData/data/fonts/opensans-regular.ttf similarity index 100% rename from flatcamGUI/VisPyData/data/fonts/opensans-regular.ttf rename to AppGUI/VisPyData/data/fonts/opensans-regular.ttf diff --git a/flatcamGUI/VisPyData/data/freetype/freetype253.dll b/AppGUI/VisPyData/data/freetype/freetype253.dll similarity index 100% rename from flatcamGUI/VisPyData/data/freetype/freetype253.dll rename to AppGUI/VisPyData/data/freetype/freetype253.dll diff --git a/flatcamGUI/VisPyData/data/freetype/freetype253_x64.dll b/AppGUI/VisPyData/data/freetype/freetype253_x64.dll similarity index 100% rename from flatcamGUI/VisPyData/data/freetype/freetype253_x64.dll rename to AppGUI/VisPyData/data/freetype/freetype253_x64.dll diff --git a/flatcamGUI/VisPyPatches.py b/AppGUI/VisPyPatches.py similarity index 100% rename from flatcamGUI/VisPyPatches.py rename to AppGUI/VisPyPatches.py diff --git a/flatcamGUI/VisPyTesselators.py b/AppGUI/VisPyTesselators.py similarity index 100% rename from flatcamGUI/VisPyTesselators.py rename to AppGUI/VisPyTesselators.py diff --git a/flatcamGUI/VisPyVisuals.py b/AppGUI/VisPyVisuals.py similarity index 99% rename from flatcamGUI/VisPyVisuals.py rename to AppGUI/VisPyVisuals.py index 9cf81e54..3796f759 100644 --- a/flatcamGUI/VisPyVisuals.py +++ b/AppGUI/VisPyVisuals.py @@ -13,7 +13,7 @@ from vispy.color import Color from shapely.geometry import Polygon, LineString, LinearRing import threading import numpy as np -from flatcamGUI.VisPyTesselators import GLUTess +from AppGUI.VisPyTesselators import GLUTess class FlatCAMLineVisual(LineVisual): diff --git a/flatcamGUI/__init__.py b/AppGUI/__init__.py similarity index 100% rename from flatcamGUI/__init__.py rename to AppGUI/__init__.py diff --git a/flatcamGUI/preferences/OptionsGroupUI.py b/AppGUI/preferences/OptionsGroupUI.py similarity index 100% rename from flatcamGUI/preferences/OptionsGroupUI.py rename to AppGUI/preferences/OptionsGroupUI.py diff --git a/flatcamGUI/preferences/PreferencesUIManager.py b/AppGUI/preferences/PreferencesUIManager.py similarity index 80% rename from flatcamGUI/preferences/PreferencesUIManager.py rename to AppGUI/preferences/PreferencesUIManager.py index 14fa8762..a712c127 100644 --- a/flatcamGUI/preferences/PreferencesUIManager.py +++ b/AppGUI/preferences/PreferencesUIManager.py @@ -5,7 +5,7 @@ from defaults import FlatCAMDefaults import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -29,7 +29,7 @@ class PreferencesUIManager: :param defaults: a dictionary storage where all the application settings are stored :param data_path: a path to the file where all the preferences are stored for persistence - :param ui: reference to the FlatCAMGUI class which constructs the UI + :param ui: reference to the MainGUI class which constructs the UI :param inform: a pyqtSignal used to display information's in the StatusBar of the GUI """ @@ -43,7 +43,7 @@ class PreferencesUIManager: self.preferences_changed_flag = False # when adding entries here read the comments in the method found below named: - # def new_object(self, kind, name, initialize, active=True, fit=True, plot=True) + # def app_obj.new_object(self, kind, name, initialize, active=True, fit=True, plot=True) self.defaults_form_fields = { # General App "decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry, @@ -127,12 +127,6 @@ class PreferencesUIManager: "gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry, # Gerber Options - "gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry, - "gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry, - "gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry, - "gerber_combine_passes": self.ui.gerber_defaults_form.gerber_opt_group.combine_passes_cb, - "gerber_iso_scope": self.ui.gerber_defaults_form.gerber_opt_group.iso_scope_radio, - "gerber_milling_type": self.ui.gerber_defaults_form.gerber_opt_group.milling_type_radio, "gerber_noncoppermargin": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_margin_entry, "gerber_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb, "gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry, @@ -143,12 +137,6 @@ class PreferencesUIManager: # "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry, # "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry, "gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb, - "gerber_tool_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.tool_type_radio, - "gerber_vtipdia": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipdia_spinner, - "gerber_vtipangle": self.ui.gerber_defaults_form.gerber_adv_opt_group.tipangle_spinner, - "gerber_vcutz": self.ui.gerber_defaults_form.gerber_adv_opt_group.cutz_spinner, - "gerber_iso_type": self.ui.gerber_defaults_form.gerber_adv_opt_group.iso_type_radio, - "gerber_buffering": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffering_radio, "gerber_simplification": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplify_cb, "gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner, @@ -180,6 +168,7 @@ class PreferencesUIManager: # Excellon General "excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb, "excellon_solid": self.ui.excellon_defaults_form.excellon_gen_group.solid_cb, + "excellon_multicolored": self.ui.excellon_defaults_form.excellon_gen_group.multicolored_cb, "excellon_format_upper_in": self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry, "excellon_format_lower_in": @@ -221,31 +210,31 @@ class PreferencesUIManager: "excellon_gcode_type": self.ui.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio, # Excellon Advanced Options - "excellon_offset": self.ui.excellon_defaults_form.excellon_adv_opt_group.offset_entry, - "excellon_toolchangexy": self.ui.excellon_defaults_form.excellon_adv_opt_group.toolchangexy_entry, - "excellon_startz": self.ui.excellon_defaults_form.excellon_adv_opt_group.estartz_entry, - "excellon_feedrate_rapid": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_rapid_entry, - "excellon_z_pdepth": self.ui.excellon_defaults_form.excellon_adv_opt_group.pdepth_entry, - "excellon_feedrate_probe": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_probe_entry, - "excellon_spindledir": self.ui.excellon_defaults_form.excellon_adv_opt_group.spindledir_radio, - "excellon_f_plunge": self.ui.excellon_defaults_form.excellon_adv_opt_group.fplunge_cb, - "excellon_f_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb, + "excellon_offset": self.ui.excellon_defaults_form.excellon_adv_opt_group.offset_entry, + "excellon_toolchangexy": self.ui.excellon_defaults_form.excellon_adv_opt_group.toolchangexy_entry, + "excellon_startz": self.ui.excellon_defaults_form.excellon_adv_opt_group.estartz_entry, + "excellon_feedrate_rapid": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_rapid_entry, + "excellon_z_pdepth": self.ui.excellon_defaults_form.excellon_adv_opt_group.pdepth_entry, + "excellon_feedrate_probe": self.ui.excellon_defaults_form.excellon_adv_opt_group.feedrate_probe_entry, + "excellon_spindledir": self.ui.excellon_defaults_form.excellon_adv_opt_group.spindledir_radio, + "excellon_f_plunge": self.ui.excellon_defaults_form.excellon_adv_opt_group.fplunge_cb, + "excellon_f_retract": self.ui.excellon_defaults_form.excellon_adv_opt_group.fretract_cb, # Excellon Export - "excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio, - "excellon_exp_format": self.ui.excellon_defaults_form.excellon_exp_group.format_radio, - "excellon_exp_integer": self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry, - "excellon_exp_decimals": self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry, - "excellon_exp_zeros": self.ui.excellon_defaults_form.excellon_exp_group.zeros_radio, - "excellon_exp_slot_type": self.ui.excellon_defaults_form.excellon_exp_group.slot_type_radio, + "excellon_exp_units": self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio, + "excellon_exp_format": self.ui.excellon_defaults_form.excellon_exp_group.format_radio, + "excellon_exp_integer": self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry, + "excellon_exp_decimals": self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry, + "excellon_exp_zeros": self.ui.excellon_defaults_form.excellon_exp_group.zeros_radio, + "excellon_exp_slot_type": self.ui.excellon_defaults_form.excellon_exp_group.slot_type_radio, # Excellon Editor - "excellon_editor_sel_limit": self.ui.excellon_defaults_form.excellon_editor_group.sel_limit_entry, - "excellon_editor_newdia": self.ui.excellon_defaults_form.excellon_editor_group.addtool_entry, - "excellon_editor_array_size": self.ui.excellon_defaults_form.excellon_editor_group.drill_array_size_entry, - "excellon_editor_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_axis_radio, - "excellon_editor_lin_pitch": self.ui.excellon_defaults_form.excellon_editor_group.drill_pitch_entry, - "excellon_editor_lin_angle": self.ui.excellon_defaults_form.excellon_editor_group.drill_angle_entry, + "excellon_editor_sel_limit": self.ui.excellon_defaults_form.excellon_editor_group.sel_limit_entry, + "excellon_editor_newdia": self.ui.excellon_defaults_form.excellon_editor_group.addtool_entry, + "excellon_editor_array_size": self.ui.excellon_defaults_form.excellon_editor_group.drill_array_size_entry, + "excellon_editor_lin_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_axis_radio, + "excellon_editor_lin_pitch": self.ui.excellon_defaults_form.excellon_editor_group.drill_pitch_entry, + "excellon_editor_lin_angle": self.ui.excellon_defaults_form.excellon_editor_group.drill_angle_entry, "excellon_editor_circ_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_dir_radio, "excellon_editor_circ_angle": self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_angle_entry, @@ -270,94 +259,117 @@ class PreferencesUIManager: self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_angle_entry, # Geometry General - "geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb, - "geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry, - "geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry, - "geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry, + "geometry_plot": self.ui.geometry_defaults_form.geometry_gen_group.plot_cb, + "geometry_multicolored": self.ui.geometry_defaults_form.geometry_gen_group.multicolored_cb, + "geometry_circle_steps": self.ui.geometry_defaults_form.geometry_gen_group.circle_steps_entry, + "geometry_cnctooldia": self.ui.geometry_defaults_form.geometry_gen_group.cnctooldia_entry, + "geometry_plot_line": self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry, # Geometry Options - "geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry, - "geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry, - "geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry, - "geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.feedrate_z_entry, - "geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry, - "geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb, - "geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry, - "geometry_ppname_g": self.ui.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb, - "geometry_toolchange": self.ui.geometry_defaults_form.geometry_opt_group.toolchange_cb, - "geometry_toolchangez": self.ui.geometry_defaults_form.geometry_opt_group.toolchangez_entry, - "geometry_endz": self.ui.geometry_defaults_form.geometry_opt_group.endz_entry, - "geometry_endxy": self.ui.geometry_defaults_form.geometry_opt_group.endxy_entry, - "geometry_depthperpass": self.ui.geometry_defaults_form.geometry_opt_group.depthperpass_entry, - "geometry_multidepth": self.ui.geometry_defaults_form.geometry_opt_group.multidepth_cb, + "geometry_cutz": self.ui.geometry_defaults_form.geometry_opt_group.cutz_entry, + "geometry_travelz": self.ui.geometry_defaults_form.geometry_opt_group.travelz_entry, + "geometry_feedrate": self.ui.geometry_defaults_form.geometry_opt_group.cncfeedrate_entry, + "geometry_feedrate_z": self.ui.geometry_defaults_form.geometry_opt_group.feedrate_z_entry, + "geometry_spindlespeed": self.ui.geometry_defaults_form.geometry_opt_group.cncspindlespeed_entry, + "geometry_dwell": self.ui.geometry_defaults_form.geometry_opt_group.dwell_cb, + "geometry_dwelltime": self.ui.geometry_defaults_form.geometry_opt_group.dwelltime_entry, + "geometry_ppname_g": self.ui.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb, + "geometry_toolchange": self.ui.geometry_defaults_form.geometry_opt_group.toolchange_cb, + "geometry_toolchangez": self.ui.geometry_defaults_form.geometry_opt_group.toolchangez_entry, + "geometry_endz": self.ui.geometry_defaults_form.geometry_opt_group.endz_entry, + "geometry_endxy": self.ui.geometry_defaults_form.geometry_opt_group.endxy_entry, + "geometry_depthperpass": self.ui.geometry_defaults_form.geometry_opt_group.depthperpass_entry, + "geometry_multidepth": self.ui.geometry_defaults_form.geometry_opt_group.multidepth_cb, # Geometry Advanced Options - "geometry_toolchangexy": self.ui.geometry_defaults_form.geometry_adv_opt_group.toolchangexy_entry, - "geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry, - "geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_rapid_entry, - "geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb, + "geometry_toolchangexy": self.ui.geometry_defaults_form.geometry_adv_opt_group.toolchangexy_entry, + "geometry_startz": self.ui.geometry_defaults_form.geometry_adv_opt_group.gstartz_entry, + "geometry_feedrate_rapid": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_rapid_entry, + "geometry_extracut": self.ui.geometry_defaults_form.geometry_adv_opt_group.extracut_cb, "geometry_extracut_length": self.ui.geometry_defaults_form.geometry_adv_opt_group.e_cut_entry, - "geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry, - "geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry, - "geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio, - "geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb, - "geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry, - "geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry, - "geometry_area_exclusion": self.ui.geometry_defaults_form.geometry_adv_opt_group.exclusion_cb, - "geometry_area_shape": self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio, - "geometry_area_strategy": self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio, - "geometry_area_overz": self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry, + "geometry_z_pdepth": self.ui.geometry_defaults_form.geometry_adv_opt_group.pdepth_entry, + "geometry_feedrate_probe": self.ui.geometry_defaults_form.geometry_adv_opt_group.feedrate_probe_entry, + "geometry_spindledir": self.ui.geometry_defaults_form.geometry_adv_opt_group.spindledir_radio, + "geometry_f_plunge": self.ui.geometry_defaults_form.geometry_adv_opt_group.fplunge_cb, + "geometry_segx": self.ui.geometry_defaults_form.geometry_adv_opt_group.segx_entry, + "geometry_segy": self.ui.geometry_defaults_form.geometry_adv_opt_group.segy_entry, + "geometry_area_exclusion": self.ui.geometry_defaults_form.geometry_adv_opt_group.exclusion_cb, + "geometry_area_shape": self.ui.geometry_defaults_form.geometry_adv_opt_group.area_shape_radio, + "geometry_area_strategy": self.ui.geometry_defaults_form.geometry_adv_opt_group.strategy_radio, + "geometry_area_overz": self.ui.geometry_defaults_form.geometry_adv_opt_group.over_z_entry, # Geometry Editor - "geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry, - "geometry_editor_milling_type": self.ui.geometry_defaults_form.geometry_editor_group.milling_type_radio, + "geometry_editor_sel_limit": self.ui.geometry_defaults_form.geometry_editor_group.sel_limit_entry, + "geometry_editor_milling_type": self.ui.geometry_defaults_form.geometry_editor_group.milling_type_radio, # CNCJob General - "cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb, - "cncjob_plot_kind": self.ui.cncjob_defaults_form.cncjob_gen_group.cncplot_method_radio, - "cncjob_annotation": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_cb, + "cncjob_plot": self.ui.cncjob_defaults_form.cncjob_gen_group.plot_cb, + "cncjob_plot_kind": self.ui.cncjob_defaults_form.cncjob_gen_group.cncplot_method_radio, + "cncjob_annotation": self.ui.cncjob_defaults_form.cncjob_gen_group.annotation_cb, - "cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry, - "cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio, - "cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry, - "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry, - "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry, - "cncjob_line_ending": self.ui.cncjob_defaults_form.cncjob_gen_group.line_ending_cb, - "cncjob_plot_line": self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry, - "cncjob_plot_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry, - "cncjob_travel_line": self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry, - "cncjob_travel_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry, + "cncjob_tooldia": self.ui.cncjob_defaults_form.cncjob_gen_group.tooldia_entry, + "cncjob_coords_type": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_type_radio, + "cncjob_coords_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.coords_dec_entry, + "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry, + "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry, + "cncjob_line_ending": self.ui.cncjob_defaults_form.cncjob_gen_group.line_ending_cb, + "cncjob_plot_line": self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry, + "cncjob_plot_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry, + "cncjob_travel_line": self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry, + "cncjob_travel_fill": self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry, # CNC Job Options - "cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text, - "cncjob_append": self.ui.cncjob_defaults_form.cncjob_opt_group.append_text, + "cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text, + "cncjob_append": self.ui.cncjob_defaults_form.cncjob_opt_group.append_text, # CNC Job Advanced Options - "cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text, - "cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb, - "cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp, + "cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text, + "cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb, + "cncjob_annotation_fontsize": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontsize_sp, "cncjob_annotation_fontcolor": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry, + # Isolation Routing Tool + "tools_iso_tooldia": self.ui.tools_defaults_form.tools_iso_group.tool_dia_entry, + "tools_iso_order": self.ui.tools_defaults_form.tools_iso_group.order_radio, + "tools_iso_tool_type": self.ui.tools_defaults_form.tools_iso_group.tool_type_radio, + "tools_iso_tool_vtipdia": self.ui.tools_defaults_form.tools_iso_group.tipdia_entry, + "tools_iso_tool_vtipangle": self.ui.tools_defaults_form.tools_iso_group.tipangle_entry, + "tools_iso_tool_cutz": self.ui.tools_defaults_form.tools_iso_group.cutz_entry, + "tools_iso_newdia": self.ui.tools_defaults_form.tools_iso_group.newdia_entry, + + "tools_iso_passes": self.ui.tools_defaults_form.tools_iso_group.passes_entry, + "tools_iso_overlap": self.ui.tools_defaults_form.tools_iso_group.overlap_entry, + "tools_iso_milling_type": self.ui.tools_defaults_form.tools_iso_group.milling_type_radio, + "tools_iso_follow": self.ui.tools_defaults_form.tools_iso_group.follow_cb, + "tools_iso_isotype": self.ui.tools_defaults_form.tools_iso_group.iso_type_radio, + + "tools_iso_rest": self.ui.tools_defaults_form.tools_iso_group.rest_cb, + "tools_iso_combine_passes": self.ui.tools_defaults_form.tools_iso_group.combine_passes_cb, + "tools_iso_isoexcept": self.ui.tools_defaults_form.tools_iso_group.except_cb, + "tools_iso_selection": self.ui.tools_defaults_form.tools_iso_group.select_combo, + "tools_iso_area_shape": self.ui.tools_defaults_form.tools_iso_group.area_shape_radio, + "tools_iso_plotting": self.ui.tools_defaults_form.tools_iso_group.plotting_radio, + # NCC Tool - "tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry, - "tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio, - "tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry, - "tools_nccmargin": self.ui.tools_defaults_form.tools_ncc_group.ncc_margin_entry, - "tools_nccmethod": self.ui.tools_defaults_form.tools_ncc_group.ncc_method_combo, - "tools_nccconnect": self.ui.tools_defaults_form.tools_ncc_group.ncc_connect_cb, - "tools_ncccontour": self.ui.tools_defaults_form.tools_ncc_group.ncc_contour_cb, - "tools_nccrest": self.ui.tools_defaults_form.tools_ncc_group.ncc_rest_cb, - "tools_ncc_offset_choice": self.ui.tools_defaults_form.tools_ncc_group.ncc_choice_offset_cb, - "tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner, - "tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo, - "tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio, - "tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.ncc_plotting_radio, - "tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio, - "tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio, - "tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry, - "tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry, - "tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry, - "tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry, + "tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry, + "tools_nccorder": self.ui.tools_defaults_form.tools_ncc_group.ncc_order_radio, + "tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry, + "tools_nccmargin": self.ui.tools_defaults_form.tools_ncc_group.ncc_margin_entry, + "tools_nccmethod": self.ui.tools_defaults_form.tools_ncc_group.ncc_method_combo, + "tools_nccconnect": self.ui.tools_defaults_form.tools_ncc_group.ncc_connect_cb, + "tools_ncccontour": self.ui.tools_defaults_form.tools_ncc_group.ncc_contour_cb, + "tools_nccrest": self.ui.tools_defaults_form.tools_ncc_group.ncc_rest_cb, + "tools_ncc_offset_choice": self.ui.tools_defaults_form.tools_ncc_group.ncc_choice_offset_cb, + "tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner, + "tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.select_combo, + "tools_ncc_area_shape": self.ui.tools_defaults_form.tools_ncc_group.area_shape_radio, + "tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio, + "tools_ncctool_type": self.ui.tools_defaults_form.tools_ncc_group.tool_type_radio, + "tools_ncccutz": self.ui.tools_defaults_form.tools_ncc_group.cutz_entry, + "tools_ncctipdia": self.ui.tools_defaults_form.tools_ncc_group.tipdia_entry, + "tools_ncctipangle": self.ui.tools_defaults_form.tools_ncc_group.tipangle_entry, + "tools_nccnewdia": self.ui.tools_defaults_form.tools_ncc_group.newdia_entry, + "tools_ncc_plotting": self.ui.tools_defaults_form.tools_ncc_group.plotting_radio, # CutOut Tool "tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry, @@ -467,6 +479,12 @@ class PreferencesUIManager: "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo, "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb, + # Corner Markers Tool + + "tools_corners_thickness": self.ui.tools_defaults_form.tools_corners_group.thick_entry, + "tools_corners_length": self.ui.tools_defaults_form.tools_corners_group.l_entry, + "tools_corners_margin": self.ui.tools_defaults_form.tools_corners_group.margin_entry, + # ####################################################################################################### # ########################################## TOOLS 2 #################################################### # ####################################################################################################### @@ -650,7 +668,7 @@ class PreferencesUIManager: def show_preferences_gui(self): """ - Called to initialize and show the Preferences GUI + Called to initialize and show the Preferences AppGUI :return: None """ @@ -913,7 +931,51 @@ class PreferencesUIManager: # make sure we update the self.current_defaults dict used to undo changes to self.defaults self.defaults.current_defaults.update(self.defaults) - if save_to_file: + # deal with theme change + theme_settings = QtCore.QSettings("Open Source", "FlatCAM") + if theme_settings.contains("theme"): + theme = theme_settings.value('theme', type=str) + else: + theme = 'white' + + should_restart = False + theme_new_val = self.ui.general_defaults_form.general_gui_group.theme_radio.get_value() + + ge = self.defaults["global_graphic_engine"] + ge_val = self.ui.general_defaults_form.general_app_group.ge_radio.get_value() + + if theme_new_val != theme or ge != ge_val: + msgbox = QtWidgets.QMessageBox() + msgbox.setText(_("Are you sure you want to continue?")) + msgbox.setWindowTitle(_("Application restart")) + msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/warning.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + + bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) + msgbox.addButton(_('Cancel'), QtWidgets.QMessageBox.NoRole) + + msgbox.setDefaultButton(bt_yes) + msgbox.exec_() + response = msgbox.clickedButton() + + if theme_new_val != theme: + if response == bt_yes: + theme_settings.setValue('theme', theme_new_val) + + # This will write the setting to the platform specific storage. + del theme_settings + + should_restart = True + else: + self.ui.general_defaults_form.general_gui_group.theme_radio.set_value(theme) + else: + if response == bt_yes: + self.defaults["global_graphic_engine"] = ge_val + should_restart = True + else: + self.ui.general_defaults_form.general_app_group.ge_radio.set_value(ge) + + if save_to_file or should_restart is True: self.save_defaults(silent=False) # load the defaults so they are updated into the app self.defaults.load(filename=os.path.join(self.data_path, 'current_defaults.FlatConfig')) @@ -935,6 +997,10 @@ class PreferencesUIManager: tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value() settgs.setValue('textbox_font_size', tb_fsize) + # save the HUD font size + hud_fsize = self.ui.general_defaults_form.general_app_set_group.hud_font_size_spinner.get_value() + settgs.setValue('hud_font_size', hud_fsize) + settgs.setValue( 'machinist', 1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0 @@ -951,6 +1017,9 @@ class PreferencesUIManager: self.ui.plot_tab_area.closeTab(idx) break + if should_restart is True: + self.ui.app.on_app_restart() + def on_pref_close_button(self): # Preferences saved, update flag self.preferences_changed_flag = False @@ -1000,6 +1069,7 @@ class PreferencesUIManager: :return: None """ self.defaults.report_usage("save_defaults") + log.debug("App.PreferencesUIManager.save_defaults()") if data_path is None: data_path = self.data_path @@ -1036,7 +1106,7 @@ class PreferencesUIManager: if self.ui.toolbarfile.isVisible(): tb_status += 1 - if self.ui.toolbargeo.isVisible(): + if self.ui.toolbaredit.isVisible(): tb_status += 2 if self.ui.toolbarview.isVisible(): @@ -1054,7 +1124,7 @@ class PreferencesUIManager: if self.ui.grb_edit_toolbar.isVisible(): tb_status += 64 - if self.ui.snap_toolbar.isVisible(): + if self.ui.status_toolbar.isVisible(): tb_status += 128 if self.ui.toolbarshell.isVisible(): @@ -1118,6 +1188,7 @@ class PreferencesUIManager: "Do you want to save the Preferences?")) msgbox.setWindowTitle(_("Save Preferences")) msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/save_as.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) diff --git a/flatcamGUI/preferences/__init__.py b/AppGUI/preferences/__init__.py similarity index 79% rename from flatcamGUI/preferences/__init__.py rename to AppGUI/preferences/__init__.py index 620fbb59..193fef34 100644 --- a/flatcamGUI/preferences/__init__.py +++ b/AppGUI/preferences/__init__.py @@ -1,6 +1,6 @@ -from flatcamGUI.GUIElements import * +from AppGUI.GUIElements import * import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins diff --git a/flatcamGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py b/AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py rename to AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py index 8d7a1cf0..f4809915 100644 --- a/flatcamGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py +++ b/AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py @@ -1,10 +1,10 @@ from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import QSettings, Qt -from flatcamGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py b/AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py similarity index 98% rename from flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py rename to AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py index 66276e23..467b6330 100644 --- a/flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py +++ b/AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py @@ -1,10 +1,10 @@ from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py b/AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py similarity index 94% rename from flatcamGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py rename to AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py index c8eb7e66..5dab3cff 100644 --- a/flatcamGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py +++ b/AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCTextArea -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCTextArea +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/cncjob/CNCJobPreferencesUI.py b/AppGUI/preferences/cncjob/CNCJobPreferencesUI.py similarity index 77% rename from flatcamGUI/preferences/cncjob/CNCJobPreferencesUI.py rename to AppGUI/preferences/cncjob/CNCJobPreferencesUI.py index cc9a7e32..79760423 100644 --- a/flatcamGUI/preferences/cncjob/CNCJobPreferencesUI.py +++ b/AppGUI/preferences/cncjob/CNCJobPreferencesUI.py @@ -1,8 +1,8 @@ from PyQt5 import QtWidgets -from flatcamGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI -from flatcamGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI -from flatcamGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI +from AppGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI +from AppGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI +from AppGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI class CNCJobPreferencesUI(QtWidgets.QWidget): diff --git a/flatcamGUI/preferences/cncjob/__init__.py b/AppGUI/preferences/cncjob/__init__.py similarity index 100% rename from flatcamGUI/preferences/cncjob/__init__.py rename to AppGUI/preferences/cncjob/__init__.py diff --git a/flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py b/AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py similarity index 94% rename from flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py rename to AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py index a63998f9..dd267816 100644 --- a/flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py +++ b/AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py @@ -1,10 +1,10 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, FCEntry, FloatEntry, RadioSet, FCCheckBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, RadioSet, FCCheckBox, NumericalEvalTupleEntry, NumericalEvalEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -60,7 +60,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI): toolchange_xy_label.setToolTip( _("Toolchange X,Y position.") ) - self.toolchangexy_entry = FCEntry() + self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9') grid1.addWidget(toolchange_xy_label, 1, 0) grid1.addWidget(self.toolchangexy_entry, 1, 1) @@ -71,7 +71,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI): _("Height of the tool just after start.\n" "Delete the value if you don't need this feature.") ) - self.estartz_entry = FloatEntry() + self.estartz_entry = NumericalEvalEntry(border_color='#0069A9') grid1.addWidget(startzlabel, 2, 0) grid1.addWidget(self.estartz_entry, 2, 1) diff --git a/flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py b/AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py similarity index 98% rename from flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py rename to AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py index 5b04cf2f..25f38aea 100644 --- a/flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py +++ b/AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/excellon/ExcellonExpPrefGroupUI.py b/AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/excellon/ExcellonExpPrefGroupUI.py rename to AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py index db2e922a..4cab1c6e 100644 --- a/flatcamGUI/preferences/excellon/ExcellonExpPrefGroupUI.py +++ b/AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py @@ -1,10 +1,10 @@ from PyQt5 import QtWidgets, QtCore from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import RadioSet, FCSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py b/AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py similarity index 76% rename from flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py rename to AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py index 06d4dfc7..174568a2 100644 --- a/flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py +++ b/AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py @@ -3,10 +3,10 @@ import platform from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -36,22 +36,31 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI): grid1 = QtWidgets.QGridLayout() self.layout.addLayout(grid1) + # Plot CB self.plot_cb = FCCheckBox(label=_('Plot')) self.plot_cb.setToolTip( "Plot (show) this object." ) grid1.addWidget(self.plot_cb, 0, 0) + # Solid CB self.solid_cb = FCCheckBox(label=_('Solid')) self.solid_cb.setToolTip( "Plot as solid circles." ) grid1.addWidget(self.solid_cb, 0, 1) + # Multicolored CB + self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color')) + self.multicolored_cb.setToolTip( + _("Draw polygons in different colors.") + ) + grid1.addWidget(self.multicolored_cb, 0, 2) + separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid1.addWidget(separator_line, 1, 0, 1, 2) + grid1.addWidget(separator_line, 1, 0, 1, 3) grid2 = QtWidgets.QGridLayout() self.layout.addLayout(grid2) @@ -341,6 +350,12 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI): # Load the defaults values into the Excellon Format and Excellon Zeros fields self.excellon_defaults_button.clicked.connect(self.on_excellon_defaults_button) + # Make sure that when the Excellon loading parameters are changed, the change is reflected in the + # Export Excellon parameters. + self.update_excellon_cb.stateChanged.connect(self.on_update_exc_export) + + # call it once to make sure it is updated at startup + self.on_update_exc_export(state=self.app.defaults["excellon_update"]) def optimization_selection(self): if self.excellon_optimization_radio.get_value() == 'M': @@ -413,3 +428,105 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI): self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_mm"].set_value('3') self.app.preferencesUiManager.defaults_form_fields["excellon_zeros"].set_value('L') self.app.preferencesUiManager.defaults_form_fields["excellon_units"].set_value('INCH') + + def on_update_exc_export(self, state): + """ + This is handling the update of Excellon Export parameters based on the ones in the Excellon General but only + if the update_excellon_cb checkbox is checked + + :param state: state of the checkbox whose signals is tied to his slot + :return: + """ + if state: + # first try to disconnect + try: + self.excellon_format_upper_in_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + try: + self.excellon_format_lower_in_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + try: + self.excellon_format_upper_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + try: + self.excellon_format_lower_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + + try: + self.excellon_zeros_radio.activated_custom.disconnect(self.on_excellon_zeros_changed) + except TypeError: + pass + try: + self.excellon_units_radio.activated_custom.disconnect(self.on_excellon_zeros_changed) + except TypeError: + pass + + # the connect them + self.excellon_format_upper_in_entry.returnPressed.connect(self.on_excellon_format_changed) + self.excellon_format_lower_in_entry.returnPressed.connect(self.on_excellon_format_changed) + self.excellon_format_upper_mm_entry.returnPressed.connect(self.on_excellon_format_changed) + self.excellon_format_lower_mm_entry.returnPressed.connect(self.on_excellon_format_changed) + self.excellon_zeros_radio.activated_custom.connect(self.on_excellon_zeros_changed) + self.excellon_units_radio.activated_custom.connect(self.on_excellon_units_changed) + else: + # disconnect the signals + try: + self.excellon_format_upper_in_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + try: + self.excellon_format_lower_in_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + try: + self.excellon_format_upper_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + try: + self.excellon_format_lower_mm_entry.returnPressed.disconnect(self.on_excellon_format_changed) + except TypeError: + pass + + try: + self.excellon_zeros_radio.activated_custom.disconnect(self.on_excellon_zeros_changed) + except TypeError: + pass + try: + self.excellon_units_radio.activated_custom.disconnect(self.on_excellon_zeros_changed) + except TypeError: + pass + + def on_excellon_format_changed(self): + """ + Slot activated when the user changes the Excellon format values in Preferences -> Excellon -> Excellon General + :return: None + """ + if self.excellon_units_radio.get_value().upper() == 'METRIC': + self.app.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value( + self.excellon_format_upper_mm_entry.get_value()) + self.app.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value( + self.excellon_format_lower_mm_entry.get_value()) + else: + self.app.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value( + self.excellon_format_upper_in_entry.get_value()) + self.app.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value( + self.excellon_format_lower_in_entry.get_value()) + + def on_excellon_zeros_changed(self, val): + """ + Slot activated when the user changes the Excellon zeros values in Preferences -> Excellon -> Excellon General + :return: None + """ + self.app.ui.excellon_defaults_form.excellon_exp_group.zeros_radio.set_value(val + 'Z') + + def on_excellon_units_changed(self, val): + """ + Slot activated when the user changes the Excellon unit values in Preferences -> Excellon -> Excellon General + :return: None + """ + self.app.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio.set_value(val) + self.on_excellon_format_changed() diff --git a/flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py b/AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py similarity index 96% rename from flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py rename to AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py index cdc6ce5e..93aec0b3 100644 --- a/flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py +++ b/AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py @@ -1,12 +1,12 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import Qt, QSettings -from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \ - FCComboBox -from flatcamGUI.preferences import machinist_setting -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \ + FCComboBox, NumericalEvalTupleEntry +from AppGUI.preferences import machinist_setting +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -198,7 +198,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI): "If no value is entered then there is no move\n" "on X,Y plane at the end of the job.") ) - self.endxy_entry = FCEntry() + self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9') grid2.addWidget(endmove_xy_label, 9, 0) grid2.addWidget(self.endxy_entry, 9, 1) diff --git a/flatcamGUI/preferences/excellon/ExcellonPreferencesUI.py b/AppGUI/preferences/excellon/ExcellonPreferencesUI.py similarity index 75% rename from flatcamGUI/preferences/excellon/ExcellonPreferencesUI.py rename to AppGUI/preferences/excellon/ExcellonPreferencesUI.py index b3d6d165..dd1305d9 100644 --- a/flatcamGUI/preferences/excellon/ExcellonPreferencesUI.py +++ b/AppGUI/preferences/excellon/ExcellonPreferencesUI.py @@ -1,14 +1,14 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.preferences.excellon.ExcellonEditorPrefGroupUI import ExcellonEditorPrefGroupUI -from flatcamGUI.preferences.excellon.ExcellonExpPrefGroupUI import ExcellonExpPrefGroupUI -from flatcamGUI.preferences.excellon.ExcellonAdvOptPrefGroupUI import ExcellonAdvOptPrefGroupUI -from flatcamGUI.preferences.excellon.ExcellonOptPrefGroupUI import ExcellonOptPrefGroupUI -from flatcamGUI.preferences.excellon.ExcellonGenPrefGroupUI import ExcellonGenPrefGroupUI +from AppGUI.preferences.excellon.ExcellonEditorPrefGroupUI import ExcellonEditorPrefGroupUI +from AppGUI.preferences.excellon.ExcellonExpPrefGroupUI import ExcellonExpPrefGroupUI +from AppGUI.preferences.excellon.ExcellonAdvOptPrefGroupUI import ExcellonAdvOptPrefGroupUI +from AppGUI.preferences.excellon.ExcellonOptPrefGroupUI import ExcellonOptPrefGroupUI +from AppGUI.preferences.excellon.ExcellonGenPrefGroupUI import ExcellonGenPrefGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -31,7 +31,7 @@ class ExcellonPreferencesUI(QtWidgets.QWidget): self.decimals = decimals self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals) - self.excellon_gen_group.setMinimumWidth(220) + self.excellon_gen_group.setMinimumWidth(240) self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals) self.excellon_opt_group.setMinimumWidth(290) self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals) diff --git a/flatcamGUI/preferences/excellon/__init__.py b/AppGUI/preferences/excellon/__init__.py similarity index 100% rename from flatcamGUI/preferences/excellon/__init__.py rename to AppGUI/preferences/excellon/__init__.py diff --git a/flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py b/AppGUI/preferences/general/GeneralAPPSetGroupUI.py similarity index 94% rename from flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py rename to AppGUI/preferences/general/GeneralAPPSetGroupUI.py index 3daff809..a056c200 100644 --- a/flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py +++ b/AppGUI/preferences/general/GeneralAPPSetGroupUI.py @@ -1,13 +1,13 @@ from PyQt5 import QtCore, QtWidgets, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \ +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \ FCEntry -from flatcamGUI.preferences import settings -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.preferences import settings +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -177,14 +177,6 @@ class GeneralAPPSetGroupUI(OptionsGroupUI): {'label': _('Landscape'), 'value': 'l'}, ], stretch=False) - self.wks = OptionalInputSection(self.workspace_cb, - [ - self.workspace_type_lbl, - self.wk_cb, - self.wk_orientation_label, - self.wk_orientation_radio - ]) - grid0.addWidget(self.wk_orientation_label, 8, 0) grid0.addWidget(self.wk_orientation_radio, 8, 1) @@ -201,7 +193,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI): self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook')) self.notebook_font_size_label.setToolTip( _("This sets the font size for the elements found in the Notebook.\n" - "The notebook is the collapsible area in the left side of the GUI,\n" + "The notebook is the collapsible area in the left side of the AppGUI,\n" "and include the Project, Selected and Tool tabs.") ) @@ -240,7 +232,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI): # TextBox Font Size self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox')) self.textbox_font_size_label.setToolTip( - _("This sets the font size for the Textbox GUI\n" + _("This sets the font size for the Textbox AppGUI\n" "elements that are used in FlatCAM.") ) @@ -257,10 +249,29 @@ class GeneralAPPSetGroupUI(OptionsGroupUI): grid0.addWidget(self.textbox_font_size_label, 13, 0) grid0.addWidget(self.textbox_font_size_spinner, 13, 1) + # HUD Font Size + self.hud_font_size_label = QtWidgets.QLabel('%s:' % _('HUD')) + self.hud_font_size_label.setToolTip( + _("This sets the font size for the Heads Up Display.") + ) + + self.hud_font_size_spinner = FCSpinner() + self.hud_font_size_spinner.set_range(8, 40) + self.hud_font_size_spinner.setWrapping(True) + + qsettings = QSettings("Open Source", "FlatCAM") + if qsettings.contains("hud_font_size"): + self.hud_font_size_spinner.set_value(settings.value('hud_font_size', type=int)) + else: + self.hud_font_size_spinner.set_value(8) + + grid0.addWidget(self.hud_font_size_label, 14, 0) + grid0.addWidget(self.hud_font_size_spinner, 14, 1) + separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 14, 0, 1, 2) + grid0.addWidget(separator_line, 16, 0, 1, 2) # ----------------------------------------------------------- # -------------- MOUSE SETTINGS ----------------------------- diff --git a/flatcamGUI/preferences/general/GeneralAppPrefGroupUI.py b/AppGUI/preferences/general/GeneralAppPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/general/GeneralAppPrefGroupUI.py rename to AppGUI/preferences/general/GeneralAppPrefGroupUI.py index 7f238b5f..81d99b7d 100644 --- a/flatcamGUI/preferences/general/GeneralAppPrefGroupUI.py +++ b/AppGUI/preferences/general/GeneralAppPrefGroupUI.py @@ -3,12 +3,12 @@ import sys from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \ +from AppGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \ FCDoubleSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -380,12 +380,11 @@ class GeneralAppPrefGroupUI(OptionsGroupUI): def on_toggle_shell_from_settings(self, state): """ - Toggle shell: if is visible close it, if it is closed then open it + Toggle shell ui: if is visible close it, if it is closed then open it + :return: None """ - self.app.defaults.report_usage("on_toggle_shell_from_settings()") - if state is True: if not self.app.ui.shell_dock.isVisible(): self.app.ui.shell_dock.show() diff --git a/flatcamGUI/preferences/general/GeneralGUIPrefGroupUI.py b/AppGUI/preferences/general/GeneralGUIPrefGroupUI.py similarity index 93% rename from flatcamGUI/preferences/general/GeneralGUIPrefGroupUI.py rename to AppGUI/preferences/general/GeneralGUIPrefGroupUI.py index 94044c27..67dabc55 100644 --- a/flatcamGUI/preferences/general/GeneralGUIPrefGroupUI.py +++ b/AppGUI/preferences/general/GeneralGUIPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import QSettings, Qt -from flatcamGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -56,13 +56,13 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): ) grid0.addWidget(self.gray_icons_cb, 1, 0, 1, 3) - self.theme_button = FCButton(_("Apply Theme")) - self.theme_button.setToolTip( - _("Select a theme for FlatCAM.\n" - "It will theme the plot area.\n" - "The application will restart after change.") - ) - grid0.addWidget(self.theme_button, 2, 0, 1, 3) + # self.theme_button = FCButton(_("Apply Theme")) + # self.theme_button.setToolTip( + # _("Select a theme for FlatCAM.\n" + # "It will theme the plot area.\n" + # "The application will restart after change.") + # ) + # grid0.addWidget(self.theme_button, 2, 0, 1, 3) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.HLine) @@ -381,8 +381,6 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): self.layout.addStretch() - self.theme_button.clicked.connect(self.on_theme_change) - # ############################################################################# # ############################# GUI COLORS SIGNALS ############################ # ############################################################################# @@ -418,16 +416,6 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): self.layout_combo.activated.connect(self.on_layout) - def on_theme_change(self): - val = self.theme_radio.get_value() - qsettings = QSettings("Open Source", "FlatCAM") - qsettings.setValue('theme', val) - - # This will write the setting to the platform specific storage. - del qsettings - - self.app.on_app_restart() - @staticmethod def handle_style(style): # set current style @@ -659,14 +647,13 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): # first remove the toolbars: try: self.app.ui.removeToolBar(self.app.ui.toolbarfile) - self.app.ui.removeToolBar(self.app.ui.toolbargeo) + self.app.ui.removeToolBar(self.app.ui.toolbaredit) self.app.ui.removeToolBar(self.app.ui.toolbarview) self.app.ui.removeToolBar(self.app.ui.toolbarshell) self.app.ui.removeToolBar(self.app.ui.toolbartools) self.app.ui.removeToolBar(self.app.ui.exc_edit_toolbar) self.app.ui.removeToolBar(self.app.ui.geo_edit_toolbar) self.app.ui.removeToolBar(self.app.ui.grb_edit_toolbar) - self.app.ui.removeToolBar(self.app.ui.snap_toolbar) self.app.ui.removeToolBar(self.app.ui.toolbarshell) except Exception: pass @@ -677,9 +664,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): self.app.ui.toolbarfile.setObjectName('File_TB') self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile) - self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar') - self.app.ui.toolbargeo.setObjectName('Edit_TB') - self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbargeo) + self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar') + self.app.ui.toolbaredit.setObjectName('Edit_TB') + self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbaredit) self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar') self.app.ui.toolbarshell.setObjectName('Shell_TB') @@ -709,22 +696,15 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB') self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar) - self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar') - self.app.ui.snap_toolbar.setObjectName('Snap_TB') - self.app.ui.snap_toolbar.setMaximumHeight(30) - self.app.ui.splitter_left.addWidget(self.app.ui.snap_toolbar) - - self.app.ui.corner_snap_btn.setVisible(True) - self.app.ui.snap_magnet.setVisible(True) else: # ## TOOLBAR INSTALLATION # ## self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar') self.app.ui.toolbarfile.setObjectName('File_TB') self.app.ui.addToolBar(self.app.ui.toolbarfile) - self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar') - self.app.ui.toolbargeo.setObjectName('Edit_TB') - self.app.ui.addToolBar(self.app.ui.toolbargeo) + self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar') + self.app.ui.toolbaredit.setObjectName('Edit_TB') + self.app.ui.addToolBar(self.app.ui.toolbaredit) self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar') self.app.ui.toolbarview.setObjectName('View_TB') @@ -755,18 +735,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB') self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar) - self.app.ui.snap_toolbar = QtWidgets.QToolBar('Grid Toolbar') - self.app.ui.snap_toolbar.setObjectName('Snap_TB') - # self.app.ui.snap_toolbar.setMaximumHeight(30) - self.app.ui.addToolBar(self.app.ui.snap_toolbar) - - self.app.ui.corner_snap_btn.setVisible(False) - self.app.ui.snap_magnet.setVisible(False) - if current_layout == 'minimal': self.app.ui.toolbarview.setVisible(False) self.app.ui.toolbarshell.setVisible(False) - self.app.ui.snap_toolbar.setVisible(False) self.app.ui.geo_edit_toolbar.setVisible(False) self.app.ui.grb_edit_toolbar.setVisible(False) self.app.ui.exc_edit_toolbar.setVisible(False) @@ -779,7 +750,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI): self.app.connect_toolbar_signals() self.app.ui.grid_snap_btn.setChecked(True) - self.app.ui.on_grid_snap_triggered(state=True) + + self.app.ui.corner_snap_btn.setVisible(False) + self.app.ui.snap_magnet.setVisible(False) self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"])) self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"])) diff --git a/flatcamGUI/preferences/general/GeneralPreferencesUI.py b/AppGUI/preferences/general/GeneralPreferencesUI.py similarity index 80% rename from flatcamGUI/preferences/general/GeneralPreferencesUI.py rename to AppGUI/preferences/general/GeneralPreferencesUI.py index 46636438..45ccf191 100644 --- a/flatcamGUI/preferences/general/GeneralPreferencesUI.py +++ b/AppGUI/preferences/general/GeneralPreferencesUI.py @@ -1,12 +1,12 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI -from flatcamGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI -from flatcamGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI +from AppGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI +from AppGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI +from AppGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/general/__init__.py b/AppGUI/preferences/general/__init__.py similarity index 100% rename from flatcamGUI/preferences/general/__init__.py rename to AppGUI/preferences/general/__init__.py diff --git a/flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py b/AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py similarity index 90% rename from flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py rename to AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py index 7ade9b90..f122186c 100644 --- a/flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py +++ b/AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py @@ -1,11 +1,12 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCEntry, FloatEntry, FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel, NumericalEvalTupleEntry, \ + NumericalEvalEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -46,8 +47,9 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): toolchange_xy_label.setToolTip( _("Toolchange X,Y position.") ) + self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9') + grid1.addWidget(toolchange_xy_label, 1, 0) - self.toolchangexy_entry = FCEntry() grid1.addWidget(self.toolchangexy_entry, 1, 1) # Start move Z @@ -56,8 +58,9 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): _("Height of the tool just after starting the work.\n" "Delete the value if you don't need this feature.") ) + self.gstartz_entry = NumericalEvalEntry(border_color='#0069A9') + grid1.addWidget(startzlabel, 2, 0) - self.gstartz_entry = FloatEntry() grid1.addWidget(self.gstartz_entry, 2, 1) # Feedrate rapids @@ -186,6 +189,11 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): grid1.addWidget(segy_label, 11, 0) grid1.addWidget(self.segy_entry, 11, 1) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid1.addWidget(separator_line, 12, 0, 1, 2) + # ----------------------------- # --- Area Exclusion ---------- # ----------------------------- @@ -195,10 +203,10 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): "Those parameters are available only for\n" "Advanced App. Level.") ) - grid1.addWidget(self.adv_label, 12, 0, 1, 2) + grid1.addWidget(self.adv_label, 13, 0, 1, 2) # Exclusion Area CB - self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas")) + self.exclusion_cb = FCCheckBox('%s' % _("Exclusion areas")) self.exclusion_cb.setToolTip( _( "Include exclusion areas.\n" @@ -206,7 +214,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): "is forbidden." ) ) - grid1.addWidget(self.exclusion_cb, 13, 0, 1, 2) + grid1.addWidget(self.exclusion_cb, 14, 0, 1, 2) # Area Selection shape self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape")) @@ -217,8 +225,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, {'label': _("Polygon"), 'value': 'polygon'}]) - grid1.addWidget(self.area_shape_label, 14, 0) - grid1.addWidget(self.area_shape_radio, 14, 1) + grid1.addWidget(self.area_shape_label, 15, 0) + grid1.addWidget(self.area_shape_radio, 15, 1) # Chose Strategy self.strategy_label = FCLabel('%s:' % _("Strategy")) @@ -229,8 +237,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI): self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'}, {'label': _('Around'), 'value': 'around'}]) - grid1.addWidget(self.strategy_label, 15, 0) - grid1.addWidget(self.strategy_radio, 15, 1) + grid1.addWidget(self.strategy_label, 16, 0) + grid1.addWidget(self.strategy_radio, 16, 1) # Over Z self.over_z_label = FCLabel('%s:' % _("Over Z")) diff --git a/flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py b/AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py similarity index 93% rename from flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py rename to AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py index 9c5204ab..1ac42f42 100644 --- a/flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py +++ b/AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCSpinner, RadioSet -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCSpinner, RadioSet +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py b/AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py similarity index 89% rename from flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py rename to AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py index 60479236..dd4f77ac 100644 --- a/flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py +++ b/AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -31,12 +31,22 @@ class GeometryGenPrefGroupUI(OptionsGroupUI): self.plot_options_label = QtWidgets.QLabel("%s:" % _("Plot Options")) self.layout.addWidget(self.plot_options_label) + plot_hlay = QtWidgets.QHBoxLayout() + self.layout.addLayout((plot_hlay)) + # Plot CB self.plot_cb = FCCheckBox(label=_('Plot')) self.plot_cb.setToolTip( _("Plot (show) this object.") ) - self.layout.addWidget(self.plot_cb) + plot_hlay.addWidget(self.plot_cb) + + # Multicolored CB + self.multicolored_cb = FCCheckBox(label=_('M-Color')) + self.multicolored_cb.setToolTip( + _("Draw polygons in different colors.") + ) + plot_hlay.addWidget(self.multicolored_cb) grid0 = QtWidgets.QGridLayout() self.layout.addLayout(grid0) diff --git a/flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py b/AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py similarity index 96% rename from flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py rename to AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py index c2e0f6e2..ebbe0c34 100644 --- a/flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py +++ b/AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py @@ -1,12 +1,13 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import Qt, QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCEntry, FCSpinner, FCComboBox -from flatcamGUI.preferences import machinist_setting -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCSpinner, FCComboBox, \ + NumericalEvalTupleEntry +from AppGUI.preferences import machinist_setting +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -176,7 +177,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI): "If no value is entered then there is no move\n" "on X,Y plane at the end of the job.") ) - self.endxy_entry = FCEntry() + self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9') grid1.addWidget(endmove_xy_label, 7, 0) grid1.addWidget(self.endxy_entry, 7, 1) diff --git a/flatcamGUI/preferences/geometry/GeometryPreferencesUI.py b/AppGUI/preferences/geometry/GeometryPreferencesUI.py similarity index 77% rename from flatcamGUI/preferences/geometry/GeometryPreferencesUI.py rename to AppGUI/preferences/geometry/GeometryPreferencesUI.py index cf906d61..fd1709ef 100644 --- a/flatcamGUI/preferences/geometry/GeometryPreferencesUI.py +++ b/AppGUI/preferences/geometry/GeometryPreferencesUI.py @@ -1,13 +1,13 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.preferences.geometry.GeometryEditorPrefGroupUI import GeometryEditorPrefGroupUI -from flatcamGUI.preferences.geometry.GeometryAdvOptPrefGroupUI import GeometryAdvOptPrefGroupUI -from flatcamGUI.preferences.geometry.GeometryOptPrefGroupUI import GeometryOptPrefGroupUI -from flatcamGUI.preferences.geometry.GeometryGenPrefGroupUI import GeometryGenPrefGroupUI +from AppGUI.preferences.geometry.GeometryEditorPrefGroupUI import GeometryEditorPrefGroupUI +from AppGUI.preferences.geometry.GeometryAdvOptPrefGroupUI import GeometryAdvOptPrefGroupUI +from AppGUI.preferences.geometry.GeometryOptPrefGroupUI import GeometryOptPrefGroupUI +from AppGUI.preferences.geometry.GeometryGenPrefGroupUI import GeometryGenPrefGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/geometry/__init__.py b/AppGUI/preferences/geometry/__init__.py similarity index 100% rename from flatcamGUI/preferences/geometry/__init__.py rename to AppGUI/preferences/geometry/__init__.py diff --git a/flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py b/AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py similarity index 52% rename from flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py rename to AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py index 20376bfd..7080b81d 100644 --- a/flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py +++ b/AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner, FCSpinner, OptionalInputSection -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner, FCSpinner, OptionalInputSection +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -63,85 +63,6 @@ class GerberAdvOptPrefGroupUI(OptionsGroupUI): separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) grid0.addWidget(separator_line, 2, 0, 1, 2) - # Tool Type - self.tool_type_label = QtWidgets.QLabel('%s' % _('Tool Type')) - self.tool_type_label.setToolTip( - _("Choose which tool to use for Gerber isolation:\n" - "'Circular' or 'V-shape'.\n" - "When the 'V-shape' is selected then the tool\n" - "diameter will depend on the chosen cut depth.") - ) - self.tool_type_radio = RadioSet([{'label': 'Circular', 'value': 'circular'}, - {'label': 'V-Shape', 'value': 'v'}]) - - grid0.addWidget(self.tool_type_label, 3, 0) - grid0.addWidget(self.tool_type_radio, 3, 1, 1, 2) - - # Tip Dia - self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia')) - self.tipdialabel.setToolTip( - _("The tip diameter for V-Shape Tool") - ) - self.tipdia_spinner = FCDoubleSpinner() - self.tipdia_spinner.set_precision(self.decimals) - self.tipdia_spinner.set_range(-99.9999, 99.9999) - self.tipdia_spinner.setSingleStep(0.1) - self.tipdia_spinner.setWrapping(True) - grid0.addWidget(self.tipdialabel, 4, 0) - grid0.addWidget(self.tipdia_spinner, 4, 1, 1, 2) - - # Tip Angle - self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle')) - self.tipanglelabel.setToolTip( - _("The tip angle for V-Shape Tool.\n" - "In degree.") - ) - self.tipangle_spinner = FCSpinner() - self.tipangle_spinner.set_range(1, 180) - self.tipangle_spinner.set_step(5) - self.tipangle_spinner.setWrapping(True) - grid0.addWidget(self.tipanglelabel, 5, 0) - grid0.addWidget(self.tipangle_spinner, 5, 1, 1, 2) - - # Cut Z - self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z')) - self.cutzlabel.setToolTip( - _("Cutting depth (negative)\n" - "below the copper surface.") - ) - self.cutz_spinner = FCDoubleSpinner() - self.cutz_spinner.set_precision(self.decimals) - self.cutz_spinner.set_range(-99.9999, 0.0000) - self.cutz_spinner.setSingleStep(0.1) - self.cutz_spinner.setWrapping(True) - - grid0.addWidget(self.cutzlabel, 6, 0) - grid0.addWidget(self.cutz_spinner, 6, 1, 1, 2) - - # Isolation Type - self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type')) - self.iso_type_label.setToolTip( - _("Choose how the isolation will be executed:\n" - "- 'Full' -> complete isolation of polygons\n" - "- 'Ext' -> will isolate only on the outside\n" - "- 'Int' -> will isolate only on the inside\n" - "'Exterior' isolation is almost always possible\n" - "(with the right tool) but 'Interior'\n" - "isolation can be done only when there is an opening\n" - "inside of the polygon (e.g polygon is a 'doughnut' shape).") - ) - self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'}, - {'label': _('Exterior'), 'value': 'ext'}, - {'label': _('Interior'), 'value': 'int'}]) - - grid0.addWidget(self.iso_type_label, 7, 0,) - grid0.addWidget(self.iso_type_radio, 7, 1, 1, 2) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 8, 0, 1, 2) - # Buffering Type buffering_label = QtWidgets.QLabel('%s:' % _('Buffering')) buffering_label.setToolTip( diff --git a/flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py b/AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py rename to AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py index 3ba0da99..aee22e96 100644 --- a/flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py +++ b/AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCComboBox, FCEntry, RadioSet -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCComboBox, FCEntry, RadioSet, NumericalEvalTupleEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -109,8 +109,9 @@ class GerberEditorPrefGroupUI(OptionsGroupUI): "The value of the diameter has to use the dot decimals separator.\n" "Valid values: 0.3, 1.0") ) + self.adddim_entry = NumericalEvalTupleEntry(border_color='#0069A9') + grid0.addWidget(self.adddim_label, 5, 0) - self.adddim_entry = FCEntry() grid0.addWidget(self.adddim_entry, 5, 1) self.grb_array_linear_label = QtWidgets.QLabel('%s:' % _('Linear Pad Array')) diff --git a/flatcamGUI/preferences/gerber/GerberExpPrefGroupUI.py b/AppGUI/preferences/gerber/GerberExpPrefGroupUI.py similarity index 96% rename from flatcamGUI/preferences/gerber/GerberExpPrefGroupUI.py rename to AppGUI/preferences/gerber/GerberExpPrefGroupUI.py index 01729dc5..edd41b45 100644 --- a/flatcamGUI/preferences/gerber/GerberExpPrefGroupUI.py +++ b/AppGUI/preferences/gerber/GerberExpPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtCore from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import RadioSet, FCSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/gerber/GerberGenPrefGroupUI.py b/AppGUI/preferences/gerber/GerberGenPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/gerber/GerberGenPrefGroupUI.py rename to AppGUI/preferences/gerber/GerberGenPrefGroupUI.py index 6f52fe45..68cfed57 100644 --- a/flatcamGUI/preferences/gerber/GerberGenPrefGroupUI.py +++ b/AppGUI/preferences/gerber/GerberGenPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -34,26 +34,26 @@ class GerberGenPrefGroupUI(OptionsGroupUI): grid0 = QtWidgets.QGridLayout() self.layout.addLayout(grid0) + # Plot CB + self.plot_cb = FCCheckBox(label='%s' % _('Plot')) + self.plot_options_label.setToolTip( + _("Plot (show) this object.") + ) + grid0.addWidget(self.plot_cb, 0, 0) + # Solid CB self.solid_cb = FCCheckBox(label='%s' % _('Solid')) self.solid_cb.setToolTip( _("Solid color polygons.") ) - grid0.addWidget(self.solid_cb, 0, 0) + grid0.addWidget(self.solid_cb, 0, 1) # Multicolored CB self.multicolored_cb = FCCheckBox(label='%s' % _('M-Color')) self.multicolored_cb.setToolTip( _("Draw polygons in different colors.") ) - grid0.addWidget(self.multicolored_cb, 0, 1) - - # Plot CB - self.plot_cb = FCCheckBox(label='%s' % _('Plot')) - self.plot_options_label.setToolTip( - _("Plot (show) this object.") - ) - grid0.addWidget(self.plot_cb, 0, 2) + grid0.addWidget(self.multicolored_cb, 0, 2) # Number of circle steps for circular aperture linear approximation self.circle_steps_label = QtWidgets.QLabel('%s:' % _("Circle Steps")) diff --git a/AppGUI/preferences/gerber/GerberOptPrefGroupUI.py b/AppGUI/preferences/gerber/GerberOptPrefGroupUI.py new file mode 100644 index 00000000..c619a3c1 --- /dev/null +++ b/AppGUI/preferences/gerber/GerberOptPrefGroupUI.py @@ -0,0 +1,100 @@ +from PyQt5 import QtWidgets +from PyQt5.QtCore import QSettings + +from AppGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox, FCComboBox +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI + +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + +settings = QSettings("Open Source", "FlatCAM") +if settings.contains("machinist"): + machinist_setting = settings.value('machinist', type=int) +else: + machinist_setting = 0 + + +class GerberOptPrefGroupUI(OptionsGroupUI): + def __init__(self, decimals=4, parent=None): + # OptionsGroupUI.__init__(self, "Gerber Options Preferences", parent=parent) + super(GerberOptPrefGroupUI, self).__init__(self, parent=parent) + + self.decimals = decimals + + self.setTitle(str(_("Gerber Options"))) + + # ## Clear non-copper regions + self.clearcopper_label = QtWidgets.QLabel("%s:" % _("Non-copper regions")) + self.clearcopper_label.setToolTip( + _("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.") + ) + self.layout.addWidget(self.clearcopper_label) + + grid1 = QtWidgets.QGridLayout() + self.layout.addLayout(grid1) + + # Margin + bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin')) + bmlabel.setToolTip( + _("Specify the edge of the PCB\n" + "by drawing a box around all\n" + "objects with this minimum\n" + "distance.") + ) + grid1.addWidget(bmlabel, 0, 0) + self.noncopper_margin_entry = FCDoubleSpinner() + self.noncopper_margin_entry.set_precision(self.decimals) + self.noncopper_margin_entry.setSingleStep(0.1) + self.noncopper_margin_entry.set_range(-9999, 9999) + grid1.addWidget(self.noncopper_margin_entry, 0, 1) + + # Rounded corners + self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo")) + self.noncopper_rounded_cb.setToolTip( + _("Resulting geometry will have rounded corners.") + ) + grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid1.addWidget(separator_line, 2, 0, 1, 2) + + # ## Bounding box + self.boundingbox_label = QtWidgets.QLabel('%s:' % _('Bounding Box')) + self.layout.addWidget(self.boundingbox_label) + + grid2 = QtWidgets.QGridLayout() + self.layout.addLayout(grid2) + + bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin')) + bbmargin.setToolTip( + _("Distance of the edges of the box\n" + "to the nearest polygon.") + ) + self.bbmargin_entry = FCDoubleSpinner() + self.bbmargin_entry.set_precision(self.decimals) + self.bbmargin_entry.setSingleStep(0.1) + self.bbmargin_entry.set_range(-9999, 9999) + + grid2.addWidget(bbmargin, 0, 0) + grid2.addWidget(self.bbmargin_entry, 0, 1) + + self.bbrounded_cb = FCCheckBox(label='%s' % _("Rounded Geo")) + self.bbrounded_cb.setToolTip( + _("If the bounding box is \n" + "to have rounded corners\n" + "their radius is equal to\n" + "the margin.") + ) + grid2.addWidget(self.bbrounded_cb, 1, 0, 1, 2) + self.layout.addStretch() diff --git a/flatcamGUI/preferences/gerber/GerberPreferencesUI.py b/AppGUI/preferences/gerber/GerberPreferencesUI.py similarity index 77% rename from flatcamGUI/preferences/gerber/GerberPreferencesUI.py rename to AppGUI/preferences/gerber/GerberPreferencesUI.py index f9d9f8aa..cee2a54c 100644 --- a/flatcamGUI/preferences/gerber/GerberPreferencesUI.py +++ b/AppGUI/preferences/gerber/GerberPreferencesUI.py @@ -1,14 +1,14 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.preferences.gerber.GerberEditorPrefGroupUI import GerberEditorPrefGroupUI -from flatcamGUI.preferences.gerber.GerberExpPrefGroupUI import GerberExpPrefGroupUI -from flatcamGUI.preferences.gerber.GerberAdvOptPrefGroupUI import GerberAdvOptPrefGroupUI -from flatcamGUI.preferences.gerber.GerberOptPrefGroupUI import GerberOptPrefGroupUI -from flatcamGUI.preferences.gerber.GerberGenPrefGroupUI import GerberGenPrefGroupUI +from AppGUI.preferences.gerber.GerberEditorPrefGroupUI import GerberEditorPrefGroupUI +from AppGUI.preferences.gerber.GerberExpPrefGroupUI import GerberExpPrefGroupUI +from AppGUI.preferences.gerber.GerberAdvOptPrefGroupUI import GerberAdvOptPrefGroupUI +from AppGUI.preferences.gerber.GerberOptPrefGroupUI import GerberOptPrefGroupUI +from AppGUI.preferences.gerber.GerberGenPrefGroupUI import GerberGenPrefGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -44,6 +44,7 @@ class GerberPreferencesUI(QtWidgets.QWidget): self.vlay = QtWidgets.QVBoxLayout() self.vlay.addWidget(self.gerber_opt_group) self.vlay.addWidget(self.gerber_exp_group) + self.vlay.addStretch() self.layout.addWidget(self.gerber_gen_group) self.layout.addLayout(self.vlay) diff --git a/flatcamGUI/preferences/gerber/__init__.py b/AppGUI/preferences/gerber/__init__.py similarity index 100% rename from flatcamGUI/preferences/gerber/__init__.py rename to AppGUI/preferences/gerber/__init__.py diff --git a/flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py b/AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py similarity index 98% rename from flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py index a3b6c0c2..7ec00a3c 100644 --- a/flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py b/AppGUI/preferences/tools/Tools2CalPrefGroupUI.py similarity index 94% rename from flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2CalPrefGroupUI.py index 35b6fb1d..04d54387 100644 --- a/flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2CalPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, NumericalEvalTupleEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -116,7 +116,7 @@ class Tools2CalPrefGroupUI(OptionsGroupUI): "(x, y) point will be used,") ) - self.toolchange_xy_entry = FCEntry() + self.toolchange_xy_entry = NumericalEvalTupleEntry(border_color='#0069A9') grid_lay.addWidget(toolchangexy_lbl, 7, 0) grid_lay.addWidget(self.toolchange_xy_entry, 7, 1, 1, 2) diff --git a/flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py b/AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py index 89a104ea..410bb655 100644 --- a/flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py b/AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py similarity index 96% rename from flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py index 32b5fdfe..77ff939c 100644 --- a/flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, RadioSet +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2InvertPrefGroupUI.py b/AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py similarity index 93% rename from flatcamGUI/preferences/tools/Tools2InvertPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py index a063fb32..f7bb9f17 100644 --- a/flatcamGUI/preferences/tools/Tools2InvertPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, RadioSet +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2OptimalPrefGroupUI.py b/AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py similarity index 91% rename from flatcamGUI/preferences/tools/Tools2OptimalPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py index 283ddf2e..6524d439 100644 --- a/flatcamGUI/preferences/tools/Tools2OptimalPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2PreferencesUI.py b/AppGUI/preferences/tools/Tools2PreferencesUI.py similarity index 76% rename from flatcamGUI/preferences/tools/Tools2PreferencesUI.py rename to AppGUI/preferences/tools/Tools2PreferencesUI.py index 01ed4def..5101fc37 100644 --- a/flatcamGUI/preferences/tools/Tools2PreferencesUI.py +++ b/AppGUI/preferences/tools/Tools2PreferencesUI.py @@ -1,18 +1,18 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.preferences.tools.Tools2InvertPrefGroupUI import Tools2InvertPrefGroupUI -from flatcamGUI.preferences.tools.Tools2PunchGerberPrefGroupUI import Tools2PunchGerberPrefGroupUI -from flatcamGUI.preferences.tools.Tools2EDrillsPrefGroupUI import Tools2EDrillsPrefGroupUI -from flatcamGUI.preferences.tools.Tools2CalPrefGroupUI import Tools2CalPrefGroupUI -from flatcamGUI.preferences.tools.Tools2FiducialsPrefGroupUI import Tools2FiducialsPrefGroupUI -from flatcamGUI.preferences.tools.Tools2CThievingPrefGroupUI import Tools2CThievingPrefGroupUI -from flatcamGUI.preferences.tools.Tools2QRCodePrefGroupUI import Tools2QRCodePrefGroupUI -from flatcamGUI.preferences.tools.Tools2OptimalPrefGroupUI import Tools2OptimalPrefGroupUI -from flatcamGUI.preferences.tools.Tools2RulesCheckPrefGroupUI import Tools2RulesCheckPrefGroupUI +from AppGUI.preferences.tools.Tools2InvertPrefGroupUI import Tools2InvertPrefGroupUI +from AppGUI.preferences.tools.Tools2PunchGerberPrefGroupUI import Tools2PunchGerberPrefGroupUI +from AppGUI.preferences.tools.Tools2EDrillsPrefGroupUI import Tools2EDrillsPrefGroupUI +from AppGUI.preferences.tools.Tools2CalPrefGroupUI import Tools2CalPrefGroupUI +from AppGUI.preferences.tools.Tools2FiducialsPrefGroupUI import Tools2FiducialsPrefGroupUI +from AppGUI.preferences.tools.Tools2CThievingPrefGroupUI import Tools2CThievingPrefGroupUI +from AppGUI.preferences.tools.Tools2QRCodePrefGroupUI import Tools2QRCodePrefGroupUI +from AppGUI.preferences.tools.Tools2OptimalPrefGroupUI import Tools2OptimalPrefGroupUI +from AppGUI.preferences.tools.Tools2RulesCheckPrefGroupUI import Tools2RulesCheckPrefGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py b/AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py index a9a3a304..073d4a68 100644 --- a/flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, RadioSet, FCDoubleSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2QRCodePrefGroupUI.py b/AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py similarity index 75% rename from flatcamGUI/preferences/tools/Tools2QRCodePrefGroupUI.py rename to AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py index 5c22863b..04f7c605 100644 --- a/flatcamGUI/preferences/tools/Tools2QRCodePrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py @@ -1,11 +1,11 @@ -from PyQt5 import QtWidgets, QtCore +from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import Qt, QSettings -from flatcamGUI.GUIElements import FCSpinner, RadioSet, FCTextArea, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCSpinner, RadioSet, FCTextArea, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -205,3 +205,67 @@ class Tools2QRCodePrefGroupUI(OptionsGroupUI): grid_lay.addWidget(self.sel_limit_label, 11, 0) grid_lay.addWidget(self.sel_limit_entry, 11, 1) # self.layout.addStretch() + + # QRCode Tool + self.fill_color_entry.editingFinished.connect(self.on_qrcode_fill_color_entry) + self.fill_color_button.clicked.connect(self.on_qrcode_fill_color_button) + self.back_color_entry.editingFinished.connect(self.on_qrcode_back_color_entry) + self.back_color_button.clicked.connect(self.on_qrcode_back_color_button) + + def on_qrcode_fill_color_entry(self): + self.app.defaults['tools_qrcode_fill_color'] = self.fill_color_entry.get_value() + self.fill_color_button.setStyleSheet( + "background-color:%s;" + "border-color: dimgray" % str(self.defaults['tools_qrcode_fill_color']) + ) + + def on_qrcode_fill_color_button(self): + current_color = QtGui.QColor(self.app.defaults['tools_qrcode_fill_color']) + + c_dialog = QtWidgets.QColorDialog() + fill_color = c_dialog.getColor(initial=current_color) + + if fill_color.isValid() is False: + return + + # if new color is different then mark that the Preferences are changed + if fill_color != current_color: + self.app.preferencesUiManager.on_preferences_edited() + + self.fill_color_button.setStyleSheet( + "background-color:%s;" + "border-color: dimgray" % str(fill_color.name()) + ) + + new_val_sel = str(fill_color.name()) + self.fill_color_entry.set_value(new_val_sel) + self.app.defaults['tools_qrcode_fill_color'] = new_val_sel + + def on_qrcode_back_color_entry(self): + self.app.defaults['tools_qrcode_back_color'] = self.back_color_entry.get_value() + self.back_color_button.setStyleSheet( + "background-color:%s;" + "border-color: dimgray" % str(self.defaults['tools_qrcode_back_color']) + ) + + def on_qrcode_back_color_button(self): + current_color = QtGui.QColor(self.app.defaults['tools_qrcode_back_color']) + + c_dialog = QtWidgets.QColorDialog() + back_color = c_dialog.getColor(initial=current_color) + + if back_color.isValid() is False: + return + + # if new color is different then mark that the Preferences are changed + if back_color != current_color: + self.app.preferencesUiManager.on_preferences_edited() + + self.back_color_button.setStyleSheet( + "background-color:%s;" + "border-color: dimgray" % str(back_color.name()) + ) + + new_val_sel = str(back_color.name()) + self.back_color_entry.set_value(new_val_sel) + self.app.defaults['tools_qrcode_back_color'] = new_val_sel diff --git a/flatcamGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py b/AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py similarity index 98% rename from flatcamGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py index 4aba7caa..cc049d41 100644 --- a/flatcamGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox, FCDoubleSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox, FCDoubleSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/Tools2sidedPrefGroupUI.py b/AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py similarity index 95% rename from flatcamGUI/preferences/tools/Tools2sidedPrefGroupUI.py rename to AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py index c7e17bc7..4f9174f8 100644 --- a/flatcamGUI/preferences/tools/Tools2sidedPrefGroupUI.py +++ b/AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, RadioSet +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py b/AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py rename to AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py index df9a31fd..f1d8cec9 100644 --- a/flatcamGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py b/AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py new file mode 100644 index 00000000..559eea86 --- /dev/null +++ b/AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py @@ -0,0 +1,81 @@ +from PyQt5 import QtWidgets +from PyQt5.QtCore import QSettings + +from AppGUI.GUIElements import FCDoubleSpinner +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI + +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + +settings = QSettings("Open Source", "FlatCAM") +if settings.contains("machinist"): + machinist_setting = settings.value('machinist', type=int) +else: + machinist_setting = 0 + + +class ToolsCornersPrefGroupUI(OptionsGroupUI): + def __init__(self, decimals=4, parent=None): + # OptionsGroupUI.__init__(self, "Calculators Tool Options", parent=parent) + super(ToolsCornersPrefGroupUI, self).__init__(self, parent=parent) + + self.setTitle(str(_("Corner Markers Options"))) + self.decimals = decimals + + grid0 = QtWidgets.QGridLayout() + grid0.setColumnStretch(0, 0) + grid0.setColumnStretch(1, 1) + self.layout.addLayout(grid0) + + self.param_label = QtWidgets.QLabel('%s:' % _('Parameters')) + self.param_label.setToolTip( + _("Parameters used for this tool.") + ) + grid0.addWidget(self.param_label, 0, 0, 1, 2) + + # Thickness # + self.thick_label = QtWidgets.QLabel('%s:' % _("Thickness")) + self.thick_label.setToolTip( + _("The thickness of the line that makes the corner marker.") + ) + self.thick_entry = FCDoubleSpinner() + self.thick_entry.set_range(0.0000, 9.9999) + self.thick_entry.set_precision(self.decimals) + self.thick_entry.setWrapping(True) + self.thick_entry.setSingleStep(10 ** -self.decimals) + + grid0.addWidget(self.thick_label, 1, 0) + grid0.addWidget(self.thick_entry, 1, 1) + + # Length # + self.l_label = QtWidgets.QLabel('%s:' % _("Length")) + self.l_label.setToolTip( + _("The length of the line that makes the corner marker.") + ) + self.l_entry = FCDoubleSpinner() + self.l_entry.set_range(-9999.9999, 9999.9999) + self.l_entry.set_precision(self.decimals) + self.l_entry.setSingleStep(10 ** -self.decimals) + + # Margin # + self.margin_label = QtWidgets.QLabel('%s:' % _("Margin")) + self.margin_label.setToolTip( + _("Bounding box margin.") + ) + self.margin_entry = FCDoubleSpinner() + self.margin_entry.set_range(-9999.9999, 9999.9999) + self.margin_entry.set_precision(self.decimals) + self.margin_entry.setSingleStep(0.1) + + grid0.addWidget(self.margin_label, 2, 0) + grid0.addWidget(self.margin_entry, 2, 1) + + grid0.addWidget(self.l_label, 4, 0) + grid0.addWidget(self.l_entry, 4, 1) + + self.layout.addStretch() diff --git a/flatcamGUI/preferences/tools/ToolsCutoutPrefGroupUI.py b/AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py similarity index 96% rename from flatcamGUI/preferences/tools/ToolsCutoutPrefGroupUI.py rename to AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py index 308c74c4..4e690474 100644 --- a/flatcamGUI/preferences/tools/ToolsCutoutPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py @@ -1,12 +1,12 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox -from flatcamGUI.preferences import machinist_setting -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox +from AppGUI.preferences import machinist_setting +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py b/AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py similarity index 89% rename from flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py rename to AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py index 17e994c5..346126ca 100644 --- a/flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py @@ -1,11 +1,11 @@ -from PyQt5 import QtWidgets, QtCore +from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import Qt, QSettings -from flatcamGUI.GUIElements import RadioSet, FCEntry, FCDoubleSpinner, FCCheckBox, FCComboBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCEntry, FCDoubleSpinner, FCCheckBox, FCComboBox +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -314,3 +314,35 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI): grid0.addWidget(self.pagesize_combo, 17, 1) self.layout.addStretch() + + # Film Tool + self.film_color_entry.editingFinished.connect(self.on_film_color_entry) + self.film_color_button.clicked.connect(self.on_film_color_button) + + def on_film_color_entry(self): + self.app.defaults['tools_film_color'] = self.film_color_entry.get_value() + self.film_color_button.setStyleSheet( + "background-color:%s;" + "border-color: dimgray" % str(self.defaults['tools_film_color']) + ) + + def on_film_color_button(self): + current_color = QtGui.QColor(self.app.defaults['tools_film_color']) + + c_dialog = QtWidgets.QColorDialog() + film_color = c_dialog.getColor(initial=current_color) + + if film_color.isValid() is False: + return + + # if new color is different then mark that the Preferences are changed + if film_color != current_color: + self.app.preferencesUiManager.on_preferences_edited() + + self.film_color_button.setStyleSheet( + "background-color:%s;" + "border-color: dimgray" % str(film_color.name()) + ) + new_val_sel = str(film_color.name()) + self.film_color_entry.set_value(new_val_sel) + self.app.defaults['tools_film_color'] = new_val_sel diff --git a/AppGUI/preferences/tools/ToolsISOPrefGroupUI.py b/AppGUI/preferences/tools/ToolsISOPrefGroupUI.py new file mode 100644 index 00000000..4fc52ebd --- /dev/null +++ b/AppGUI/preferences/tools/ToolsISOPrefGroupUI.py @@ -0,0 +1,319 @@ +from PyQt5 import QtWidgets +from PyQt5.QtCore import QSettings + +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, NumericalEvalTupleEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI + +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + +settings = QSettings("Open Source", "FlatCAM") +if settings.contains("machinist"): + machinist_setting = settings.value('machinist', type=int) +else: + machinist_setting = 0 + + +class ToolsISOPrefGroupUI(OptionsGroupUI): + def __init__(self, decimals=4, parent=None): + super(ToolsISOPrefGroupUI, self).__init__(self, parent=parent) + + self.setTitle(str(_("Isolation Tool Options"))) + self.decimals = decimals + + # ## Clear non-copper regions + self.iso_label = QtWidgets.QLabel("%s:" % _("Parameters")) + self.iso_label.setToolTip( + _("Create a Geometry object with\n" + "toolpaths to cut around polygons.") + ) + self.layout.addWidget(self.iso_label) + + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + + # Tool Dias + isotdlabel = QtWidgets.QLabel('%s:' % _('Tools Dia')) + isotdlabel.setToolTip( + _("Diameters of the tools, separated by comma.\n" + "The value of the diameter has to use the dot decimals separator.\n" + "Valid values: 0.3, 1.0") + ) + self.tool_dia_entry = NumericalEvalTupleEntry(border_color='#0069A9') + self.tool_dia_entry.setPlaceholderText(_("Comma separated values")) + + grid0.addWidget(isotdlabel, 0, 0) + grid0.addWidget(self.tool_dia_entry, 0, 1, 1, 2) + + # Tool order Radio Button + self.order_label = QtWidgets.QLabel('%s:' % _('Tool order')) + self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n" + "'No' --> means that the used order is the one in the tool table\n" + "'Forward' --> means that the tools will be ordered from small to big\n" + "'Reverse' --> means that the tools will ordered from big to small\n\n" + "WARNING: using rest machining will automatically set the order\n" + "in reverse and disable this control.")) + + self.order_radio = RadioSet([{'label': _('No'), 'value': 'no'}, + {'label': _('Forward'), 'value': 'fwd'}, + {'label': _('Reverse'), 'value': 'rev'}]) + + grid0.addWidget(self.order_label, 1, 0) + grid0.addWidget(self.order_radio, 1, 1, 1, 2) + + # Tool Type Radio Button + self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type')) + self.tool_type_label.setToolTip( + _("Default tool type:\n" + "- 'V-shape'\n" + "- Circular") + ) + + self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'}, + {'label': _('Circular'), 'value': 'C1'}]) + self.tool_type_radio.setToolTip( + _("Default tool type:\n" + "- 'V-shape'\n" + "- Circular") + ) + + grid0.addWidget(self.tool_type_label, 2, 0) + grid0.addWidget(self.tool_type_radio, 2, 1, 1, 2) + + # Tip Dia + self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia')) + self.tipdialabel.setToolTip( + _("The tip diameter for V-Shape Tool")) + self.tipdia_entry = FCDoubleSpinner() + self.tipdia_entry.set_precision(self.decimals) + self.tipdia_entry.set_range(0, 1000) + self.tipdia_entry.setSingleStep(0.1) + + grid0.addWidget(self.tipdialabel, 3, 0) + grid0.addWidget(self.tipdia_entry, 3, 1, 1, 2) + + # Tip Angle + self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle')) + self.tipanglelabel.setToolTip( + _("The tip angle for V-Shape Tool.\n" + "In degrees.")) + self.tipangle_entry = FCDoubleSpinner() + self.tipangle_entry.set_precision(self.decimals) + self.tipangle_entry.set_range(1, 180) + self.tipangle_entry.setSingleStep(5) + self.tipangle_entry.setWrapping(True) + + grid0.addWidget(self.tipanglelabel, 4, 0) + grid0.addWidget(self.tipangle_entry, 4, 1, 1, 2) + + # Cut Z entry + cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z')) + cutzlabel.setToolTip( + _("Depth of cut into material. Negative value.\n" + "In FlatCAM units.") + ) + self.cutz_entry = FCDoubleSpinner() + self.cutz_entry.set_precision(self.decimals) + self.cutz_entry.set_range(-9999.9999, 0.0000) + self.cutz_entry.setSingleStep(0.1) + + self.cutz_entry.setToolTip( + _("Depth of cut into material. Negative value.\n" + "In FlatCAM units.") + ) + + grid0.addWidget(cutzlabel, 5, 0) + grid0.addWidget(self.cutz_entry, 5, 1, 1, 2) + + # New Diameter + self.newdialabel = QtWidgets.QLabel('%s:' % _('New Dia')) + self.newdialabel.setToolTip( + _("Diameter for the new tool to add in the Tool Table.\n" + "If the tool is V-shape type then this value is automatically\n" + "calculated from the other parameters.") + ) + self.newdia_entry = FCDoubleSpinner() + self.newdia_entry.set_precision(self.decimals) + self.newdia_entry.set_range(0.0001, 9999.9999) + self.newdia_entry.setSingleStep(0.1) + + grid0.addWidget(self.newdialabel, 6, 0) + grid0.addWidget(self.newdia_entry, 6, 1, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 7, 0, 1, 3) + + # Passes + passlabel = QtWidgets.QLabel('%s:' % _('Passes')) + passlabel.setToolTip( + _("Width of the isolation gap in\n" + "number (integer) of tool widths.") + ) + self.passes_entry = FCSpinner() + self.passes_entry.set_range(1, 999) + self.passes_entry.setObjectName("i_passes") + + grid0.addWidget(passlabel, 8, 0) + grid0.addWidget(self.passes_entry, 8, 1, 1, 2) + + # Overlap Entry + overlabel = QtWidgets.QLabel('%s:' % _('Overlap')) + overlabel.setToolTip( + _("How much (percentage) of the tool width to overlap each tool pass.") + ) + self.overlap_entry = FCDoubleSpinner(suffix='%') + self.overlap_entry.set_precision(self.decimals) + self.overlap_entry.setWrapping(True) + self.overlap_entry.set_range(0.0000, 99.9999) + self.overlap_entry.setSingleStep(0.1) + self.overlap_entry.setObjectName("i_overlap") + + grid0.addWidget(overlabel, 9, 0) + grid0.addWidget(self.overlap_entry, 9, 1, 1, 2) + + # Milling Type Radio Button + self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) + self.milling_type_label.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, + {'label': _('Conventional'), 'value': 'cv'}]) + self.milling_type_radio.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + grid0.addWidget(self.milling_type_label, 10, 0) + grid0.addWidget(self.milling_type_radio, 10, 1, 1, 2) + + # Follow + self.follow_label = QtWidgets.QLabel('%s:' % _('Follow')) + self.follow_label.setToolTip( + _("Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace.") + ) + + self.follow_cb = FCCheckBox() + self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace.")) + self.follow_cb.setObjectName("i_follow") + + grid0.addWidget(self.follow_label, 11, 0) + grid0.addWidget(self.follow_cb, 11, 1, 1, 2) + + # Isolation Type + self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type')) + self.iso_type_label.setToolTip( + _("Choose how the isolation will be executed:\n" + "- 'Full' -> complete isolation of polygons\n" + "- 'Ext' -> will isolate only on the outside\n" + "- 'Int' -> will isolate only on the inside\n" + "'Exterior' isolation is almost always possible\n" + "(with the right tool) but 'Interior'\n" + "isolation can be done only when there is an opening\n" + "inside of the polygon (e.g polygon is a 'doughnut' shape).") + ) + self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'}, + {'label': _('Ext'), 'value': 'ext'}, + {'label': _('Int'), 'value': 'int'}]) + self.iso_type_radio.setObjectName("i_type") + + grid0.addWidget(self.iso_type_label, 12, 0) + grid0.addWidget(self.iso_type_radio, 12, 1, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 13, 0, 1, 3) + + # Rest machining CheckBox + self.rest_cb = FCCheckBox('%s' % _("Rest")) + self.rest_cb.setObjectName("i_rest_machining") + self.rest_cb.setToolTip( + _("If checked, use 'rest machining'.\n" + "Basically it will isolate outside PCB features,\n" + "using the biggest tool and continue with the next tools,\n" + "from bigger to smaller, to isolate the copper features that\n" + "could not be cleared by previous tool, until there is\n" + "no more copper features to isolate or there are no more tools.\n" + "If not checked, use the standard algorithm.") + ) + + grid0.addWidget(self.rest_cb, 17, 0) + + # Combine All Passes + self.combine_passes_cb = FCCheckBox(label=_('Combine')) + self.combine_passes_cb.setToolTip( + _("Combine all passes into one object") + ) + self.combine_passes_cb.setObjectName("i_combine") + + grid0.addWidget(self.combine_passes_cb, 17, 1) + + # Exception Areas + self.except_cb = FCCheckBox(label=_('Except')) + self.except_cb.setToolTip(_("When the isolation geometry is generated,\n" + "by checking this, the area of the object below\n" + "will be subtracted from the isolation geometry.")) + self.except_cb.setObjectName("i_except") + grid0.addWidget(self.except_cb, 17, 2) + + # Isolation Scope + self.select_label = QtWidgets.QLabel('%s:' % _("Selection")) + self.select_label.setToolTip( + _("Isolation scope. Choose what to isolate:\n" + "- 'All' -> Isolate all the polygons in the object\n" + "- 'Selection' -> Isolate a selection of polygons.\n" + "- 'Reference Object' - will process the area specified by another object.") + ) + self.select_combo = FCComboBox() + self.select_combo.addItems( + [_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")] + ) + self.select_combo.setObjectName("i_selection") + + grid0.addWidget(self.select_label, 20, 0) + grid0.addWidget(self.select_combo, 20, 1, 1, 2) + + # Area Shape + self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape")) + self.area_shape_label.setToolTip( + _("The kind of selection shape used for area selection.") + ) + + self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, + {'label': _("Polygon"), 'value': 'polygon'}]) + + grid0.addWidget(self.area_shape_label, 21, 0) + grid0.addWidget(self.area_shape_radio, 21, 1, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 22, 0, 1, 3) + + # ## Plotting type + self.plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'}, + {"label": _("Progressive"), "value": "progressive"}]) + plotting_label = QtWidgets.QLabel('%s:' % _("Plotting")) + plotting_label.setToolTip( + _("- 'Normal' - normal plotting, done at the end of the job\n" + "- 'Progressive' - each shape is plotted after it is generated") + ) + grid0.addWidget(plotting_label, 23, 0) + grid0.addWidget(self.plotting_radio, 23, 1, 1, 2) + + self.layout.addStretch() diff --git a/flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py b/AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py similarity index 94% rename from flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py rename to AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py index 00ec96cb..28c300da 100644 --- a/flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCEntry, RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, NumericalEvalTupleEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -45,7 +45,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "Valid values: 0.3, 1.0") ) grid0.addWidget(ncctdlabel, 0, 0) - self.ncc_tool_dia_entry = FCEntry(border_color='#0069A9') + self.ncc_tool_dia_entry = NumericalEvalTupleEntry(border_color='#0069A9') self.ncc_tool_dia_entry.setPlaceholderText(_("Comma separated values")) grid0.addWidget(self.ncc_tool_dia_entry, 0, 1) @@ -227,7 +227,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): # ], orientation='vertical', stretch=False) self.ncc_method_combo = FCComboBox() self.ncc_method_combo.addItems( - [_("Standard"), _("Seed"), _("Lines")] + [_("Standard"), _("Seed"), _("Lines"), _("Combo")] ) grid0.addWidget(methodlabel, 12, 0) @@ -285,7 +285,7 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): grid0.addWidget(separator_line, 16, 0, 1, 2) # Rest machining CheckBox - self.ncc_rest_cb = FCCheckBox('%s' % _("Rest Machining")) + self.ncc_rest_cb = FCCheckBox('%s' % _("Rest")) self.ncc_rest_cb.setToolTip( _("If checked, use 'rest machining'.\n" "Basically it will clear copper outside PCB features,\n" @@ -336,14 +336,14 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): grid0.addWidget(separator_line, 20, 0, 1, 2) # ## Plotting type - self.ncc_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'}, - {"label": _("Progressive"), "value": "progressive"}]) - plotting_label = QtWidgets.QLabel('%s:' % _("NCC Plotting")) + self.plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'}, + {"label": _("Progressive"), "value": "progressive"}]) + plotting_label = QtWidgets.QLabel('%s:' % _("Plotting")) plotting_label.setToolTip( - _("- 'Normal' - normal plotting, done at the end of the NCC job\n" - "- 'Progressive' - after each shape is generated it will be plotted.") + _("- 'Normal' - normal plotting, done at the end of the job\n" + "- 'Progressive' - each shape is plotted after it is generated") ) grid0.addWidget(plotting_label, 21, 0) - grid0.addWidget(self.ncc_plotting_radio, 21, 1) + grid0.addWidget(self.plotting_radio, 21, 1) self.layout.addStretch() diff --git a/flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py b/AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py similarity index 95% rename from flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py rename to AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py index a86b270b..9634ebb7 100644 --- a/flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCEntry, RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCComboBox, FCCheckBox, NumericalEvalTupleEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -53,7 +53,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI): ) grid0.addWidget(ptdlabel, 0, 0) - self.painttooldia_entry = FCEntry(border_color='#0069A9') + self.painttooldia_entry = NumericalEvalTupleEntry(border_color='#0069A9') self.painttooldia_entry.setPlaceholderText(_("Comma separated values")) grid0.addWidget(self.painttooldia_entry, 0, 1) @@ -241,8 +241,8 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI): separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) grid0.addWidget(separator_line, 13, 0, 1, 2) - self.rest_cb = FCCheckBox('%s' % _("Rest Machining")) - self.rest_cb.setObjectName(_("Rest Machining")) + self.rest_cb = FCCheckBox('%s' % _("Rest")) + self.rest_cb.setObjectName(_("Rest")) self.rest_cb.setToolTip( _("If checked, use 'rest machining'.\n" "Basically it will clear copper outside PCB features,\n" @@ -277,7 +277,7 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI): # ) self.selectmethod_combo = FCComboBox() self.selectmethod_combo.addItems( - [_("Polygon Selection"), _("Area Selection"), _("All Polygons"), _("Reference Object")] + [_("Polygon Selection"), _("Area Selection"), _("All"), _("Reference Object")] ) grid0.addWidget(selectlabel, 15, 0) @@ -302,10 +302,10 @@ class ToolsPaintPrefGroupUI(OptionsGroupUI): # ## Plotting type self.paint_plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'}, {"label": _("Progressive"), "value": "progressive"}]) - plotting_label = QtWidgets.QLabel('%s:' % _("Paint Plotting")) + plotting_label = QtWidgets.QLabel('%s:' % _("Plotting")) plotting_label.setToolTip( - _("- 'Normal' - normal plotting, done at the end of the Paint job\n" - "- 'Progressive' - after each shape is generated it will be plotted.") + _("- 'Normal' - normal plotting, done at the end of the job\n" + "- 'Progressive' - each shape is plotted after it is generated") ) grid0.addWidget(plotting_label, 20, 0) grid0.addWidget(self.paint_plotting_radio, 20, 1) diff --git a/flatcamGUI/preferences/tools/ToolsPanelizePrefGroupUI.py b/AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py similarity index 96% rename from flatcamGUI/preferences/tools/ToolsPanelizePrefGroupUI.py rename to AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py index df791742..f217ec00 100644 --- a/flatcamGUI/preferences/tools/ToolsPanelizePrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/ToolsPreferencesUI.py b/AppGUI/preferences/tools/ToolsPreferencesUI.py similarity index 66% rename from flatcamGUI/preferences/tools/ToolsPreferencesUI.py rename to AppGUI/preferences/tools/ToolsPreferencesUI.py index dc3061a8..9d30c9b9 100644 --- a/flatcamGUI/preferences/tools/ToolsPreferencesUI.py +++ b/AppGUI/preferences/tools/ToolsPreferencesUI.py @@ -1,19 +1,22 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.preferences.tools.ToolsSubPrefGroupUI import ToolsSubPrefGroupUI -from flatcamGUI.preferences.tools.ToolsSolderpastePrefGroupUI import ToolsSolderpastePrefGroupUI -from flatcamGUI.preferences.tools.ToolsTransformPrefGroupUI import ToolsTransformPrefGroupUI -from flatcamGUI.preferences.tools.ToolsCalculatorsPrefGroupUI import ToolsCalculatorsPrefGroupUI -from flatcamGUI.preferences.tools.ToolsPanelizePrefGroupUI import ToolsPanelizePrefGroupUI -from flatcamGUI.preferences.tools.ToolsFilmPrefGroupUI import ToolsFilmPrefGroupUI -from flatcamGUI.preferences.tools.ToolsPaintPrefGroupUI import ToolsPaintPrefGroupUI -from flatcamGUI.preferences.tools.Tools2sidedPrefGroupUI import Tools2sidedPrefGroupUI -from flatcamGUI.preferences.tools.ToolsCutoutPrefGroupUI import ToolsCutoutPrefGroupUI -from flatcamGUI.preferences.tools.ToolsNCCPrefGroupUI import ToolsNCCPrefGroupUI +from AppGUI.preferences.tools.ToolsSubPrefGroupUI import ToolsSubPrefGroupUI +from AppGUI.preferences.tools.ToolsSolderpastePrefGroupUI import ToolsSolderpastePrefGroupUI +from AppGUI.preferences.tools.ToolsCornersPrefGroupUI import ToolsCornersPrefGroupUI +from AppGUI.preferences.tools.ToolsTransformPrefGroupUI import ToolsTransformPrefGroupUI +from AppGUI.preferences.tools.ToolsCalculatorsPrefGroupUI import ToolsCalculatorsPrefGroupUI +from AppGUI.preferences.tools.ToolsPanelizePrefGroupUI import ToolsPanelizePrefGroupUI +from AppGUI.preferences.tools.ToolsFilmPrefGroupUI import ToolsFilmPrefGroupUI +from AppGUI.preferences.tools.Tools2sidedPrefGroupUI import Tools2sidedPrefGroupUI + +from AppGUI.preferences.tools.ToolsCutoutPrefGroupUI import ToolsCutoutPrefGroupUI +from AppGUI.preferences.tools.ToolsNCCPrefGroupUI import ToolsNCCPrefGroupUI +from AppGUI.preferences.tools.ToolsPaintPrefGroupUI import ToolsPaintPrefGroupUI +from AppGUI.preferences.tools.ToolsISOPrefGroupUI import ToolsISOPrefGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -35,6 +38,9 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.setLayout(self.layout) self.decimals = decimals + self.tools_iso_group = ToolsISOPrefGroupUI(decimals=self.decimals) + self.tools_iso_group.setMinimumWidth(220) + self.tools_ncc_group = ToolsNCCPrefGroupUI(decimals=self.decimals) self.tools_ncc_group.setMinimumWidth(220) @@ -62,6 +68,9 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.tools_solderpaste_group = ToolsSolderpastePrefGroupUI(decimals=self.decimals) self.tools_solderpaste_group.setMinimumWidth(200) + self.tools_corners_group = ToolsCornersPrefGroupUI(decimals=self.decimals) + self.tools_corners_group.setMinimumWidth(200) + self.tools_sub_group = ToolsSubPrefGroupUI(decimals=self.decimals) self.tools_sub_group.setMinimumWidth(200) @@ -71,7 +80,7 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.vlay1 = QtWidgets.QVBoxLayout() self.vlay1.addWidget(self.tools_paint_group) - self.vlay1.addWidget(self.tools_panelize_group) + self.vlay1.addWidget(self.tools_iso_group) self.vlay2 = QtWidgets.QVBoxLayout() self.vlay2.addWidget(self.tools_transform_group) @@ -84,6 +93,8 @@ class ToolsPreferencesUI(QtWidgets.QWidget): self.vlay4 = QtWidgets.QVBoxLayout() self.vlay4.addWidget(self.tools_solderpaste_group) + self.vlay4.addWidget(self.tools_corners_group) + self.vlay4.addWidget(self.tools_panelize_group) self.layout.addLayout(self.vlay) self.layout.addLayout(self.vlay1) diff --git a/flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py b/AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py similarity index 96% rename from flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py rename to AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py index c1f60821..832b76d9 100644 --- a/flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCEntry, FCDoubleSpinner, FCSpinner, FCComboBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, FCSpinner, FCComboBox, NumericalEvalTupleEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -45,7 +45,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): "The value of the diameter has to use the dot decimals separator.\n" "Valid values: 0.3, 1.0") ) - self.nozzle_tool_dia_entry = FCEntry() + self.nozzle_tool_dia_entry = NumericalEvalTupleEntry(border_color='#0069A9') grid0.addWidget(nozzletdlabel, 0, 0) grid0.addWidget(self.nozzle_tool_dia_entry, 0, 1) @@ -130,7 +130,7 @@ class ToolsSolderpastePrefGroupUI(OptionsGroupUI): grid0.addWidget(self.z_toolchange_entry, 6, 1) # X,Y Toolchange location - self.xy_toolchange_entry = FCEntry() + self.xy_toolchange_entry = NumericalEvalTupleEntry(border_color='#0069A9') self.xy_toolchange_label = QtWidgets.QLabel('%s:' % _("Toolchange X-Y")) self.xy_toolchange_label.setToolTip( _("The X,Y location for tool (nozzle) change.\n" diff --git a/flatcamGUI/preferences/tools/ToolsSubPrefGroupUI.py b/AppGUI/preferences/tools/ToolsSubPrefGroupUI.py similarity index 88% rename from flatcamGUI/preferences/tools/ToolsSubPrefGroupUI.py rename to AppGUI/preferences/tools/ToolsSubPrefGroupUI.py index ff9430e1..2cc78d87 100644 --- a/flatcamGUI/preferences/tools/ToolsSubPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsSubPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCCheckBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCCheckBox +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py b/AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py similarity index 97% rename from flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py rename to AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py index 60a8bf24..cecff2bf 100644 --- a/flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py +++ b/AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, EvalEntry2 -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, NumericalEvalTupleEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -191,7 +191,7 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI): "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") ) - self.flip_ref_entry = EvalEntry2("(0, 0)") + self.flip_ref_entry = NumericalEvalTupleEntry(border_color='#0069A9') grid0.addWidget(self.flip_ref_label, 14, 0, 1, 2) grid0.addWidget(self.flip_ref_entry, 15, 0, 1, 2) diff --git a/flatcamGUI/preferences/tools/__init__.py b/AppGUI/preferences/tools/__init__.py similarity index 100% rename from flatcamGUI/preferences/tools/__init__.py rename to AppGUI/preferences/tools/__init__.py diff --git a/flatcamGUI/preferences/utilities/AutoCompletePrefGroupUI.py b/AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py similarity index 94% rename from flatcamGUI/preferences/utilities/AutoCompletePrefGroupUI.py rename to AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py index fd138091..4f491f49 100644 --- a/flatcamGUI/preferences/utilities/AutoCompletePrefGroupUI.py +++ b/AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCButton, FCTextArea, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/utilities/FAExcPrefGroupUI.py b/AppGUI/preferences/utilities/FAExcPrefGroupUI.py similarity index 95% rename from flatcamGUI/preferences/utilities/FAExcPrefGroupUI.py rename to AppGUI/preferences/utilities/FAExcPrefGroupUI.py index 18a3b971..4cdd89f7 100644 --- a/flatcamGUI/preferences/utilities/FAExcPrefGroupUI.py +++ b/AppGUI/preferences/utilities/FAExcPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import VerticalScrollArea, FCButton, FCTextArea, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import VerticalScrollArea, FCButton, FCTextArea, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/utilities/FAGcoPrefGroupUI.py b/AppGUI/preferences/utilities/FAGcoPrefGroupUI.py similarity index 94% rename from flatcamGUI/preferences/utilities/FAGcoPrefGroupUI.py rename to AppGUI/preferences/utilities/FAGcoPrefGroupUI.py index 2ac1a1b9..9c367ed5 100644 --- a/flatcamGUI/preferences/utilities/FAGcoPrefGroupUI.py +++ b/AppGUI/preferences/utilities/FAGcoPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCButton, FCTextArea, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/utilities/FAGrbPrefGroupUI.py b/AppGUI/preferences/utilities/FAGrbPrefGroupUI.py similarity index 94% rename from flatcamGUI/preferences/utilities/FAGrbPrefGroupUI.py rename to AppGUI/preferences/utilities/FAGrbPrefGroupUI.py index 480fdb39..70e0aa78 100644 --- a/flatcamGUI/preferences/utilities/FAGrbPrefGroupUI.py +++ b/AppGUI/preferences/utilities/FAGrbPrefGroupUI.py @@ -1,11 +1,11 @@ from PyQt5 import QtWidgets, QtGui from PyQt5.QtCore import QSettings -from flatcamGUI.GUIElements import FCButton, FCTextArea, FCEntry -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.GUIElements import FCButton, FCTextArea, FCEntry +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamGUI/preferences/utilities/UtilPreferencesUI.py b/AppGUI/preferences/utilities/UtilPreferencesUI.py similarity index 76% rename from flatcamGUI/preferences/utilities/UtilPreferencesUI.py rename to AppGUI/preferences/utilities/UtilPreferencesUI.py index ae9e2110..e227c924 100644 --- a/flatcamGUI/preferences/utilities/UtilPreferencesUI.py +++ b/AppGUI/preferences/utilities/UtilPreferencesUI.py @@ -1,9 +1,9 @@ from PyQt5 import QtWidgets -from flatcamGUI.preferences.utilities.AutoCompletePrefGroupUI import AutoCompletePrefGroupUI -from flatcamGUI.preferences.utilities.FAGrbPrefGroupUI import FAGrbPrefGroupUI -from flatcamGUI.preferences.utilities.FAGcoPrefGroupUI import FAGcoPrefGroupUI -from flatcamGUI.preferences.utilities.FAExcPrefGroupUI import FAExcPrefGroupUI +from AppGUI.preferences.utilities.AutoCompletePrefGroupUI import AutoCompletePrefGroupUI +from AppGUI.preferences.utilities.FAGrbPrefGroupUI import FAGrbPrefGroupUI +from AppGUI.preferences.utilities.FAGcoPrefGroupUI import FAGcoPrefGroupUI +from AppGUI.preferences.utilities.FAExcPrefGroupUI import FAExcPrefGroupUI class UtilPreferencesUI(QtWidgets.QWidget): diff --git a/flatcamGUI/preferences/utilities/__init__.py b/AppGUI/preferences/utilities/__init__.py similarity index 100% rename from flatcamGUI/preferences/utilities/__init__.py rename to AppGUI/preferences/utilities/__init__.py diff --git a/AppObjects/AppObject.py b/AppObjects/AppObject.py new file mode 100644 index 00000000..05fcbfa0 --- /dev/null +++ b/AppObjects/AppObject.py @@ -0,0 +1,394 @@ +# ########################################################### +# FlatCAM: 2D Post-processing for Manufacturing # +# http://flatcam.org # +# Author: Juan Pablo Caram (c) # +# Date: 2/5/2014 # +# MIT Licence # +# Modified by Marius Stanciu (2020) # +# ########################################################### + +from PyQt5 import QtCore +from AppObjects.ObjectCollection import * +from AppObjects.FlatCAMCNCJob import CNCJobObject +from AppObjects.FlatCAMDocument import DocumentObject +from AppObjects.FlatCAMExcellon import ExcellonObject +from AppObjects.FlatCAMGeometry import GeometryObject +from AppObjects.FlatCAMGerber import GerberObject +from AppObjects.FlatCAMScript import ScriptObject + +import time +import traceback + +# FlatCAM Translation +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + + +class AppObject(QtCore.QObject): + + # Emitted by app_obj.new_object() and passes the new object as argument, plot flag. + # on_object_created() adds the object to the collection, plots on appropriate flag + # and emits app_obj.new_object_available. + object_created = QtCore.pyqtSignal(object, bool, bool) + + # Emitted when a object has been changed (like scaled, mirrored) + object_changed = QtCore.pyqtSignal(object) + + # Emitted after object has been plotted. + # Calls 'on_zoom_fit' method to fit object in scene view in main thread to prevent drawing glitches. + object_plotted = QtCore.pyqtSignal(object) + + plots_updated = QtCore.pyqtSignal() + + def __init__(self, app): + super(AppObject, self).__init__() + self.app = app + self.inform = app.inform + + # signals that are emitted when object state changes + self.object_created.connect(self.on_object_created) + self.object_changed.connect(self.on_object_changed) + self.object_plotted.connect(self.on_object_plotted) + self.plots_updated.connect(self.app.on_plots_updated) + + def new_object(self, kind, name, initialize, plot=True, autoselected=True): + """ + Creates a new specialized FlatCAMObj and attaches it to the application, + this is, updates the GUI accordingly, any other records and plots it. + This method is thread-safe. + + Notes: + * If the name is in use, the self.collection will modify it + when appending it to the collection. There is no need to handle + name conflicts here. + + :param kind: The kind of object to create. One of 'gerber', 'excellon', 'cncjob' and 'geometry'. + :type kind: str + :param name: Name for the object. + :type name: str + :param initialize: Function to run after creation of the object but before it is attached to the application. + The function is called with 2 parameters: the new object and the App instance. + :type initialize: function + :param plot: If to plot the resulting object + :param autoselected: if the resulting object is autoselected in the Project tab and therefore in the + self.collection + :return: None + :rtype: None + """ + + log.debug("AppObject.new_object()") + obj_plot = plot + obj_autoselected = autoselected + + t0 = time.time() # Debug + + # ## Create object + classdict = { + "gerber": GerberObject, + "excellon": ExcellonObject, + "cncjob": CNCJobObject, + "geometry": GeometryObject, + "script": ScriptObject, + "document": DocumentObject + } + + log.debug("Calling object constructor...") + + # Object creation/instantiation + obj = classdict[kind](name) + + obj.units = self.app.options["units"] + + # IMPORTANT + # The key names in defaults and options dictionary's are not random: + # they have to have in name first the type of the object (geometry, excellon, cncjob and gerber) or how it's + # called here, the 'kind' followed by an underline. Above the App default values from self.defaults are + # copied to self.options. After that, below, depending on the type of + # object that is created, it will strip the name of the object and the underline (if the original key was + # let's say "excellon_toolchange", it will strip the excellon_) and to the obj.options the key will become + # "toolchange" + + for option in self.app.options: + if option.find(kind + "_") == 0: + oname = option[len(kind) + 1:] + obj.options[oname] = self.app.options[option] + + obj.isHovering = False + obj.notHovering = True + + # Initialize as per user request + # User must take care to implement initialize + # in a thread-safe way as is is likely that we + # have been invoked in a separate thread. + t1 = time.time() + log.debug("%f seconds before initialize()." % (t1 - t0)) + try: + return_value = initialize(obj, self.app) + except Exception as e: + msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n") + msg += _("Object ({kind}) failed because: {error} \n\n").format(kind=kind, error=str(e)) + msg += traceback.format_exc() + self.app.inform.emit(msg) + return "fail" + + t2 = time.time() + log.debug("%f seconds executing initialize()." % (t2 - t1)) + + if return_value == 'fail': + log.debug("Object (%s) parsing and/or geometry creation failed." % kind) + return "fail" + + # Check units and convert if necessary + # This condition CAN be true because initialize() can change obj.units + if self.app.options["units"].upper() != obj.units.upper(): + self.app.inform.emit('%s: %s' % (_("Converting units to "), self.app.options["units"])) + obj.convert_units(self.app.options["units"]) + t3 = time.time() + log.debug("%f seconds converting units." % (t3 - t2)) + + # Create the bounding box for the object and then add the results to the obj.options + # But not for Scripts or for Documents + if kind != 'document' and kind != 'script': + try: + xmin, ymin, xmax, ymax = obj.bounds() + obj.options['xmin'] = xmin + obj.options['ymin'] = ymin + obj.options['xmax'] = xmax + obj.options['ymax'] = ymax + except Exception as e: + log.warning("AppObject.new_object() -> The object has no bounds properties. %s" % str(e)) + return "fail" + + try: + if kind == 'excellon': + obj.fill_color = self.app.defaults["excellon_plot_fill"] + obj.outline_color = self.app.defaults["excellon_plot_line"] + + if kind == 'gerber': + obj.fill_color = self.app.defaults["gerber_plot_fill"] + obj.outline_color = self.app.defaults["gerber_plot_line"] + except Exception as e: + log.warning("AppObject.new_object() -> setting colors error. %s" % str(e)) + + # update the KeyWords list with the name of the file + self.app.myKeywords.append(obj.options['name']) + + log.debug("Moving new object back to main thread.") + + # Move the object to the main thread and let the app know that it is available. + obj.moveToThread(self.app.main_thread) + self.object_created.emit(obj, obj_plot, obj_autoselected) + + return obj + + def new_excellon_object(self): + """ + Creates a new, blank Excellon object. + + :return: None + """ + + self.new_object('excellon', 'new_exc', lambda x, y: None, plot=False) + + def new_geometry_object(self): + """ + Creates a new, blank and single-tool Geometry object. + + :return: None + """ + + def initialize(obj, app): + obj.multitool = False + + self.new_object('geometry', 'new_geo', initialize, plot=False) + + def new_gerber_object(self): + """ + Creates a new, blank Gerber object. + + :return: None + """ + + def initialize(grb_obj, app): + grb_obj.multitool = False + grb_obj.source_file = [] + grb_obj.multigeo = False + grb_obj.follow = False + grb_obj.apertures = {} + grb_obj.solid_geometry = [] + + try: + grb_obj.options['xmin'] = 0 + grb_obj.options['ymin'] = 0 + grb_obj.options['xmax'] = 0 + grb_obj.options['ymax'] = 0 + except KeyError: + pass + + self.new_object('gerber', 'new_grb', initialize, plot=False) + + def new_script_object(self): + """ + Creates a new, blank TCL Script object. + + :return: None + """ + + # commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \ + # "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \ + # "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \ + # "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \ + # "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \ + # "ListSys, MillDrills,\n" \ + # "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \ + # "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \ + # "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \ + # "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \ + # "# SubtractRectangle, Version, WriteGCode\n" + + new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \ + '# %s:\n' % _('TCL Tutorial is here') + \ + '# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \ + '# %s:\n' % _("FlatCAM commands list") + new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands " + "(displayed in Tcl Shell).") + + def initialize(obj, app): + obj.source_file = deepcopy(new_source_file) + + outname = 'new_script' + self.new_object('script', outname, initialize, plot=False) + + def new_document_object(self): + """ + Creates a new, blank Document object. + + :return: None + """ + + def initialize(obj, app): + obj.source_file = "" + + self.new_object('document', 'new_document', initialize, plot=False) + + def on_object_created(self, obj, plot, auto_select): + """ + Event callback for object creation. + It will add the new object to the collection. After that it will plot the object in a threaded way + + :param obj: The newly created FlatCAM object. + :param plot: if the newly create object t obe plotted + :param auto_select: if the newly created object to be autoselected after creation + :return: None + """ + t0 = time.time() # DEBUG + log.debug("on_object_created()") + + # The Collection might change the name if there is a collision + self.app.collection.append(obj) + + # after adding the object to the collection always update the list of objects that are in the collection + self.app.all_objects_list = self.app.collection.get_list() + + # self.app.inform.emit('[selected] %s created & selected: %s' % + # (str(obj.kind).capitalize(), str(obj.options['name']))) + if obj.kind == 'gerber': + self.app.inform.emit('[selected] {kind} {tx}: {name}'.format( + kind=obj.kind.capitalize(), + color='green', + name=str(obj.options['name']), tx=_("created/selected")) + ) + elif obj.kind == 'excellon': + self.app.inform.emit('[selected] {kind} {tx}: {name}'.format( + kind=obj.kind.capitalize(), + color='brown', + name=str(obj.options['name']), tx=_("created/selected")) + ) + elif obj.kind == 'cncjob': + self.app.inform.emit('[selected] {kind} {tx}: {name}'.format( + kind=obj.kind.capitalize(), + color='blue', + name=str(obj.options['name']), tx=_("created/selected")) + ) + elif obj.kind == 'geometry': + self.app.inform.emit('[selected] {kind} {tx}: {name}'.format( + kind=obj.kind.capitalize(), + color='red', + name=str(obj.options['name']), tx=_("created/selected")) + ) + elif obj.kind == 'script': + self.app.inform.emit('[selected] {kind} {tx}: {name}'.format( + kind=obj.kind.capitalize(), + color='orange', + name=str(obj.options['name']), tx=_("created/selected")) + ) + elif obj.kind == 'document': + self.app.inform.emit('[selected] {kind} {tx}: {name}'.format( + kind=obj.kind.capitalize(), + color='darkCyan', + name=str(obj.options['name']), tx=_("created/selected")) + ) + + # update the SHELL auto-completer model with the name of the new object + self.app.shell._edit.set_model_data(self.app.myKeywords) + + if auto_select: + # select the just opened object but deselect the previous ones + self.app.collection.set_all_inactive() + self.app.collection.set_active(obj.options["name"]) + else: + self.app.collection.set_all_inactive() + + # here it is done the object plotting + def task(t_obj): + with self.app.proc_container.new(_("Plotting")): + if t_obj.kind == 'cncjob': + t_obj.plot(kind=self.app.defaults["cncjob_plot_kind"]) + else: + t_obj.plot() + + t1 = time.time() # DEBUG + log.debug("%f seconds adding object and plotting." % (t1 - t0)) + self.object_plotted.emit(t_obj) + + # Send to worker + # self.worker.add_task(worker_task, [self]) + if plot is True: + self.app.worker_task.emit({'fcn': task, 'params': [obj]}) + + def on_object_changed(self, obj): + """ + Called whenever the geometry of the object was changed in some way. + This require the update of it's bounding values so it can be the selected on canvas. + Update the bounding box data from obj.options + + :param obj: the object that was changed + :return: None + """ + + try: + xmin, ymin, xmax, ymax = obj.bounds() + except TypeError: + return + obj.options['xmin'] = xmin + obj.options['ymin'] = ymin + obj.options['xmax'] = xmax + obj.options['ymax'] = ymax + + log.debug("Object changed, updating the bounding box data on self.options") + # delete the old selection shape + self.app.delete_selection_shape() + self.app.should_we_save = True + + def on_object_plotted(self): + """ + Callback called whenever the plotted object needs to be fit into the viewport (canvas) + + :return: None + """ + self.app.on_zoom_fit() diff --git a/flatcamObjects/FlatCAMCNCJob.py b/AppObjects/FlatCAMCNCJob.py similarity index 97% rename from flatcamObjects/FlatCAMCNCJob.py rename to AppObjects/FlatCAMCNCJob.py index c9a47076..f0fe0765 100644 --- a/flatcamObjects/FlatCAMCNCJob.py +++ b/AppObjects/FlatCAMCNCJob.py @@ -14,8 +14,8 @@ from copy import deepcopy from io import StringIO from datetime import datetime -from flatcamEditors.FlatCAMTextEditor import TextEditor -from flatcamObjects.FlatCAMObj import * +from AppEditors.FlatCAMTextEditor import TextEditor +from AppObjects.FlatCAMObj import * from camlib import CNCjob @@ -24,7 +24,7 @@ import sys import math import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -506,10 +506,10 @@ class CNCJobObject(FlatCAMObj, CNCjob): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export Machine Code ..."), directory=dir_file_to_save, - filter=_filter_ + ext_filter=_filter_ ) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Machine Code ..."), filter=_filter_) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Machine Code ..."), ext_filter=_filter_) filename = str(filename) @@ -564,7 +564,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): # delete the absolute and relative position and messages in the infobar self.app.ui.position_label.setText("") - self.app.ui.rel_position_label.setText("") + # self.app.ui.rel_position_label.setText("") # first clear previous text in text editor (if any) self.gcode_editor_tab.code_editor.clear() @@ -989,7 +989,6 @@ class CNCJobObject(FlatCAMObj, CNCjob): for key in self.cnc_tools: ppg = self.cnc_tools[key]['data']['ppname_g'] if 'toolchange_custom' not in str(ppg).lower(): - print(ppg) if self.ui.toolchange_cb.get_value(): self.ui.toolchange_cb.set_value(False) self.app.inform.emit('[WARNING_NOTCL] %s' % @@ -1107,7 +1106,7 @@ class CNCJobObject(FlatCAMObj, CNCjob): except ValueError: # we may have a tuple with only one element and a comma dia_plot = [float(el) for el in self.options["tooldia"].split(',') if el != ''][0] - self.plot2(dia_plot, obj=self, visible=visible, kind=kind) + self.plot2(tooldia=dia_plot, obj=self, visible=visible, kind=kind) else: # multiple tools usage if self.cnc_tools: @@ -1117,12 +1116,16 @@ class CNCJobObject(FlatCAMObj, CNCjob): self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) # TODO: until the gcode parsed will be stored on each Excellon tool this will not get executed - if self.exc_cnc_tools: - for tooldia_key in self.exc_cnc_tools: - tooldia = float('%.*f' % (self.decimals, float(tooldia_key))) - # gcode_parsed = self.cnc_tools[tooldia_key]['gcode_parsed'] - gcode_parsed = self.gcode_parsed - self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) + # I do this so the travel lines thickness will reflect the tool diameter + # may work only for objects created within the app and not Gcode imported from elsewhere for which we + # don't know the origin + if self.origin_kind == "excellon": + if self.exc_cnc_tools: + for tooldia_key in self.exc_cnc_tools: + tooldia = float('%.*f' % (self.decimals, float(tooldia_key))) + # gcode_parsed = self.exc_cnc_tools[tooldia_key]['gcode_parsed'] + gcode_parsed = self.gcode_parsed + self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) self.shapes.redraw() except (ObjectDeleted, AttributeError): diff --git a/flatcamObjects/FlatCAMDocument.py b/AppObjects/FlatCAMDocument.py similarity index 95% rename from flatcamObjects/FlatCAMDocument.py rename to AppObjects/FlatCAMDocument.py index 5f33b73a..c9aadca9 100644 --- a/flatcamObjects/FlatCAMDocument.py +++ b/AppObjects/FlatCAMDocument.py @@ -10,11 +10,11 @@ # File modified by: Marius Stanciu # # ########################################################## -from flatcamEditors.FlatCAMTextEditor import TextEditor -from flatcamObjects.FlatCAMObj import * +from AppEditors.FlatCAMTextEditor import TextEditor +from AppObjects.FlatCAMObj import * import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -284,6 +284,27 @@ class DocumentObject(FlatCAMObj): self.ui.sel_color_entry.set_value(new_val) self.app.defaults['document_sel_color'] = new_val + def mirror(self, axis, point): + pass + + def offset(self, vect): + pass + + def rotate(self, angle, point): + pass + + def scale(self, xfactor, yfactor=None, point=None): + pass + + def skew(self, angle_x, angle_y, point): + pass + + def buffer(self, distance, join, factor=None): + pass + + def bounds(self, flatten=False): + return None, None, None, None + def to_dict(self): """ Returns a representation of the object as a dictionary. diff --git a/flatcamObjects/FlatCAMExcellon.py b/AppObjects/FlatCAMExcellon.py similarity index 81% rename from flatcamObjects/FlatCAMExcellon.py rename to AppObjects/FlatCAMExcellon.py index 79020ab1..71ce705c 100644 --- a/flatcamObjects/FlatCAMExcellon.py +++ b/AppObjects/FlatCAMExcellon.py @@ -15,13 +15,14 @@ from shapely.geometry import Point, LineString from copy import deepcopy -from flatcamParsers.ParseExcellon import Excellon -from flatcamObjects.FlatCAMObj import * +from AppParsers.ParseExcellon import Excellon +from AppObjects.FlatCAMObj import * import itertools +import numpy as np import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -31,7 +32,7 @@ if '_' not in builtins.__dict__: class ExcellonObject(FlatCAMObj, Excellon): """ - Represents Excellon/Drill code. + Represents Excellon/Drill code. An object stored in the FlatCAM objects collection (a dict) """ ui_type = ExcellonObjectUI @@ -50,6 +51,7 @@ class ExcellonObject(FlatCAMObj, Excellon): self.options.update({ "plot": True, "solid": False, + "multicolored": False, "operation": "drill", "milling_type": "drills", @@ -125,6 +127,10 @@ class ExcellonObject(FlatCAMObj, Excellon): self.outline_color = self.app.defaults['excellon_plot_line'] self.alpha_level = 'bf' + # store here the state of the exclusion checkbox state to be restored after building the UI + # TODO add this in the sel.app.defaults dict and in Preferences + self.exclusion_area_cb_is_checked = False + # Attributes to be included in serialization # Always append to it because it carries contents # from predecessors. @@ -142,9 +148,11 @@ class ExcellonObject(FlatCAMObj, Excellon): If only one object is in exc_list parameter then this function will copy that object in the exc_final - :param exc_list: List or one object of ExcellonObject Objects to join. - :param exc_final: Destination ExcellonObject object. - :return: None + :param exc_list: List or one object of ExcellonObject Objects to join. + :type exc_list: list + :param exc_final: Destination ExcellonObject object. + :type exc_final: class + :return: None """ if decimals is None: @@ -312,8 +320,23 @@ class ExcellonObject(FlatCAMObj, Excellon): exc_final.create_geometry() def build_ui(self): + """ + Will (re)build the Excellon UI updating it (the tool table) + + :return: None + :rtype: + """ FlatCAMObj.build_ui(self) + # Area Exception - exclusion shape added signal + # first disconnect it from any other object + try: + self.app.exc_areas.e_shape_modified.disconnect() + except (TypeError, AttributeError): + pass + # then connect it to the current build_ui() method + self.app.exc_areas.e_shape_modified.connect(self.update_exclusion_table) + self.units = self.app.defaults['units'].upper() for row in range(self.ui.tools_table.rowCount()): @@ -514,6 +537,58 @@ class ExcellonObject(FlatCAMObj, Excellon): "%s: %s" % (_('Parameters for'), _("Multiple Tools")) ) + # Build Exclusion Areas section + e_len = len(self.app.exc_areas.exclusion_areas_storage) + self.ui.exclusion_table.setRowCount(e_len) + + area_id = 0 + + for area in range(e_len): + area_id += 1 + + area_dict = self.app.exc_areas.exclusion_areas_storage[area] + + area_id_item = QtWidgets.QTableWidgetItem('%d' % int(area_id)) + area_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 0, area_id_item) # Area id + + object_item = QtWidgets.QTableWidgetItem('%s' % area_dict["obj_type"]) + object_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 1, object_item) # Origin Object + + strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"]) + strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 2, strategy_item) # Strategy + + overz_item = QtWidgets.QTableWidgetItem('%s' % area_dict["overz"]) + overz_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 3, overz_item) # Over Z + + self.ui.exclusion_table.resizeColumnsToContents() + self.ui.exclusion_table.resizeRowsToContents() + + area_vheader = self.ui.exclusion_table.verticalHeader() + area_vheader.hide() + self.ui.exclusion_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + area_hheader = self.ui.exclusion_table.horizontalHeader() + area_hheader.setMinimumSectionSize(10) + area_hheader.setDefaultSectionSize(70) + + area_hheader.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed) + area_hheader.resizeSection(0, 20) + area_hheader.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) + area_hheader.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) + area_hheader.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) + + # area_hheader.setStretchLastSection(True) + self.ui.exclusion_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + self.ui.exclusion_table.setColumnWidth(0, 20) + + self.ui.exclusion_table.setMinimumHeight(self.ui.exclusion_table.getHeight()) + self.ui.exclusion_table.setMaximumHeight(self.ui.exclusion_table.getHeight()) + self.ui_connect() def set_ui(self, ui): @@ -521,9 +596,9 @@ class ExcellonObject(FlatCAMObj, Excellon): Configures the user interface for this object. Connects options to form fields. - :param ui: User interface object. - :type ui: ExcellonObjectUI - :return: None + :param ui: User interface object. + :type ui: ExcellonObjectUI + :return: None """ FlatCAMObj.set_ui(self, ui) @@ -534,6 +609,7 @@ class ExcellonObject(FlatCAMObj, Excellon): self.form_fields.update({ "plot": self.ui.plot_cb, "solid": self.ui.solid_cb, + "multicolored": self.ui.multicolored_cb, "operation": self.ui.operation_radio, "milling_type": self.ui.milling_type_radio, @@ -567,7 +643,11 @@ class ExcellonObject(FlatCAMObj, Excellon): "ppname_g": self.ui.pp_geo_name_cb, "z_pdepth": self.ui.pdepth_entry, "feedrate_probe": self.ui.feedrate_probe_entry, - # "gcode_type": self.ui.excellon_gcode_type_radio + # "gcode_type": self.ui.excellon_gcode_type_radio, + "area_exclusion": self.ui.exclusion_cb, + "area_shape": self.ui.area_shape_radio, + "area_strategy": self.ui.strategy_radio, + "area_overz": self.ui.over_z_entry, }) self.name2option = { @@ -628,12 +708,24 @@ class ExcellonObject(FlatCAMObj, Excellon): assert isinstance(self.ui, ExcellonObjectUI), \ "Expected a ExcellonObjectUI, got %s" % type(self.ui) + self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click) self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click) + self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click) + self.ui.generate_cnc_button.clicked.connect(self.on_create_cncjob_button_click) self.ui.generate_milling_button.clicked.connect(self.on_generate_milling_button_click) self.ui.generate_milling_slots_button.clicked.connect(self.on_generate_milling_slots_button_click) + # Exclusion areas signals + self.ui.exclusion_table.horizontalHeader().sectionClicked.connect(self.exclusion_table_toggle_all) + self.ui.exclusion_table.lost_focus.connect(self.clear_selection) + self.ui.exclusion_table.itemClicked.connect(self.draw_sel_shape) + self.ui.add_area_button.clicked.connect(self.on_add_area_click) + self.ui.delete_area_button.clicked.connect(self.on_clear_area_click) + self.ui.delete_sel_area_button.clicked.connect(self.on_delete_sel_areas) + self.ui.strategy_radio.activated_custom.connect(self.on_strategy) + self.on_operation_type(val='drill') self.ui.operation_radio.activated_custom.connect(self.on_operation_type) @@ -650,6 +742,12 @@ class ExcellonObject(FlatCAMObj, Excellon): self.ui.operation_radio.setEnabled(False) def ui_connect(self): + """ + Will connect all signals in the Excellon UI that needs to be connected + + :return: None + :rtype: + """ # selective plotting for row in range(self.ui.tools_table.rowCount() - 2): @@ -672,6 +770,12 @@ class ExcellonObject(FlatCAMObj, Excellon): current_widget.returnPressed.connect(self.form_to_storage) def ui_disconnect(self): + """ + Will disconnect all signals in the Excellon UI that needs to be disconnected + + :return: None + :rtype: + """ # selective plotting for row in range(self.ui.tools_table.rowCount()): try: @@ -714,6 +818,12 @@ class ExcellonObject(FlatCAMObj, Excellon): pass def on_row_selection_change(self): + """ + Called when the user clicks on a row in Tools Table + + :return: None + :rtype: + """ self.ui_disconnect() sel_rows = [] @@ -764,6 +874,14 @@ class ExcellonObject(FlatCAMObj, Excellon): self.ui_connect() def storage_to_form(self, dict_storage): + """ + Will update the GUI with data from the "storage" in this case the dict self.tools + + :param dict_storage: A dictionary holding the data relevant for gnerating Gcode from Excellon + :type dict_storage: dict + :return: None + :rtype: + """ for form_key in self.form_fields: for storage_key in dict_storage: if form_key == storage_key and form_key not in \ @@ -775,6 +893,12 @@ class ExcellonObject(FlatCAMObj, Excellon): pass def form_to_storage(self): + """ + Will update the 'storage' attribute which is the dict self.tools with data collected from GUI + + :return: None + :rtype: + """ if self.ui.tools_table.rowCount() == 0: # there is no tool in tool table so we can't save the GUI elements values to storage return @@ -803,6 +927,14 @@ class ExcellonObject(FlatCAMObj, Excellon): self.ui_connect() def on_operation_type(self, val): + """ + Called by a RadioSet activated_custom signal + + :param val: Parameter passes by the signal that called this method + :type val: str + :return: None + :rtype: + """ if val == 'mill': self.ui.mill_type_label.show() self.ui.milling_type_radio.show() @@ -831,10 +963,10 @@ class ExcellonObject(FlatCAMObj, Excellon): def get_selected_tools_list(self): """ Returns the keys to the self.tools dictionary corresponding - to the selections on the tool list in the GUI. + to the selections on the tool list in the AppGUI. - :return: List of tools. - :rtype: list + :return: List of tools. + :rtype: list """ return [str(x.text()) for x in self.ui.tools_table.selectedItems()] @@ -843,8 +975,8 @@ class ExcellonObject(FlatCAMObj, Excellon): """ Returns a list of lists, each list in the list is made out of row elements - :return: List of table_tools items. - :rtype: list + :return: List of table_tools items. + :rtype: list """ table_tools_items = [] for x in self.ui.tools_table.selectedItems(): @@ -872,7 +1004,21 @@ class ExcellonObject(FlatCAMObj, Excellon): def export_excellon(self, whole, fract, e_zeros=None, form='dec', factor=1, slot_type='routing'): """ Returns two values, first is a boolean , if 1 then the file has slots and second contain the Excellon code - :return: has_slots and Excellon_code + + :param whole: Integer part digits + :type whole: int + :param fract: Fractional part digits + :type fract: int + :param e_zeros: Excellon zeros suppression: LZ or TZ + :type e_zeros: str + :param form: Excellon format: 'dec', + :type form: str + :param factor: Conversion factor + :type factor: float + :param slot_type: How to treat slots: "routing" or "drilling" + :type slot_type: str + :return: A tuple: (has_slots, Excellon_code) -> (bool, str) + :rtype: tuple """ excellon_code = '' @@ -1039,13 +1185,25 @@ class ExcellonObject(FlatCAMObj, Excellon): def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False): """ + Will generate an Geometry Object allowing to cut a drill hole instead of drilling it. + Note: This method is a good template for generic operations as it takes it's options from parameters or otherwise from the object's options and returns a (success, msg) tuple as feedback for shell operations. - :return: Success/failure condition tuple (bool, str). - :rtype: tuple + :param tools: A list of tools where the drills are to be milled or a string: "all" + :type tools: + :param outname: the name of the resulting Geometry object + :type outname: str + :param tooldia: the tool diameter to be used in creation of the milling path (Geometry Object) + :type tooldia: float + :param plot: if to plot the resulting object + :type plot: bool + :param use_thread: if to use threading for creation of the Geometry object + :type use_thread: bool + :return: Success/failure condition tuple (bool, str). + :rtype: tuple """ # Get the tools from the list. These are keys @@ -1088,6 +1246,15 @@ class ExcellonObject(FlatCAMObj, Excellon): return False, "Error: Milling tool is larger than hole." def geo_init(geo_obj, app_obj): + """ + + :param geo_obj: New object + :type geo_obj: GeometryObject + :param app_obj: App + :type app_obj: FlatCAMApp.App + :return: + :rtype: + """ assert geo_obj.kind == 'geometry', "Initializer expected a GeometryObject, got %s" % type(geo_obj) # ## Add properties to the object @@ -1100,7 +1267,7 @@ class ExcellonObject(FlatCAMObj, Excellon): geo_obj.options['Tools_in_use'] = tool_table_items geo_obj.options['type'] = 'Excellon Geometry' geo_obj.options["cnctooldia"] = str(tooldia) - + geo_obj.options["multidepth"] = self.options["multidepth"] geo_obj.solid_geometry = [] # in case that the tool used has the same diameter with the hole, and since the maximum resolution @@ -1115,9 +1282,10 @@ class ExcellonObject(FlatCAMObj, Excellon): else: geo_obj.solid_geometry.append( Point(hole['point']).buffer(buffer_value).exterior) + if use_thread: - def geo_thread(app_obj): - app_obj.new_object("geometry", outname, geo_init, plot=plot) + def geo_thread(a_obj): + a_obj.app_obj.new_object("geometry", outname, geo_init, plot=plot) # Create a promise with the new name self.app.collection.promise(outname) @@ -1125,19 +1293,31 @@ class ExcellonObject(FlatCAMObj, Excellon): # Send to worker self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]}) else: - self.app.new_object("geometry", outname, geo_init, plot=plot) + self.app.app_obj.new_object("geometry", outname, geo_init, plot=plot) return True, "" - def generate_milling_slots(self, tools=None, outname=None, tooldia=None, plot=True, use_thread=False): + def generate_milling_slots(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False): """ + Will generate an Geometry Object allowing to cut/mill a slot hole. + Note: This method is a good template for generic operations as it takes it's options from parameters or otherwise from the object's options and returns a (success, msg) tuple as feedback for shell operations. - :return: Success/failure condition tuple (bool, str). - :rtype: tuple + :param tools: A list of tools where the drills are to be milled or a string: "all" + :type tools: + :param outname: the name of the resulting Geometry object + :type outname: str + :param tooldia: the tool diameter to be used in creation of the milling path (Geometry Object) + :type tooldia: float + :param plot: if to plot the resulting object + :type plot: bool + :param use_thread: if to use threading for creation of the Geometry object + :type use_thread: bool + :return: Success/failure condition tuple (bool, str). + :rtype: tuple """ # Get the tools from the list. These are keys @@ -1178,7 +1358,7 @@ class ExcellonObject(FlatCAMObj, Excellon): return False, "Error: Milling tool is larger than hole." def geo_init(geo_obj, app_obj): - assert geo_obj.kind == 'geometry' "Initializer expected a GeometryObject, got %s" % type(geo_obj) + assert geo_obj.kind == 'geometry', "Initializer expected a GeometryObject, got %s" % type(geo_obj) # ## Add properties to the object @@ -1190,7 +1370,7 @@ class ExcellonObject(FlatCAMObj, Excellon): geo_obj.options['Tools_in_use'] = tool_table_items geo_obj.options['type'] = 'Excellon Geometry' geo_obj.options["cnctooldia"] = str(tooldia) - + geo_obj.options["multidepth"] = self.options["multidepth"] geo_obj.solid_geometry = [] # in case that the tool used has the same diameter with the hole, and since the maximum resolution @@ -1220,8 +1400,8 @@ class ExcellonObject(FlatCAMObj, Excellon): geo_obj.solid_geometry.append(poly) if use_thread: - def geo_thread(app_obj): - app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot) + def geo_thread(a_obj): + a_obj.app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot) # Create a promise with the new name self.app.collection.promise(outname) @@ -1229,7 +1409,7 @@ class ExcellonObject(FlatCAMObj, Excellon): # Send to worker self.app.worker_task.emit({'fcn': geo_thread, 'params': [self.app]}) else: - self.app.new_object("geometry", outname + '_slot', geo_init, plot=plot) + self.app.app_obj.new_object("geometry", outname + '_slot', geo_init, plot=plot) return True, "" @@ -1237,13 +1417,13 @@ class ExcellonObject(FlatCAMObj, Excellon): self.app.defaults.report_usage("excellon_on_create_milling_drills button") self.read_form() - self.generate_milling_drills(use_thread=False) + self.generate_milling_drills(use_thread=False, plot=True) def on_generate_milling_slots_button_click(self, *args): self.app.defaults.report_usage("excellon_on_create_milling_slots_button") self.read_form() - self.generate_milling_slots(use_thread=False) + self.generate_milling_slots(use_thread=False, plot=True) def on_pp_changed(self): current_pp = self.ui.pp_excellon_name_cb.get_value() @@ -1363,7 +1543,7 @@ class ExcellonObject(FlatCAMObj, Excellon): job_name = self.options["name"] + "_cnc" pp_excellon_name = self.options["ppname_e"] - # Object initialization function for app.new_object() + # Object initialization function for app.app_obj.new_object() def job_init(job_obj, app_obj): assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj) @@ -1426,9 +1606,9 @@ class ExcellonObject(FlatCAMObj, Excellon): job_obj.create_geometry() # To be run in separate thread - def job_thread(app_obj): + def job_thread(a_obj): with self.app.proc_container.new(_("Generating CNC Code")): - app_obj.new_object("cncjob", job_name, job_init) + a_obj.app_obj.new_object("cncjob", job_name, job_init) # Create promise for the new name. self.app.collection.promise(job_name) @@ -1466,12 +1646,122 @@ class ExcellonObject(FlatCAMObj, Excellon): # self.options['startz'] = float(self.options['startz']) * factor # self.options['endz'] = float(self.options['endz']) * factor + def on_add_area_click(self): + shape_button = self.ui.area_shape_radio + overz_button = self.ui.over_z_entry + strategy_radio = self.ui.strategy_radio + cnc_button = self.ui.generate_cnc_button + solid_geo = self.solid_geometry + obj_type = self.kind + + self.app.exc_areas.on_add_area_click( + shape_button=shape_button, overz_button=overz_button, cnc_button=cnc_button, strategy_radio=strategy_radio, + solid_geo=solid_geo, obj_type=obj_type) + + def on_clear_area_click(self): + if not self.app.exc_areas.exclusion_areas_storage: + self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. There are no exclusion areas to delete.")) + return + + self.app.exc_areas.on_clear_area_click() + self.app.exc_areas.e_shape_modified.emit() + + def on_delete_sel_areas(self): + sel_model = self.ui.exclusion_table.selectionModel() + sel_indexes = sel_model.selectedIndexes() + + # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + # so the duplicate rows will not be added + sel_rows = set() + for idx in sel_indexes: + sel_rows.add(idx.row()) + + if not sel_rows: + self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. Nothing is selected.")) + return + + self.app.exc_areas.delete_sel_shapes(idxs=list(sel_rows)) + self.app.exc_areas.e_shape_modified.emit() + + def draw_sel_shape(self): + sel_model = self.ui.exclusion_table.selectionModel() + sel_indexes = sel_model.selectedIndexes() + + # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + sel_rows = set() + for idx in sel_indexes: + sel_rows.add(idx.row()) + + self.delete_sel_shape() + + if self.app.is_legacy is False: + face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.2 * 255)))[2:] + outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(0.8 * 255)))[2:] + else: + face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.4 * 255)))[2:] + outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(1.0 * 255)))[2:] + + for row in sel_rows: + sel_rect = self.app.exc_areas.exclusion_areas_storage[row]['shape'] + self.app.move_tool.sel_shapes.add(sel_rect, color=outline, face_color=face, update=True, layer=0, + tolerance=None) + if self.app.is_legacy is True: + self.app.move_tool.sel_shapes.redraw() + + def clear_selection(self): + self.app.delete_selection_shape() + # self.ui.exclusion_table.clearSelection() + + def delete_sel_shape(self): + self.app.delete_selection_shape() + + def update_exclusion_table(self): + self.exclusion_area_cb_is_checked = True if self.ui.exclusion_cb.isChecked() else False + + self.build_ui() + self.ui.exclusion_cb.set_value(self.exclusion_area_cb_is_checked) + + def on_strategy(self, val): + if val == 'around': + self.ui.over_z_label.setDisabled(True) + self.ui.over_z_entry.setDisabled(True) + else: + self.ui.over_z_label.setDisabled(False) + self.ui.over_z_entry.setDisabled(False) + + def exclusion_table_toggle_all(self): + """ + will toggle the selection of all rows in Exclusion Areas table + + :return: + """ + sel_model = self.ui.exclusion_table.selectionModel() + sel_indexes = sel_model.selectedIndexes() + + # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + sel_rows = set() + for idx in sel_indexes: + sel_rows.add(idx.row()) + + if sel_rows: + self.ui.exclusion_table.clearSelection() + self.delete_sel_shape() + else: + self.ui.exclusion_table.selectAll() + self.draw_sel_shape() + def on_solid_cb_click(self, *args): if self.muted_ui: return self.read_form_item('solid') self.plot() + def on_multicolored_cb_click(self, *args): + if self.muted_ui: + return + self.read_form_item('multicolored') + self.plot() + def on_plot_cb_click(self, *args): if self.muted_ui: return @@ -1545,6 +1835,27 @@ class ExcellonObject(FlatCAMObj, Excellon): if not FlatCAMObj.plot(self): return + if self.app.is_legacy is False: + def random_color(): + r_color = np.random.rand(4) + r_color[3] = 1 + return r_color + else: + def random_color(): + while True: + r_color = np.random.rand(4) + r_color[3] = 1 + + new_color = '#' + for idx in range(len(r_color)): + new_color += '%x' % int(r_color[idx] * 255) + # do it until a valid color is generated + # a valid color has the # symbol, another 6 chars for the color and the last 2 chars for alpha + # for a total of 9 chars + if len(new_color) == 9: + break + return new_color + # try: # # Plot Excellon (All polygons?) # if self.options["solid"]: @@ -1576,12 +1887,26 @@ class ExcellonObject(FlatCAMObj, Excellon): try: # Plot Excellon (All polygons?) if self.options["solid"]: - for geo in self.solid_geometry: - self.add_shape(shape=geo, - color=self.outline_color, - face_color=self.fill_color, - visible=visible, - layer=2) + # for geo in self.solid_geometry: + # self.add_shape(shape=geo, + # color=self.outline_color, + # face_color=random_color() if self.options['multicolored'] else self.fill_color, + # visible=visible, + # layer=2) + + # plot polygons for each tool separately + for tool in self.tools: + # set the color here so we have one color for each tool + geo_color = random_color() + + # tool is a dict also + for geo in self.tools[tool]["solid_geometry"]: + self.add_shape(shape=geo, + color=geo_color if self.options['multicolored'] else self.outline_color, + face_color=geo_color if self.options['multicolored'] else self.fill_color, + visible=visible, + layer=2) + else: for geo in self.solid_geometry: self.add_shape(shape=geo.exterior, color='red', visible=visible) diff --git a/flatcamObjects/FlatCAMGeometry.py b/AppObjects/FlatCAMGeometry.py similarity index 89% rename from flatcamObjects/FlatCAMGeometry.py rename to AppObjects/FlatCAMGeometry.py index f4d0554f..cdb4b9ad 100644 --- a/flatcamObjects/FlatCAMGeometry.py +++ b/AppObjects/FlatCAMGeometry.py @@ -15,8 +15,7 @@ import shapely.affinity as affinity from camlib import Geometry -from flatcamObjects.FlatCAMObj import * -import FlatCAMTool +from AppObjects.FlatCAMObj import * import ezdxf import math @@ -25,7 +24,7 @@ from copy import deepcopy import traceback import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -53,6 +52,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.options.update({ "plot": True, + "multicolored": False, "cutz": -0.002, "vtipdia": 0.1, "vtipangle": 30, @@ -151,17 +151,9 @@ class GeometryObject(FlatCAMObj, Geometry): self.param_fields = {} - # Event signals disconnect id holders - self.mr = None - self.mm = None - self.kp = None - - # variables to be used in area exclusion - self.cursor_pos = (0, 0) - self.exclusion_areas_list = [] - self.first_click = False - self.points = [] - self.poly_drawn = False + # store here the state of the exclusion checkbox state to be restored after building the UI + # TODO add this in the sel.app.defaults dict and in Preferences + self.exclusion_area_cb_is_checked = False # Attributes to be included in serialization # Always append to it because it carries contents @@ -172,6 +164,15 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui_disconnect() FlatCAMObj.build_ui(self) + # Area Exception - exclusion shape added signal + # first disconnect it from any other object + try: + self.app.exc_areas.e_shape_modified.disconnect() + except (TypeError, AttributeError): + pass + # then connect it to the current build_ui() method + self.app.exc_areas.e_shape_modified.connect(self.update_exclusion_table) + self.units = self.app.defaults['units'] tool_idx = 0 @@ -192,7 +193,6 @@ class GeometryObject(FlatCAMObj, Geometry): # For INCH the decimals should be no more than 3. There are no tools under 10mils. dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooluid_value['tooldia']))) - dia_item.setFlags(QtCore.Qt.ItemIsEnabled) offset_item = FCComboBox() @@ -305,6 +305,63 @@ class GeometryObject(FlatCAMObj, Geometry): self.set_tool_offset_visibility(selected_row) + # ----------------------------- + # Build Exclusion Areas section + # ----------------------------- + e_len = len(self.app.exc_areas.exclusion_areas_storage) + self.ui.exclusion_table.setRowCount(e_len) + + area_id = 0 + + for area in range(e_len): + area_id += 1 + + area_dict = self.app.exc_areas.exclusion_areas_storage[area] + + area_id_item = QtWidgets.QTableWidgetItem('%d' % int(area_id)) + area_id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 0, area_id_item) # Area id + + object_item = QtWidgets.QTableWidgetItem('%s' % area_dict["obj_type"]) + object_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 1, object_item) # Origin Object + + strategy_item = QtWidgets.QTableWidgetItem('%s' % area_dict["strategy"]) + strategy_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 2, strategy_item) # Strategy + + overz_item = QtWidgets.QTableWidgetItem('%s' % area_dict["overz"]) + overz_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + self.ui.exclusion_table.setItem(area, 3, overz_item) # Over Z + + self.ui.exclusion_table.resizeColumnsToContents() + self.ui.exclusion_table.resizeRowsToContents() + + area_vheader = self.ui.exclusion_table.verticalHeader() + area_vheader.hide() + self.ui.exclusion_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + area_hheader = self.ui.exclusion_table.horizontalHeader() + area_hheader.setMinimumSectionSize(10) + area_hheader.setDefaultSectionSize(70) + + area_hheader.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed) + area_hheader.resizeSection(0, 20) + area_hheader.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) + area_hheader.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents) + area_hheader.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents) + + # area_hheader.setStretchLastSection(True) + self.ui.exclusion_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + self.ui.exclusion_table.setColumnWidth(0, 20) + + self.ui.exclusion_table.setMinimumHeight(self.ui.exclusion_table.getHeight()) + self.ui.exclusion_table.setMaximumHeight(self.ui.exclusion_table.getHeight()) + + # End Build Exclusion Areas + # ----------------------------- + # HACK: for whatever reasons the name in Selected tab is reverted to the original one after a successful rename # done in the collection view but only for Geometry objects. Perhaps some references remains. Should be fixed. self.ui.name_entry.set_value(self.options['name']) @@ -340,6 +397,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.form_fields.update({ "plot": self.ui.plot_cb, + "multicolored": self.ui.multicolored_cb, "cutz": self.ui.cutz_entry, "vtipdia": self.ui.tipdia_entry, "vtipangle": self.ui.tipangle_entry, @@ -363,7 +421,7 @@ class GeometryObject(FlatCAMObj, Geometry): "endxy": self.ui.endxy_entry, "cnctooldia": self.ui.addtool_entry, "area_exclusion": self.ui.exclusion_cb, - "area_shape":self.ui.area_shape_radio, + "area_shape": self.ui.area_shape_radio, "area_strategy": self.ui.strategy_radio, "area_overz": self.ui.over_z_entry, }) @@ -535,6 +593,8 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.extracut_cb.toggled.connect(lambda state: self.ui.e_cut_entry.setDisabled(not state)) self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click) + self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click) + self.ui.generate_cnc_button.clicked.connect(self.on_generatecnc_button_click) self.ui.paint_tool_button.clicked.connect(lambda: self.app.paint_tool.run(toggle=False)) self.ui.generate_ncc_button.clicked.connect(lambda: self.app.ncclear_tool.run(toggle=False)) @@ -547,8 +607,14 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked) self.ui.cutz_entry.returnPressed.connect(self.on_cut_z_changed) + # Exclusion areas signals + self.ui.exclusion_table.horizontalHeader().sectionClicked.connect(self.exclusion_table_toggle_all) + self.ui.exclusion_table.lost_focus.connect(self.clear_selection) + self.ui.exclusion_table.itemClicked.connect(self.draw_sel_shape) self.ui.add_area_button.clicked.connect(self.on_add_area_click) self.ui.delete_area_button.clicked.connect(self.on_clear_area_click) + self.ui.delete_sel_area_button.clicked.connect(self.on_delete_sel_areas) + self.ui.strategy_radio.activated_custom.connect(self.on_strategy) def on_cut_z_changed(self): self.old_cutz = self.ui.cutz_entry.get_value() @@ -595,8 +661,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.tool_offset_entry.get_value().replace(',', '.') ) except ValueError: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Wrong value format entered, use a number.")) + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number.")) return def ui_connect(self): @@ -604,6 +669,7 @@ class GeometryObject(FlatCAMObj, Geometry): # changes in geometry UI for i in self.param_fields: current_widget = self.param_fields[i] + if isinstance(current_widget, FCCheckBox): current_widget.stateChanged.connect(self.gui_form_to_storage) elif isinstance(current_widget, FCComboBox): @@ -874,7 +940,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui_connect() self.build_ui() - # if there is no tool left in the Tools Table, enable the parameters GUI + # if there is no tool left in the Tools Table, enable the parameters AppGUI if self.ui.geo_tools_table.rowCount() != 0: self.ui.geo_param_frame.setDisabled(False) @@ -950,7 +1016,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui_connect() self.build_ui() - # if there is no tool left in the Tools Table, enable the parameters GUI + # if there is no tool left in the Tools Table, enable the parameters AppGUI if self.ui.geo_tools_table.rowCount() != 0: self.ui.geo_param_frame.setDisabled(False) @@ -1131,7 +1197,7 @@ class GeometryObject(FlatCAMObj, Geometry): obj_active.options['xmax'] = 0 obj_active.options['ymax'] = 0 - # if there is no tool left in the Tools Table, disable the parameters GUI + # if there is no tool left in the Tools Table, disable the parameters AppGUI if self.ui.geo_tools_table.rowCount() == 0: self.ui.geo_param_frame.setDisabled(True) @@ -1149,8 +1215,7 @@ class GeometryObject(FlatCAMObj, Geometry): "- 'V-tip Angle' -> angle at the tip of the tool\n" "- 'V-tip Dia' -> diameter at the tip of the tool \n" "- Tool Dia -> 'Dia' column found in the Tool Table\n" - "NB: a value of zero means that Tool Dia = 'V-tip Dia'" - ) + "NB: a value of zero means that Tool Dia = 'V-tip Dia'") ) self.ui.cutz_entry.setToolTip( _("Disabled because the tool is V-shape.\n" @@ -1159,8 +1224,7 @@ class GeometryObject(FlatCAMObj, Geometry): "- 'V-tip Angle' -> angle at the tip of the tool\n" "- 'V-tip Dia' -> diameter at the tip of the tool \n" "- Tool Dia -> 'Dia' column found in the Tool Table\n" - "NB: a value of zero means that Tool Dia = 'V-tip Dia'" - ) + "NB: a value of zero means that Tool Dia = 'V-tip Dia'") ) self.update_cutz() @@ -1172,8 +1236,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.cutz_entry.setDisabled(False) self.ui.cutzlabel.setToolTip( _("Cutting depth (negative)\n" - "below the copper surface." - ) + "below the copper surface.") ) self.ui.cutz_entry.setToolTip('') @@ -1328,16 +1391,18 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui_connect() def gui_form_to_storage(self): + self.ui_disconnect() + if self.ui.geo_tools_table.rowCount() == 0: # there is no tool in tool table so we can't save the GUI elements values to storage log.debug("GeometryObject.gui_form_to_storage() --> no tool in Tools Table, aborting.") return - self.ui_disconnect() widget_changed = self.sender() try: widget_idx = self.ui.grid3.indexOf(widget_changed) - except Exception: + except Exception as e: + log.debug("GeometryObject.gui_form_to_storage() -- wdg index -> %s" % str(e)) return # those are the indexes for the V-Tip Dia and V-Tip Angle, if edited calculate the new Cut Z @@ -1690,15 +1755,11 @@ class GeometryObject(FlatCAMObj, Geometry): The actual work is done by the target CNCJobObject object's `generate_from_geometry_2()` method. - :param tools_dict: a dictionary that holds the whole data needed to create the Gcode - (including the solid_geometry) - - :param tools_in_use: the tools that are used, needed by some preprocessors - :type list of lists, each list in the list is made out of row elements of tools table from GUI - :param outname: - :param tools_dict: - :param tools_in_use: + :param tools_dict: a dictionary that holds the whole data needed to create the Gcode + (including the solid_geometry) + :param tools_in_use: the tools that are used, needed by some preprocessors + :type tools_in_use list of lists, each list in the list is made out of row elements of tools table from AppGUI :param segx: number of segments on the X axis, for auto-levelling :param segy: number of segments on the Y axis, for auto-levelling :param plot: if True the generated object will be plotted; if False will not be plotted @@ -1728,7 +1789,7 @@ class GeometryObject(FlatCAMObj, Geometry): self.app.inform.emit(msg) return - # Object initialization function for app.new_object() + # Object initialization function for app.app_obj.new_object() # RUNNING ON SEPARATE THREAD! def job_init_single_geometry(job_obj, app_obj): log.debug("Creating a CNCJob out of a single-geometry") @@ -1742,7 +1803,7 @@ class GeometryObject(FlatCAMObj, Geometry): # count the tools tool_cnt = 0 - dia_cnc_dict = {} + # dia_cnc_dict = {} # this turn on the FlatCAMCNCJob plot for multiple tools job_obj.multitool = True @@ -1868,7 +1929,7 @@ class GeometryObject(FlatCAMObj, Geometry): }) dia_cnc_dict.clear() - # Object initialization function for app.new_object() + # Object initialization function for app.app_obj.new_object() # RUNNING ON SEPARATE THREAD! def job_init_multi_geometry(job_obj, app_obj): log.debug("Creating a CNCJob out of a multi-geometry") @@ -1882,7 +1943,7 @@ class GeometryObject(FlatCAMObj, Geometry): # count the tools tool_cnt = 0 - dia_cnc_dict = {} + # dia_cnc_dict = {} # this turn on the FlatCAMCNCJob plot for multiple tools job_obj.multitool = True @@ -2022,15 +2083,15 @@ class GeometryObject(FlatCAMObj, Geometry): if use_thread: # To be run in separate thread - def job_thread(app_obj): + def job_thread(a_obj): if self.multigeo is False: with self.app.proc_container.new(_("Generating CNC Code")): - if app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) != 'fail': - app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname)) + if a_obj.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) != 'fail': + a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname)) 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] %s: %s' % (_("CNCjob created"), outname)) + if a_obj.app_obj.new_object("cncjob", outname, job_init_multi_geometry) != 'fail': + a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname)) # Create a promise with the name self.app.collection.promise(outname) @@ -2038,25 +2099,17 @@ class GeometryObject(FlatCAMObj, Geometry): self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: if self.solid_geometry: - self.app.new_object("cncjob", outname, job_init_single_geometry, plot=plot) + self.app.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) else: - self.app.new_object("cncjob", outname, job_init_multi_geometry, plot=plot) + self.app.app_obj.new_object("cncjob", outname, job_init_multi_geometry, plot=plot) - def generatecncjob( - self, outname=None, - dia=None, offset=None, - z_cut=None, z_move=None, - feedrate=None, feedrate_z=None, feedrate_rapid=None, - spindlespeed=None, dwell=None, dwelltime=None, - multidepth=None, depthperpass=None, - toolchange=None, toolchangez=None, toolchangexy=None, - extracut=None, extracut_length=None, startz=None, endz=None, - pp=None, - segx=None, segy=None, - use_thread=True, - plot=True): + def generatecncjob(self, outname=None, dia=None, offset=None, z_cut=None, z_move=None, + feedrate=None, feedrate_z=None, feedrate_rapid=None, spindlespeed=None, dwell=None, dwelltime=None, + multidepth=None, dpp=None, toolchange=None, toolchangez=None, toolchangexy=None, + extracut=None, extracut_length=None, startz=None, endz=None, endxy=None, pp=None, segx=None, segy=None, + use_thread=True, plot=True): """ - Only used for TCL Command. + Only used by the TCL Command Cncjob. Creates a CNCJob out of this Geometry object. The actual work is done by the target camlib.CNCjob `generate_from_geometry_2()` method. @@ -2073,14 +2126,17 @@ class GeometryObject(FlatCAMObj, Geometry): :param dwell: :param dwelltime: :param multidepth: - :param depthperpass: + :param dpp: Depth for each pass when multidepth parameter is True :param toolchange: :param toolchangez: - :param toolchangexy: + :param toolchangexy: A sequence ox X,Y coordinates: a 2-length tuple or a string. + Coordinates in X,Y plane for the Toolchange event :param extracut: :param extracut_length: :param startz: :param endz: + :param endxy: A sequence ox X,Y coordinates: a 2-length tuple or a string. + Coordinates in X, Y plane for the last move after ending the job. :param pp: Name of the preprocessor :param segx: :param segy: @@ -2100,7 +2156,7 @@ class GeometryObject(FlatCAMObj, Geometry): feedrate_rapid = feedrate_rapid if feedrate_rapid is not None else float(self.options["feedrate_rapid"]) multidepth = multidepth if multidepth is not None else self.options["multidepth"] - depthperpass = depthperpass if depthperpass is not None else float(self.options["depthperpass"]) + depthperpass = dpp if dpp is not None else float(self.options["depthperpass"]) segx = segx if segx is not None else float(self.app.defaults['geometry_segx']) segy = segy if segy is not None else float(self.app.defaults['geometry_segy']) @@ -2110,10 +2166,21 @@ class GeometryObject(FlatCAMObj, Geometry): startz = startz if startz is not None else self.options["startz"] endz = endz if endz is not None else float(self.options["endz"]) - endxy = self.options["endxy"] + + endxy = endxy if endxy else self.options["endxy"] + if isinstance(endxy, str): + endxy = re.sub('[()\[\]]', '', endxy) + if endxy and endxy != '': + endxy = [float(eval(a)) for a in endxy.split(",")] toolchangez = toolchangez if toolchangez else float(self.options["toolchangez"]) + toolchangexy = toolchangexy if toolchangexy else self.options["toolchangexy"] + if isinstance(toolchangexy, str): + toolchangexy = re.sub('[()\[\]]', '', toolchangexy) + if toolchangexy and toolchangexy != '': + toolchangexy = [float(eval(a)) for a in toolchangexy.split(",")] + toolchange = toolchange if toolchange else self.options["toolchange"] offset = offset if offset else 0.0 @@ -2125,7 +2192,7 @@ class GeometryObject(FlatCAMObj, Geometry): ppname_g = pp if pp else self.options["ppname_g"] - # Object initialization function for app.new_object() + # Object initialization function for app.app_obj.new_object() # RUNNING ON SEPARATE THREAD! def job_init(job_obj, app_obj): assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj) @@ -2174,7 +2241,7 @@ class GeometryObject(FlatCAMObj, Geometry): # To be run in separate thread def job_thread(app_obj): with self.app.proc_container.new(_("Generating CNC Code")): - app_obj.new_object("cncjob", outname, job_init, plot=plot) + app_obj.app_obj.new_object("cncjob", outname, job_init, plot=plot) app_obj.inform.emit('[success] %s: %s' % (_("CNCjob created")), outname) # Create a promise with the name @@ -2182,7 +2249,7 @@ class GeometryObject(FlatCAMObj, Geometry): # Send to worker self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: - self.app.new_object("cncjob", outname, job_init, plot=plot) + self.app.app_obj.new_object("cncjob", outname, job_init, plot=plot) # def on_plot_cb_click(self, *args): # if self.muted_ui: @@ -2469,6 +2536,110 @@ class GeometryObject(FlatCAMObj, Geometry): return factor + def on_add_area_click(self): + shape_button = self.ui.area_shape_radio + overz_button = self.ui.over_z_entry + strategy_radio = self.ui.strategy_radio + cnc_button = self.ui.generate_cnc_button + solid_geo = self.solid_geometry + obj_type = self.kind + + self.app.exc_areas.on_add_area_click( + shape_button=shape_button, overz_button=overz_button, cnc_button=cnc_button, strategy_radio=strategy_radio, + solid_geo=solid_geo, obj_type=obj_type) + + def on_clear_area_click(self): + if not self.app.exc_areas.exclusion_areas_storage: + self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. There are no exclusion areas to delete.")) + return + + self.app.exc_areas.on_clear_area_click() + self.app.exc_areas.e_shape_modified.emit() + + def on_delete_sel_areas(self): + sel_model = self.ui.exclusion_table.selectionModel() + sel_indexes = sel_model.selectedIndexes() + + # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + # so the duplicate rows will not be added + sel_rows = set() + for idx in sel_indexes: + sel_rows.add(idx.row()) + + if not sel_rows: + self.app.inform.emit("[WARNING_NOTCL] %s" % _("Delete failed. Nothing is selected.")) + return + + self.app.exc_areas.delete_sel_shapes(idxs=list(sel_rows)) + self.app.exc_areas.e_shape_modified.emit() + + def draw_sel_shape(self): + sel_model = self.ui.exclusion_table.selectionModel() + sel_indexes = sel_model.selectedIndexes() + + # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + sel_rows = set() + for idx in sel_indexes: + sel_rows.add(idx.row()) + + self.delete_sel_shape() + + if self.app.is_legacy is False: + face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.2 * 255)))[2:] + outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(0.8 * 255)))[2:] + else: + face = self.app.defaults['global_sel_fill'][:-2] + str(hex(int(0.4 * 255)))[2:] + outline = self.app.defaults['global_sel_line'][:-2] + str(hex(int(1.0 * 255)))[2:] + + for row in sel_rows: + sel_rect = self.app.exc_areas.exclusion_areas_storage[row]['shape'] + self.app.move_tool.sel_shapes.add(sel_rect, color=outline, face_color=face, update=True, layer=0, + tolerance=None) + if self.app.is_legacy is True: + self.app.move_tool.sel_shapes.redraw() + + def clear_selection(self): + self.app.delete_selection_shape() + # self.ui.exclusion_table.clearSelection() + + def delete_sel_shape(self): + self.app.delete_selection_shape() + + def update_exclusion_table(self): + self.exclusion_area_cb_is_checked = True if self.ui.exclusion_cb.isChecked() else False + + self.build_ui() + self.ui.exclusion_cb.set_value(self.exclusion_area_cb_is_checked) + + def on_strategy(self, val): + if val == 'around': + self.ui.over_z_label.setDisabled(True) + self.ui.over_z_entry.setDisabled(True) + else: + self.ui.over_z_label.setDisabled(False) + self.ui.over_z_entry.setDisabled(False) + + def exclusion_table_toggle_all(self): + """ + will toggle the selection of all rows in Exclusion Areas table + + :return: + """ + sel_model = self.ui.exclusion_table.selectionModel() + sel_indexes = sel_model.selectedIndexes() + + # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + sel_rows = set() + for idx in sel_indexes: + sel_rows.add(idx.row()) + + if sel_rows: + self.ui.exclusion_table.clearSelection() + self.delete_sel_shape() + else: + self.ui.exclusion_table.selectAll() + self.draw_sel_shape() + def plot_element(self, element, color=None, visible=None): if color is None: @@ -2498,6 +2669,27 @@ class GeometryObject(FlatCAMObj, Geometry): if not FlatCAMObj.plot(self): return + if self.app.is_legacy is False: + def random_color(): + r_color = np.random.rand(4) + r_color[3] = 1 + return r_color + else: + def random_color(): + while True: + r_color = np.random.rand(4) + r_color[3] = 1 + + new_color = '#' + for idx in range(len(r_color)): + new_color += '%x' % int(r_color[idx] * 255) + # do it until a valid color is generated + # a valid color has the # symbol, another 6 chars for the color and the last 2 chars for alpha + # for a total of 9 chars + if len(new_color) == 9: + break + return new_color + try: # plot solid geometries found as members of self.tools attribute dict # for MultiGeo @@ -2505,7 +2697,8 @@ class GeometryObject(FlatCAMObj, Geometry): for tooluid_key in self.tools: solid_geometry = self.tools[tooluid_key]['solid_geometry'] self.plot_element(solid_geometry, visible=visible, - color=self.app.defaults["geometry_plot_line"]) + color=random_color() if self.options['multicolored'] + else self.app.defaults["geometry_plot_line"]) else: # plot solid geometry that may be an direct attribute of the geometry object # for SingleGeo @@ -2573,218 +2766,11 @@ class GeometryObject(FlatCAMObj, Geometry): self.ui.plot_cb.setChecked(True) self.ui_connect() - def on_add_area_click(self): - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area.")) - self.app.call_source = 'geometry' - - if self.app.is_legacy is False: - self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot) - self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot) - self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot) - else: - self.app.plotcanvas.graph_event_disconnect(self.app.mp) - self.app.plotcanvas.graph_event_disconnect(self.app.mm) - self.app.plotcanvas.graph_event_disconnect(self.app.mr) - - self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release) - self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move) - # self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press) - - # To be called after clicking on the plot. - def on_mouse_release(self, event): - if self.app.is_legacy is False: - event_pos = event.pos - # event_is_dragging = event.is_dragging - right_button = 2 - else: - event_pos = (event.xdata, event.ydata) - # event_is_dragging = self.app.plotcanvas.is_dragging - right_button = 3 - - event_pos = self.app.plotcanvas.translate_coords(event_pos) - if self.app.grid_status(): - curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1]) - else: - curr_pos = (event_pos[0], event_pos[1]) - - x1, y1 = curr_pos[0], curr_pos[1] - - shape_type = self.ui.area_shape_radio.get_value() - - # do clear area only for left mouse clicks - if event.button == 1: - if shape_type == "square": - if self.first_click is False: - self.first_click = True - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area.")) - - self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos) - if self.app.grid_status(): - self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1]) - else: - self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish.")) - self.app.delete_selection_shape() - - x0, y0 = self.cursor_pos[0], self.cursor_pos[1] - - pt1 = (x0, y0) - pt2 = (x1, y0) - pt3 = (x1, y1) - pt4 = (x0, y1) - - new_rectangle = Polygon([pt1, pt2, pt3, pt4]) - self.exclusion_areas_list.append(new_rectangle) - - # add a temporary shape on canvas - FlatCAMTool.FlatCAMTool.draw_tool_selection_shape(self, old_coords=(x0, y0), coords=(x1, y1)) - - self.first_click = False - return - else: - self.points.append((x1, y1)) - - if len(self.points) > 1: - self.poly_drawn = True - self.app.inform.emit(_("Click on next Point or click right mouse button to complete ...")) - - return "" - elif event.button == right_button and self.mouse_is_dragging is False: - - shape_type = self.ui.area_shape_radio.get_value() - - if shape_type == "square": - self.first_click = False - else: - # if we finish to add a polygon - if self.poly_drawn is True: - try: - # try to add the point where we last clicked if it is not already in the self.points - last_pt = (x1, y1) - if last_pt != self.points[-1]: - self.points.append(last_pt) - except IndexError: - pass - - # we need to add a Polygon and a Polygon can be made only from at least 3 points - if len(self.points) > 2: - FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self) - pol = Polygon(self.points) - # do not add invalid polygons even if they are drawn by utility geometry - if pol.is_valid: - self.exclusion_areas_list.append(pol) - FlatCAMTool.FlatCAMTool.draw_selection_shape_polygon(self, points=self.points) - self.app.inform.emit( - _("Zone added. Click to start adding next zone or right click to finish.")) - - self.points = [] - self.poly_drawn = False - return - - FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self) - - if self.app.is_legacy is False: - self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) - self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) - # self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press) - else: - self.app.plotcanvas.graph_event_disconnect(self.mr) - self.app.plotcanvas.graph_event_disconnect(self.mm) - # self.app.plotcanvas.graph_event_disconnect(self.kp) - - self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', - self.app.on_mouse_click_over_plot) - self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move', - self.app.on_mouse_move_over_plot) - self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', - self.app.on_mouse_click_release_over_plot) - - self.app.call_source = 'app' - - if len(self.exclusion_areas_list) == 0: - return - - def area_disconnect(self): - if self.app.is_legacy is False: - self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) - self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) - else: - self.app.plotcanvas.graph_event_disconnect(self.mr) - self.app.plotcanvas.graph_event_disconnect(self.mm) - self.app.plotcanvas.graph_event_disconnect(self.kp) - - self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', - self.app.on_mouse_click_over_plot) - self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move', - self.app.on_mouse_move_over_plot) - self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', - self.app.on_mouse_click_release_over_plot) - self.points = [] - self.poly_drawn = False - self.exclusion_areas_list = [] - - FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self) - FlatCAMTool.FlatCAMTool.delete_tool_selection_shape(self) - - self.app.call_source = "app" - self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted.")) - - # called on mouse move - def on_mouse_move(self, event): - shape_type = self.ui.area_shape_radio.get_value() - - if self.app.is_legacy is False: - event_pos = event.pos - event_is_dragging = event.is_dragging - # right_button = 2 - else: - event_pos = (event.xdata, event.ydata) - event_is_dragging = self.app.plotcanvas.is_dragging - # right_button = 3 - - curr_pos = self.app.plotcanvas.translate_coords(event_pos) - - # detect mouse dragging motion - if event_is_dragging is True: - self.mouse_is_dragging = True - else: - self.mouse_is_dragging = False - - # update the cursor position - if self.app.grid_status(): - # Update cursor - curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1]) - - self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]), - symbol='++', edge_color=self.app.cursor_color_3D, - edge_width=self.app.defaults["global_cursor_width"], - size=self.app.defaults["global_cursor_size"]) - - # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) - if self.cursor_pos is None: - self.cursor_pos = (0, 0) - - self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) - self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) - - # draw the utility geometry - if shape_type == "square": - if self.first_click: - self.app.delete_selection_shape() - self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]), - coords=(curr_pos[0], curr_pos[1])) - else: - FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self) - FlatCAMTool.FlatCAMTool.draw_moving_selection_shape_poly( - self, points=self.points, data=(curr_pos[0], curr_pos[1])) - - def on_clear_area_click(self): - self.exclusion_areas_list = [] - FlatCAMTool.FlatCAMTool.delete_moving_selection_shape(self) - self.app.delete_selection_shape() + def on_multicolored_cb_click(self, *args): + if self.muted_ui: + return + self.read_form_item('multicolored') + self.plot() @staticmethod def merge(geo_list, geo_final, multigeo=None): diff --git a/flatcamObjects/FlatCAMGerber.py b/AppObjects/FlatCAMGerber.py similarity index 63% rename from flatcamObjects/FlatCAMGerber.py rename to AppObjects/FlatCAMGerber.py index e3c80f71..592f5b47 100644 --- a/flatcamObjects/FlatCAMGerber.py +++ b/AppObjects/FlatCAMGerber.py @@ -14,15 +14,15 @@ from shapely.geometry import Point, Polygon, MultiPolygon, MultiLineString, LineString, LinearRing from shapely.ops import cascaded_union -from flatcamParsers.ParseGerber import Gerber -from flatcamObjects.FlatCAMObj import * +from AppParsers.ParseGerber import Gerber +from AppObjects.FlatCAMObj import * import math import numpy as np from copy import deepcopy import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -115,23 +115,12 @@ class GerberObject(FlatCAMObj, Gerber): "plot": True, "multicolored": False, "solid": False, - "tool_type": 'circular', - "vtipdia": 0.1, - "vtipangle": 30, - "vcutz": -0.05, - "isotooldia": 0.016, - "isopasses": 1, - "isooverlap": 15, - "milling_type": "cl", - "combine_passes": True, "noncoppermargin": 0.0, "noncopperrounded": False, "bboxmargin": 0.0, "bboxrounded": False, "aperture_display": False, "follow": False, - "iso_scope": 'all', - "iso_type": 'full' }) # type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors) @@ -197,88 +186,44 @@ class GerberObject(FlatCAMObj, Gerber): "plot": self.ui.plot_cb, "multicolored": self.ui.multicolored_cb, "solid": self.ui.solid_cb, - "tool_type": self.ui.tool_type_radio, - "vtipdia": self.ui.tipdia_spinner, - "vtipangle": self.ui.tipangle_spinner, - "vcutz": self.ui.cutz_spinner, - "isotooldia": self.ui.iso_tool_dia_entry, - "isopasses": self.ui.iso_width_entry, - "isooverlap": self.ui.iso_overlap_entry, - "milling_type": self.ui.milling_type_radio, - "combine_passes": self.ui.combine_passes_cb, "noncoppermargin": self.ui.noncopper_margin_entry, "noncopperrounded": self.ui.noncopper_rounded_cb, "bboxmargin": self.ui.bbmargin_entry, "bboxrounded": self.ui.bbrounded_cb, "aperture_display": self.ui.aperture_table_visibility_cb, - "follow": self.ui.follow_cb, - "iso_scope": self.ui.iso_scope_radio, - "iso_type": self.ui.iso_type_radio + "follow": self.ui.follow_cb }) # Fill form fields only on object create self.to_form() assert isinstance(self.ui, GerberObjectUI) + self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click) self.ui.solid_cb.stateChanged.connect(self.on_solid_cb_click) self.ui.multicolored_cb.stateChanged.connect(self.on_multicolored_cb_click) - self.ui.generate_iso_button.clicked.connect(self.on_iso_button_click) + + # Tools + self.ui.iso_button.clicked.connect(self.app.isolation_tool.run) self.ui.generate_ncc_button.clicked.connect(self.app.ncclear_tool.run) self.ui.generate_cutout_button.clicked.connect(self.app.cutout_tool.run) + self.ui.generate_bb_button.clicked.connect(self.on_generatebb_button_click) 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) - # set the model for the Area Exception comboboxes - self.ui.obj_combo.setModel(self.app.collection) - self.ui.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) - self.ui.obj_combo.is_last = True - self.ui.obj_combo.obj_type = { - _("Gerber"): "Gerber", _("Geometry"): "Geometry" - }[self.ui.type_obj_combo.get_value()] - self.on_type_obj_index_changed() - - self.ui.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed) - - self.ui.tool_type_radio.activated_custom.connect(self.on_tool_type_change) - # establish visibility for the GUI elements found in the slot function - self.ui.tool_type_radio.activated_custom.emit(self.options['tool_type']) - # Show/Hide Advanced Options if self.app.defaults["global_app_level"] == 'b': self.ui.level.setText('%s' % _('Basic')) - self.options['tool_type'] = 'circular' - - self.ui.tool_type_label.hide() - self.ui.tool_type_radio.hide() - - # override the Preferences Value; in Basic mode the Tool Type is always Circular ('C1') - self.ui.tool_type_radio.set_value('circular') - - self.ui.tipdialabel.hide() - self.ui.tipdia_spinner.hide() - self.ui.tipanglelabel.hide() - self.ui.tipangle_spinner.hide() - self.ui.cutzlabel.hide() - self.ui.cutz_spinner.hide() self.ui.apertures_table_label.hide() self.ui.aperture_table_visibility_cb.hide() - self.ui.milling_type_label.hide() - self.ui.milling_type_radio.hide() - self.ui.iso_type_label.hide() - self.ui.iso_type_radio.hide() self.ui.follow_cb.hide() - self.ui.except_cb.setChecked(False) - self.ui.except_cb.hide() + else: self.ui.level.setText('%s' % _('Advanced')) - self.ui.tipdia_spinner.valueChanged.connect(self.on_calculate_tooldia) - self.ui.tipangle_spinner.valueChanged.connect(self.on_calculate_tooldia) - self.ui.cutz_spinner.valueChanged.connect(self.on_calculate_tooldia) if self.app.defaults["gerber_buffering"] == 'no': self.ui.create_buffer_button.show() @@ -296,58 +241,6 @@ class GerberObject(FlatCAMObj, Gerber): self.build_ui() self.units_found = self.app.defaults['units'] - def on_calculate_tooldia(self): - try: - tdia = float(self.ui.tipdia_spinner.get_value()) - except Exception: - return - try: - dang = float(self.ui.tipangle_spinner.get_value()) - except Exception: - return - try: - cutz = float(self.ui.cutz_spinner.get_value()) - except Exception: - return - - cutz *= -1 - if cutz < 0: - cutz *= -1 - - half_tip_angle = dang / 2 - - tool_diameter = tdia + (2 * cutz * math.tan(math.radians(half_tip_angle))) - self.ui.iso_tool_dia_entry.set_value(tool_diameter) - - def on_type_obj_index_changed(self): - val = self.ui.type_obj_combo.get_value() - obj_type = {"Gerber": 0, "Geometry": 2}[val] - self.ui.obj_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) - self.ui.obj_combo.setCurrentIndex(0) - self.ui.obj_combo.obj_type = {_("Gerber"): "Gerber", _("Geometry"): "Geometry"}[val] - - def on_tool_type_change(self, state): - if state == 'circular': - self.ui.tipdialabel.hide() - self.ui.tipdia_spinner.hide() - self.ui.tipanglelabel.hide() - self.ui.tipangle_spinner.hide() - self.ui.cutzlabel.hide() - self.ui.cutz_spinner.hide() - self.ui.iso_tool_dia_entry.setDisabled(False) - # update the value in the self.iso_tool_dia_entry once this is selected - self.ui.iso_tool_dia_entry.set_value(self.options['isotooldia']) - else: - self.ui.tipdialabel.show() - self.ui.tipdia_spinner.show() - self.ui.tipanglelabel.show() - self.ui.tipangle_spinner.show() - self.ui.cutzlabel.show() - self.ui.cutz_spinner.show() - self.ui.iso_tool_dia_entry.setDisabled(True) - # update the value in the self.iso_tool_dia_entry once this is selected - self.on_calculate_tooldia() - def build_ui(self): FlatCAMObj.build_ui(self) @@ -530,7 +423,7 @@ class GerberObject(FlatCAMObj, Gerber): return "fail" geo_obj.solid_geometry = non_copper - self.app.new_object("geometry", name, geo_init) + self.app.app_obj.new_object("geometry", name, geo_init) def on_generatebb_button_click(self, *args): self.app.defaults.report_usage("gerber_on_generatebb_button") @@ -556,523 +449,7 @@ class GerberObject(FlatCAMObj, Gerber): return "fail" geo_obj.solid_geometry = bounding_box - self.app.new_object("geometry", name, geo_init) - - def on_iso_button_click(self, *args): - - obj = self.app.collection.get_active() - - self.iso_type = 2 - if self.ui.iso_type_radio.get_value() == 'ext': - self.iso_type = 0 - if self.ui.iso_type_radio.get_value() == 'int': - self.iso_type = 1 - - def worker_task(iso_obj, app_obj): - with self.app.proc_container.new(_("Isolating...")): - if self.ui.follow_cb.get_value() is True: - iso_obj.follow_geo() - # in the end toggle the visibility of the origin object so we can see the generated Geometry - iso_obj.ui.plot_cb.toggle() - else: - app_obj.defaults.report_usage("gerber_on_iso_button") - self.read_form() - - iso_scope = 'all' if self.ui.iso_scope_radio.get_value() == 'all' else 'single' - self.isolate_handler(iso_type=self.iso_type, iso_scope=iso_scope) - - self.app.worker_task.emit({'fcn': worker_task, 'params': [obj, self.app]}) - - def follow_geo(self, outname=None): - """ - Creates a geometry object "following" the gerber paths. - - :return: None - """ - - # default_name = self.options["name"] + "_follow" - # follow_name = outname or default_name - - if outname is None: - follow_name = self.options["name"] + "_follow" - else: - follow_name = outname - - def follow_init(follow_obj, app): - # Propagate options - follow_obj.options["cnctooldia"] = str(self.options["isotooldia"]) - follow_obj.solid_geometry = self.follow_geometry - - # TODO: Do something if this is None. Offer changing name? - try: - self.app.new_object("geometry", follow_name, follow_init) - except Exception as e: - return "Operation failed: %s" % str(e) - - def isolate_handler(self, iso_type, iso_scope): - - if iso_scope == 'all': - self.isolate(iso_type=iso_type) - else: - # disengage the grid snapping since it may be hard to click on polygons with grid snapping on - if self.app.ui.grid_snap_btn.isChecked(): - self.grid_status_memory = True - self.app.ui.grid_snap_btn.trigger() - else: - self.grid_status_memory = False - - self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_click_release) - - if self.app.is_legacy is False: - self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot) - else: - self.app.plotcanvas.graph_event_disconnect(self.app.mr) - - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click on a polygon to isolate it.")) - - def on_mouse_click_release(self, event): - if self.app.is_legacy is False: - event_pos = event.pos - right_button = 2 - self.app.event_is_dragging = self.app.event_is_dragging - else: - event_pos = (event.xdata, event.ydata) - right_button = 3 - self.app.event_is_dragging = self.app.ui.popMenu.mouse_is_panning - - try: - x = float(event_pos[0]) - y = float(event_pos[1]) - except TypeError: - return - - event_pos = (x, y) - curr_pos = self.app.plotcanvas.translate_coords(event_pos) - if self.app.grid_status(): - curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1]) - else: - curr_pos = (curr_pos[0], curr_pos[1]) - - if event.button == 1: - clicked_poly = self.find_polygon(point=(curr_pos[0], curr_pos[1])) - - if self.app.selection_type is not None: - self.selection_area_handler(self.app.pos, curr_pos, self.app.selection_type) - self.app.selection_type = None - elif clicked_poly: - if clicked_poly not in self.poly_dict.values(): - shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, shape=clicked_poly, - color=self.app.defaults['global_sel_draw_color'] + 'AF', - face_color=self.app.defaults['global_sel_draw_color'] + 'AF', - visible=True) - self.poly_dict[shape_id] = clicked_poly - self.app.inform.emit( - '%s: %d. %s' % (_("Added polygon"), int(len(self.poly_dict)), - _("Click to add next polygon or right click to start isolation.")) - ) - else: - try: - for k, v in list(self.poly_dict.items()): - if v == clicked_poly: - self.app.tool_shapes.remove(k) - self.poly_dict.pop(k) - break - except TypeError: - return - self.app.inform.emit( - '%s. %s' % (_("Removed polygon"), - _("Click to add/remove next polygon or right click to start isolation.")) - ) - - self.app.tool_shapes.redraw() - else: - self.app.inform.emit(_("No polygon detected under click position.")) - elif event.button == right_button and self.app.event_is_dragging is False: - # restore the Grid snapping if it was active before - if self.grid_status_memory is True: - self.app.ui.grid_snap_btn.trigger() - - if self.app.is_legacy is False: - self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release) - else: - self.app.plotcanvas.graph_event_disconnect(self.mr) - - self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', - self.app.on_mouse_click_release_over_plot) - - self.app.tool_shapes.clear(update=True) - - if self.poly_dict: - poly_list = deepcopy(list(self.poly_dict.values())) - self.isolate(iso_type=self.iso_type, geometry=poly_list) - self.poly_dict.clear() - else: - self.app.inform.emit('[ERROR_NOTCL] %s' % _("List of single polygons is empty. Aborting.")) - - def 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 - :return: - """ - poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])]) - - # delete previous selection shape - self.app.delete_selection_shape() - - added_poly_count = 0 - try: - for geo in self.solid_geometry: - if geo not in self.poly_dict.values(): - if sel_type is True: - if geo.within(poly_selection): - shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, - shape=geo, - color=self.app.defaults['global_sel_draw_color'] + 'AF', - face_color=self.app.defaults[ - 'global_sel_draw_color'] + 'AF', - visible=True) - self.poly_dict[shape_id] = geo - added_poly_count += 1 - else: - if poly_selection.intersects(geo): - shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, - shape=geo, - color=self.app.defaults['global_sel_draw_color'] + 'AF', - face_color=self.app.defaults[ - 'global_sel_draw_color'] + 'AF', - visible=True) - self.poly_dict[shape_id] = geo - added_poly_count += 1 - except TypeError: - if self.solid_geometry not in self.poly_dict.values(): - if sel_type is True: - if self.solid_geometry.within(poly_selection): - shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, - shape=self.solid_geometry, - color=self.app.defaults['global_sel_draw_color'] + 'AF', - face_color=self.app.defaults[ - 'global_sel_draw_color'] + 'AF', - visible=True) - self.poly_dict[shape_id] = self.solid_geometry - added_poly_count += 1 - else: - if poly_selection.intersects(self.solid_geometry): - shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, - shape=self.solid_geometry, - color=self.app.defaults['global_sel_draw_color'] + 'AF', - face_color=self.app.defaults[ - 'global_sel_draw_color'] + 'AF', - visible=True) - self.poly_dict[shape_id] = self.solid_geometry - added_poly_count += 1 - - if added_poly_count > 0: - self.app.tool_shapes.redraw() - self.app.inform.emit( - '%s: %d. %s' % (_("Added polygon"), - int(added_poly_count), - _("Click to add next polygon or right click to start isolation.")) - ) - else: - self.app.inform.emit(_("No polygon in selection.")) - - def isolate(self, iso_type=None, geometry=None, dia=None, passes=None, overlap=None, outname=None, combine=None, - milling_type=None, follow=None, plot=True): - """ - Creates an isolation routing geometry object in the project. - - :param iso_type: type of isolation to be done: 0 = exteriors, 1 = interiors and 2 = both - :param geometry: specific geometry to isolate - :param dia: Tool diameter - :param passes: Number of tool widths to cut - :param overlap: Overlap between passes in fraction of tool diameter - :param outname: Base name of the output object - :param combine: Boolean: if to combine passes in one resulting object in case of multiple passes - :param milling_type: type of milling: conventional or climbing - :param follow: Boolean: if to generate a 'follow' geometry - :param plot: Boolean: if to plot the resulting geometry object - :return: None - """ - - if geometry is None: - work_geo = self.follow_geometry if follow is True else self.solid_geometry - else: - work_geo = geometry - - if dia is None: - dia = float(self.options["isotooldia"]) - - if passes is None: - passes = int(self.options["isopasses"]) - - if overlap is None: - overlap = float(self.options["isooverlap"]) - - overlap /= 100.0 - - combine = self.options["combine_passes"] if combine is None else bool(combine) - - if milling_type is None: - milling_type = self.options["milling_type"] - - if iso_type is None: - iso_t = 2 - else: - iso_t = iso_type - - base_name = self.options["name"] - - if combine: - if outname is None: - if self.iso_type == 0: - iso_name = base_name + "_ext_iso" - elif self.iso_type == 1: - iso_name = base_name + "_int_iso" - else: - iso_name = base_name + "_iso" - else: - iso_name = outname - - def iso_init(geo_obj, app_obj): - # Propagate options - geo_obj.options["cnctooldia"] = str(self.options["isotooldia"]) - geo_obj.tool_type = self.ui.tool_type_radio.get_value().upper() - - geo_obj.solid_geometry = [] - - # transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI - if self.ui.tool_type_radio.get_value() == 'v': - new_cutz = self.ui.cutz_spinner.get_value() - new_vtipdia = self.ui.tipdia_spinner.get_value() - new_vtipangle = self.ui.tipangle_spinner.get_value() - tool_type = 'V' - else: - new_cutz = self.app.defaults['geometry_cutz'] - new_vtipdia = self.app.defaults['geometry_vtipdia'] - new_vtipangle = self.app.defaults['geometry_vtipangle'] - tool_type = 'C1' - - # store here the default data for Geometry Data - default_data = {} - default_data.update({ - "name": iso_name, - "plot": self.app.defaults['geometry_plot'], - "cutz": new_cutz, - "vtipdia": new_vtipdia, - "vtipangle": new_vtipangle, - "travelz": self.app.defaults['geometry_travelz'], - "feedrate": self.app.defaults['geometry_feedrate'], - "feedrate_z": self.app.defaults['geometry_feedrate_z'], - "feedrate_rapid": self.app.defaults['geometry_feedrate_rapid'], - "dwell": self.app.defaults['geometry_dwell'], - "dwelltime": self.app.defaults['geometry_dwelltime'], - "multidepth": self.app.defaults['geometry_multidepth'], - "ppname_g": self.app.defaults['geometry_ppname_g'], - "depthperpass": self.app.defaults['geometry_depthperpass'], - "extracut": self.app.defaults['geometry_extracut'], - "extracut_length": self.app.defaults['geometry_extracut_length'], - "toolchange": self.app.defaults['geometry_toolchange'], - "toolchangez": self.app.defaults['geometry_toolchangez'], - "endz": self.app.defaults['geometry_endz'], - "spindlespeed": self.app.defaults['geometry_spindlespeed'], - "toolchangexy": self.app.defaults['geometry_toolchangexy'], - "startz": self.app.defaults['geometry_startz'] - }) - - geo_obj.tools = {} - geo_obj.tools['1'] = {} - geo_obj.tools.update({ - '1': { - 'tooldia': float(self.options["isotooldia"]), - 'offset': 'Path', - 'offset_value': 0.0, - 'type': _('Rough'), - 'tool_type': tool_type, - 'data': default_data, - 'solid_geometry': geo_obj.solid_geometry - } - }) - - for nr_pass in range(passes): - iso_offset = dia * ((2 * nr_pass + 1) / 2.0) - (nr_pass * overlap * dia) - - # if milling type is climb then the move is counter-clockwise around features - mill_dir = 1 if milling_type == 'cl' else 0 - geom = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t, - follow=follow, nr_passes=nr_pass) - - if geom == 'fail': - app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated.")) - return 'fail' - geo_obj.solid_geometry.append(geom) - - # update the geometry in the tools - geo_obj.tools['1']['solid_geometry'] = geo_obj.solid_geometry - - # detect if solid_geometry is empty and this require list flattening which is "heavy" - # or just looking in the lists (they are one level depth) and if any is not empty - # proceed with object creation, if there are empty and the number of them is the length - # of the list then we have an empty solid_geometry which should raise a Custom Exception - empty_cnt = 0 - if not isinstance(geo_obj.solid_geometry, list) and \ - not isinstance(geo_obj.solid_geometry, MultiPolygon): - geo_obj.solid_geometry = [geo_obj.solid_geometry] - - for g in geo_obj.solid_geometry: - if g: - break - else: - empty_cnt += 1 - - if empty_cnt == len(geo_obj.solid_geometry): - raise ValidationError("Empty Geometry", None) - else: - app_obj.inform.emit('[success] %s" %s' % (_("Isolation geometry created"), geo_obj.options["name"])) - - # even if combine is checked, one pass is still single-geo - geo_obj.multigeo = True if passes > 1 else False - - # ############################################################ - # ########## AREA SUBTRACTION ################################ - # ############################################################ - if self.ui.except_cb.get_value(): - self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo")) - geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry) - - # TODO: Do something if this is None. Offer changing name? - self.app.new_object("geometry", iso_name, iso_init, plot=plot) - else: - for i in range(passes): - - offset = dia * ((2 * i + 1) / 2.0) - (i * overlap * dia) - if passes > 1: - if outname is None: - if self.iso_type == 0: - iso_name = base_name + "_ext_iso" + str(i + 1) - elif self.iso_type == 1: - iso_name = base_name + "_int_iso" + str(i + 1) - else: - iso_name = base_name + "_iso" + str(i + 1) - else: - iso_name = outname - else: - if outname is None: - if self.iso_type == 0: - iso_name = base_name + "_ext_iso" - elif self.iso_type == 1: - iso_name = base_name + "_int_iso" - else: - iso_name = base_name + "_iso" - else: - iso_name = outname - - def iso_init(geo_obj, app_obj): - # Propagate options - geo_obj.options["cnctooldia"] = str(self.options["isotooldia"]) - if self.ui.tool_type_radio.get_value() == 'v': - geo_obj.tool_type = 'V' - else: - geo_obj.tool_type = 'C1' - - # if milling type is climb then the move is counter-clockwise around features - mill_dir = 1 if milling_type == 'cl' else 0 - geom = self.generate_envelope(offset, mill_dir, geometry=work_geo, env_iso_type=iso_t, - follow=follow, - nr_passes=i) - - if geom == 'fail': - app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated.")) - return 'fail' - - geo_obj.solid_geometry = geom - - # transfer the Cut Z and Vtip and VAngle values in case that we use the V-Shape tool in Gerber UI - # even if the resulting geometry is not multigeo we add the tools dict which will hold the data - # required to be transfered to the Geometry object - if self.ui.tool_type_radio.get_value() == 'v': - new_cutz = self.ui.cutz_spinner.get_value() - new_vtipdia = self.ui.tipdia_spinner.get_value() - new_vtipangle = self.ui.tipangle_spinner.get_value() - tool_type = 'V' - else: - new_cutz = self.app.defaults['geometry_cutz'] - new_vtipdia = self.app.defaults['geometry_vtipdia'] - new_vtipangle = self.app.defaults['geometry_vtipangle'] - tool_type = 'C1' - - # store here the default data for Geometry Data - default_data = {} - default_data.update({ - "name": iso_name, - "plot": self.app.defaults['geometry_plot'], - "cutz": new_cutz, - "vtipdia": new_vtipdia, - "vtipangle": new_vtipangle, - "travelz": self.app.defaults['geometry_travelz'], - "feedrate": self.app.defaults['geometry_feedrate'], - "feedrate_z": self.app.defaults['geometry_feedrate_z'], - "feedrate_rapid": self.app.defaults['geometry_feedrate_rapid'], - "dwell": self.app.defaults['geometry_dwell'], - "dwelltime": self.app.defaults['geometry_dwelltime'], - "multidepth": self.app.defaults['geometry_multidepth'], - "ppname_g": self.app.defaults['geometry_ppname_g'], - "depthperpass": self.app.defaults['geometry_depthperpass'], - "extracut": self.app.defaults['geometry_extracut'], - "extracut_length": self.app.defaults['geometry_extracut_length'], - "toolchange": self.app.defaults['geometry_toolchange'], - "toolchangez": self.app.defaults['geometry_toolchangez'], - "endz": self.app.defaults['geometry_endz'], - "spindlespeed": self.app.defaults['geometry_spindlespeed'], - "toolchangexy": self.app.defaults['geometry_toolchangexy'], - "startz": self.app.defaults['geometry_startz'] - }) - - geo_obj.tools = {} - geo_obj.tools['1'] = {} - geo_obj.tools.update({ - '1': { - 'tooldia': float(self.options["isotooldia"]), - 'offset': 'Path', - 'offset_value': 0.0, - 'type': _('Rough'), - 'tool_type': tool_type, - 'data': default_data, - 'solid_geometry': geo_obj.solid_geometry - } - }) - - # detect if solid_geometry is empty and this require list flattening which is "heavy" - # or just looking in the lists (they are one level depth) and if any is not empty - # proceed with object creation, if there are empty and the number of them is the length - # of the list then we have an empty solid_geometry which should raise a Custom Exception - empty_cnt = 0 - if not isinstance(geo_obj.solid_geometry, list): - geo_obj.solid_geometry = [geo_obj.solid_geometry] - - for g in geo_obj.solid_geometry: - if g: - break - else: - empty_cnt += 1 - - if empty_cnt == len(geo_obj.solid_geometry): - raise ValidationError("Empty Geometry", None) - else: - app_obj.inform.emit('[success] %s: %s' % - (_("Isolation geometry created"), geo_obj.options["name"])) - geo_obj.multigeo = False - - # ############################################################ - # ########## AREA SUBTRACTION ################################ - # ############################################################ - if self.ui.except_cb.get_value(): - self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo")) - geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry) - - # TODO: Do something if this is None. Offer changing name? - self.app.new_object("geometry", iso_name, iso_init, plot=plot) + self.app.app_obj.new_object("geometry", name, geo_init) def generate_envelope(self, offset, invert, geometry=None, env_iso_type=2, follow=None, nr_passes=0): # isolation_geometry produces an envelope that is going on the left of the geometry @@ -1114,64 +491,28 @@ class GerberObject(FlatCAMObj, Gerber): return 'fail' return geom - def area_subtraction(self, geo, subtractor_geo=None): + def follow_geo(self, outname=None): """ - Subtracts the subtractor_geo (if present else self.solid_geometry) from the geo + Creates a geometry object "following" the gerber paths. - :param geo: target geometry from which to subtract - :param subtractor_geo: geometry that acts as subtractor - :return: + :return: None """ - new_geometry = [] - target_geo = geo - if subtractor_geo: - sub_union = cascaded_union(subtractor_geo) + if outname is None: + follow_name = self.options["name"] + "_follow" else: - name = self.ui.obj_combo.currentText() - subtractor_obj = self.app.collection.get_by_name(name) - sub_union = cascaded_union(subtractor_obj.solid_geometry) + follow_name = outname + def follow_init(follow_obj, app): + # Propagate options + follow_obj.options["cnctooldia"] = str(self.options["isotooldia"]) + follow_obj.solid_geometry = self.follow_geometry + + # TODO: Do something if this is None. Offer changing name? try: - for geo_elem in target_geo: - if isinstance(geo_elem, Polygon): - for ring in self.poly2rings(geo_elem): - new_geo = ring.difference(sub_union) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(geo_elem, MultiPolygon): - for poly in geo_elem: - for ring in self.poly2rings(poly): - new_geo = ring.difference(sub_union) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(geo_elem, LineString): - new_geo = geo_elem.difference(sub_union) - if new_geo: - if not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(geo_elem, MultiLineString): - for line_elem in geo_elem: - new_geo = line_elem.difference(sub_union) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - except TypeError: - if isinstance(target_geo, Polygon): - for ring in self.poly2rings(target_geo): - new_geo = ring.difference(sub_union) - if new_geo: - if not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(target_geo, LineString): - new_geo = target_geo.difference(sub_union) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(target_geo, MultiLineString): - for line_elem in target_geo: - new_geo = line_elem.difference(sub_union) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - return new_geometry + self.app.app_obj.new_object("geometry", follow_name, follow_init) + except Exception as e: + return "Operation failed: %s" % str(e) def on_plot_cb_click(self, *args): if self.muted_ui: diff --git a/flatcamObjects/FlatCAMObj.py b/AppObjects/FlatCAMObj.py similarity index 92% rename from flatcamObjects/FlatCAMObj.py rename to AppObjects/FlatCAMObj.py index 94108cc9..7c292aec 100644 --- a/flatcamObjects/FlatCAMObj.py +++ b/AppObjects/FlatCAMObj.py @@ -12,15 +12,15 @@ import inspect # TODO: For debugging only. -from flatcamGUI.ObjectUI import * +from AppGUI.ObjectUI import * -from FlatCAMCommon import LoudDict -from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy +from Common import LoudDict +from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy import sys import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -43,7 +43,7 @@ class ValidationError(Exception): class FlatCAMObj(QtCore.QObject): """ Base type of objects handled in FlatCAM. These become interactive - in the GUI, can be plotted, and their options can be modified + in the AppGUI, can be plotted, and their options can be modified by the user in their respective forms. """ @@ -186,11 +186,9 @@ class FlatCAMObj(QtCore.QObject): def build_ui(self): """ - Sets up the UI/form for this object. Show the UI - in the App. + Sets up the UI/form for this object. Show the UI in the App. :return: None - :rtype: None """ self.muted_ui = True @@ -247,14 +245,14 @@ class FlatCAMObj(QtCore.QObject): self.app.proc_container.update_view_text('') with self.app.proc_container.new('%s...' % _("Plotting")): self.plot() - self.app.object_changed.emit(self) + self.app.app_obj.object_changed.emit(self) self.app.worker_task.emit({'fcn': worker_task, 'params': []}) def on_scale_button_click(self): self.read_form() try: - factor = float(eval(self.ui.scale_entry.get_value())) + factor = float(self.ui.scale_entry.get_value()) except Exception as e: self.app.inform.emit('[ERROR_NOTCL] %s' % _("Scaling could not be executed.")) log.debug("FlatCAMObj.on_scale_button_click() -- %s" % str(e)) @@ -278,7 +276,7 @@ class FlatCAMObj(QtCore.QObject): self.app.proc_container.update_view_text('') with self.app.proc_container.new('%s...' % _("Plotting")): self.plot() - self.app.object_changed.emit(self) + self.app.app_obj.object_changed.emit(self) self.app.worker_task.emit({'fcn': worker_task, 'params': []}) @@ -294,7 +292,7 @@ class FlatCAMObj(QtCore.QObject): self.app.proc_container.update_view_text('') with self.app.proc_container.new('%s...' % _("Plotting")): self.plot() - self.app.object_changed.emit(self) + self.app.app_obj.object_changed.emit(self) self.app.worker_task.emit({'fcn': worker_task, 'params': []}) @@ -308,8 +306,8 @@ class FlatCAMObj(QtCore.QObject): for option in self.options: try: self.set_form_item(option) - except Exception: - self.app.log.warning("Unexpected error:", sys.exc_info()) + except Exception as err: + self.app.log.warning("Unexpected error: %s" % str(sys.exc_info()), str(err)) def read_form(self): """ @@ -323,7 +321,7 @@ class FlatCAMObj(QtCore.QObject): try: self.read_form_item(option) except Exception: - self.app.log.warning("Unexpected error:", sys.exc_info()) + self.app.log.warning("Unexpected error: %s" % str(sys.exc_info())) def set_form_item(self, option): """ @@ -374,7 +372,7 @@ class FlatCAMObj(QtCore.QObject): def plot_task(): with self.app.proc_container.new('%s...' % _("Plotting")): self.plot() - self.app.object_changed.emit(self) + self.app.app_obj.object_changed.emit(self) self.app.worker_task.emit({'fcn': plot_task, 'params': []}) @@ -461,20 +459,20 @@ class FlatCAMObj(QtCore.QObject): def visible(self, value, threaded=True): log.debug("FlatCAMObj.visible()") - def worker_task(app_obj): - self.shapes.visible = value - - if self.app.is_legacy is False: - # Not all object types has annotations - try: - self.annotation.visible = value - except Exception: - pass - - if threaded is False: - worker_task(app_obj=self.app) + # self.shapes.visible = value # maybe this is slower in VisPy? use enabled property? + if self.shapes.visible is True: + if value is False: + self.shapes.visible = False else: - self.app.worker_task.emit({'fcn': worker_task, 'params': [self]}) + if value is True: + self.shapes.visible = True + + if self.app.is_legacy is False: + # Not all object types has annotations + try: + self.annotation.visible = value + except Exception: + pass @property def drawing_tolerance(self): diff --git a/flatcamObjects/FlatCAMScript.py b/AppObjects/FlatCAMScript.py similarity index 90% rename from flatcamObjects/FlatCAMScript.py rename to AppObjects/FlatCAMScript.py index 946f578f..93892715 100644 --- a/flatcamObjects/FlatCAMScript.py +++ b/AppObjects/FlatCAMScript.py @@ -10,16 +10,16 @@ # File modified by: Marius Stanciu # # ########################################################## -from flatcamEditors.FlatCAMTextEditor import TextEditor -from flatcamObjects.FlatCAMObj import * -from flatcamGUI.ObjectUI import * +from AppEditors.FlatCAMTextEditor import TextEditor +from AppObjects.FlatCAMObj import * +from AppGUI.ObjectUI import * import tkinter as tk import sys from copy import deepcopy import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -183,7 +183,13 @@ class ScriptObject(FlatCAMObj): if self.app.ui.shell_dock.isHidden(): self.app.ui.shell_dock.show() - self.script_code = deepcopy(self.script_editor_tab.code_editor.toPlainText()) + self.app.shell.open_processing() # Disables input box. + + # make sure that the pixmaps are not updated when running this as they will crash + # TODO find why the pixmaps load crash when run from this object (perhaps another thread?) + self.app.ui.fcinfo.lock_pmaps = True + + self.script_code = self.script_editor_tab.code_editor.toPlainText() old_line = '' for tcl_command_line in self.script_code.splitlines(): @@ -202,8 +208,6 @@ class ScriptObject(FlatCAMObj): # execute the actual Tcl command try: - self.app.shell.open_processing() # Disables input box. - result = self.app.shell.tcl.eval(str(new_command)) if result != 'None': self.app.shell.append_output(result + '\n') @@ -220,6 +224,7 @@ class ScriptObject(FlatCAMObj): log.error("Exec command Exception: %s\n" % result) self.app.shell.append_error('ERROR: %s\n '% result) + self.app.ui.fcinfo.lock_pmaps = False self.app.shell.close_processing() def on_autocomplete_changed(self, state): @@ -228,6 +233,27 @@ class ScriptObject(FlatCAMObj): else: self.script_editor_tab.code_editor.completer_enable = False + def mirror(self, axis, point): + pass + + def offset(self, vect): + pass + + def rotate(self, angle, point): + pass + + def scale(self, xfactor, yfactor=None, point=None): + pass + + def skew(self, angle_x, angle_y, point): + pass + + def buffer(self, distance, join, factor=None): + pass + + def bounds(self, flatten=False): + return None, None, None, None + def to_dict(self): """ Returns a representation of the object as a dictionary. diff --git a/flatcamObjects/ObjectCollection.py b/AppObjects/ObjectCollection.py similarity index 79% rename from flatcamObjects/ObjectCollection.py rename to AppObjects/ObjectCollection.py index ee1cf933..477eb0e6 100644 --- a/flatcamObjects/ObjectCollection.py +++ b/AppObjects/ObjectCollection.py @@ -16,13 +16,13 @@ from PyQt5.QtCore import Qt, QSettings from PyQt5.QtGui import QColor # from PyQt5.QtCore import QModelIndex -from flatcamObjects.FlatCAMObj import FlatCAMObj -from flatcamObjects.FlatCAMCNCJob import CNCJobObject -from flatcamObjects.FlatCAMDocument import DocumentObject -from flatcamObjects.FlatCAMExcellon import ExcellonObject -from flatcamObjects.FlatCAMGeometry import GeometryObject -from flatcamObjects.FlatCAMGerber import GerberObject -from flatcamObjects.FlatCAMScript import ScriptObject +from AppObjects.FlatCAMObj import FlatCAMObj +from AppObjects.FlatCAMCNCJob import CNCJobObject +from AppObjects.FlatCAMDocument import DocumentObject +from AppObjects.FlatCAMExcellon import ExcellonObject +from AppObjects.FlatCAMGeometry import GeometryObject +from AppObjects.FlatCAMGerber import GerberObject +from AppObjects.FlatCAMScript import ScriptObject import inspect # TODO: Remove @@ -32,7 +32,7 @@ from copy import deepcopy from numpy import Inf import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -50,12 +50,13 @@ class KeySensitiveListView(QtWidgets.QTreeView): def __init__(self, app, parent=None): super(KeySensitiveListView, self).__init__(parent) self.setHeaderHidden(True) - self.setEditTriggers(QtWidgets.QTreeView.SelectedClicked) + # self.setEditTriggers(QtWidgets.QTreeView.SelectedClicked) # allow Edit on Tree + self.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers) # self.setRootIsDecorated(False) # self.setExpandsOnDoubleClick(False) - # Enable dragging and dropping onto the GUI + # Enable dragging and dropping onto the AppGUI self.setAcceptDrops(True) self.filename = "" self.app = app @@ -334,6 +335,8 @@ class ObjectCollection(QtCore.QAbstractItemModel): self.click_modifier = None self.update_list_signal.connect(self.on_update_list_signal) + self.view.activated.connect(self.on_row_activated) + self.item_selected.connect(self.on_row_selected) def promise(self, obj_name): log.debug("Object %s has been promised." % obj_name) @@ -382,8 +385,9 @@ class ObjectCollection(QtCore.QAbstractItemModel): if type(obj) != GeometryObject: self.app.ui.menuprojectgeneratecnc.setVisible(False) - if type(obj) != GeometryObject and type(obj) != ExcellonObject and type(obj) != GerberObject: - self.app.ui.menuprojectedit.setVisible(False) + # if type(obj) != GeometryObject and type(obj) != ExcellonObject and type(obj) != GerberObject or \ + # type(obj) != CNCJobObject: + # self.app.ui.menuprojectedit.setVisible(False) if type(obj) != GerberObject and type(obj) != ExcellonObject and type(obj) != CNCJobObject: self.app.ui.menuprojectviewsource.setVisible(False) if type(obj) != GerberObject and type(obj) != GeometryObject and type(obj) != ExcellonObject and \ @@ -550,7 +554,7 @@ class ObjectCollection(QtCore.QAbstractItemModel): while name in self.get_names(): # ## Create a new name # Ends with number? - log.debug("new_object(): Object name (%s) exists, changing." % name) + log.debug("app_obj.new_object(): Object name (%s) exists, changing." % name) match = re.search(r'(.*[^\d])?(\d+)$', name) if match: # Yes: Increment the number! base = match.group(1) or '' @@ -596,8 +600,8 @@ class ObjectCollection(QtCore.QAbstractItemModel): """ Gets a list of the names of all objects in the collection. - :return: List of names. - :rtype: list + :return: List of names. + :rtype: list """ # log.debug(str(inspect.stack()[1][3]) + " --> OC.get_names()") @@ -985,7 +989,7 @@ class ObjectCollection(QtCore.QAbstractItemModel): def get_list(self): """ - Will return a list of all objects currently opened. + Will return a list of all objects currently opened. Except FlatCAMScript and FlatCAMDocuments :return: """ @@ -998,3 +1002,199 @@ class ObjectCollection(QtCore.QAbstractItemModel): def update_view(self): self.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex()) + + def on_row_activated(self, index): + if index.isValid(): + if index.internalPointer().parent_item != self.root_item: + self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) + self.on_item_activated(index) + + def on_row_selected(self, obj_name): + """ + This is a special string; when received it will make all Menu -> Objects entries unchecked + It mean we clicked outside of the items and deselected all + + :param obj_name: + :return: + """ + if obj_name == 'none': + for act in self.app.ui.menuobjects.actions(): + act.setChecked(False) + return + + # get the name of the selected objects and add them to a list + name_list = [] + for obj in self.get_selected(): + name_list.append(obj.options['name']) + + # set all actions as unchecked but the ones selected make them checked + for act in self.app.ui.menuobjects.actions(): + act.setChecked(False) + if act.text() in name_list: + act.setChecked(True) + + def on_collection_updated(self, obj, state, old_name): + """ + Create a menu from the object loaded in the collection. + + :param obj: object that was changed (added, deleted, renamed) + :param state: what was done with the object. Can be: added, deleted, delete_all, renamed + :param old_name: the old name of the object before the action that triggered this slot happened + :return: None + """ + icon_files = { + "gerber": self.app.resource_location + "/flatcam_icon16.png", + "excellon": self.app.resource_location + "/drill16.png", + "cncjob": self.app.resource_location + "/cnc16.png", + "geometry": self.app.resource_location + "/geometry16.png", + "script": self.app.resource_location + "/script_new16.png", + "document": self.app.resource_location + "/notes16_1.png" + } + + if state == 'append': + for act in self.app.ui.menuobjects.actions(): + try: + act.triggered.disconnect() + except TypeError: + pass + self.app.ui.menuobjects.clear() + + gerber_list = [] + exc_list = [] + cncjob_list = [] + geo_list = [] + script_list = [] + doc_list = [] + + for name in self.get_names(): + obj_named = self.get_by_name(name) + if obj_named.kind == 'gerber': + gerber_list.append(name) + elif obj_named.kind == 'excellon': + exc_list.append(name) + elif obj_named.kind == 'cncjob': + cncjob_list.append(name) + elif obj_named.kind == 'geometry': + geo_list.append(name) + elif obj_named.kind == 'script': + script_list.append(name) + elif obj_named.kind == 'document': + doc_list.append(name) + + def add_act(o_name): + obj_for_icon = self.get_by_name(o_name) + add_action = QtWidgets.QAction(parent=self.app.ui.menuobjects) + add_action.setCheckable(True) + add_action.setText(o_name) + add_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind])) + add_action.triggered.connect( + lambda: self.set_active(o_name) if add_action.isChecked() is True else + self.set_inactive(o_name)) + self.app.ui.menuobjects.addAction(add_action) + + for name in gerber_list: + add_act(name) + self.app.ui.menuobjects.addSeparator() + + for name in exc_list: + add_act(name) + self.app.ui.menuobjects.addSeparator() + + for name in cncjob_list: + add_act(name) + self.app.ui.menuobjects.addSeparator() + + for name in geo_list: + add_act(name) + self.app.ui.menuobjects.addSeparator() + + for name in script_list: + add_act(name) + self.app.ui.menuobjects.addSeparator() + + for name in doc_list: + add_act(name) + + self.app.ui.menuobjects.addSeparator() + self.app.ui.menuobjects_selall = self.app.ui.menuobjects.addAction( + QtGui.QIcon(self.app.resource_location + '/select_all.png'), + _('Select All') + ) + self.app.ui.menuobjects_unselall = self.app.ui.menuobjects.addAction( + QtGui.QIcon(self.app.resource_location + '/deselect_all32.png'), + _('Deselect All') + ) + self.app.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True)) + self.app.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False)) + + elif state == 'delete': + for act in self.app.ui.menuobjects.actions(): + if act.text() == obj.options['name']: + try: + act.triggered.disconnect() + except TypeError: + pass + self.app.ui.menuobjects.removeAction(act) + break + elif state == 'rename': + for act in self.app.ui.menuobjects.actions(): + if act.text() == old_name: + add_action = QtWidgets.QAction(parent=self.app.ui.menuobjects) + add_action.setText(obj.options['name']) + add_action.setIcon(QtGui.QIcon(icon_files[obj.kind])) + add_action.triggered.connect( + lambda: self.set_active(obj.options['name']) if add_action.isChecked() is True else + self.set_inactive(obj.options['name'])) + + self.app.ui.menuobjects.insertAction(act, add_action) + + try: + act.triggered.disconnect() + except TypeError: + pass + self.app.ui.menuobjects.removeAction(act) + break + elif state == 'delete_all': + for act in self.app.ui.menuobjects.actions(): + try: + act.triggered.disconnect() + except TypeError: + pass + self.app.ui.menuobjects.clear() + + self.app.ui.menuobjects.addSeparator() + self.app.ui.menuobjects_selall = self.app.ui.menuobjects.addAction( + QtGui.QIcon(self.app.resource_location + '/select_all.png'), + _('Select All') + ) + self.app.ui.menuobjects_unselall = self.app.ui.menuobjects.addAction( + QtGui.QIcon(self.app.resource_location + '/deselect_all32.png'), + _('Deselect All') + ) + self.app.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True)) + self.app.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False)) + + def on_objects_selection(self, on_off): + obj_list = self.get_names() + + if on_off is True: + self.set_all_active() + for act in self.app.ui.menuobjects.actions(): + try: + act.setChecked(True) + except Exception: + pass + if obj_list: + self.app.inform[str, bool].emit('[selected] %s' % _("All objects are selected."), False) + else: + self.set_all_inactive() + for act in self.app.ui.menuobjects.actions(): + try: + act.setChecked(False) + except Exception: + pass + + if obj_list: + self.app.inform[str, bool].emit('%s' % _("Objects selection is cleared."), False) + else: + self.app.inform[str, bool].emit('', False) diff --git a/flatcamObjects/__init__.py b/AppObjects/__init__.py similarity index 100% rename from flatcamObjects/__init__.py rename to AppObjects/__init__.py diff --git a/flatcamParsers/ParseDXF.py b/AppParsers/ParseDXF.py similarity index 99% rename from flatcamParsers/ParseDXF.py rename to AppParsers/ParseDXF.py index d3d482fd..698e87b8 100644 --- a/flatcamParsers/ParseDXF.py +++ b/AppParsers/ParseDXF.py @@ -12,8 +12,8 @@ import logging log = logging.getLogger('base2') -from flatcamParsers.ParseFont import * -from flatcamParsers.ParseDXF_Spline import * +from AppParsers.ParseFont import * +from AppParsers.ParseDXF_Spline import * def distance(pt1, pt2): diff --git a/flatcamParsers/ParseDXF_Spline.py b/AppParsers/ParseDXF_Spline.py similarity index 100% rename from flatcamParsers/ParseDXF_Spline.py rename to AppParsers/ParseDXF_Spline.py diff --git a/flatcamParsers/ParseExcellon.py b/AppParsers/ParseExcellon.py similarity index 98% rename from flatcamParsers/ParseExcellon.py rename to AppParsers/ParseExcellon.py index 07930005..fbf1bb6f 100644 --- a/flatcamParsers/ParseExcellon.py +++ b/AppParsers/ParseExcellon.py @@ -6,7 +6,7 @@ # MIT Licence # # ########################################################## ## -from camlib import Geometry +from camlib import Geometry, grace import shapely.affinity as affinity from shapely.geometry import Point, LineString @@ -17,8 +17,7 @@ import logging import traceback from copy import deepcopy -import FlatCAMTranslation as fcTranslate -from FlatCAMCommon import GracefulException as grace +# import AppTranslation as fcTranslate import gettext import builtins @@ -967,7 +966,7 @@ class Excellon(Geometry): :return: None """ - log.debug("flatcamParsers.ParseExcellon.Excellon.create_geometry()") + log.debug("AppParsers.ParseExcellon.Excellon.create_geometry()") self.solid_geometry = [] try: # clear the solid_geometry in self.tools @@ -982,7 +981,7 @@ class Excellon(Geometry): _("Excellon.create_geometry() -> a drill location was skipped " "due of not having a tool associated.\n" "Check the resulting GCode.")) - log.debug("flatcamParsers.ParseExcellon.Excellon.create_geometry() -> a drill location was skipped " + log.debug("AppParsers.ParseExcellon.Excellon.create_geometry() -> a drill location was skipped " "due of not having a tool associated") continue tooldia = self.tools[drill['tool']]['C'] @@ -1006,7 +1005,7 @@ class Excellon(Geometry): self.tools[tool_in_slots]['solid_geometry'].append(poly) self.tools[tool_in_slots]['data'] = deepcopy(self.default_data) except Exception as e: - log.debug("flatcamParsers.ParseExcellon.Excellon.create_geometry() -> " + log.debug("AppParsers.ParseExcellon.Excellon.create_geometry() -> " "Excellon geometry creation failed due of ERROR: %s" % str(e)) return "fail" @@ -1018,10 +1017,10 @@ class Excellon(Geometry): :param flatten: No used """ - log.debug("flatcamParsers.ParseExcellon.Excellon.bounds()") + log.debug("AppParsers.ParseExcellon.Excellon.bounds()") if self.solid_geometry is None or not self.tools: - log.debug("flatcamParsers.ParseExcellon.Excellon -> solid_geometry is None") + log.debug("AppParsers.ParseExcellon.Excellon -> solid_geometry is None") return 0, 0, 0, 0 def bounds_rec(obj): @@ -1069,7 +1068,7 @@ class Excellon(Geometry): This function first convert to the the units found in the Excellon file but it converts tools that are not there yet so it has no effect other than it signal that the units are the ones in the file. - On object creation, in new_object(), true conversion is done because this is done at the end of the + On object creation, in app_obj.new_object(), true conversion is done because this is done at the end of the Excellon file parsing, the tools are inside and self.tools is really converted from the units found inside the file to the FlatCAM units. @@ -1092,7 +1091,7 @@ class Excellon(Geometry): else: log.error("Unsupported units: %s" % str(obj_units)) factor = 1.0 - log.debug("flatcamParsers.ParseExcellon.Excellon.convert_units() --> Factor: %s" % str(factor)) + log.debug("AppParsers.ParseExcellon.Excellon.convert_units() --> Factor: %s" % str(factor)) self.units = obj_units self.scale(factor, factor) @@ -1118,7 +1117,7 @@ class Excellon(Geometry): :return: None :rtype: None """ - log.debug("flatcamParsers.ParseExcellon.Excellon.scale()") + log.debug("AppParsers.ParseExcellon.Excellon.scale()") if yfactor is None: yfactor = xfactor @@ -1183,7 +1182,7 @@ class Excellon(Geometry): :type vect: tuple :return: None """ - log.debug("flatcamParsers.ParseExcellon.Excellon.offset()") + log.debug("AppParsers.ParseExcellon.Excellon.offset()") dx, dy = vect @@ -1243,7 +1242,7 @@ class Excellon(Geometry): :type point: list :return: None """ - log.debug("flatcamParsers.ParseExcellon.Excellon.mirror()") + log.debug("AppParsers.ParseExcellon.Excellon.mirror()") px, py = point xscale, yscale = {"X": (1.0, -1.0), "Y": (-1.0, 1.0)}[axis] @@ -1309,7 +1308,7 @@ class Excellon(Geometry): See shapely manual for more information: http://toblerity.org/shapely/manual.html#affine-transformations """ - log.debug("flatcamParsers.ParseExcellon.Excellon.skew()") + log.debug("AppParsers.ParseExcellon.Excellon.skew()") if angle_x is None: angle_x = 0.0 @@ -1396,7 +1395,7 @@ class Excellon(Geometry): :param point: tuple of coordinates (x, y) :return: None """ - log.debug("flatcamParsers.ParseExcellon.Excellon.rotate()") + log.debug("AppParsers.ParseExcellon.Excellon.rotate()") if angle == 0: return @@ -1479,7 +1478,7 @@ class Excellon(Geometry): :param join: The type of line joint used by the shapely buffer method: round, square, bevel :return: None """ - log.debug("flatcamParsers.ParseExcellon.Excellon.buffer()") + log.debug("AppParsers.ParseExcellon.Excellon.buffer()") if distance == 0: return diff --git a/flatcamParsers/ParseFont.py b/AppParsers/ParseFont.py similarity index 99% rename from flatcamParsers/ParseFont.py rename to AppParsers/ParseFont.py index cb09a862..7fa2b0d0 100644 --- a/flatcamParsers/ParseFont.py +++ b/AppParsers/ParseFont.py @@ -22,7 +22,7 @@ from fontTools import ttLib import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/flatcamParsers/ParseGerber.py b/AppParsers/ParseGerber.py similarity index 99% rename from flatcamParsers/ParseGerber.py rename to AppParsers/ParseGerber.py index 57ba4d49..73e0b939 100644 --- a/flatcamParsers/ParseGerber.py +++ b/AppParsers/ParseGerber.py @@ -1,5 +1,5 @@ from PyQt5 import QtWidgets -from camlib import Geometry, arc, arc_angle, ApertureMacro +from camlib import Geometry, arc, arc_angle, ApertureMacro, grace import numpy as np import re @@ -14,9 +14,8 @@ import shapely.affinity as affinity from shapely.geometry import box as shply_box, Polygon, LineString, Point, MultiPolygon from lxml import etree as ET -from flatcamParsers.ParseSVG import svgparselength, getsvggeo -from FlatCAMCommon import GracefulException as grace -import FlatCAMTranslation as fcTranslate +from AppParsers.ParseSVG import svgparselength, getsvggeo +import AppTranslation as fcTranslate import gettext import builtins @@ -1477,7 +1476,7 @@ class Gerber(Geometry): sol_geo_length = 1 try: - if buff_length == 0 and sol_geo_length in [0, 1]: + if buff_length == 0 and sol_geo_length in [0, 1] and self.solid_geometry.area == 0: log.error("Object is not Gerber file or empty. Aborting Object creation.") return 'fail' except TypeError as e: @@ -1763,7 +1762,7 @@ class Gerber(Geometry): :return: None """ - log.debug("flatcamParsers.ParseGerber.Gerber.import_svg()") + log.debug("AppParsers.ParseGerber.Gerber.import_svg()") # Parse into list of shapely objects svg_tree = ET.parse(filename) @@ -2389,7 +2388,7 @@ class Gerber(Geometry): geo_p = shply_box(minx, miny, maxx, maxy) new_geo_el['solid'] = geo_p else: - log.debug("flatcamParsers.ParseGerber.Gerber.buffer() --> " + log.debug("AppParsers.ParseGerber.Gerber.buffer() --> " "ap type not supported") else: new_geo_el['solid'] = geo_el['follow'].buffer( diff --git a/flatcamParsers/ParseHPGL2.py b/AppParsers/ParseHPGL2.py similarity index 99% rename from flatcamParsers/ParseHPGL2.py rename to AppParsers/ParseHPGL2.py index a90a3184..200453b0 100644 --- a/flatcamParsers/ParseHPGL2.py +++ b/AppParsers/ParseHPGL2.py @@ -6,7 +6,7 @@ # MIT Licence # # ############################################################ -from camlib import arc, three_point_circle +from camlib import arc, three_point_circle, grace import numpy as np import re @@ -18,8 +18,7 @@ import sys from shapely.ops import unary_union from shapely.geometry import LineString, Point -from FlatCAMCommon import GracefulException as grace -import FlatCAMTranslation as fcTranslate +# import AppTranslation as fcTranslate import gettext import builtins diff --git a/flatcamTools/ToolPDF.py b/AppParsers/ParsePDF.py similarity index 77% rename from flatcamTools/ToolPDF.py rename to AppParsers/ParsePDF.py index a21e0f7a..3022e519 100644 --- a/flatcamTools/ToolPDF.py +++ b/AppParsers/ParsePDF.py @@ -5,50 +5,27 @@ # MIT Licence # # ########################################################## -from PyQt5 import QtWidgets, QtCore +from PyQt5 import QtCore -from FlatCAMTool import FlatCAMTool -from FlatCAMCommon import GracefulException as grace +from Common import GracefulException as grace -from shapely.geometry import Point, Polygon, LineString, MultiPolygon -from shapely.ops import unary_union +from shapely.geometry import Polygon, LineString, MultiPolygon from copy import copy, deepcopy import numpy as np - -import zlib import re -import time import logging -import traceback - -import gettext -import FlatCAMTranslation as fcTranslate -import builtins - -fcTranslate.apply_language('strings') -if '_' not in builtins.__dict__: - _ = gettext.gettext log = logging.getLogger('base') -class ToolPDF(FlatCAMTool): - """ - Parse a PDF file. - Reference here: https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf - Return a list of geometries - """ - toolName = _("PDF Import Tool") +class PdfParser(QtCore.QObject): def __init__(self, app): - FlatCAMTool.__init__(self, app) + super().__init__() self.app = app - self.decimals = self.app.decimals self.step_per_circles = self.app.defaults["gerber_circle_steps"] - self.stream_re = re.compile(b'.*?FlateDecode.*?stream(.*?)endstream', re.S) - # detect stroke color change; it means a new object to be created self.stroke_color_re = re.compile(r'^\s*(\d+\.?\d*) (\d+\.?\d*) (\d+\.?\d*)\s*RG$') @@ -110,75 +87,12 @@ class ToolPDF(FlatCAMTool): # (each sublist has 2 lists each having 2 elements: first is offset like: # offset_geo = [off_x, off_y], second element is scale list with 2 elements, like: scale_geo = [sc_x, sc_yy]) self.gs['transform'] = [] - self.gs['line_width'] = [] # each element is a float - - self.pdf_decompressed = {} - - # key = file name and extension - # value is a dict to store the parsed content of the PDF - self.pdf_parsed = {} - - # QTimer for periodic check - self.check_thread = QtCore.QTimer() - - # Every time a parser is started we add a promise; every time a parser finished we remove a promise - # when empty we start the layer rendering - self.parsing_promises = [] + self.gs['line_width'] = [] # each element is a float # conversion factor to INCH self.point_to_unit_factor = 0.01388888888 - def run(self, toggle=True): - self.app.defaults.report_usage("ToolPDF()") - - self.set_tool_ui() - self.on_open_pdf_click() - - def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Ctrl+Q', **kwargs) - - def set_tool_ui(self): - pass - - def on_open_pdf_click(self): - """ - File menu callback for opening an PDF file. - - :return: None - """ - - self.app.defaults.report_usage("ToolPDF.on_open_pdf_click()") - self.app.log.debug("ToolPDF.on_open_pdf_click()") - - _filter_ = "Adobe PDF Files (*.pdf);;" \ - "All Files (*.*)" - - try: - filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), - directory=self.app.get_last_folder(), - filter=_filter_) - except TypeError: - filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), filter=_filter_) - - if len(filenames) == 0: - self.app.inform.emit('[WARNING_NOTCL] %s.' % _("Open PDF cancelled")) - else: - # start the parsing timer with a period of 1 second - self.periodic_check(1000) - - for filename in filenames: - if filename != '': - self.app.worker_task.emit({'fcn': self.open_pdf, - 'params': [filename]}) - - def open_pdf(self, filename): - short_name = filename.split('/')[-1].split('\\')[-1] - self.parsing_promises.append(short_name) - self.pdf_parsed[short_name] = {} - self.pdf_parsed[short_name]['pdf'] = {} - self.pdf_parsed[short_name]['filename'] = filename - - self.pdf_decompressed[short_name] = '' + def parse_pdf(self, pdf_content): # the UNITS in PDF files are points and here we set the factor to convert them to real units (either MM or INCH) if self.app.defaults['units'].upper() == 'MM': @@ -188,260 +102,14 @@ class ToolPDF(FlatCAMTool): # 1 inch = 72 points => 1 point = 1 / 72 = 0.01388888888 inch self.point_to_unit_factor = 1 / 72 - if self.app.abort_flag: - # graceful abort requested by the user - raise grace - - with self.app.proc_container.new(_("Parsing PDF file ...")): - with open(filename, "rb") as f: - pdf = f.read() - - stream_nr = 0 - for s in re.findall(self.stream_re, pdf): - if self.app.abort_flag: - # graceful abort requested by the user - raise grace - - stream_nr += 1 - log.debug(" PDF STREAM: %d\n" % stream_nr) - s = s.strip(b'\r\n') - try: - self.pdf_decompressed[short_name] += (zlib.decompress(s).decode('UTF-8') + '\r\n') - except Exception as e: - log.debug("ToolPDF.open_pdf().obj_init() --> %s" % str(e)) - - self.pdf_parsed[short_name]['pdf'] = self.parse_pdf(pdf_content=self.pdf_decompressed[short_name]) - # we used it, now we delete it - self.pdf_decompressed[short_name] = '' - - # removal from list is done in a multithreaded way therefore not always the removal can be done - # try to remove until it's done - try: - while True: - self.parsing_promises.remove(short_name) - time.sleep(0.1) - except Exception as e: - log.debug("ToolPDF.open_pdf() --> %s" % str(e)) - self.app.inform.emit('[success] %s: %s' % (_("Opened"), str(filename))) - - def layer_rendering_as_excellon(self, filename, ap_dict, layer_nr): - outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr) - - # store the points here until reconstitution: - # keys are diameters and values are list of (x,y) coords - points = {} - - def obj_init(exc_obj, app_obj): - clear_geo = [geo_el['clear'] for geo_el in ap_dict['0']['geometry']] - - for geo in clear_geo: - xmin, ymin, xmax, ymax = geo.bounds - center = (((xmax - xmin) / 2) + xmin, ((ymax - ymin) / 2) + ymin) - - # for drill bits, even in INCH, it's enough 3 decimals - correction_factor = 0.974 - dia = (xmax - xmin) * correction_factor - dia = round(dia, 3) - if dia in points: - points[dia].append(center) - else: - points[dia] = [center] - - sorted_dia = sorted(points.keys()) - - name_tool = 0 - for dia in sorted_dia: - name_tool += 1 - - # create tools dictionary - spec = {"C": dia, 'solid_geometry': []} - exc_obj.tools[str(name_tool)] = spec - - # create drill list of dictionaries - for dia_points in points: - if dia == dia_points: - for pt in points[dia_points]: - exc_obj.drills.append({'point': Point(pt), 'tool': str(name_tool)}) - break - - ret = exc_obj.create_geometry() - if ret == 'fail': - log.debug("Could not create geometry for Excellon object.") - return "fail" - for tool in exc_obj.tools: - if exc_obj.tools[tool]['solid_geometry']: - return - app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % - (_("No geometry found in file"), outname)) - return "fail" - - with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)): - - ret_val = self.app.new_object("excellon", outname, obj_init, autoselected=False) - if ret_val == 'fail': - self.app.inform.emit('[ERROR_NOTCL] %s' % - _('Open PDF file failed.')) - return - # Register recent file - self.app.file_opened.emit("excellon", filename) - # GUI feedback - self.app.inform.emit('[success] %s: %s' % - (_("Rendered"), outname)) - - def layer_rendering_as_gerber(self, filename, ap_dict, layer_nr): - outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr) - - def obj_init(grb_obj): - - grb_obj.apertures = ap_dict - - poly_buff = [] - follow_buf = [] - for ap in grb_obj.apertures: - for k in grb_obj.apertures[ap]: - if k == 'geometry': - for geo_el in ap_dict[ap][k]: - if 'solid' in geo_el: - poly_buff.append(geo_el['solid']) - if 'follow' in geo_el: - follow_buf.append(geo_el['follow']) - poly_buff = unary_union(poly_buff) - - if '0' in grb_obj.apertures: - global_clear_geo = [] - if 'geometry' in grb_obj.apertures['0']: - for geo_el in ap_dict['0']['geometry']: - if 'clear' in geo_el: - global_clear_geo.append(geo_el['clear']) - - if global_clear_geo: - solid = [] - for apid in grb_obj.apertures: - if 'geometry' in grb_obj.apertures[apid]: - for elem in grb_obj.apertures[apid]['geometry']: - if 'solid' in elem: - solid_geo = deepcopy(elem['solid']) - for clear_geo in global_clear_geo: - # Make sure that the clear_geo is within the solid_geo otherwise we loose - # the solid_geometry. We want for clear_geometry just to cut into solid_geometry - # not to delete it - if clear_geo.within(solid_geo): - solid_geo = solid_geo.difference(clear_geo) - if solid_geo.is_empty: - solid_geo = elem['solid'] - try: - for poly in solid_geo: - solid.append(poly) - except TypeError: - solid.append(solid_geo) - poly_buff = deepcopy(MultiPolygon(solid)) - - follow_buf = unary_union(follow_buf) - - try: - poly_buff = poly_buff.buffer(0.0000001) - except ValueError: - pass - try: - poly_buff = poly_buff.buffer(-0.0000001) - except ValueError: - pass - - grb_obj.solid_geometry = deepcopy(poly_buff) - grb_obj.follow_geometry = deepcopy(follow_buf) - - with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)): - - ret = self.app.new_object('gerber', outname, obj_init, autoselected=False) - if ret == 'fail': - self.app.inform.emit('[ERROR_NOTCL] %s' % - _('Open PDF file failed.')) - return - # Register recent file - self.app.file_opened.emit('gerber', filename) - # GUI feedback - self.app.inform.emit('[success] %s: %s' % (_("Rendered"), outname)) - - def periodic_check(self, check_period): - """ - This function starts an QTimer and it will periodically check if parsing was done - - :param check_period: time at which to check periodically if all plots finished to be plotted - :return: - """ - - # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period)) - # self.plot_thread.start() - log.debug("ToolPDF --> Periodic Check started.") - - try: - self.check_thread.stop() - except TypeError: - pass - - self.check_thread.setInterval(check_period) - try: - self.check_thread.timeout.disconnect(self.periodic_check_handler) - except (TypeError, AttributeError): - pass - - self.check_thread.timeout.connect(self.periodic_check_handler) - self.check_thread.start(QtCore.QThread.HighPriority) - - def periodic_check_handler(self): - """ - If the parsing worker finished then start multithreaded rendering - :return: - """ - # log.debug("checking parsing --> %s" % str(self.parsing_promises)) - - try: - if not self.parsing_promises: - self.check_thread.stop() - # parsing finished start the layer rendering - if self.pdf_parsed: - obj_to_delete = [] - for object_name in self.pdf_parsed: - if self.app.abort_flag: - # graceful abort requested by the user - raise grace - - filename = deepcopy(self.pdf_parsed[object_name]['filename']) - pdf_content = deepcopy(self.pdf_parsed[object_name]['pdf']) - obj_to_delete.append(object_name) - for k in pdf_content: - if self.app.abort_flag: - # graceful abort requested by the user - raise grace - - ap_dict = pdf_content[k] - if ap_dict: - layer_nr = k - if k == 0: - self.app.worker_task.emit({'fcn': self.layer_rendering_as_excellon, - 'params': [filename, ap_dict, layer_nr]}) - else: - self.app.worker_task.emit({'fcn': self.layer_rendering_as_gerber, - 'params': [filename, ap_dict, layer_nr]}) - # delete the object already processed so it will not be processed again for other objects - # that were opened at the same time; like in drag & drop on GUI - for obj_name in obj_to_delete: - if obj_name in self.pdf_parsed: - self.pdf_parsed.pop(obj_name) - - log.debug("ToolPDF --> Periodic check finished.") - except Exception: - traceback.print_exc() - - def parse_pdf(self, pdf_content): path = {} - path['lines'] = [] # it's a list of lines subpaths - path['bezier'] = [] # it's a list of bezier arcs subpaths + path['lines'] = [] # it's a list of lines subpaths + path['bezier'] = [] # it's a list of bezier arcs subpaths path['rectangle'] = [] # it's a list of rectangle subpaths subpath = {} - subpath['lines'] = [] # it's a list of points - subpath['bezier'] = [] # it's a list of sublists each like this [start, c1, c2, stop] + subpath['lines'] = [] # it's a list of points + subpath['bezier'] = [] # it's a list of sublists each like this [start, c1, c2, stop] subpath['rectangle'] = [] # it's a list of sublists of points # store the start point (when 'm' command is encountered) @@ -503,7 +171,7 @@ class ToolPDF(FlatCAMTool): if match: color = [float(match.group(1)), float(match.group(2)), float(match.group(3))] log.debug( - "ToolPDF.parse_pdf() --> STROKE Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" % + "parse_pdf() --> STROKE Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" % (line_nr, color[0], color[1], color[2])) if color[0] == old_color[0] and color[1] == old_color[1] and color[2] == old_color[2]: @@ -526,7 +194,7 @@ class ToolPDF(FlatCAMTool): if match: fill_color = [float(match.group(1)), float(match.group(2)), float(match.group(3))] log.debug( - "ToolPDF.parse_pdf() --> FILL Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" % + "parse_pdf() --> FILL Color change on line: %s --> RED=%f GREEN=%f BLUE=%f" % (line_nr, fill_color[0], fill_color[1], fill_color[2])) # if the color is white we are seeing 'clear_geometry' that can't be seen. It may be that those # geometries are actually holes from which we can make an Excellon file @@ -545,7 +213,7 @@ class ToolPDF(FlatCAMTool): # sometimes they combine save_to_graphics_stack with the transformation on the same line if match.group(1) == 'q': log.debug( - "ToolPDF.parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" % + "parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" % (line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1])) self.gs['transform'].append(deepcopy([offset_geo, scale_geo])) @@ -555,7 +223,7 @@ class ToolPDF(FlatCAMTool): if (float(match.group(3)) == 0 and float(match.group(4)) == 0) and \ (float(match.group(6)) != 0 or float(match.group(7)) != 0): log.debug( - "ToolPDF.parse_pdf() --> OFFSET transformation found on line: %s --> %s" % (line_nr, pline)) + "parse_pdf() --> OFFSET transformation found on line: %s --> %s" % (line_nr, pline)) offset_geo[0] += float(match.group(6)) offset_geo[1] += float(match.group(7)) @@ -564,7 +232,7 @@ class ToolPDF(FlatCAMTool): # transformation = SCALING if float(match.group(2)) != 1 and float(match.group(5)) != 1: log.debug( - "ToolPDF.parse_pdf() --> SCALE transformation found on line: %s --> %s" % (line_nr, pline)) + "parse_pdf() --> SCALE transformation found on line: %s --> %s" % (line_nr, pline)) scale_geo[0] *= float(match.group(2)) scale_geo[1] *= float(match.group(5)) @@ -576,7 +244,7 @@ class ToolPDF(FlatCAMTool): match = self.save_gs_re.search(pline) if match: log.debug( - "ToolPDF.parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" % + "parse_pdf() --> Save to GS found on line: %s --> offset=[%f, %f] ||| scale=[%f, %f]" % (line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1])) self.gs['transform'].append(deepcopy([offset_geo, scale_geo])) self.gs['line_width'].append(deepcopy(size)) @@ -590,18 +258,18 @@ class ToolPDF(FlatCAMTool): scale_geo = restored_transform[1] except IndexError: # nothing to remove - log.debug("ToolPDF.parse_pdf() --> Nothing to restore") + log.debug("parse_pdf() --> Nothing to restore") pass try: size = self.gs['line_width'].pop(-1) except IndexError: - log.debug("ToolPDF.parse_pdf() --> Nothing to restore") + log.debug("parse_pdf() --> Nothing to restore") # nothing to remove pass log.debug( - "ToolPDF.parse_pdf() --> Restore from GS found on line: %s --> " + "parse_pdf() --> Restore from GS found on line: %s --> " "restored_offset=[%f, %f] ||| restored_scale=[%f, %f]" % (line_nr, offset_geo[0], offset_geo[1], scale_geo[0], scale_geo[1])) # log.debug("Restored Offset= [%f, %f]" % (offset_geo[0], offset_geo[1])) @@ -712,9 +380,9 @@ class ToolPDF(FlatCAMTool): width = (float(match.group(3)) + offset_geo[0]) * self.point_to_unit_factor * scale_geo[0] height = (float(match.group(4)) + offset_geo[1]) * self.point_to_unit_factor * scale_geo[1] pt1 = (x, y) - pt2 = (x+width, y) - pt3 = (x+width, y+height) - pt4 = (x, y+height) + pt2 = (x + width, y) + pt3 = (x + width, y + height) + pt4 = (x, y + height) subpath['rectangle'] += [pt1, pt2, pt3, pt4, pt1] current_point = pt1 continue @@ -948,7 +616,8 @@ class ToolPDF(FlatCAMTool): geo += self.bezier_to_points(start=b[0], c1=b[1], c2=b[2], stop=b[3]) # close the subpath if it was not closed already if close_subpath is False: - geo.append(geo[0]) + new_g = geo[0] + geo.append(new_g) try: geo_el = Polygon(geo).buffer(0.0000001, resolution=self.step_per_circles) path_geo.append(geo_el) diff --git a/flatcamParsers/ParseSVG.py b/AppParsers/ParseSVG.py similarity index 99% rename from flatcamParsers/ParseSVG.py rename to AppParsers/ParseSVG.py index a36e4770..713d50f1 100644 --- a/flatcamParsers/ParseSVG.py +++ b/AppParsers/ParseSVG.py @@ -27,7 +27,7 @@ from shapely.geometry import LineString, LinearRing, MultiLineString from shapely.affinity import skew, affine_transform, rotate import numpy as np -from flatcamParsers.ParseFont import * +from AppParsers.ParseFont import * log = logging.getLogger('base2') diff --git a/flatcamParsers/__init__.py b/AppParsers/__init__.py similarity index 100% rename from flatcamParsers/__init__.py rename to AppParsers/__init__.py diff --git a/FlatCAMPool.py b/AppPool.py similarity index 100% rename from FlatCAMPool.py rename to AppPool.py diff --git a/FlatCAMPostProc.py b/AppPreProcessor.py similarity index 93% rename from FlatCAMPostProc.py rename to AppPreProcessor.py index 15991d80..4ae297f7 100644 --- a/FlatCAMPostProc.py +++ b/AppPreProcessor.py @@ -19,10 +19,10 @@ log = logging.getLogger('base') preprocessors = {} -class ABCPostProcRegister(ABCMeta): +class ABCPreProcRegister(ABCMeta): # handles preprocessors registration on instantiation def __new__(cls, clsname, bases, attrs): - newclass = super(ABCPostProcRegister, cls).__new__(cls, clsname, bases, attrs) + newclass = super(ABCPreProcRegister, cls).__new__(cls, clsname, bases, attrs) if object not in bases: if newclass.__name__ in preprocessors: log.warning('Preprocessor %s has been overriden' % newclass.__name__) @@ -30,7 +30,7 @@ class ABCPostProcRegister(ABCMeta): return newclass -class FlatCAMPostProc(object, metaclass=ABCPostProcRegister): +class PreProc(object, metaclass=ABCPreProcRegister): @abstractmethod def start_code(self, p): pass @@ -76,7 +76,7 @@ class FlatCAMPostProc(object, metaclass=ABCPostProcRegister): pass -class FlatCAMPostProc_Tools(object, metaclass=ABCPostProcRegister): +class AppPreProcTools(object, metaclass=ABCPreProcRegister): @abstractmethod def start_code(self, p): pass diff --git a/FlatCAMProcess.py b/AppProcess.py similarity index 98% rename from FlatCAMProcess.py rename to AppProcess.py index e225f83e..313e8581 100644 --- a/FlatCAMProcess.py +++ b/AppProcess.py @@ -6,12 +6,12 @@ # MIT Licence # # ########################################################## -from flatcamGUI.FlatCAMGUI import FlatCAMActivityView +from AppGUI.GUIElements import FlatCAMActivityView from PyQt5 import QtCore import weakref import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') diff --git a/FlatCAMTool.py b/AppTool.py similarity index 69% rename from FlatCAMTool.py rename to AppTool.py index 3b7f8d0f..f288cdf0 100644 --- a/FlatCAMTool.py +++ b/AppTool.py @@ -6,13 +6,12 @@ # MIT Licence # # ########################################################## ## -from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets -from PyQt5.QtCore import Qt +from PyQt5 import QtCore, QtWidgets from shapely.geometry import Polygon, LineString import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -20,22 +19,23 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class FlatCAMTool(QtWidgets.QWidget): +class AppTool(QtWidgets.QWidget): toolName = "FlatCAM Generic Tool" def __init__(self, app, parent=None): """ - :param app: The application this tool will run in. - :type app: App - :param parent: Qt Parent - :return: FlatCAMTool + :param app: The application this tool will run in. + :type app: App_Main.App + :param parent: Qt Parent + :return: AppTool """ - self.app = app - self.decimals = app.decimals - QtWidgets.QWidget.__init__(self, parent) + + self.app = app + self.decimals = self.app.decimals + # self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) self.layout = QtWidgets.QVBoxLayout() @@ -87,10 +87,10 @@ class FlatCAMTool(QtWidgets.QWidget): if self.app.tool_tab_locked is True: return - # Remove anything else in the GUI + # Remove anything else in the AppGUI self.app.ui.tool_scroll_area.takeWidget() - # Put ourself in the GUI + # Put ourself in the AppGUI self.app.ui.tool_scroll_area.setWidget(self) # Switch notebook to tool page @@ -110,6 +110,11 @@ class FlatCAMTool(QtWidgets.QWidget): :return: """ + if 'shapes_storage' in kwargs: + s_storage = kwargs['shapes_storage'] + else: + s_storage = self.app.tool_shapes + if 'color' in kwargs: color = kwargs['color'] else: @@ -139,10 +144,9 @@ class FlatCAMTool(QtWidgets.QWidget): color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:] - self.app.tool_shapes.add(sel_rect, color=color, face_color=color_t, update=True, - layer=0, tolerance=None) + s_storage.add(sel_rect, color=color, face_color=color_t, update=True, layer=0, tolerance=None) if self.app.is_legacy is True: - self.app.tool_shapes.redraw() + s_storage.redraw() def draw_selection_shape_polygon(self, points, **kwargs): """ @@ -151,6 +155,12 @@ class FlatCAMTool(QtWidgets.QWidget): :param kwargs: :return: """ + + if 'shapes_storage' in kwargs: + s_storage = kwargs['shapes_storage'] + else: + s_storage = self.app.tool_shapes + if 'color' in kwargs: color = kwargs['color'] else: @@ -165,6 +175,7 @@ class FlatCAMTool(QtWidgets.QWidget): face_alpha = kwargs['face_alpha'] else: face_alpha = 0.3 + if len(points) < 3: sel_rect = LineString(points) else: @@ -175,14 +186,24 @@ class FlatCAMTool(QtWidgets.QWidget): color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:] - self.app.tool_shapes.add(sel_rect, color=color, face_color=color_t, update=True, - layer=0, tolerance=None) + s_storage.add(sel_rect, color=color, face_color=color_t, update=True, layer=0, tolerance=None) if self.app.is_legacy is True: - self.app.tool_shapes.redraw() + s_storage.redraw() - def delete_tool_selection_shape(self): - self.app.tool_shapes.clear() - self.app.tool_shapes.redraw() + def delete_tool_selection_shape(self, **kwargs): + """ + + :param kwargs: + :return: + """ + + if 'shapes_storage' in kwargs: + s_storage = kwargs['shapes_storage'] + else: + s_storage = self.app.tool_shapes + + s_storage.clear() + s_storage.redraw() def draw_moving_selection_shape_poly(self, points, data, **kwargs): """ @@ -192,6 +213,12 @@ class FlatCAMTool(QtWidgets.QWidget): :param kwargs: :return: """ + + if 'shapes_storage' in kwargs: + s_storage = kwargs['shapes_storage'] + else: + s_storage = self.app.move_tool.sel_shapes + if 'color' in kwargs: color = kwargs['color'] else: @@ -226,37 +253,49 @@ class FlatCAMTool(QtWidgets.QWidget): color_t_error = "#00000000" if geo.is_valid and not geo.is_empty: - self.app.move_tool.sel_shapes.add(geo, color=color, face_color=color_t, update=True, - layer=0, tolerance=None) + s_storage.add(geo, color=color, face_color=color_t, update=True, layer=0, tolerance=None) elif not geo.is_valid: - self.app.move_tool.sel_shapes.add(geo, color="red", face_color=color_t_error, update=True, - layer=0, tolerance=None) + s_storage.add(geo, color="red", face_color=color_t_error, update=True, layer=0, tolerance=None) if self.app.is_legacy is True: - self.app.move_tool.sel_shapes.redraw() + s_storage.redraw() - def delete_moving_selection_shape(self): - self.app.move_tool.sel_shapes.clear() - self.app.move_tool.sel_shapes.redraw() + def delete_moving_selection_shape(self, **kwargs): + """ + + :param kwargs: + :return: + """ + + if 'shapes_storage' in kwargs: + s_storage = kwargs['shapes_storage'] + else: + s_storage = self.app.move_tool.sel_shapes + + s_storage.clear() + s_storage.redraw() def confirmation_message(self, accepted, minval, maxval): if accepted is False: - self.app.inform.emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % - (_("Edited value is out of range"), self.decimals, minval, self.decimals, maxval)) + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"), + self.decimals, + minval, + self.decimals, + maxval), False) else: - self.app.inform.emit('[success] %s' % _("Edited value is within limits.")) + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) def confirmation_message_int(self, accepted, minval, maxval): if accepted is False: - self.app.inform.emit('[WARNING_NOTCL] %s: [%d, %d]' % - (_("Edited value is out of range"), minval, maxval)) + self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' % + (_("Edited value is out of range"), minval, maxval), False) else: - self.app.inform.emit('[success] %s' % _("Edited value is within limits.")) + self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False) def sizeHint(self): """ I've overloaded this just in case I will need to make changes in the future to enforce dimensions :return: """ - default_hint_size = super(FlatCAMTool, self).sizeHint() + default_hint_size = super(AppTool, self).sizeHint() return QtCore.QSize(default_hint_size.width(), default_hint_size.height()) diff --git a/flatcamTools/ToolAlignObjects.py b/AppTools/ToolAlignObjects.py similarity index 98% rename from flatcamTools/ToolAlignObjects.py rename to AppTools/ToolAlignObjects.py index 33b9611c..35e956c4 100644 --- a/flatcamTools/ToolAlignObjects.py +++ b/AppTools/ToolAlignObjects.py @@ -5,10 +5,10 @@ # MIT Licence # # ########################################################## -from PyQt5 import QtWidgets, QtGui, QtCore -from FlatCAMTool import FlatCAMTool +from PyQt5 import QtWidgets, QtCore +from AppTool import AppTool -from flatcamGUI.GUIElements import FCComboBox, RadioSet +from AppGUI.GUIElements import FCComboBox, RadioSet import math @@ -16,7 +16,7 @@ from shapely.geometry import Point from shapely.affinity import translate import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins import logging @@ -27,12 +27,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class AlignObjects(FlatCAMTool): +class AlignObjects(AppTool): toolName = _("Align Objects") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.decimals = app.decimals @@ -238,13 +238,13 @@ class AlignObjects(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Align Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+A', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+A', **kwargs) def set_tool_ui(self): self.reset_fields() diff --git a/flatcamTools/ToolCalculators.py b/AppTools/ToolCalculators.py similarity index 98% rename from flatcamTools/ToolCalculators.py rename to AppTools/ToolCalculators.py index 4b1bbbb1..08edc27d 100644 --- a/flatcamTools/ToolCalculators.py +++ b/AppTools/ToolCalculators.py @@ -6,12 +6,12 @@ # ########################################################## from PyQt5 import QtWidgets -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCEntry +from AppTool import AppTool +from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, FCEntry import math import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -19,7 +19,7 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class ToolCalculator(FlatCAMTool): +class ToolCalculator(AppTool): toolName = _("Calculators") v_shapeName = _("V-Shape Tool Calculator") @@ -27,7 +27,7 @@ class ToolCalculator(FlatCAMTool): eplateName = _("ElectroPlating Calculator") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.decimals = self.app.decimals @@ -292,14 +292,14 @@ class ToolCalculator(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Calc. Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+C', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+C', **kwargs) def set_tool_ui(self): self.units = self.app.defaults['units'].upper() diff --git a/flatcamTools/ToolCalibration.py b/AppTools/ToolCalibration.py similarity index 98% rename from flatcamTools/ToolCalibration.py rename to AppTools/ToolCalibration.py index 98b487cf..b808f00c 100644 --- a/flatcamTools/ToolCalibration.py +++ b/AppTools/ToolCalibration.py @@ -7,10 +7,10 @@ from PyQt5 import QtWidgets, QtCore, QtGui -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCDoubleSpinner, EvalEntry, FCCheckBox, OptionalInputSection, FCEntry -from flatcamGUI.GUIElements import FCTable, FCComboBox, RadioSet -from flatcamEditors.FlatCAMTextEditor import TextEditor +from AppTool import AppTool +from AppGUI.GUIElements import FCDoubleSpinner, EvalEntry, FCCheckBox, OptionalInputSection, FCEntry +from AppGUI.GUIElements import FCTable, FCComboBox, RadioSet +from AppEditors.FlatCAMTextEditor import TextEditor from shapely.geometry import Point from shapely.geometry.base import * @@ -22,7 +22,7 @@ import logging from copy import deepcopy import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -32,12 +32,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolCalibration(FlatCAMTool): +class ToolCalibration(AppTool): toolName = _("Calibration Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.canvas = self.app.plotcanvas @@ -752,14 +752,14 @@ class ToolCalibration(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Calibration Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+E', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+E', **kwargs) def set_tool_ui(self): self.units = self.app.defaults['units'].upper() @@ -1069,7 +1069,7 @@ class ToolCalibration(FlatCAMTool): # delete the absolute and relative position and messages in the infobar self.app.ui.position_label.setText("") - self.app.ui.rel_position_label.setText("") + # self.app.ui.rel_position_label.setText("") # first clear previous text in text editor (if any) self.gcode_editor_tab.code_editor.clear() @@ -1352,11 +1352,11 @@ class ToolCalibration(FlatCAMTool): try: if obj.kind.lower() == 'excellon': - self.app.new_object("excellon", str(obj_name), initialize_excellon) + self.app.app_obj.new_object("excellon", str(obj_name), initialize_excellon) elif obj.kind.lower() == 'gerber': - self.app.new_object("gerber", str(obj_name), initialize_gerber) + self.app.app_obj.new_object("gerber", str(obj_name), initialize_gerber) elif obj.kind.lower() == 'geometry': - self.app.new_object("geometry", str(obj_name), initialize_geometry) + self.app.app_obj.new_object("geometry", str(obj_name), initialize_geometry) except Exception as e: log.debug("ToolCalibration.new_calibrated_object() --> %s" % str(e)) return "Operation failed: %s" % str(e) diff --git a/flatcamTools/ToolCopperThieving.py b/AppTools/ToolCopperThieving.py similarity index 98% rename from flatcamTools/ToolCopperThieving.py rename to AppTools/ToolCopperThieving.py index d6fd310a..20e35cd0 100644 --- a/flatcamTools/ToolCopperThieving.py +++ b/AppTools/ToolCopperThieving.py @@ -7,9 +7,9 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMCommon import GracefulException as grace -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox +from camlib import grace +from AppTool import AppTool +from AppGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox import shapely.geometry.base as base from shapely.ops import cascaded_union, unary_union @@ -23,7 +23,7 @@ import numpy as np from collections import Iterable import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -33,13 +33,13 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolCopperThieving(FlatCAMTool): +class ToolCopperThieving(AppTool): work_finished = QtCore.pyqtSignal() toolName = _("Copper Thieving Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.canvas = self.app.plotcanvas @@ -561,14 +561,14 @@ class ToolCopperThieving(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Copper Thieving Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+F', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+J', **kwargs) def set_tool_ui(self): self.units = self.app.defaults['units'] @@ -910,16 +910,22 @@ class ToolCopperThieving(FlatCAMTool): edge_width=self.app.defaults["global_cursor_width"], size=self.app.defaults["global_cursor_size"]) - # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) if self.cursor_pos is None: self.cursor_pos = (0, 0) self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + + # # update the positions on status bar + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) # draw the utility geometry if self.first_click: @@ -1468,7 +1474,7 @@ class ToolCopperThieving(FlatCAMTool): obj_name, separatpr, obj_extension = self.sm_object.options['name'].rpartition('.') name = '%s_%s.%s' % (obj_name, 'plating_mask', obj_extension) - self.app.new_object('gerber', name, obj_init, autoselected=False) + self.app.app_obj.new_object('gerber', name, obj_init, autoselected=False) # Register recent file self.app.file_opened.emit("gerber", name) diff --git a/AppTools/ToolCorners.py b/AppTools/ToolCorners.py new file mode 100644 index 00000000..e91652b2 --- /dev/null +++ b/AppTools/ToolCorners.py @@ -0,0 +1,440 @@ +# ########################################################## +# FlatCAM: 2D Post-processing for Manufacturing # +# File Author: Marius Adrian Stanciu (c) # +# Date: 5/17/2020 # +# MIT Licence # +# ########################################################## + +from PyQt5 import QtWidgets, QtCore + +from AppTool import AppTool +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, FCButton + +from shapely.geometry import MultiPolygon, LineString + +from copy import deepcopy +import logging + +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + +log = logging.getLogger('base') + + +class ToolCorners(AppTool): + + toolName = _("Corner Markers Tool") + + def __init__(self, app): + AppTool.__init__(self, app) + + self.app = app + self.canvas = self.app.plotcanvas + + self.decimals = self.app.decimals + self.units = '' + + # ## Title + title_label = QtWidgets.QLabel("%s" % self.toolName) + title_label.setStyleSheet(""" + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) + self.layout.addWidget(title_label) + self.layout.addWidget(QtWidgets.QLabel('')) + + # Gerber object # + self.object_label = QtWidgets.QLabel('%s:' % _("GERBER")) + self.object_label.setToolTip( + _("The Gerber object that to which will be added corner markers.") + ) + self.object_combo = FCComboBox() + self.object_combo.setModel(self.app.collection) + self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.object_combo.is_last = True + self.object_combo.obj_type = "Gerber" + + self.layout.addWidget(self.object_label) + self.layout.addWidget(self.object_combo) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.layout.addWidget(separator_line) + + self.points_label = QtWidgets.QLabel('%s:' % _('Locations')) + self.points_label.setToolTip( + _("Locations where to place corner markers.") + ) + self.layout.addWidget(self.points_label) + + # BOTTOM LEFT + self.bl_cb = FCCheckBox(_("Bottom Left")) + self.layout.addWidget(self.bl_cb) + + # BOTTOM RIGHT + self.br_cb = FCCheckBox(_("Bottom Right")) + self.layout.addWidget(self.br_cb) + + # TOP LEFT + self.tl_cb = FCCheckBox(_("Top Left")) + self.layout.addWidget(self.tl_cb) + + # TOP RIGHT + self.tr_cb = FCCheckBox(_("Top Right")) + self.layout.addWidget(self.tr_cb) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.layout.addWidget(separator_line) + + # Toggle ALL + self.toggle_all_cb = FCCheckBox(_("Toggle ALL")) + self.layout.addWidget(self.toggle_all_cb) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.layout.addWidget(separator_line) + + # ## Grid Layout + grid_lay = QtWidgets.QGridLayout() + self.layout.addLayout(grid_lay) + grid_lay.setColumnStretch(0, 0) + grid_lay.setColumnStretch(1, 1) + + self.param_label = QtWidgets.QLabel('%s:' % _('Parameters')) + self.param_label.setToolTip( + _("Parameters used for this tool.") + ) + grid_lay.addWidget(self.param_label, 0, 0, 1, 2) + + # Thickness # + self.thick_label = QtWidgets.QLabel('%s:' % _("Thickness")) + self.thick_label.setToolTip( + _("The thickness of the line that makes the corner marker.") + ) + self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.thick_entry.set_range(0.0000, 9.9999) + self.thick_entry.set_precision(self.decimals) + self.thick_entry.setWrapping(True) + self.thick_entry.setSingleStep(10 ** -self.decimals) + + grid_lay.addWidget(self.thick_label, 1, 0) + grid_lay.addWidget(self.thick_entry, 1, 1) + + # Length # + self.l_label = QtWidgets.QLabel('%s:' % _("Length")) + self.l_label.setToolTip( + _("The length of the line that makes the corner marker.") + ) + self.l_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.l_entry.set_range(-9999.9999, 9999.9999) + self.l_entry.set_precision(self.decimals) + self.l_entry.setSingleStep(10 ** -self.decimals) + + grid_lay.addWidget(self.l_label, 2, 0) + grid_lay.addWidget(self.l_entry, 2, 1) + + # Margin # + self.margin_label = QtWidgets.QLabel('%s:' % _("Margin")) + self.margin_label.setToolTip( + _("Bounding box margin.") + ) + self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.margin_entry.set_range(-9999.9999, 9999.9999) + self.margin_entry.set_precision(self.decimals) + self.margin_entry.setSingleStep(0.1) + + grid_lay.addWidget(self.margin_label, 3, 0) + grid_lay.addWidget(self.margin_entry, 3, 1) + + separator_line_2 = QtWidgets.QFrame() + separator_line_2.setFrameShape(QtWidgets.QFrame.HLine) + separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken) + grid_lay.addWidget(separator_line_2, 4, 0, 1, 2) + + # ## Insert Corner Marker + self.add_marker_button = FCButton(_("Add Marker")) + self.add_marker_button.setToolTip( + _("Will add corner markers to the selected Gerber file.") + ) + self.add_marker_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + grid_lay.addWidget(self.add_marker_button, 11, 0, 1, 2) + + self.layout.addStretch() + + # ## Reset Tool + self.reset_button = QtWidgets.QPushButton(_("Reset Tool")) + self.reset_button.setToolTip( + _("Will reset the tool parameters.") + ) + self.reset_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.layout.addWidget(self.reset_button) + + # Objects involved in Copper thieving + self.grb_object = None + + # store the flattened geometry here: + self.flat_geometry = [] + + # Tool properties + self.fid_dia = None + + self.grb_steps_per_circle = self.app.defaults["gerber_circle_steps"] + + # SIGNALS + self.add_marker_button.clicked.connect(self.add_markers) + self.toggle_all_cb.toggled.connect(self.on_toggle_all) + + def run(self, toggle=True): + self.app.defaults.report_usage("ToolCorners()") + + if toggle: + # 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]) + else: + try: + if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName: + # if tab is populated with the tool but it does not have the focus, focus on it + if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab: + # focus on Tool Tab + self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) + else: + self.app.ui.splitter.setSizes([0, 1]) + except AttributeError: + pass + else: + if self.app.ui.splitter.sizes()[0] == 0: + self.app.ui.splitter.setSizes([1, 1]) + + AppTool.run(self) + + self.set_tool_ui() + + self.app.ui.notebook.setTabText(2, _("Corners Tool")) + + def install(self, icon=None, separator=None, **kwargs): + AppTool.install(self, icon, separator, shortcut='Alt+M', **kwargs) + + def set_tool_ui(self): + self.units = self.app.defaults['units'] + self.thick_entry.set_value(self.app.defaults["tools_corners_thickness"]) + self.l_entry.set_value(float(self.app.defaults["tools_corners_length"])) + self.margin_entry.set_value(float(self.app.defaults["tools_corners_margin"])) + self.toggle_all_cb.set_value(False) + + def on_toggle_all(self, val): + self.bl_cb.set_value(val) + self.br_cb.set_value(val) + self.tl_cb.set_value(val) + self.tr_cb.set_value(val) + + def add_markers(self): + self.app.call_source = "corners_tool" + tl_state = self.tl_cb.get_value() + tr_state = self.tr_cb.get_value() + bl_state = self.bl_cb.get_value() + br_state = self.br_cb.get_value() + + # get the Gerber object on which the corner marker will be inserted + selection_index = self.object_combo.currentIndex() + model_index = self.app.collection.index(selection_index, 0, self.object_combo.rootModelIndex()) + + try: + self.grb_object = model_index.internalPointer().obj + except Exception as e: + log.debug("ToolCorners.add_markers() --> %s" % str(e)) + self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ...")) + return + + xmin, ymin, xmax, ymax = self.grb_object.bounds() + points = {} + if tl_state: + points['tl'] = (xmin, ymax) + if tr_state: + points['tr'] = (xmax, ymax) + if bl_state: + points['bl'] = (xmin, ymin) + if br_state: + points['br'] = (xmax, ymin) + + self.add_corners_geo(points, g_obj=self.grb_object) + + self.grb_object.source_file = self.app.export_gerber(obj_name=self.grb_object.options['name'], + filename=None, + local_use=self.grb_object, use_thread=False) + self.on_exit() + + def add_corners_geo(self, points_storage, g_obj): + """ + Add geometry to the solid_geometry of the copper Gerber object + + :param points_storage: a dictionary holding the points where to add corners + :param g_obj: the Gerber object where to add the geometry + :return: None + """ + + line_thickness = self.thick_entry.get_value() + line_length = self.l_entry.get_value() + margin = self.margin_entry.get_value() + + geo_list = [] + + if not points_storage: + self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location")) + return + + for key in points_storage: + if key == 'tl': + pt = points_storage[key] + x = pt[0] - margin - line_thickness / 2.0 + y = pt[1] + margin + line_thickness / 2.0 + line_geo_hor = LineString([ + (x, y), (x + line_length, y) + ]) + line_geo_vert = LineString([ + (x, y), (x, y - line_length) + ]) + geo_list.append(line_geo_hor) + geo_list.append(line_geo_vert) + if key == 'tr': + pt = points_storage[key] + x = pt[0] + margin + line_thickness / 2.0 + y = pt[1] + margin + line_thickness / 2.0 + line_geo_hor = LineString([ + (x, y), (x - line_length, y) + ]) + line_geo_vert = LineString([ + (x, y), (x, y - line_length) + ]) + geo_list.append(line_geo_hor) + geo_list.append(line_geo_vert) + if key == 'bl': + pt = points_storage[key] + x = pt[0] - margin - line_thickness / 2.0 + y = pt[1] - margin - line_thickness / 2.0 + line_geo_hor = LineString([ + (x, y), (x + line_length, y) + ]) + line_geo_vert = LineString([ + (x, y), (x, y + line_length) + ]) + geo_list.append(line_geo_hor) + geo_list.append(line_geo_vert) + if key == 'br': + pt = points_storage[key] + x = pt[0] + margin + line_thickness / 2.0 + y = pt[1] - margin - line_thickness / 2.0 + line_geo_hor = LineString([ + (x, y), (x - line_length, y) + ]) + line_geo_vert = LineString([ + (x, y), (x, y + line_length) + ]) + geo_list.append(line_geo_hor) + geo_list.append(line_geo_vert) + + aperture_found = None + for ap_id, ap_val in g_obj.apertures.items(): + if ap_val['type'] == 'C' and ap_val['size'] == line_thickness: + aperture_found = ap_id + break + + geo_buff_list = [] + if aperture_found: + for geo in geo_list: + geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=2) + geo_buff_list.append(geo_buff) + + dict_el = {} + dict_el['follow'] = geo + dict_el['solid'] = geo_buff + g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el)) + else: + ap_keys = list(g_obj.apertures.keys()) + if ap_keys: + new_apid = str(int(max(ap_keys)) + 1) + else: + new_apid = '10' + + g_obj.apertures[new_apid] = {} + g_obj.apertures[new_apid]['type'] = 'C' + g_obj.apertures[new_apid]['size'] = line_thickness + g_obj.apertures[new_apid]['geometry'] = [] + + for geo in geo_list: + geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=3) + geo_buff_list.append(geo_buff) + + dict_el = {} + dict_el['follow'] = geo + dict_el['solid'] = geo_buff + g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el)) + + s_list = [] + if g_obj.solid_geometry: + try: + for poly in g_obj.solid_geometry: + s_list.append(poly) + except TypeError: + s_list.append(g_obj.solid_geometry) + + geo_buff_list = MultiPolygon(geo_buff_list) + geo_buff_list = geo_buff_list.buffer(0) + for poly in geo_buff_list: + s_list.append(poly) + g_obj.solid_geometry = MultiPolygon(s_list) + + def replot(self, obj, run_thread=True): + def worker_task(): + with self.app.proc_container.new('%s...' % _("Plotting")): + obj.plot() + + if run_thread: + self.app.worker_task.emit({'fcn': worker_task, 'params': []}) + else: + worker_task() + + def on_exit(self): + # plot the object + try: + self.replot(obj=self.grb_object) + except (AttributeError, TypeError): + return + + # update the bounding box values + try: + a, b, c, d = self.grb_object.bounds() + self.grb_object.options['xmin'] = a + self.grb_object.options['ymin'] = b + self.grb_object.options['xmax'] = c + self.grb_object.options['ymax'] = d + except Exception as e: + log.debug("ToolCorners.on_exit() copper_obj bounds error --> %s" % str(e)) + + # reset the variables + self.grb_object = None + + self.app.call_source = "app" + self.app.inform.emit('[success] %s' % _("Corners Tool exit.")) diff --git a/flatcamTools/ToolCutOut.py b/AppTools/ToolCutOut.py similarity index 97% rename from flatcamTools/ToolCutOut.py rename to AppTools/ToolCutOut.py index 67295b90..97a31975 100644 --- a/flatcamTools/ToolCutOut.py +++ b/AppTools/ToolCutOut.py @@ -6,8 +6,8 @@ # ########################################################## from PyQt5 import QtWidgets, QtGui, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, OptionalInputSection, FCButton +from AppTool import AppTool +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, OptionalInputSection, FCButton from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing from shapely.ops import cascaded_union, unary_union @@ -20,7 +20,7 @@ from copy import deepcopy import math import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -36,12 +36,12 @@ else: machinist_setting = 0 -class CutOut(FlatCAMTool): +class CutOut(AppTool): toolName = _("Cutout PCB") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.canvas = app.plotcanvas @@ -434,13 +434,13 @@ class CutOut(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Cutout Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+X', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+X', **kwargs) def set_tool_ui(self): self.reset_fields() @@ -501,6 +501,13 @@ class CutOut(FlatCAMTool): "tools_paintmethod": self.app.defaults["tools_paintmethod"], "tools_pathconnect": self.app.defaults["tools_pathconnect"], "tools_paintcontour": self.app.defaults["tools_paintcontour"], + + # Isolation Tool + "tools_iso_passes": self.app.defaults["tools_iso_passes"], + "tools_iso_overlap": self.app.defaults["tools_iso_overlap"], + "tools_iso_milling_type": self.app.defaults["tools_iso_milling_type"], + "tools_iso_follow": self.app.defaults["tools_iso_follow"], + "tools_iso_isotype": self.app.defaults["tools_iso_isotype"], }) def on_freeform_cutout(self): @@ -700,7 +707,7 @@ class CutOut(FlatCAMTool): geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value() outname = cutout_obj.options["name"] + "_cutout" - self.app.new_object('geometry', outname, geo_init) + self.app.app_obj.new_object('geometry', outname, geo_init) cutout_obj.plot() self.app.inform.emit('[success] %s' % _("Any form CutOut operation finished.")) @@ -896,7 +903,7 @@ class CutOut(FlatCAMTool): geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value() outname = cutout_obj.options["name"] + "_cutout" - ret = self.app.new_object('geometry', outname, geo_init) + ret = self.app.app_obj.new_object('geometry', outname, geo_init) if ret != 'fail': # cutout_obj.plot() @@ -912,7 +919,7 @@ class CutOut(FlatCAMTool): if 0 in {self.cutting_dia}: self.app.inform.emit('[ERROR_NOTCL] %s' % _("Tool Diameter is zero value. Change it to a positive real number.")) - return "Tool Diameter is zero value. Change it to a positive real number." + return self.cutting_gapsize = float(self.gapsize.get_value()) @@ -923,7 +930,7 @@ class CutOut(FlatCAMTool): except Exception as e: log.debug("CutOut.on_manual_cutout() --> %s" % str(e)) self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve Geometry object"), name)) - return "Could not retrieve object: %s" % name + return if self.app.is_legacy is False: self.app.plotcanvas.graph_event_disconnect('key_press', self.app.ui.keyPressEvent) @@ -1056,7 +1063,7 @@ class CutOut(FlatCAMTool): geo_obj.tools[1]['data']['depthperpass'] = self.maxdepth_entry.get_value() outname = cutout_obj.options["name"] + "_cutout" - self.app.new_object('geometry', outname, geo_init) + self.app.app_obj.new_object('geometry', outname, geo_init) def cutting_geo(self, pos): self.cutting_dia = float(self.dia.get_value()) @@ -1306,10 +1313,10 @@ class CutOut(FlatCAMTool): This only operates on the paths in the original geometry, i.e. it converts polygons into paths. - :param x0: x coord for lower left vertice of the polygon. - :param y0: y coord for lower left vertice of the polygon. - :param x1: x coord for upper right vertice of the polygon. - :param y1: y coord for upper right vertice of the polygon. + :param x0: x coord for lower left vertex of the polygon. + :param y0: y coord for lower left vertex of the polygon. + :param x1: x coord for upper right vertex of the polygon. + :param y1: y coord for upper right vertex of the polygon. :param solid_geo: Geometry from which to substract. If none, use the solid_geomety property of the object :return: none @@ -1367,6 +1374,7 @@ def flatten(geometry): def recursive_bounds(geometry): """ + Return the bounds of the biggest bounding box in geometry, one that include all. :param geometry: a iterable object that holds geometry :return: Returns coordinates of rectangular bounds of geometry: (xmin, ymin, xmax, ymax). diff --git a/flatcamTools/ToolDblSided.py b/AppTools/ToolDblSided.py similarity index 98% rename from flatcamTools/ToolDblSided.py rename to AppTools/ToolDblSided.py index 2aed9a3f..23bb781f 100644 --- a/flatcamTools/ToolDblSided.py +++ b/AppTools/ToolDblSided.py @@ -1,8 +1,8 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry, FCEntry, FCButton, FCComboBox +from AppTool import AppTool +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry, FCEntry, FCButton, FCComboBox from numpy import Inf @@ -11,7 +11,7 @@ from shapely import affinity import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -21,12 +21,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class DblSidedTool(FlatCAMTool): +class DblSidedTool(AppTool): toolName = _("2-Sided PCB") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.decimals = self.app.decimals # ## Title @@ -511,7 +511,7 @@ class DblSidedTool(FlatCAMTool): self.drill_values = "" def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+D', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+D', **kwargs) def run(self, toggle=True): self.app.defaults.report_usage("Tool2Sided()") @@ -535,7 +535,7 @@ class DblSidedTool(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("2-Sided Tool")) @@ -643,7 +643,7 @@ class DblSidedTool(FlatCAMTool): obj_inst.source_file = app_inst.export_excellon(obj_name=obj_inst.options['name'], local_use=obj_inst, filename=None, use_thread=False) - self.app.new_object("excellon", "Alignment Drills", obj_init) + self.app.app_obj.new_object("excellon", "Alignment Drills", obj_init) self.drill_values = '' self.app.inform.emit('[success] %s' % _("Excellon object with alignment drills created...")) @@ -686,7 +686,7 @@ class DblSidedTool(FlatCAMTool): py = 0.5 * (ymin + ymax) fcobj.mirror(axis, [px, py]) - self.app.object_changed.emit(fcobj) + self.app.app_obj.object_changed.emit(fcobj) fcobj.plot() self.app.inform.emit('[success] Gerber %s %s...' % (str(fcobj.options['name']), _("was mirrored"))) @@ -730,7 +730,7 @@ class DblSidedTool(FlatCAMTool): py = 0.5 * (ymin + ymax) fcobj.mirror(axis, [px, py]) - self.app.object_changed.emit(fcobj) + self.app.app_obj.object_changed.emit(fcobj) fcobj.plot() self.app.inform.emit('[success] Excellon %s %s...' % (str(fcobj.options['name']), _("was mirrored"))) @@ -767,7 +767,7 @@ class DblSidedTool(FlatCAMTool): py = 0.5 * (ymin + ymax) fcobj.mirror(axis, [px, py]) - self.app.object_changed.emit(fcobj) + self.app.app_obj.object_changed.emit(fcobj) fcobj.plot() self.app.inform.emit('[success] Geometry %s %s...' % (str(fcobj.options['name']), _("was mirrored"))) diff --git a/flatcamTools/ToolDistance.py b/AppTools/ToolDistance.py similarity index 91% rename from flatcamTools/ToolDistance.py rename to AppTools/ToolDistance.py index ea62c64e..6798ae8e 100644 --- a/flatcamTools/ToolDistance.py +++ b/AppTools/ToolDistance.py @@ -7,15 +7,15 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.VisPyVisuals import * -from flatcamGUI.GUIElements import FCEntry, FCButton, FCCheckBox +from AppTool import AppTool +from AppGUI.VisPyVisuals import * +from AppGUI.GUIElements import FCEntry, FCButton, FCCheckBox from shapely.geometry import Point, MultiLineString, Polygon -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate from camlib import FlatCAMRTreeStorage -from flatcamEditors.FlatCAMGeoEditor import DrawToolShape +from AppEditors.FlatCAMGeoEditor import DrawToolShape from copy import copy import math @@ -30,12 +30,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class Distance(FlatCAMTool): +class Distance(AppTool): toolName = _("Distance Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.decimals = self.app.decimals @@ -170,11 +170,13 @@ class Distance(FlatCAMTool): # store here if the snap button was clicked self.snap_toggled = None + self.mouse_is_dragging = False + # VisPy visuals if self.app.is_legacy is False: self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) else: - from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.sel_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='measurement') self.measure_btn.clicked.connect(self.activate_measure_tool) @@ -206,13 +208,13 @@ class Distance(FlatCAMTool): self.deactivate_measure_tool() def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Ctrl+M', **kwargs) + AppTool.install(self, icon, separator, shortcut='Ctrl+M', **kwargs) def set_tool_ui(self): - # Remove anything else in the GUI + # Remove anything else in the AppGUI self.app.ui.tool_scroll_area.takeWidget() - # Put ourselves in the GUI + # Put ourselves in the AppGUI self.app.ui.tool_scroll_area.setWidget(self) # Switch notebook to tool page @@ -392,12 +394,16 @@ class Distance(FlatCAMTool): # are used for panning on the canvas log.debug("Distance Tool --> mouse click release") - if event.button == 1: - if self.app.is_legacy is False: - event_pos = event.pos - else: - event_pos = (event.xdata, event.ydata) + if self.app.is_legacy is False: + event_pos = event.pos + right_button = 2 + event_is_dragging = self.mouse_is_dragging + else: + event_pos = (event.xdata, event.ydata) + right_button = 3 + event_is_dragging = self.app.plotcanvas.is_dragging + if event.button == 1: pos_canvas = self.canvas.translate_coords(event_pos) if self.snap_center_cb.get_value() is False: @@ -469,15 +475,18 @@ class Distance(FlatCAMTool): # Reset here the relative coordinates so there is a new reference on the click position if self.rel_point1 is None: - self.app.ui.rel_position_label.setText("Dx: %.*f   Dy: " - "%.*f    " % - (self.decimals, 0.0, self.decimals, 0.0)) + # self.app.ui.rel_position_label.setText("Dx: %.*f   Dy: " + # "%.*f    " % + # (self.decimals, 0.0, self.decimals, 0.0)) self.rel_point1 = pos else: self.rel_point2 = copy(self.rel_point1) self.rel_point1 = pos self.calculate_distance(pos=pos) + elif event.button == right_button and event_is_dragging is False: + self.deactivate_measure_tool() + self.app.inform.emit(_("Distance Tool cancelled.")) def calculate_distance(self, pos): if len(self.points) == 1: @@ -510,11 +519,11 @@ class Distance(FlatCAMTool): pass self.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d))) - self.app.ui.rel_position_label.setText( - "Dx: {}   Dy: {}    ".format( - '%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1]) - ) - ) + # self.app.ui.rel_position_label.setText( + # "Dx: {}   Dy: {}    ".format( + # '%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1]) + # ) + # ) self.tool_done = True self.deactivate_measure_tool() @@ -522,6 +531,7 @@ class Distance(FlatCAMTool): try: # May fail in case mouse not within axes if self.app.is_legacy is False: event_pos = event.pos + self.mouse_is_dragging = event.is_dragging else: event_pos = (event.xdata, event.ydata) @@ -550,6 +560,11 @@ class Distance(FlatCAMTool): ) ) + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + 0.0000, units, 0.0000, units, pos[0], units, pos[1], units) + if self.rel_point1 is not None: dx = pos[0] - float(self.rel_point1[0]) dy = pos[1] - float(self.rel_point1[1]) @@ -557,11 +572,11 @@ class Distance(FlatCAMTool): dx = pos[0] dy = pos[1] - self.app.ui.rel_position_label.setText( - "Dx: {}   Dy: {}    ".format( - '%.*f' % (self.decimals, dx), '%.*f' % (self.decimals, dy) - ) - ) + # self.app.ui.rel_position_label.setText( + # "Dx: {}   Dy: {}    ".format( + # '%.*f' % (self.decimals, dx), '%.*f' % (self.decimals, dy) + # ) + # ) # update utility geometry if len(self.points) == 1: @@ -578,7 +593,7 @@ class Distance(FlatCAMTool): except Exception as e: log.debug("Distance.on_mouse_move_meas() --> %s" % str(e)) self.app.ui.position_label.setText("") - self.app.ui.rel_position_label.setText("") + # self.app.ui.rel_position_label.setText("") def utility_geometry(self, pos): # first delete old shape diff --git a/flatcamTools/ToolDistanceMin.py b/AppTools/ToolDistanceMin.py similarity index 97% rename from flatcamTools/ToolDistanceMin.py rename to AppTools/ToolDistanceMin.py index dc4369ea..0e932863 100644 --- a/flatcamTools/ToolDistanceMin.py +++ b/AppTools/ToolDistanceMin.py @@ -6,9 +6,8 @@ # ########################################################## from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.VisPyVisuals import * -from flatcamGUI.GUIElements import FCEntry +from AppTool import AppTool +from AppGUI.GUIElements import FCEntry from shapely.ops import nearest_points from shapely.geometry import Point, MultiPolygon @@ -17,7 +16,7 @@ from shapely.ops import cascaded_union import math import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -27,12 +26,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class DistanceMin(FlatCAMTool): +class DistanceMin(AppTool): toolName = _("Minimum Distance Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.canvas = self.app.plotcanvas @@ -155,13 +154,13 @@ class DistanceMin(FlatCAMTool): _("Select two objects and no more, to measure the distance between them ...")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Shift+M', **kwargs) + AppTool.install(self, icon, separator, shortcut='Shift+M', **kwargs) def set_tool_ui(self): - # Remove anything else in the GUI + # Remove anything else in the AppGUI self.app.ui.tool_scroll_area.takeWidget() - # Put oneself in the GUI + # Put oneself in the AppGUI self.app.ui.tool_scroll_area.setWidget(self) # Switch notebook to tool page diff --git a/AppTools/ToolEtchCompensation.py b/AppTools/ToolEtchCompensation.py new file mode 100644 index 00000000..9318ca39 --- /dev/null +++ b/AppTools/ToolEtchCompensation.py @@ -0,0 +1,449 @@ +# ########################################################## +# FlatCAM: 2D Post-processing for Manufacturing # +# File Author: Marius Adrian Stanciu (c) # +# Date: 2/14/2020 # +# MIT Licence # +# ########################################################## + +from PyQt5 import QtWidgets, QtCore + +from AppTool import AppTool +from AppGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox, NumericalEvalEntry, FCEntry + +from shapely.ops import unary_union + +from copy import deepcopy +import math + +import logging +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + +log = logging.getLogger('base') + + +class ToolEtchCompensation(AppTool): + + toolName = _("Etch Compensation Tool") + + def __init__(self, app): + self.app = app + self.decimals = self.app.decimals + + AppTool.__init__(self, app) + + self.tools_frame = QtWidgets.QFrame() + self.tools_frame.setContentsMargins(0, 0, 0, 0) + self.layout.addWidget(self.tools_frame) + self.tools_box = QtWidgets.QVBoxLayout() + self.tools_box.setContentsMargins(0, 0, 0, 0) + self.tools_frame.setLayout(self.tools_box) + + # Title + title_label = QtWidgets.QLabel("%s" % self.toolName) + title_label.setStyleSheet(""" + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) + self.tools_box.addWidget(title_label) + + # Grid Layout + grid0 = QtWidgets.QGridLayout() + grid0.setColumnStretch(0, 0) + grid0.setColumnStretch(1, 1) + self.tools_box.addLayout(grid0) + + grid0.addWidget(QtWidgets.QLabel(''), 0, 0, 1, 2) + + # Target Gerber Object + self.gerber_combo = FCComboBox() + self.gerber_combo.setModel(self.app.collection) + self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.gerber_combo.is_last = True + self.gerber_combo.obj_type = "Gerber" + + self.gerber_label = QtWidgets.QLabel('%s:' % _("GERBER")) + self.gerber_label.setToolTip( + _("Gerber object that will be inverted.") + ) + + grid0.addWidget(self.gerber_label, 1, 0, 1, 2) + grid0.addWidget(self.gerber_combo, 2, 0, 1, 2) + + grid0.addWidget(QtWidgets.QLabel(""), 3, 0, 1, 2) + + self.util_label = QtWidgets.QLabel("%s:" % _("Utilities")) + self.util_label.setToolTip('%s.' % _("Conversion utilities")) + + grid0.addWidget(self.util_label, 4, 0, 1, 2) + + # Oz to um conversion + self.oz_um_label = QtWidgets.QLabel('%s:' % _('Oz to Microns')) + self.oz_um_label.setToolTip( + _("Will convert from oz thickness to microns [um].\n" + "Can use formulas with operators: /, *, +, -, %, .\n" + "The real numbers use the dot decimals separator.") + ) + grid0.addWidget(self.oz_um_label, 5, 0, 1, 2) + + hlay_1 = QtWidgets.QHBoxLayout() + + self.oz_entry = NumericalEvalEntry(border_color='#0069A9') + self.oz_entry.setPlaceholderText(_("Oz value")) + self.oz_to_um_entry = FCEntry() + self.oz_to_um_entry.setPlaceholderText(_("Microns value")) + self.oz_to_um_entry.setReadOnly(True) + + hlay_1.addWidget(self.oz_entry) + hlay_1.addWidget(self.oz_to_um_entry) + grid0.addLayout(hlay_1, 6, 0, 1, 2) + + # Mils to um conversion + self.mils_um_label = QtWidgets.QLabel('%s:' % _('Mils to Microns')) + self.mils_um_label.setToolTip( + _("Will convert from mils to microns [um].\n" + "Can use formulas with operators: /, *, +, -, %, .\n" + "The real numbers use the dot decimals separator.") + ) + grid0.addWidget(self.mils_um_label, 7, 0, 1, 2) + + hlay_2 = QtWidgets.QHBoxLayout() + + self.mils_entry = NumericalEvalEntry(border_color='#0069A9') + self.mils_entry.setPlaceholderText(_("Mils value")) + self.mils_to_um_entry = FCEntry() + self.mils_to_um_entry.setPlaceholderText(_("Microns value")) + self.mils_to_um_entry.setReadOnly(True) + + hlay_2.addWidget(self.mils_entry) + hlay_2.addWidget(self.mils_to_um_entry) + grid0.addLayout(hlay_2, 8, 0, 1, 2) + + grid0.addWidget(QtWidgets.QLabel(""), 9, 0, 1, 2) + + self.param_label = QtWidgets.QLabel("%s:" % _("Parameters")) + self.param_label.setToolTip('%s.' % _("Parameters for this tool")) + + grid0.addWidget(self.param_label, 10, 0, 1, 2) + + # Thickness + self.thick_label = QtWidgets.QLabel('%s:' % _('Copper Thickness')) + self.thick_label.setToolTip( + _("The thickness of the copper foil.\n" + "In microns [um].") + ) + self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.thick_entry.set_precision(self.decimals) + self.thick_entry.set_range(0.0000, 9999.9999) + self.thick_entry.setObjectName(_("Thickness")) + + grid0.addWidget(self.thick_label, 12, 0) + grid0.addWidget(self.thick_entry, 12, 1) + + self.ratio_label = QtWidgets.QLabel('%s:' % _("Ratio")) + self.ratio_label.setToolTip( + _("The ratio of lateral etch versus depth etch.\n" + "Can be:\n" + "- custom -> the user will enter a custom value\n" + "- preselection -> value which depends on a selection of etchants") + ) + self.ratio_radio = RadioSet([ + {'label': _('Etch Factor'), 'value': 'factor'}, + {'label': _('Etchants list'), 'value': 'etch_list'}, + {'label': _('Manual offset'), 'value': 'manual'} + ], orientation='vertical', stretch=False) + + grid0.addWidget(self.ratio_label, 14, 0, 1, 2) + grid0.addWidget(self.ratio_radio, 16, 0, 1, 2) + + # Etchants + self.etchants_label = QtWidgets.QLabel('%s:' % _('Etchants')) + self.etchants_label.setToolTip( + _("A list of etchants.") + ) + self.etchants_combo = FCComboBox(callback=self.confirmation_message) + self.etchants_combo.setObjectName(_("Etchants")) + self.etchants_combo.addItems(["CuCl2", "Fe3Cl"]) + + grid0.addWidget(self.etchants_label, 18, 0) + grid0.addWidget(self.etchants_combo, 18, 1) + + # Etch Factor + self.factor_label = QtWidgets.QLabel('%s:' % _('Etch factor')) + self.factor_label.setToolTip( + _("The ratio between depth etch and lateral etch .\n" + "Accepts real numbers and formulas using the operators: /,*,+,-,%") + ) + self.factor_entry = NumericalEvalEntry(border_color='#0069A9') + self.factor_entry.setPlaceholderText(_("Real number or formula")) + self.factor_entry.setObjectName(_("Etch_factor")) + + grid0.addWidget(self.factor_label, 19, 0) + grid0.addWidget(self.factor_entry, 19, 1) + + # Manual Offset + self.offset_label = QtWidgets.QLabel('%s:' % _('Offset')) + self.offset_label.setToolTip( + _("Value with which to increase or decrease (buffer)\n" + "the copper features. In microns [um].") + ) + self.offset_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.offset_entry.set_precision(self.decimals) + self.offset_entry.set_range(-9999.9999, 9999.9999) + self.offset_entry.setObjectName(_("Offset")) + + grid0.addWidget(self.offset_label, 20, 0) + grid0.addWidget(self.offset_entry, 20, 1) + + # Hide the Etchants and Etch factor + self.etchants_label.hide() + self.etchants_combo.hide() + self.factor_label.hide() + self.factor_entry.hide() + self.offset_label.hide() + self.offset_entry.hide() + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 22, 0, 1, 2) + + self.compensate_btn = FCButton(_('Compensate')) + self.compensate_btn.setToolTip( + _("Will increase the copper features thickness to compensate the lateral etch.") + ) + self.compensate_btn.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + grid0.addWidget(self.compensate_btn, 24, 0, 1, 2) + + self.tools_box.addStretch() + + # ## Reset Tool + self.reset_button = QtWidgets.QPushButton(_("Reset Tool")) + self.reset_button.setToolTip( + _("Will reset the tool parameters.") + ) + self.reset_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.tools_box.addWidget(self.reset_button) + + self.compensate_btn.clicked.connect(self.on_compensate) + self.reset_button.clicked.connect(self.set_tool_ui) + self.ratio_radio.activated_custom.connect(self.on_ratio_change) + + self.oz_entry.textChanged.connect(self.on_oz_conversion) + self.mils_entry.textChanged.connect(self.on_mils_conversion) + + def install(self, icon=None, separator=None, **kwargs): + AppTool.install(self, icon, separator, shortcut='', **kwargs) + + def run(self, toggle=True): + self.app.defaults.report_usage("ToolEtchCompensation()") + log.debug("ToolEtchCompensation() is running ...") + + if toggle: + # 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]) + else: + try: + if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName: + # if tab is populated with the tool but it does not have the focus, focus on it + if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab: + # focus on Tool Tab + self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) + else: + self.app.ui.splitter.setSizes([0, 1]) + except AttributeError: + pass + else: + if self.app.ui.splitter.sizes()[0] == 0: + self.app.ui.splitter.setSizes([1, 1]) + + AppTool.run(self) + self.set_tool_ui() + + self.app.ui.notebook.setTabText(2, _("Etch Compensation Tool")) + + def set_tool_ui(self): + self.thick_entry.set_value(18.0) + self.ratio_radio.set_value('factor') + + def on_ratio_change(self, val): + """ + Called on activated_custom signal of the RadioSet GUI element self.radio_ratio + + :param val: 'c' or 'p': 'c' means custom factor and 'p' means preselected etchants + :type val: str + :return: None + :rtype: + """ + if val == 'factor': + self.etchants_label.hide() + self.etchants_combo.hide() + self.factor_label.show() + self.factor_entry.show() + self.offset_label.hide() + self.offset_entry.hide() + elif val == 'etch_list': + self.etchants_label.show() + self.etchants_combo.show() + self.factor_label.hide() + self.factor_entry.hide() + self.offset_label.hide() + self.offset_entry.hide() + else: + self.etchants_label.hide() + self.etchants_combo.hide() + self.factor_label.hide() + self.factor_entry.hide() + self.offset_label.show() + self.offset_entry.show() + + def on_oz_conversion(self, txt): + try: + val = eval(txt) + # oz thickness to mils by multiplying with 1.37 + # mils to microns by multiplying with 25.4 + val *= 34.798 + except Exception: + self.oz_to_um_entry.set_value('') + return + self.oz_to_um_entry.set_value(val, self.decimals) + + def on_mils_conversion(self, txt): + try: + val = eval(txt) + val *= 25.4 + except Exception: + self.mils_to_um_entry.set_value('') + return + self.mils_to_um_entry.set_value(val, self.decimals) + + def on_compensate(self): + log.debug("ToolEtchCompensation.on_compensate()") + + ratio_type = self.ratio_radio.get_value() + thickness = self.thick_entry.get_value() / 1000 # in microns + + grb_circle_steps = int(self.app.defaults["gerber_circle_steps"]) + obj_name = self.gerber_combo.currentText() + + outname = obj_name + "_comp" + + # Get source object. + try: + grb_obj = self.app.collection.get_by_name(obj_name) + except Exception as e: + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name))) + return "Could not retrieve object: %s with error: %s" % (obj_name, str(e)) + + if grb_obj is None: + if obj_name == '': + obj_name = 'None' + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name))) + return + + if ratio_type == 'factor': + etch_factor = 1 / self.factor_entry.get_value() + offset = thickness / etch_factor + elif ratio_type == 'etch_list': + etchant = self.etchants_combo.get_value() + if etchant == "CuCl2": + etch_factor = 0.33 + else: + etch_factor = 0.25 + offset = thickness / etch_factor + else: + offset = self.offset_entry.get_value() / 1000 # in microns + + try: + __ = iter(grb_obj.solid_geometry) + except TypeError: + grb_obj.solid_geometry = list(grb_obj.solid_geometry) + + new_solid_geometry = [] + + for poly in grb_obj.solid_geometry: + new_solid_geometry.append(poly.buffer(offset, int(grb_circle_steps))) + new_solid_geometry = unary_union(new_solid_geometry) + + new_options = {} + for opt in grb_obj.options: + new_options[opt] = deepcopy(grb_obj.options[opt]) + + new_apertures = deepcopy(grb_obj.apertures) + + # update the apertures attributes (keys in the apertures dict) + for ap in new_apertures: + type = new_apertures[ap]['type'] + for k in new_apertures[ap]: + if type == 'R' or type == 'O': + if k == 'width' or k == 'height': + new_apertures[ap][k] += offset + else: + if k == 'size' or k == 'width' or k == 'height': + new_apertures[ap][k] += offset + + if k == 'geometry': + for geo_el in new_apertures[ap][k]: + if 'solid' in geo_el: + geo_el['solid'] = geo_el['solid'].buffer(offset, int(grb_circle_steps)) + + # in case of 'R' or 'O' aperture type we need to update the aperture 'size' after + # the 'width' and 'height' keys were updated + for ap in new_apertures: + type = new_apertures[ap]['type'] + for k in new_apertures[ap]: + if type == 'R' or type == 'O': + if k == 'size': + new_apertures[ap][k] = math.sqrt( + new_apertures[ap]['width'] ** 2 + new_apertures[ap]['height'] ** 2) + + def init_func(new_obj, app_obj): + """ + Init a new object in FlatCAM Object collection + + :param new_obj: New object + :type new_obj: ObjectCollection + :param app_obj: App + :type app_obj: App_Main.App + :return: None + :rtype: + """ + new_obj.options.update(new_options) + new_obj.options['name'] = outname + new_obj.fill_color = deepcopy(grb_obj.fill_color) + new_obj.outline_color = deepcopy(grb_obj.outline_color) + + new_obj.apertures = deepcopy(new_apertures) + + new_obj.solid_geometry = deepcopy(new_solid_geometry) + new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None, + local_use=new_obj, use_thread=False) + + self.app.app_obj.new_object('gerber', outname, init_func) + + def reset_fields(self): + self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + + @staticmethod + def poly2rings(poly): + return [poly.exterior] + [interior for interior in poly.interiors] +# end of file diff --git a/flatcamTools/ToolExtractDrills.py b/AppTools/ToolExtractDrills.py similarity index 98% rename from flatcamTools/ToolExtractDrills.py rename to AppTools/ToolExtractDrills.py index 2c127c2b..71e8fb41 100644 --- a/flatcamTools/ToolExtractDrills.py +++ b/AppTools/ToolExtractDrills.py @@ -7,14 +7,14 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox +from AppTool import AppTool +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox from shapely.geometry import Point import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -24,12 +24,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolExtractDrills(FlatCAMTool): +class ToolExtractDrills(AppTool): toolName = _("Extract Drills") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.decimals = self.app.decimals # ## Title @@ -363,7 +363,7 @@ class ToolExtractDrills(FlatCAMTool): ) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+I', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+I', **kwargs) def run(self, toggle=True): self.app.defaults.report_usage("Extract Drills()") @@ -387,7 +387,7 @@ class ToolExtractDrills(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Extract Drills Tool")) @@ -655,7 +655,7 @@ class ToolExtractDrills(FlatCAMTool): obj_inst.source_file = self.app.export_excellon(obj_name=outname, local_use=obj_inst, filename=None, use_thread=False) - self.app.new_object("excellon", outname, obj_init) + self.app.app_obj.new_object("excellon", outname, obj_init) def on_hole_size_toggle(self, val): if val == "fixed": diff --git a/flatcamTools/ToolFiducials.py b/AppTools/ToolFiducials.py similarity index 97% rename from flatcamTools/ToolFiducials.py rename to AppTools/ToolFiducials.py index 1b3513d4..b19add63 100644 --- a/flatcamTools/ToolFiducials.py +++ b/AppTools/ToolFiducials.py @@ -7,8 +7,8 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable, FCComboBox +from AppTool import AppTool +from AppGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable, FCComboBox from shapely.geometry import Point, Polygon, MultiPolygon, LineString from shapely.geometry import box as box @@ -18,7 +18,7 @@ import logging from copy import deepcopy import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -28,12 +28,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolFiducials(FlatCAMTool): +class ToolFiducials(AppTool): toolName = _("Fiducials Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.canvas = self.app.plotcanvas @@ -234,7 +234,7 @@ class ToolFiducials(FlatCAMTool): # Line Thickness # self.line_thickness_label = QtWidgets.QLabel('%s:' % _("Line thickness")) self.line_thickness_label.setToolTip( - _("Bounding box margin.") + _("Thickness of the line that makes the fiducial.") ) self.line_thickness_entry = FCDoubleSpinner(callback=self.confirmation_message) self.line_thickness_entry.set_range(0.00001, 9999.9999) @@ -353,7 +353,8 @@ class ToolFiducials(FlatCAMTool): self.margin_val = None self.sec_position = None - self.geo_steps_per_circle = 128 + + self.grb_steps_per_circle = self.app.defaults["gerber_circle_steps"] self.click_points = [] @@ -388,14 +389,14 @@ class ToolFiducials(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Fiducials Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+J', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+F', **kwargs) def set_tool_ui(self): self.units = self.app.defaults['units'] @@ -471,7 +472,7 @@ class ToolFiducials(FlatCAMTool): if self.mode_method == 'auto': xmin, ymin, xmax, ymax = self.grb_object.bounds() bbox = box(xmin, ymin, xmax, ymax) - buf_bbox = bbox.buffer(self.margin_val, join_style=2) + buf_bbox = bbox.buffer(self.margin_val, self.grb_steps_per_circle, join_style=2) x0, y0, x1, y1 = buf_bbox.bounds self.click_points.append( @@ -539,7 +540,7 @@ class ToolFiducials(FlatCAMTool): radius = fid_size / 2.0 if fid_type == 'circular': - geo_list = [Point(pt).buffer(radius) for pt in points_list] + geo_list = [Point(pt).buffer(radius, self.grb_steps_per_circle) for pt in points_list] aperture_found = None for ap_id, ap_val in g_obj.apertures.items(): @@ -604,8 +605,8 @@ class ToolFiducials(FlatCAMTool): geo_buff_list = [] if aperture_found: for geo in geo_list: - geo_buff_h = geo[0].buffer(line_thickness / 2.0) - geo_buff_v = geo[1].buffer(line_thickness / 2.0) + geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle) + geo_buff_v = geo[1].buffer(line_thickness / 2.0, self.grb_steps_per_circle) geo_buff_list.append(geo_buff_h) geo_buff_list.append(geo_buff_v) @@ -629,8 +630,8 @@ class ToolFiducials(FlatCAMTool): g_obj.apertures[new_apid]['geometry'] = [] for geo in geo_list: - geo_buff_h = geo[0].buffer(line_thickness / 2.0) - geo_buff_v = geo[1].buffer(line_thickness / 2.0) + geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle) + geo_buff_v = geo[1].buffer(line_thickness / 2.0, self.grb_steps_per_circle) geo_buff_list.append(geo_buff_h) geo_buff_list.append(geo_buff_v) diff --git a/flatcamTools/ToolFilm.py b/AppTools/ToolFilm.py similarity index 94% rename from flatcamTools/ToolFilm.py rename to AppTools/ToolFilm.py index f386f0e8..9da564da 100644 --- a/flatcamTools/ToolFilm.py +++ b/AppTools/ToolFilm.py @@ -5,10 +5,10 @@ # MIT Licence # # ########################################################## -from PyQt5 import QtGui, QtCore, QtWidgets +from PyQt5 import QtCore, QtWidgets -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \ +from AppTool import AppTool +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \ OptionalHideInputSection, OptionalInputSection, FCComboBox, FCFileSaveDialog from copy import deepcopy @@ -27,7 +27,7 @@ from lxml import etree as ET from io import StringIO import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -37,12 +37,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class Film(FlatCAMTool): +class Film(AppTool): toolName = _("Film PCB") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.decimals = self.app.decimals @@ -65,13 +65,8 @@ class Film(FlatCAMTool): grid0.setColumnStretch(1, 1) # Type of object for which to create the film - self.tf_type_obj_combo = FCComboBox() - self.tf_type_obj_combo.addItems([_("Gerber"), _("Geometry")]) - - # we get rid of item1 ("Excellon") as it is not suitable for creating film - # self.tf_type_obj_combo.view().setRowHidden(1, True) - self.tf_type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png")) - self.tf_type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png")) + self.tf_type_obj_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'}, + {'label': _('Geometry'), 'value': 'geo'}]) self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type")) self.tf_type_obj_combo_label.setToolTip( @@ -89,26 +84,12 @@ class Film(FlatCAMTool): self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) self.tf_object_combo.is_last = True - self.tf_object_label = QtWidgets.QLabel('%s:' % _("Film Object")) - self.tf_object_label.setToolTip( - _("Object for which to create the film.") - ) - grid0.addWidget(self.tf_object_label, 1, 0) - grid0.addWidget(self.tf_object_combo, 1, 1) + grid0.addWidget(self.tf_object_combo, 1, 0, 1, 2) # Type of Box Object to be used as an envelope for film creation # Within this we can create negative - self.tf_type_box_combo = FCComboBox() - self.tf_type_box_combo.addItems([_("Gerber"), _("Geometry")]) - - # self.tf_type_box_combo.addItem("Gerber") - # self.tf_type_box_combo.addItem("Excellon") - # self.tf_type_box_combo.addItem("Geometry") - - # we get rid of item1 ("Excellon") as it is not suitable for box when creating film - # self.tf_type_box_combo.view().setRowHidden(1, True) - self.tf_type_box_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png")) - self.tf_type_box_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png")) + self.tf_type_box_combo = RadioSet([{'label': _('Gerber'), 'value': 'grb'}, + {'label': _('Geometry'), 'value': 'geo'}]) self.tf_type_box_combo_label = QtWidgets.QLabel(_("Box Type:")) self.tf_type_box_combo_label.setToolTip( @@ -126,17 +107,19 @@ class Film(FlatCAMTool): self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) self.tf_box_combo.is_last = True - self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object")) - self.tf_box_combo_label.setToolTip( - _("The actual object that is used as 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.") - ) - grid0.addWidget(self.tf_box_combo_label, 3, 0) - grid0.addWidget(self.tf_box_combo, 3, 1) + # self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object")) + # self.tf_box_combo_label.setToolTip( + # _("The actual object that is used as 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.") + # ) + grid0.addWidget(self.tf_box_combo, 3, 0, 1, 2) - grid0.addWidget(QtWidgets.QLabel(''), 4, 0) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 4, 0, 1, 2) self.film_adj_label = QtWidgets.QLabel('%s' % _("Film Adjustments")) self.film_adj_label.setToolTip( @@ -533,28 +516,28 @@ class Film(FlatCAMTool): # ## Signals self.film_object_button.clicked.connect(self.on_film_creation) - self.tf_type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed) - self.tf_type_box_combo.currentIndexChanged.connect(self.on_type_box_index_changed) + self.tf_type_obj_combo.activated_custom.connect(self.on_type_obj_index_changed) + self.tf_type_box_combo.activated_custom.connect(self.on_type_box_index_changed) self.film_type.activated_custom.connect(self.on_film_type) self.source_punch.activated_custom.connect(self.on_punch_source) self.file_type_radio.activated_custom.connect(self.on_file_type) self.reset_button.clicked.connect(self.set_tool_ui) - def on_type_obj_index_changed(self): - obj_type = self.tf_type_obj_combo.currentIndex() + def on_type_obj_index_changed(self, val): + obj_type = 2 if val == 'geo' else 0 self.tf_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) self.tf_object_combo.setCurrentIndex(0) self.tf_object_combo.obj_type = { - _("Gerber"): "Gerber", _("Geometry"): "Geometry" + "grb": "gerber", "geo": "geometry" }[self.tf_type_obj_combo.get_value()] - def on_type_box_index_changed(self): - obj_type = self.tf_type_box_combo.currentIndex() + def on_type_box_index_changed(self, val): + obj_type = 2 if val == 'geo' else 0 self.tf_box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) self.tf_box_combo.setCurrentIndex(0) self.tf_box_combo.obj_type = { - _("Gerber"): "Gerber", _("Geometry"): "Geometry" + "grb": "gerber", "geo": "geometry" }[self.tf_type_obj_combo.get_value()] def run(self, toggle=True): @@ -579,14 +562,14 @@ class Film(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Film Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+L', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+L', **kwargs) def set_tool_ui(self): self.reset_fields() @@ -618,9 +601,11 @@ class Film(FlatCAMTool): self.orientation_radio.set_value(self.app.defaults["tools_film_orientation"]) self.pagesize_combo.set_value(self.app.defaults["tools_film_pagesize"]) + self.tf_type_obj_combo.set_value('grb') + self.tf_type_box_combo.set_value('grb') # run once to update the obj_type attribute in the FCCombobox so the last object is showed in cb - self.on_type_obj_index_changed() - self.on_type_box_index_changed() + self.on_type_obj_index_changed(val='grb') + self.on_type_box_index_changed(val='grb') def on_film_type(self, val): type_of_film = val @@ -659,7 +644,7 @@ class Film(FlatCAMTool): self.exc_label.show() self.exc_combo.show() - if val == 'pad' and self.tf_type_obj_combo.currentText() == 'Geometry': + if val == 'pad' and self.tf_type_obj_combo.get_value() == 'geo': self.source_punch.set_value('exc') self.app.inform.emit('[WARNING_NOTCL] %s' % _("Using the Pad center does not work on Geometry objects. " "Only a Gerber object has pads.")) @@ -744,7 +729,7 @@ class Film(FlatCAMTool): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export positive film"), directory=self.app.get_last_save_folder() + '/' + name + '_film', - filter=filter_ext) + ext_filter=filter_ext) except TypeError: filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export positive film")) @@ -790,7 +775,7 @@ class Film(FlatCAMTool): new_obj.solid_geometry = deepcopy(punched_solid_geometry) outname = name + "_punched" - self.app.new_object('gerber', outname, init_func) + self.app.app_obj.new_object('gerber', outname, init_func) self.generate_positive_normal_film(outname, boxname, factor=factor, ftype=ftype) else: @@ -841,7 +826,7 @@ class Film(FlatCAMTool): new_obj.solid_geometry = deepcopy(punched_solid_geometry) outname = name + "_punched" - self.app.new_object('gerber', outname, init_func) + self.app.app_obj.new_object('gerber', outname, init_func) self.generate_positive_normal_film(outname, boxname, factor=factor, ftype=ftype) @@ -890,7 +875,7 @@ class Film(FlatCAMTool): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export negative film"), directory=self.app.get_last_save_folder() + '/' + name + '_film', - filter=filter_ext) + ext_filter=filter_ext) except TypeError: filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export negative film")) diff --git a/flatcamTools/ToolImage.py b/AppTools/ToolImage.py similarity index 96% rename from flatcamTools/ToolImage.py rename to AppTools/ToolImage.py index 6a077a0d..5d520c78 100644 --- a/flatcamTools/ToolImage.py +++ b/AppTools/ToolImage.py @@ -7,11 +7,11 @@ from PyQt5 import QtGui, QtWidgets -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCComboBox, FCSpinner +from AppTool import AppTool +from AppGUI.GUIElements import RadioSet, FCComboBox, FCSpinner import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -19,12 +19,12 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class ToolImage(FlatCAMTool): +class ToolImage(AppTool): toolName = _("Image as Object") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.decimals = self.app.decimals @@ -176,13 +176,13 @@ class ToolImage(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Image Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, **kwargs) + AppTool.install(self, icon, separator, **kwargs) def set_tool_ui(self): # ## Initialize form @@ -288,7 +288,7 @@ class ToolImage(FlatCAMTool): name = outname or filename.split('/')[-1].split('\\')[-1] units = self.app.defaults['units'] - self.app.new_object(obj_type, name, obj_init) + self.app.app_obj.new_object(obj_type, name, obj_init) # Register recent file self.app.file_opened.emit("image", filename) diff --git a/flatcamTools/ToolInvertGerber.py b/AppTools/ToolInvertGerber.py similarity index 95% rename from flatcamTools/ToolInvertGerber.py rename to AppTools/ToolInvertGerber.py index b1329882..b97ec269 100644 --- a/flatcamTools/ToolInvertGerber.py +++ b/AppTools/ToolInvertGerber.py @@ -7,8 +7,8 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox +from AppTool import AppTool +from AppGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox from shapely.geometry import box @@ -16,7 +16,7 @@ from copy import deepcopy import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -26,7 +26,7 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolInvertGerber(FlatCAMTool): +class ToolInvertGerber(AppTool): toolName = _("Invert Gerber Tool") @@ -34,7 +34,7 @@ class ToolInvertGerber(FlatCAMTool): self.app = app self.decimals = self.app.decimals - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.tools_frame = QtWidgets.QFrame() self.tools_frame.setContentsMargins(0, 0, 0, 0) @@ -153,7 +153,7 @@ class ToolInvertGerber(FlatCAMTool): self.reset_button.clicked.connect(self.set_tool_ui) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='', **kwargs) + AppTool.install(self, icon, separator, shortcut='', **kwargs) def run(self, toggle=True): self.app.defaults.report_usage("ToolInvertGerber()") @@ -178,7 +178,7 @@ class ToolInvertGerber(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Invert Tool")) @@ -228,6 +228,11 @@ class ToolInvertGerber(FlatCAMTool): for poly in grb_obj.solid_geometry: new_solid_geometry = new_solid_geometry.difference(poly) + try: + __ = iter(new_solid_geometry) + except TypeError: + new_solid_geometry = [new_solid_geometry] + new_options = {} for opt in grb_obj.options: new_options[opt] = deepcopy(grb_obj.options[opt]) @@ -278,9 +283,6 @@ class ToolInvertGerber(FlatCAMTool): new_el['follow'] = new_solid_geometry.exterior new_apertures['0']['geometry'].append(new_el) - for td in new_apertures: - print(td, new_apertures[td]) - def init_func(new_obj, app_obj): new_obj.options.update(new_options) new_obj.options['name'] = outname @@ -293,7 +295,7 @@ class ToolInvertGerber(FlatCAMTool): new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None, local_use=new_obj, use_thread=False) - self.app.new_object('gerber', outname, init_func) + self.app.app_obj.new_object('gerber', outname, init_func) def reset_fields(self): self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) diff --git a/AppTools/ToolIsolation.py b/AppTools/ToolIsolation.py new file mode 100644 index 00000000..de7f8593 --- /dev/null +++ b/AppTools/ToolIsolation.py @@ -0,0 +1,2979 @@ +# ########################################################## +# FlatCAM: 2D Post-processing for Manufacturing # +# File by: Marius Adrian Stanciu (c) # +# Date: 5/25/2020 # +# License: MIT Licence # +# ########################################################## + +from PyQt5 import QtWidgets, QtCore, QtGui + +from AppTool import AppTool +from AppGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog, FCButton, \ + FCComboBox, OptionalHideInputSection, FCSpinner +from AppParsers.ParseGerber import Gerber + +from copy import deepcopy + +import numpy as np +import math + +from shapely.ops import cascaded_union +from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing + +from matplotlib.backend_bases import KeyEvent as mpl_key_event + +import logging +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + +log = logging.getLogger('base') + + +class ToolIsolation(AppTool, Gerber): + toolName = _("Isolation Tool") + + def __init__(self, app): + self.app = app + self.decimals = self.app.decimals + + AppTool.__init__(self, app) + Gerber.__init__(self, steps_per_circle=self.app.defaults["gerber_circle_steps"]) + + self.tools_frame = QtWidgets.QFrame() + self.tools_frame.setContentsMargins(0, 0, 0, 0) + self.layout.addWidget(self.tools_frame) + self.tools_box = QtWidgets.QVBoxLayout() + self.tools_box.setContentsMargins(0, 0, 0, 0) + self.tools_frame.setLayout(self.tools_box) + + self.title_box = QtWidgets.QHBoxLayout() + self.tools_box.addLayout(self.title_box) + + # ## Title + title_label = QtWidgets.QLabel("%s" % self.toolName) + title_label.setStyleSheet(""" + QLabel + { + font-size: 16px; + font-weight: bold; + } + """) + title_label.setToolTip( + _("Create a Geometry object with\n" + "toolpaths to cut around polygons.") + ) + + self.title_box.addWidget(title_label) + + # App Level label + self.level = QtWidgets.QLabel("") + self.level.setToolTip( + _( + "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." + ) + ) + self.level.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) + self.title_box.addWidget(self.level) + + # Grid Layout + grid0 = QtWidgets.QGridLayout() + grid0.setColumnStretch(0, 0) + grid0.setColumnStretch(1, 1) + self.tools_box.addLayout(grid0) + + self.obj_combo_label = QtWidgets.QLabel('%s:' % _("GERBER")) + self.obj_combo_label.setToolTip( + _("Gerber object for isolation routing.") + ) + + grid0.addWidget(self.obj_combo_label, 0, 0, 1, 2) + + # ################################################ + # ##### The object to be copper cleaned ########## + # ################################################ + self.object_combo = FCComboBox() + self.object_combo.setModel(self.app.collection) + self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + # self.object_combo.setCurrentIndex(1) + self.object_combo.is_last = True + + grid0.addWidget(self.object_combo, 1, 0, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid0.addWidget(separator_line, 2, 0, 1, 2) + + # ### Tools ## ## + self.tools_table_label = QtWidgets.QLabel('%s' % _('Tools Table')) + self.tools_table_label.setToolTip( + _("Tools pool from which the algorithm\n" + "will pick the ones used for copper clearing.") + ) + grid0.addWidget(self.tools_table_label, 3, 0, 1, 2) + + self.tools_table = FCTable() + grid0.addWidget(self.tools_table, 4, 0, 1, 2) + + self.tools_table.setColumnCount(4) + # 3rd column is reserved (and hidden) for the tool ID + self.tools_table.setHorizontalHeaderLabels(['#', _('Diameter'), _('TT'), '']) + self.tools_table.setColumnHidden(3, True) + self.tools_table.setSortingEnabled(False) + # self.tools_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) + + self.tools_table.horizontalHeaderItem(0).setToolTip( + _("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 Isolation 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.") + ) + self.tools_table.horizontalHeaderItem(1).setToolTip( + _("Tool Diameter. It's value (in current FlatCAM units)\n" + "is the cut width into the material.")) + + self.tools_table.horizontalHeaderItem(2).setToolTip( + _("The Tool Type (TT) can be:\n" + "- Circular with 1 ... 4 teeth -> it is informative only. Being circular,\n" + "the cut width in material is exactly the tool diameter.\n" + "- Ball -> informative only and make reference to the Ball type endmill.\n" + "- V-Shape -> it will disable Z-Cut parameter in the resulting geometry UI form\n" + "and enable two additional UI form fields in the resulting geometry: V-Tip Dia and\n" + "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\n" + "column of this table.\n" + "Choosing the 'V-Shape' Tool Type automatically will select the Operation Type\n" + "in the resulting geometry as Isolation.")) + + grid1 = QtWidgets.QGridLayout() + grid1.setColumnStretch(0, 0) + grid1.setColumnStretch(1, 1) + self.tools_box.addLayout(grid1) + + # Tool order + self.order_label = QtWidgets.QLabel('%s:' % _('Tool order')) + self.order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n" + "'No' --> means that the used order is the one in the tool table\n" + "'Forward' --> means that the tools will be ordered from small to big\n" + "'Reverse' --> means that the tools will ordered from big to small\n\n" + "WARNING: using rest machining will automatically set the order\n" + "in reverse and disable this control.")) + + self.order_radio = RadioSet([{'label': _('No'), 'value': 'no'}, + {'label': _('Forward'), 'value': 'fwd'}, + {'label': _('Reverse'), 'value': 'rev'}]) + + grid1.addWidget(self.order_label, 1, 0) + grid1.addWidget(self.order_radio, 1, 1) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + grid1.addWidget(separator_line, 2, 0, 1, 2) + + # ############################################################# + # ############### Tool selection ############################## + # ############################################################# + + self.grid3 = QtWidgets.QGridLayout() + self.grid3.setColumnStretch(0, 0) + self.grid3.setColumnStretch(1, 1) + self.tools_box.addLayout(self.grid3) + + self.tool_sel_label = QtWidgets.QLabel('%s' % _("New Tool")) + self.grid3.addWidget(self.tool_sel_label, 1, 0, 1, 2) + + # Tool Type Radio Button + self.tool_type_label = QtWidgets.QLabel('%s:' % _('Tool Type')) + self.tool_type_label.setToolTip( + _("Default tool type:\n" + "- 'V-shape'\n" + "- Circular") + ) + + self.tool_type_radio = RadioSet([{'label': _('V-shape'), 'value': 'V'}, + {'label': _('Circular'), 'value': 'C1'}]) + self.tool_type_radio.setToolTip( + _("Default tool type:\n" + "- 'V-shape'\n" + "- Circular") + ) + self.tool_type_radio.setObjectName("i_tool_type") + + self.grid3.addWidget(self.tool_type_label, 2, 0) + self.grid3.addWidget(self.tool_type_radio, 2, 1) + + # Tip Dia + self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia')) + self.tipdialabel.setToolTip( + _("The tip diameter for V-Shape Tool")) + self.tipdia_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.tipdia_entry.set_precision(self.decimals) + self.tipdia_entry.set_range(0.0000, 9999.9999) + self.tipdia_entry.setSingleStep(0.1) + self.tipdia_entry.setObjectName("i_vtipdia") + + self.grid3.addWidget(self.tipdialabel, 3, 0) + self.grid3.addWidget(self.tipdia_entry, 3, 1) + + # Tip Angle + self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle')) + self.tipanglelabel.setToolTip( + _("The tip angle for V-Shape Tool.\n" + "In degree.")) + self.tipangle_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.tipangle_entry.set_precision(self.decimals) + self.tipangle_entry.set_range(0.0000, 180.0000) + self.tipangle_entry.setSingleStep(5) + self.tipangle_entry.setObjectName("i_vtipangle") + + self.grid3.addWidget(self.tipanglelabel, 4, 0) + self.grid3.addWidget(self.tipangle_entry, 4, 1) + + # Cut Z entry + cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z')) + cutzlabel.setToolTip( + _("Depth of cut into material. Negative value.\n" + "In FlatCAM units.") + ) + self.cutz_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.cutz_entry.set_precision(self.decimals) + self.cutz_entry.set_range(-99999.9999, 0.0000) + self.cutz_entry.setObjectName("i_vcutz") + + self.grid3.addWidget(cutzlabel, 5, 0) + self.grid3.addWidget(self.cutz_entry, 5, 1) + + # ### Tool Diameter #### + self.addtool_entry_lbl = QtWidgets.QLabel('%s:' % _('Tool Dia')) + self.addtool_entry_lbl.setToolTip( + _("Diameter for the new tool to add in the Tool Table.\n" + "If the tool is V-shape type then this value is automatically\n" + "calculated from the other parameters.") + ) + self.addtool_entry = FCDoubleSpinner(callback=self.confirmation_message) + self.addtool_entry.set_precision(self.decimals) + self.addtool_entry.set_range(0.000, 9999.9999) + self.addtool_entry.setObjectName("i_new_tooldia") + + self.grid3.addWidget(self.addtool_entry_lbl, 6, 0) + self.grid3.addWidget(self.addtool_entry, 6, 1) + + bhlay = QtWidgets.QHBoxLayout() + + self.addtool_btn = QtWidgets.QPushButton(_('Add')) + self.addtool_btn.setToolTip( + _("Add a new tool to the Tool Table\n" + "with the diameter specified above.") + ) + + self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add from DB')) + self.addtool_from_db_btn.setToolTip( + _("Add a new tool to the Tool Table\n" + "from the Tool DataBase.") + ) + + bhlay.addWidget(self.addtool_btn) + bhlay.addWidget(self.addtool_from_db_btn) + + self.grid3.addLayout(bhlay, 7, 0, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.grid3.addWidget(separator_line, 8, 0, 1, 2) + + self.deltool_btn = QtWidgets.QPushButton(_('Delete')) + self.deltool_btn.setToolTip( + _("Delete a selection of tools in the Tool Table\n" + "by first selecting a row(s) in the Tool Table.") + ) + self.grid3.addWidget(self.deltool_btn, 9, 0, 1, 2) + + # self.grid3.addWidget(QtWidgets.QLabel(''), 10, 0, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.grid3.addWidget(separator_line, 11, 0, 1, 2) + + self.tool_data_label = QtWidgets.QLabel( + "%s: %s %d" % (_('Parameters for'), _("Tool"), int(1))) + self.tool_data_label.setToolTip( + _( + "The data used for creating GCode.\n" + "Each tool store it's own set of such data." + ) + ) + self.grid3.addWidget(self.tool_data_label, 12, 0, 1, 2) + + # Passes + passlabel = QtWidgets.QLabel('%s:' % _('Passes')) + passlabel.setToolTip( + _("Width of the isolation gap in\n" + "number (integer) of tool widths.") + ) + self.passes_entry = FCSpinner(callback=self.confirmation_message_int) + self.passes_entry.set_range(1, 999) + self.passes_entry.setObjectName("i_passes") + + self.grid3.addWidget(passlabel, 13, 0) + self.grid3.addWidget(self.passes_entry, 13, 1) + + # Overlap Entry + overlabel = QtWidgets.QLabel('%s:' % _('Overlap')) + overlabel.setToolTip( + _("How much (percentage) of the tool width to overlap each tool pass.") + ) + self.iso_overlap_entry = FCDoubleSpinner(suffix='%', callback=self.confirmation_message) + self.iso_overlap_entry.set_precision(self.decimals) + self.iso_overlap_entry.setWrapping(True) + self.iso_overlap_entry.set_range(0.0000, 99.9999) + self.iso_overlap_entry.setSingleStep(0.1) + self.iso_overlap_entry.setObjectName("i_overlap") + + self.grid3.addWidget(overlabel, 14, 0) + self.grid3.addWidget(self.iso_overlap_entry, 14, 1) + + # Milling Type Radio Button + self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) + self.milling_type_label.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, + {'label': _('Conventional'), 'value': 'cv'}]) + self.milling_type_radio.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + self.milling_type_radio.setObjectName("i_milling_type") + + self.grid3.addWidget(self.milling_type_label, 15, 0) + self.grid3.addWidget(self.milling_type_radio, 15, 1) + + # Follow + self.follow_label = QtWidgets.QLabel('%s:' % _('Follow')) + self.follow_label.setToolTip( + _("Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace.") + ) + + self.follow_cb = FCCheckBox() + self.follow_cb.setToolTip(_("Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace.")) + self.follow_cb.setObjectName("i_follow") + + self.grid3.addWidget(self.follow_label, 16, 0) + self.grid3.addWidget(self.follow_cb, 16, 1) + + # Isolation Type + self.iso_type_label = QtWidgets.QLabel('%s:' % _('Isolation Type')) + self.iso_type_label.setToolTip( + _("Choose how the isolation will be executed:\n" + "- 'Full' -> complete isolation of polygons\n" + "- 'Ext' -> will isolate only on the outside\n" + "- 'Int' -> will isolate only on the inside\n" + "'Exterior' isolation is almost always possible\n" + "(with the right tool) but 'Interior'\n" + "isolation can be done only when there is an opening\n" + "inside of the polygon (e.g polygon is a 'doughnut' shape).") + ) + self.iso_type_radio = RadioSet([{'label': _('Full'), 'value': 'full'}, + {'label': _('Ext'), 'value': 'ext'}, + {'label': _('Int'), 'value': 'int'}]) + self.iso_type_radio.setObjectName("i_iso_type") + + self.grid3.addWidget(self.iso_type_label, 17, 0) + self.grid3.addWidget(self.iso_type_radio, 17, 1) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.grid3.addWidget(separator_line, 18, 0, 1, 2) + + self.apply_param_to_all = FCButton(_("Apply parameters to all tools")) + self.apply_param_to_all.setToolTip( + _("The parameters in the current form will be applied\n" + "on all the tools from the Tool Table.") + ) + self.grid3.addWidget(self.apply_param_to_all, 22, 0, 1, 2) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.grid3.addWidget(separator_line, 23, 0, 1, 2) + + # General Parameters + self.gen_param_label = QtWidgets.QLabel('%s' % _("Common Parameters")) + self.gen_param_label.setToolTip( + _("Parameters that are common for all tools.") + ) + self.grid3.addWidget(self.gen_param_label, 24, 0, 1, 2) + + # Rest Machining + self.rest_cb = FCCheckBox('%s' % _("Rest")) + self.rest_cb.setObjectName("i_rest") + self.rest_cb.setToolTip( + _("If checked, use 'rest machining'.\n" + "Basically it will isolate outside PCB features,\n" + "using the biggest tool and continue with the next tools,\n" + "from bigger to smaller, to isolate the copper features that\n" + "could not be cleared by previous tool, until there is\n" + "no more copper features to isolate or there are no more tools.\n" + "If not checked, use the standard algorithm.") + ) + + self.grid3.addWidget(self.rest_cb, 25, 0) + + # Combine All Passes + self.combine_passes_cb = FCCheckBox(label=_('Combine')) + self.combine_passes_cb.setToolTip( + _("Combine all passes into one object") + ) + self.combine_passes_cb.setObjectName("i_combine") + + self.grid3.addWidget(self.combine_passes_cb, 25, 1) + + # Exception Areas + self.except_cb = FCCheckBox(label=_('Except')) + self.except_cb.setToolTip(_("When the isolation geometry is generated,\n" + "by checking this, the area of the object below\n" + "will be subtracted from the isolation geometry.")) + self.except_cb.setObjectName("i_except") + self.grid3.addWidget(self.except_cb, 27, 0) + + # Type of object to be excepted + self.type_excobj_radio = RadioSet([{'label': _("Geometry"), 'value': 'geometry'}, + {'label': _("Gerber"), 'value': 'gerber'}]) + self.type_excobj_radio.setToolTip( + _("Specify the type of object to be excepted from isolation.\n" + "It can be of type: Gerber or Geometry.\n" + "What is selected here will dictate the kind\n" + "of objects that will populate the 'Object' combobox.") + ) + + self.grid3.addWidget(self.type_excobj_radio, 27, 1) + + # The object to be excepted + self.exc_obj_combo = FCComboBox() + self.exc_obj_combo.setToolTip(_("Object whose area will be removed from isolation geometry.")) + + # set the model for the Area Exception comboboxes + self.exc_obj_combo.setModel(self.app.collection) + self.exc_obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.exc_obj_combo.is_last = True + self.exc_obj_combo.obj_type = self.type_excobj_radio.get_value() + + self.grid3.addWidget(self.exc_obj_combo, 29, 0, 1, 2) + + self.e_ois = OptionalHideInputSection(self.except_cb, + [ + self.type_excobj_radio, + self.exc_obj_combo + ]) + + # Isolation Scope + self.select_label = QtWidgets.QLabel('%s:' % _("Selection")) + self.select_label.setToolTip( + _("Isolation scope. Choose what to isolate:\n" + "- 'All' -> Isolate all the polygons in the object\n" + "- 'Selection' -> Isolate a selection of polygons.\n" + "- 'Reference Object' - will process the area specified by another object.") + ) + self.select_combo = FCComboBox() + self.select_combo.addItems( + [_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")] + ) + self.select_combo.setObjectName("i_selection") + + self.grid3.addWidget(self.select_label, 30, 0) + self.grid3.addWidget(self.select_combo, 30, 1) + + self.reference_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type")) + self.reference_combo_type_label.setToolTip( + _("The type of FlatCAM object to be used as non copper clearing reference.\n" + "It can be Gerber, Excellon or Geometry.") + ) + self.reference_combo_type = FCComboBox() + self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")]) + + self.grid3.addWidget(self.reference_combo_type_label, 31, 0) + self.grid3.addWidget(self.reference_combo_type, 31, 1) + + self.reference_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object")) + self.reference_combo_label.setToolTip( + _("The FlatCAM object to be used as non copper clearing reference.") + ) + self.reference_combo = FCComboBox() + self.reference_combo.setModel(self.app.collection) + self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.reference_combo.is_last = True + + self.grid3.addWidget(self.reference_combo_label, 32, 0) + self.grid3.addWidget(self.reference_combo, 32, 1) + + self.reference_combo.hide() + self.reference_combo_label.hide() + self.reference_combo_type.hide() + self.reference_combo_type_label.hide() + + # Area Selection shape + self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape")) + self.area_shape_label.setToolTip( + _("The kind of selection shape used for area selection.") + ) + + self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, + {'label': _("Polygon"), 'value': 'polygon'}]) + + self.grid3.addWidget(self.area_shape_label, 33, 0) + self.grid3.addWidget(self.area_shape_radio, 33, 1) + + self.area_shape_label.hide() + self.area_shape_radio.hide() + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.grid3.addWidget(separator_line, 34, 0, 1, 2) + + self.generate_iso_button = QtWidgets.QPushButton("%s" % _("Generate Isolation Geometry")) + self.generate_iso_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.generate_iso_button.setToolTip( + _("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.") + ) + self.tools_box.addWidget(self.generate_iso_button) + + self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry')) + self.create_buffer_button.setToolTip( + _("This button is shown only when the Gerber file\n" + "is loaded without buffering.\n" + "Clicking this will create the buffered geometry\n" + "required for isolation.") + ) + self.tools_box.addWidget(self.create_buffer_button) + + self.tools_box.addStretch() + + # ## Reset Tool + self.reset_button = QtWidgets.QPushButton(_("Reset Tool")) + self.reset_button.setToolTip( + _("Will reset the tool parameters.") + ) + self.reset_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.tools_box.addWidget(self.reset_button) + # ############################ FINSIHED GUI ################################### + # ############################################################################# + + # ############################################################################# + # ###################### Setup CONTEXT MENU ################################### + # ############################################################################# + self.tools_table.setupContextMenu() + self.tools_table.addContextMenu( + _("Add"), self.on_add_tool_by_key, icon=QtGui.QIcon(self.app.resource_location + "/plus16.png") + ) + self.tools_table.addContextMenu( + _("Add from DB"), self.on_add_tool_by_key, icon=QtGui.QIcon(self.app.resource_location + "/plus16.png") + ) + self.tools_table.addContextMenu( + _("Delete"), lambda: + self.on_tool_delete(rows_to_delete=None, all_tools=None), + icon=QtGui.QIcon(self.app.resource_location + "/delete32.png") + ) + + # ############################################################################# + # ########################## VARIABLES ######################################## + # ############################################################################# + self.units = '' + self.iso_tools = {} + self.tooluid = 0 + + # store here the default data for Geometry Data + self.default_data = {} + + self.obj_name = "" + self.grb_obj = None + + self.sel_rect = [] + + self.first_click = False + self.cursor_pos = None + self.mouse_is_dragging = False + + # store here the points for the "Polygon" area selection shape + self.points = [] + + # set this as True when in middle of drawing a "Polygon" area selection shape + # it is made False by first click to signify that the shape is complete + self.poly_drawn = False + + self.mm = None + self.mr = None + self.kp = None + + # store geometry from Polygon selection + self.poly_dict = {} + + self.grid_status_memory = self.app.ui.grid_snap_btn.isChecked() + + # store here the state of the combine_cb GUI element + # used when the rest machining is toggled + self.old_combine_state = None + + # store here solid_geometry when there are tool with isolation job + self.solid_geometry = [] + + self.tool_type_item_options = [] + + self.grb_circle_steps = int(self.app.defaults["gerber_circle_steps"]) + + self.tooldia = None + + # multiprocessing + self.pool = self.app.pool + self.results = [] + + # disconnect flags + self.area_sel_disconnect_flag = False + self.poly_sel_disconnect_flag = False + + self.form_fields = { + "tools_iso_passes": self.passes_entry, + "tools_iso_overlap": self.iso_overlap_entry, + "tools_iso_milling_type": self.milling_type_radio, + "tools_iso_combine": self.combine_passes_cb, + "tools_iso_follow": self.follow_cb, + "tools_iso_isotype": self.iso_type_radio + } + + self.name2option = { + "i_passes": "tools_iso_passes", + "i_overlap": "tools_iso_overlap", + "i_milling_type": "tools_iso_milling_type", + "i_combine": "tools_iso_combine", + "i_follow": "tools_iso_follow", + "i_iso_type": "tools_iso_isotype" + } + + self.old_tool_dia = None + + # ############################################################################# + # ############################ SIGNALS ######################################## + # ############################################################################# + self.addtool_btn.clicked.connect(self.on_tool_add) + self.addtool_entry.returnPressed.connect(self.on_tooldia_updated) + self.deltool_btn.clicked.connect(self.on_tool_delete) + + self.tipdia_entry.returnPressed.connect(self.on_calculate_tooldia) + self.tipangle_entry.returnPressed.connect(self.on_calculate_tooldia) + self.cutz_entry.returnPressed.connect(self.on_calculate_tooldia) + + self.reference_combo_type.currentIndexChanged.connect(self.on_reference_combo_changed) + self.select_combo.currentIndexChanged.connect(self.on_toggle_reference) + + self.rest_cb.stateChanged.connect(self.on_rest_machining_check) + self.order_radio.activated_custom[str].connect(self.on_order_changed) + + self.type_excobj_radio.activated_custom.connect(self.on_type_excobj_index_changed) + self.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked) + self.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked) + + self.generate_iso_button.clicked.connect(self.on_iso_button_click) + self.reset_button.clicked.connect(self.set_tool_ui) + + # Cleanup on Graceful exit (CTRL+ALT+X combo key) + self.app.cleanup.connect(self.reset_usage) + + def on_type_excobj_index_changed(self, val): + obj_type = 0 if val == 'gerber' else 2 + self.exc_obj_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) + self.exc_obj_combo.setCurrentIndex(0) + self.exc_obj_combo.obj_type = { + "gerber": "Gerber", "geometry": "Geometry" + }[self.type_excobj_radio.get_value()] + + def install(self, icon=None, separator=None, **kwargs): + AppTool.install(self, icon, separator, shortcut='Alt+I', **kwargs) + + def run(self, toggle=True): + self.app.defaults.report_usage("ToolIsolation()") + log.debug("ToolIsolation().run() was launched ...") + + if toggle: + # 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]) + else: + try: + if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName: + # if tab is populated with the tool but it does not have the focus, focus on it + if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab: + # focus on Tool Tab + self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab) + else: + self.app.ui.splitter.setSizes([0, 1]) + except AttributeError: + pass + else: + if self.app.ui.splitter.sizes()[0] == 0: + self.app.ui.splitter.setSizes([1, 1]) + + AppTool.run(self) + self.set_tool_ui() + + # reset those objects on a new run + self.grb_obj = None + self.obj_name = '' + + self.build_ui() + self.app.ui.notebook.setTabText(2, _("Isolation Tool")) + + def set_tool_ui(self): + self.units = self.app.defaults['units'].upper() + self.old_tool_dia = self.app.defaults["tools_iso_newdia"] + + # try to select in the Gerber combobox the active object + try: + selected_obj = self.app.collection.get_active() + if selected_obj.kind == 'gerber': + current_name = selected_obj.options['name'] + self.object_combo.set_value(current_name) + except Exception: + pass + + app_mode = self.app.defaults["global_app_level"] + + # Show/Hide Advanced Options + if app_mode == 'b': + self.level.setText('%s' % _('Basic')) + + # override the Preferences Value; in Basic mode the Tool Type is always Circular ('C1') + self.tool_type_radio.set_value('C1') + self.tool_type_label.hide() + self.tool_type_radio.hide() + + self.milling_type_label.hide() + self.milling_type_radio.hide() + + self.iso_type_label.hide() + self.iso_type_radio.set_value('full') + self.iso_type_radio.hide() + + self.follow_cb.set_value(False) + self.follow_cb.hide() + self.follow_label.hide() + + self.rest_cb.set_value(False) + self.rest_cb.hide() + + self.except_cb.set_value(False) + self.except_cb.hide() + + self.select_combo.setCurrentIndex(0) + self.select_combo.hide() + self.select_label.hide() + else: + self.level.setText('%s' % _('Advanced')) + + self.tool_type_radio.set_value(self.app.defaults["tools_iso_tool_type"]) + self.tool_type_label.show() + self.tool_type_radio.show() + + self.milling_type_label.show() + self.milling_type_radio.show() + + self.iso_type_label.show() + self.iso_type_radio.set_value(self.app.defaults["tools_iso_isotype"]) + self.iso_type_radio.show() + + self.follow_cb.set_value(self.app.defaults["tools_iso_follow"]) + self.follow_cb.show() + self.follow_label.show() + + self.rest_cb.set_value(self.app.defaults["tools_iso_rest"]) + self.rest_cb.show() + + self.except_cb.set_value(self.app.defaults["tools_iso_isoexcept"]) + self.except_cb.show() + + self.select_combo.set_value(self.app.defaults["tools_iso_selection"]) + self.select_combo.show() + self.select_label.show() + + if self.app.defaults["gerber_buffering"] == 'no': + self.create_buffer_button.show() + try: + self.create_buffer_button.clicked.disconnect(self.on_generate_buffer) + except TypeError: + pass + self.create_buffer_button.clicked.connect(self.on_generate_buffer) + else: + self.create_buffer_button.hide() + + self.tools_frame.show() + + self.type_excobj_radio.set_value('gerber') + + # run those once so the obj_type attribute is updated for the FCComboboxes + # so the last loaded object is displayed + self.on_type_excobj_index_changed(val="gerber") + self.on_reference_combo_changed() + + self.order_radio.set_value(self.app.defaults["tools_iso_order"]) + self.passes_entry.set_value(self.app.defaults["tools_iso_passes"]) + self.iso_overlap_entry.set_value(self.app.defaults["tools_iso_overlap"]) + self.milling_type_radio.set_value(self.app.defaults["tools_iso_milling_type"]) + self.combine_passes_cb.set_value(self.app.defaults["tools_iso_combine_passes"]) + self.area_shape_radio.set_value(self.app.defaults["tools_iso_area_shape"]) + + self.cutz_entry.set_value(self.app.defaults["tools_iso_tool_cutz"]) + self.tool_type_radio.set_value(self.app.defaults["tools_iso_tool_type"]) + self.tipdia_entry.set_value(self.app.defaults["tools_iso_tool_vtipdia"]) + self.tipangle_entry.set_value(self.app.defaults["tools_iso_tool_vtipangle"]) + self.addtool_entry.set_value(self.app.defaults["tools_iso_newdia"]) + + self.on_tool_type(val=self.tool_type_radio.get_value()) + + loaded_obj = self.app.collection.get_by_name(self.object_combo.get_value()) + if loaded_obj: + outname = loaded_obj.options['name'] + else: + outname = '' + + # init the working variables + self.default_data.clear() + self.default_data = { + "name": outname + '_iso', + "plot": self.app.defaults["geometry_plot"], + "cutz": float(self.cutz_entry.get_value()), + "vtipdia": float(self.tipdia_entry.get_value()), + "vtipangle": float(self.tipangle_entry.get_value()), + "travelz": self.app.defaults["geometry_travelz"], + "feedrate": self.app.defaults["geometry_feedrate"], + "feedrate_z": self.app.defaults["geometry_feedrate_z"], + "feedrate_rapid": self.app.defaults["geometry_feedrate_rapid"], + "dwell": self.app.defaults["geometry_dwell"], + "dwelltime": self.app.defaults["geometry_dwelltime"], + "multidepth": self.app.defaults["geometry_multidepth"], + "ppname_g": self.app.defaults["geometry_ppname_g"], + "depthperpass": self.app.defaults["geometry_depthperpass"], + "extracut": self.app.defaults["geometry_extracut"], + "extracut_length": self.app.defaults["geometry_extracut_length"], + "toolchange": self.app.defaults["geometry_toolchange"], + "toolchangez": self.app.defaults["geometry_toolchangez"], + "endz": self.app.defaults["geometry_endz"], + "endxy": self.app.defaults["geometry_endxy"], + + "spindlespeed": self.app.defaults["geometry_spindlespeed"], + "toolchangexy": self.app.defaults["geometry_toolchangexy"], + "startz": self.app.defaults["geometry_startz"], + + "area_exclusion": self.app.defaults["geometry_area_exclusion"], + "area_shape": self.app.defaults["geometry_area_shape"], + "area_strategy": self.app.defaults["geometry_area_strategy"], + "area_overz": float(self.app.defaults["geometry_area_overz"]), + + "tools_iso_passes": self.app.defaults["tools_iso_passes"], + "tools_iso_overlap": self.app.defaults["tools_iso_overlap"], + "tools_iso_milling_type": self.app.defaults["tools_iso_milling_type"], + "tools_iso_follow": self.app.defaults["tools_iso_follow"], + "tools_iso_isotype": self.app.defaults["tools_iso_isotype"], + + "tools_iso_rest": self.app.defaults["tools_iso_rest"], + "tools_iso_combine_passes": self.app.defaults["tools_iso_combine_passes"], + "tools_iso_isoexcept": self.app.defaults["tools_iso_isoexcept"], + "tools_iso_selection": self.app.defaults["tools_iso_selection"], + "tools_iso_area_shape": self.app.defaults["tools_iso_area_shape"] + } + + try: + dias = [float(self.app.defaults["tools_iso_tooldia"])] + except (ValueError, TypeError): + dias = [float(eval(dia)) for dia in self.app.defaults["tools_iso_tooldia"].split(",") if dia != ''] + + if not dias: + log.error("At least one tool diameter needed. Verify in Edit -> Preferences -> TOOLS -> Isolation Tools.") + return + + self.tooluid = 0 + + self.iso_tools.clear() + for tool_dia in dias: + self.tooluid += 1 + self.iso_tools.update({ + int(self.tooluid): { + 'tooldia': float('%.*f' % (self.decimals, tool_dia)), + 'offset': 'Path', + 'offset_value': 0.0, + 'type': 'Iso', + 'tool_type': self.tool_type_radio.get_value(), + 'data': deepcopy(self.default_data), + 'solid_geometry': [] + } + }) + + self.obj_name = "" + self.grb_obj = None + + self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"] + self.units = self.app.defaults['units'].upper() + + def build_ui(self): + self.ui_disconnect() + + # updated units + self.units = self.app.defaults['units'].upper() + + sorted_tools = [] + for k, v in self.iso_tools.items(): + sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia'])))) + + order = self.order_radio.get_value() + if order == 'fwd': + sorted_tools.sort(reverse=False) + elif order == 'rev': + sorted_tools.sort(reverse=True) + else: + pass + + n = len(sorted_tools) + self.tools_table.setRowCount(n) + tool_id = 0 + + for tool_sorted in sorted_tools: + for tooluid_key, tooluid_value in self.iso_tools.items(): + if float('%.*f' % (self.decimals, tooluid_value['tooldia'])) == tool_sorted: + tool_id += 1 + id_ = QtWidgets.QTableWidgetItem('%d' % int(tool_id)) + id_.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + row_no = tool_id - 1 + self.tools_table.setItem(row_no, 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 2 decimals diameter + # For INCH the decimals should be no more than 4. There are no drills under 10mils + dia = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, tooluid_value['tooldia'])) + + dia.setFlags(QtCore.Qt.ItemIsEnabled) + + tool_type_item = FCComboBox() + tool_type_item.addItems(self.tool_type_item_options) + + # tool_type_item.setStyleSheet('background-color: rgb(255,255,255)') + idx = tool_type_item.findText(tooluid_value['tool_type']) + tool_type_item.setCurrentIndex(idx) + + tool_uid_item = QtWidgets.QTableWidgetItem(str(int(tooluid_key))) + + self.tools_table.setItem(row_no, 1, dia) # Diameter + self.tools_table.setCellWidget(row_no, 2, tool_type_item) + + # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ## + self.tools_table.setItem(row_no, 3, tool_uid_item) # Tool unique ID + + # make the diameter column editable + for row in range(tool_id): + self.tools_table.item(row, 1).setFlags( + QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) + + # all the tools are selected by default + self.tools_table.selectColumn(0) + # + self.tools_table.resizeColumnsToContents() + self.tools_table.resizeRowsToContents() + + vertical_header = self.tools_table.verticalHeader() + vertical_header.hide() + self.tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + + horizontal_header = self.tools_table.horizontalHeader() + horizontal_header.setMinimumSectionSize(10) + horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed) + horizontal_header.resizeSection(0, 20) + horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch) + + # self.tools_table.setSortingEnabled(True) + # sort by tool diameter + # self.tools_table.sortItems(1) + + self.tools_table.setMinimumHeight(self.tools_table.getHeight()) + self.tools_table.setMaximumHeight(self.tools_table.getHeight()) + + self.ui_connect() + + # set the text on tool_data_label after loading the object + sel_rows = [] + sel_items = self.tools_table.selectedItems() + for it in sel_items: + sel_rows.append(it.row()) + if len(sel_rows) > 1: + self.tool_data_label.setText( + "%s: %s" % (_('Parameters for'), _("Multiple Tools")) + ) + + def ui_connect(self): + self.tools_table.itemChanged.connect(self.on_tool_edit) + + # rows selected + self.tools_table.clicked.connect(self.on_row_selection_change) + self.tools_table.horizontalHeader().sectionClicked.connect(self.on_row_selection_change) + + for row in range(self.tools_table.rowCount()): + try: + self.tools_table.cellWidget(row, 2).currentIndexChanged.connect(self.on_tooltable_cellwidget_change) + except AttributeError: + pass + + self.tool_type_radio.activated_custom.connect(self.on_tool_type) + + for opt in self.form_fields: + current_widget = self.form_fields[opt] + if isinstance(current_widget, FCCheckBox): + current_widget.stateChanged.connect(self.form_to_storage) + if isinstance(current_widget, RadioSet): + current_widget.activated_custom.connect(self.form_to_storage) + elif isinstance(current_widget, FCDoubleSpinner) or isinstance(current_widget, FCSpinner): + current_widget.returnPressed.connect(self.form_to_storage) + elif isinstance(current_widget, FCComboBox): + current_widget.currentIndexChanged.connect(self.form_to_storage) + + self.rest_cb.stateChanged.connect(self.on_rest_machining_check) + self.order_radio.activated_custom[str].connect(self.on_order_changed) + + def ui_disconnect(self): + + try: + # if connected, disconnect the signal from the slot on item_changed as it creates issues + self.tools_table.itemChanged.disconnect() + except (TypeError, AttributeError): + pass + + try: + # if connected, disconnect the signal from the slot on item_changed as it creates issues + self.tool_type_radio.activated_custom.disconnect() + except (TypeError, AttributeError): + pass + + for row in range(self.tools_table.rowCount()): + + try: + self.tools_table.cellWidget(row, 2).currentIndexChanged.disconnect() + except (TypeError, AttributeError): + pass + + for opt in self.form_fields: + current_widget = self.form_fields[opt] + if isinstance(current_widget, FCCheckBox): + try: + current_widget.stateChanged.disconnect(self.form_to_storage) + except (TypeError, ValueError): + pass + if isinstance(current_widget, RadioSet): + try: + current_widget.activated_custom.disconnect(self.form_to_storage) + except (TypeError, ValueError): + pass + elif isinstance(current_widget, FCDoubleSpinner) or isinstance(current_widget, FCSpinner): + try: + current_widget.returnPressed.disconnect(self.form_to_storage) + except (TypeError, ValueError): + pass + elif isinstance(current_widget, FCComboBox): + try: + current_widget.currentIndexChanged.disconnect(self.form_to_storage) + except (TypeError, ValueError): + pass + + try: + self.rest_cb.stateChanged.disconnect() + except (TypeError, ValueError): + pass + try: + self.order_radio.activated_custom[str].disconnect() + except (TypeError, ValueError): + pass + + # rows selected + try: + self.tools_table.clicked.disconnect() + except (TypeError, AttributeError): + pass + try: + self.tools_table.horizontalHeader().sectionClicked.disconnect() + except (TypeError, AttributeError): + pass + + def on_row_selection_change(self): + self.blockSignals(True) + + sel_rows = [it.row() for it in self.tools_table.selectedItems()] + # sel_rows = sorted(set(index.row() for index in self.tools_table.selectedIndexes())) + + if not sel_rows: + sel_rows = [0] + + for current_row in sel_rows: + # populate the form with the data from the tool associated with the row parameter + try: + item = self.tools_table.item(current_row, 3) + if item is not None: + tooluid = int(item.text()) + else: + return + except Exception as e: + log.debug("Tool missing. Add a tool in the Tool Table. %s" % str(e)) + return + + # update the QLabel that shows for which Tool we have the parameters in the UI form + if len(sel_rows) == 1: + cr = current_row + 1 + self.tool_data_label.setText( + "%s: %s %d" % (_('Parameters for'), _("Tool"), cr) + ) + try: + # set the form with data from the newly selected tool + for tooluid_key, tooluid_value in list(self.iso_tools.items()): + if int(tooluid_key) == tooluid: + for key, value in tooluid_value.items(): + if key == 'data': + form_value_storage = tooluid_value[key] + self.storage_to_form(form_value_storage) + except Exception as e: + log.debug("ToolIsolation ---> update_ui() " + str(e)) + else: + self.tool_data_label.setText( + "%s: %s" % (_('Parameters for'), _("Multiple Tools")) + ) + + self.blockSignals(False) + + def storage_to_form(self, dict_storage): + for form_key in self.form_fields: + for storage_key in dict_storage: + if form_key == storage_key: + try: + self.form_fields[form_key].set_value(dict_storage[form_key]) + except Exception as e: + log.debug("ToolIsolation.storage_to_form() --> %s" % str(e)) + pass + + def form_to_storage(self): + if self.tools_table.rowCount() == 0: + # there is no tool in tool table so we can't save the GUI elements values to storage + return + + self.blockSignals(True) + + widget_changed = self.sender() + wdg_objname = widget_changed.objectName() + option_changed = self.name2option[wdg_objname] + + # row = self.tools_table.currentRow() + rows = sorted(set(index.row() for index in self.tools_table.selectedIndexes())) + for row in rows: + if row < 0: + row = 0 + tooluid_item = int(self.tools_table.item(row, 3).text()) + + for tooluid_key, tooluid_val in self.iso_tools.items(): + if int(tooluid_key) == tooluid_item: + new_option_value = self.form_fields[option_changed].get_value() + if option_changed in tooluid_val: + tooluid_val[option_changed] = new_option_value + if option_changed in tooluid_val['data']: + tooluid_val['data'][option_changed] = new_option_value + + self.blockSignals(False) + + def on_apply_param_to_all_clicked(self): + if self.tools_table.rowCount() == 0: + # there is no tool in tool table so we can't save the GUI elements values to storage + log.debug("ToolIsolation.on_apply_param_to_all_clicked() --> no tool in Tools Table, aborting.") + return + + self.blockSignals(True) + + row = self.tools_table.currentRow() + if row < 0: + row = 0 + + tooluid_item = int(self.tools_table.item(row, 3).text()) + temp_tool_data = {} + + for tooluid_key, tooluid_val in self.iso_tools.items(): + if int(tooluid_key) == tooluid_item: + # this will hold the 'data' key of the self.tools[tool] dictionary that corresponds to + # the current row in the tool table + temp_tool_data = tooluid_val['data'] + break + + for tooluid_key, tooluid_val in self.iso_tools.items(): + tooluid_val['data'] = deepcopy(temp_tool_data) + + self.app.inform.emit('[success] %s' % _("Current Tool parameters were applied to all tools.")) + self.blockSignals(False) + + def on_add_tool_by_key(self): + tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"), + text='%s:' % _('Enter a Tool Diameter'), + min=0.0001, max=9999.9999, decimals=self.decimals) + tool_add_popup.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/letter_t_32.png')) + + val, ok = tool_add_popup.get_value() + if ok: + if float(val) == 0: + self.app.inform.emit('[WARNING_NOTCL] %s' % + _("Please enter a tool diameter with non-zero value, in Float format.")) + return + self.on_tool_add(dia=float(val)) + else: + self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Adding Tool cancelled")) + + def on_tooldia_updated(self): + if self.tool_type_radio.get_value() == 'C1': + self.old_tool_dia = self.addtool_entry.get_value() + + def on_reference_combo_changed(self): + obj_type = self.reference_combo_type.currentIndex() + self.reference_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) + self.reference_combo.setCurrentIndex(0) + self.reference_combo.obj_type = { + _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry" + }[self.reference_combo_type.get_value()] + + def on_toggle_reference(self): + val = self.select_combo.get_value() + + if val == _("All"): + self.reference_combo.hide() + self.reference_combo_label.hide() + self.reference_combo_type.hide() + self.reference_combo_type_label.hide() + self.area_shape_label.hide() + self.area_shape_radio.hide() + + # disable rest-machining for area painting + self.rest_cb.setDisabled(False) + elif val == _("Area Selection"): + self.reference_combo.hide() + self.reference_combo_label.hide() + self.reference_combo_type.hide() + self.reference_combo_type_label.hide() + self.area_shape_label.show() + self.area_shape_radio.show() + + # disable rest-machining for area isolation + self.rest_cb.set_value(False) + self.rest_cb.setDisabled(True) + elif val == _("Polygon Selection"): + self.reference_combo.hide() + self.reference_combo_label.hide() + self.reference_combo_type.hide() + self.reference_combo_type_label.hide() + self.area_shape_label.hide() + self.area_shape_radio.hide() + else: + self.reference_combo.show() + self.reference_combo_label.show() + self.reference_combo_type.show() + self.reference_combo_type_label.show() + self.area_shape_label.hide() + self.area_shape_radio.hide() + + # disable rest-machining for area painting + self.rest_cb.setDisabled(False) + + def on_order_changed(self, order): + if order != 'no': + self.build_ui() + + def on_rest_machining_check(self, state): + if state: + self.order_radio.set_value('rev') + self.order_label.setDisabled(True) + self.order_radio.setDisabled(True) + + self.old_combine_state = self.combine_passes_cb.get_value() + self.combine_passes_cb.set_value(True) + self.combine_passes_cb.setDisabled(True) + else: + self.order_label.setDisabled(False) + self.order_radio.setDisabled(False) + + self.combine_passes_cb.set_value(self.old_combine_state) + self.combine_passes_cb.setDisabled(False) + + def on_tooltable_cellwidget_change(self): + cw = self.sender() + assert isinstance(cw, QtWidgets.QComboBox), \ + "Expected a QtWidgets.QComboBox, got %s" % isinstance(cw, QtWidgets.QComboBox) + + cw_index = self.tools_table.indexAt(cw.pos()) + cw_row = cw_index.row() + cw_col = cw_index.column() + + current_uid = int(self.tools_table.item(cw_row, 3).text()) + + # if the sender is in the column with index 2 then we update the tool_type key + if cw_col == 2: + tt = cw.currentText() + typ = 'Iso' if tt == 'V' else "Rough" + + self.iso_tools[current_uid].update({ + 'type': typ, + 'tool_type': tt, + }) + + def on_tool_type(self, val): + if val == 'V': + self.addtool_entry_lbl.setDisabled(True) + self.addtool_entry.setDisabled(True) + self.tipdialabel.show() + self.tipdia_entry.show() + self.tipanglelabel.show() + self.tipangle_entry.show() + + self.on_calculate_tooldia() + else: + self.addtool_entry_lbl.setDisabled(False) + self.addtool_entry.setDisabled(False) + self.tipdialabel.hide() + self.tipdia_entry.hide() + self.tipanglelabel.hide() + self.tipangle_entry.hide() + + self.addtool_entry.set_value(self.old_tool_dia) + + def on_calculate_tooldia(self): + if self.tool_type_radio.get_value() == 'V': + tip_dia = float(self.tipdia_entry.get_value()) + tip_angle = float(self.tipangle_entry.get_value()) / 2.0 + cut_z = float(self.cutz_entry.get_value()) + cut_z = -cut_z if cut_z < 0 else cut_z + + # calculated tool diameter so the cut_z parameter is obeyed + tool_dia = tip_dia + (2 * cut_z * math.tan(math.radians(tip_angle))) + + # update the default_data so it is used in the iso_tools dict + self.default_data.update({ + "vtipdia": tip_dia, + "vtipangle": (tip_angle * 2), + }) + + self.addtool_entry.set_value(tool_dia) + + return tool_dia + else: + return float(self.addtool_entry.get_value()) + + def on_tool_add(self, dia=None, muted=None): + self.blockSignals(True) + + self.units = self.app.defaults['units'].upper() + + if dia: + tool_dia = dia + else: + tool_dia = self.on_calculate_tooldia() + if tool_dia is None: + self.build_ui() + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter to add, in Float format.")) + return + + tool_dia = float('%.*f' % (self.decimals, tool_dia)) + + if tool_dia == 0: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, " + "in Float format.")) + return + + # construct a list of all 'tooluid' in the self.tools + tool_uid_list = [] + for tooluid_key in self.iso_tools: + tool_uid_item = int(tooluid_key) + tool_uid_list.append(tool_uid_item) + + # find maximum from the temp_uid, add 1 and this is the new 'tooluid' + if not tool_uid_list: + max_uid = 0 + else: + max_uid = max(tool_uid_list) + self.tooluid = int(max_uid + 1) + + tool_dias = [] + for k, v in self.iso_tools.items(): + for tool_v in v.keys(): + if tool_v == 'tooldia': + tool_dias.append(float('%.*f' % (self.decimals, (v[tool_v])))) + + if float('%.*f' % (self.decimals, tool_dia)) in tool_dias: + if muted is None: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Tool already in Tool Table.")) + # self.tools_table.itemChanged.connect(self.on_tool_edit) + self.blockSignals(False) + + return + else: + if muted is None: + self.app.inform.emit('[success] %s' % _("New tool added to Tool Table.")) + self.iso_tools.update({ + int(self.tooluid): { + 'tooldia': float('%.*f' % (self.decimals, tool_dia)), + 'offset': 'Path', + 'offset_value': 0.0, + 'type': 'Iso', + 'tool_type': self.tool_type_radio.get_value(), + 'data': deepcopy(self.default_data), + 'solid_geometry': [] + } + }) + + self.blockSignals(False) + self.build_ui() + + def on_tool_edit(self): + self.blockSignals(True) + + old_tool_dia = '' + tool_dias = [] + for k, v in self.iso_tools.items(): + for tool_v in v.keys(): + if tool_v == 'tooldia': + tool_dias.append(float('%.*f' % (self.decimals, v[tool_v]))) + + for row in range(self.tools_table.rowCount()): + + try: + new_tool_dia = float(self.tools_table.item(row, 1).text()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + new_tool_dia = float(self.tools_table.item(row, 1).text().replace(',', '.')) + except ValueError: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number.")) + self.blockSignals(False) + return + + tooluid = int(self.tools_table.item(row, 3).text()) + + # identify the tool that was edited and get it's tooluid + if new_tool_dia not in tool_dias: + self.iso_tools[tooluid]['tooldia'] = new_tool_dia + self.app.inform.emit('[success] %s' % _("Tool from Tool Table was edited.")) + self.blockSignals(False) + self.build_ui() + return + else: + # identify the old tool_dia and restore the text in tool table + for k, v in self.iso_tools.items(): + if k == tooluid: + old_tool_dia = v['tooldia'] + break + restore_dia_item = self.tools_table.item(row, 1) + restore_dia_item.setText(str(old_tool_dia)) + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. " + "New diameter value is already in the Tool Table.")) + self.blockSignals(False) + self.build_ui() + + def on_tool_delete(self, rows_to_delete=None, all_tools=None): + """ + Will delete a tool in the tool table + + :param rows_to_delete: which rows to delete; can be a list + :param all_tools: delete all tools in the tool table + :return: + """ + self.blockSignals(True) + + deleted_tools_list = [] + + if all_tools: + self.iso_tools.clear() + self.blockSignals(False) + self.build_ui() + return + + if rows_to_delete: + try: + for row in rows_to_delete: + tooluid_del = int(self.tools_table.item(row, 3).text()) + deleted_tools_list.append(tooluid_del) + except TypeError: + tooluid_del = int(self.tools_table.item(rows_to_delete, 3).text()) + deleted_tools_list.append(tooluid_del) + + for t in deleted_tools_list: + self.iso_tools.pop(t, None) + + self.blockSignals(False) + self.build_ui() + return + + try: + if self.tools_table.selectedItems(): + for row_sel in self.tools_table.selectedItems(): + row = row_sel.row() + if row < 0: + continue + tooluid_del = int(self.tools_table.item(row, 3).text()) + deleted_tools_list.append(tooluid_del) + + for t in deleted_tools_list: + self.iso_tools.pop(t, None) + + except AttributeError: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Delete failed. Select a tool to delete.")) + self.blockSignals(False) + return + except Exception as e: + log.debug(str(e)) + + self.app.inform.emit('[success] %s' % _("Tool(s) deleted from Tool Table.")) + self.blockSignals(False) + self.build_ui() + + def on_generate_buffer(self): + self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Buffering solid geometry")) + + self.obj_name = self.object_combo.currentText() + + # Get source object. + try: + self.grb_obj = self.app.collection.get_by_name(self.obj_name) + except Exception as e: + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name))) + return "Could not retrieve object: %s with error: %s" % (self.obj_name, str(e)) + + if self.grb_obj is None: + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name))) + return + + def buffer_task(): + with self.app.proc_container.new('%s...' % _("Buffering")): + if isinstance(self.grb_obj.solid_geometry, list): + self.grb_obj.solid_geometry = MultiPolygon(self.grb_obj.solid_geometry) + + self.grb_obj.solid_geometry = self.grb_obj.solid_geometry.buffer(0.0000001) + self.grb_obj.solid_geometry = self.grb_obj.solid_geometry.buffer(-0.0000001) + self.app.inform.emit('[success] %s.' % _("Done")) + self.grb_obj.plot_single_object.emit() + + self.app.worker_task.emit({'fcn': buffer_task, 'params': []}) + + def on_iso_button_click(self): + + self.obj_name = self.object_combo.currentText() + + # Get source object. + try: + self.grb_obj = self.app.collection.get_by_name(self.obj_name) + except Exception as e: + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(self.obj_name))) + return "Could not retrieve object: %s with error: %s" % (self.obj_name, str(e)) + + if self.grb_obj is None: + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name))) + return + + def worker_task(iso_obj): + with self.app.proc_container.new(_("Isolating...")): + self.isolate_handler(iso_obj) + + self.app.worker_task.emit({'fcn': worker_task, 'params': [self.grb_obj]}) + + def follow_geo(self, followed_obj, outname): + """ + Creates a geometry object "following" the gerber paths. + + :param followed_obj: Gerber object for which to generate the follow geometry + :type followed_obj: AppObjects.FlatCAMGerber.GerberObject + :param outname: Nme of the resulting Geometry object + :type outname: str + :return: None + """ + + def follow_init(follow_obj, app_obj): + # Propagate options + follow_obj.options["cnctooldia"] = str(tooldia) + follow_obj.solid_geometry = self.grb_obj.follow_geometry + + # in the end toggle the visibility of the origin object so we can see the generated Geometry + followed_obj.ui.plot_cb.set_value(False) + follow_name = outname + + for tool in self.iso_tools: + tooldia = self.iso_tools[tool]['tooldia'] + new_name = "%s_%.*f" % (follow_name, self.decimals, tooldia) + + follow_state = self.iso_tools[tool]['data']['tools_iso_follow'] + if follow_state: + ret = self.app.app_obj.new_object("geometry", new_name, follow_init) + if ret == 'fail': + self.app.inform.emit("[ERROR_NOTCL] %s: %.*f" % ( + _("Failed to create Follow Geometry with tool diameter"), self.decimals, tooldia)) + else: + self.app.inform.emit("[success] %s: %.*f" % ( + _("Follow Geometry was created with tool diameter"), self.decimals, tooldia)) + + def isolate_handler(self, isolated_obj): + """ + Creates a geometry object with paths around the gerber features. + + :param isolated_obj: Gerber object for which to generate the isolating routing geometry + :type isolated_obj: AppObjects.FlatCAMGerber.GerberObject + :return: None + """ + selection = self.select_combo.get_value() + + if selection == _("All"): + self.isolate(isolated_obj=isolated_obj) + elif selection == _("Area Selection"): + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area.")) + + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot) + self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot) + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot) + else: + self.app.plotcanvas.graph_event_disconnect(self.app.mp) + self.app.plotcanvas.graph_event_disconnect(self.app.mm) + self.app.plotcanvas.graph_event_disconnect(self.app.mr) + + self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release) + self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move) + self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press) + + # disconnect flags + self.area_sel_disconnect_flag = True + + elif selection == _("Polygon Selection"): + # disengage the grid snapping since it may be hard to click on polygons with grid snapping on + if self.app.ui.grid_snap_btn.isChecked(): + self.grid_status_memory = True + self.app.ui.grid_snap_btn.trigger() + else: + self.grid_status_memory = False + + self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_poly_mouse_click_release) + self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press) + + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_release', + self.app.on_mouse_click_release_over_plot) + else: + self.app.plotcanvas.graph_event_disconnect(self.app.mr) + + # disconnect flags + self.poly_sel_disconnect_flag = True + + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click on a polygon to isolate it.")) + elif selection == _("Reference Object"): + ref_obj = self.app.collection.get_by_name(self.reference_combo.get_value()) + ref_geo = cascaded_union(ref_obj.solid_geometry) + use_geo = cascaded_union(isolated_obj.solid_geometry).difference(ref_geo) + self.isolate(isolated_obj=isolated_obj, geometry=use_geo) + + def isolate(self, isolated_obj, geometry=None, limited_area=None, plot=True): + """ + Creates an isolation routing geometry object in the project. + + :param isolated_obj: Gerber object for which to generate the isolating routing geometry + :type isolated_obj: AppObjects.FlatCAMGerber.GerberObject + :param geometry: specific geometry to isolate + :type geometry: List of Shapely polygon + :param limited_area: if not None isolate only this area + :type limited_area: Shapely Polygon or a list of them + :param plot: if to plot the resulting geometry object + :type plot: bool + :return: None + """ + + combine = self.combine_passes_cb.get_value() + tools_storage = self.iso_tools + + # update the Common Parameters valuse in the self.iso_tools + for tool_iso in self.iso_tools: + for key in self.iso_tools[tool_iso]: + if key == 'data': + self.iso_tools[tool_iso][key]["tools_iso_rest"] = self.rest_cb.get_value() + self.iso_tools[tool_iso][key]["tools_iso_combine_passes"] = combine + self.iso_tools[tool_iso][key]["tools_iso_isoexcept"] = self.except_cb.get_value() + self.iso_tools[tool_iso][key]["tools_iso_selection"] = self.select_combo.get_value() + self.iso_tools[tool_iso][key]["tools_iso_area_shape"] = self.area_shape_radio.get_value() + + if combine: + if self.rest_cb.get_value(): + self.combined_rest(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage, + lim_area=limited_area, plot=plot) + else: + self.combined_normal(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage, + lim_area=limited_area, plot=plot) + + else: + for tool in tools_storage: + tool_data = tools_storage[tool]['data'] + to_follow = tool_data['tools_iso_follow'] + + work_geo = geometry + if work_geo is None: + work_geo = isolated_obj.follow_geometry if to_follow else isolated_obj.solid_geometry + + iso_t = { + 'ext': 0, + 'int': 1, + 'full': 2 + }[tool_data['tools_iso_isotype']] + + passes = tool_data['tools_iso_passes'] + overlap = tool_data['tools_iso_overlap'] + overlap /= 100.0 + + milling_type = tool_data['tools_iso_milling_type'] + + iso_except = self.except_cb.get_value() + + for i in range(passes): + tool_dia = tools_storage[tool]['tooldia'] + tool_type = tools_storage[tool]['tool_type'] + + iso_offset = tool_dia * ((2 * i + 1) / 2.0000001) - (i * overlap * tool_dia) + outname = "%s_%.*f" % (isolated_obj.options["name"], self.decimals, float(tool_dia)) + + if passes > 1: + iso_name = outname + "_iso" + str(i + 1) + if iso_t == 0: + iso_name = outname + "_ext_iso" + str(i + 1) + elif iso_t == 1: + iso_name = outname + "_int_iso" + str(i + 1) + else: + iso_name = outname + "_iso" + if iso_t == 0: + iso_name = outname + "_ext_iso" + elif iso_t == 1: + iso_name = outname + "_int_iso" + + # if milling type is climb then the move is counter-clockwise around features + mill_dir = 1 if milling_type == 'cl' else 0 + + iso_geo = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t, + follow=to_follow, nr_passes=i) + if iso_geo == 'fail': + self.app.inform.emit( + '[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated.")) + continue + + # ############################################################ + # ########## AREA SUBTRACTION ################################ + # ############################################################ + if iso_except: + self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo")) + iso_geo = self.area_subtraction(iso_geo) + + if limited_area: + self.app.proc_container.update_view_text(' %s' % _("Intersecting Geo")) + iso_geo = self.area_intersection(iso_geo, intersection_geo=limited_area) + + tool_data.update({ + "name": iso_name, + }) + + def iso_init(geo_obj, fc_obj): + # Propagate options + geo_obj.options["cnctooldia"] = str(tool_dia) + geo_obj.solid_geometry = deepcopy(iso_geo) + + # ############################################################ + # ########## AREA SUBTRACTION ################################ + # ############################################################ + if self.except_cb.get_value(): + self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo")) + geo_obj.solid_geometry = self.area_subtraction(geo_obj.solid_geometry) + + geo_obj.tools = {} + geo_obj.tools['1'] = {} + geo_obj.tools.update({ + '1': { + 'tooldia': float(tool_dia), + 'offset': 'Path', + 'offset_value': 0.0, + 'type': _('Rough'), + 'tool_type': tool_type, + 'data': tool_data, + 'solid_geometry': geo_obj.solid_geometry + } + }) + + # detect if solid_geometry is empty and this require list flattening which is "heavy" + # or just looking in the lists (they are one level depth) and if any is not empty + # proceed with object creation, if there are empty and the number of them is the length + # of the list then we have an empty solid_geometry which should raise a Custom Exception + empty_cnt = 0 + if not isinstance(geo_obj.solid_geometry, list): + geo_obj.solid_geometry = [geo_obj.solid_geometry] + + for g in geo_obj.solid_geometry: + if g: + break + else: + empty_cnt += 1 + + if empty_cnt == len(geo_obj.solid_geometry): + fc_obj.inform.emit('[ERROR_NOTCL] %s: %s' % ( + _("Empty Geometry in"), geo_obj.options["name"])) + return 'fail' + else: + fc_obj.inform.emit('[success] %s: %s' % + (_("Isolation geometry created"), geo_obj.options["name"])) + geo_obj.multigeo = False + + self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot) + + def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, plot=True): + """ + + :param iso_obj: the isolated Gerber object + :type iso_obj: AppObjects.FlatCAMGerber.GerberObject + :param iso2geo: specific geometry to isolate + :type iso2geo: list of Shapely Polygon + :param tools_storage: a dictionary that holds the tools and geometry + :type tools_storage: dict + :param lim_area: if not None restrict isolation to this area + :type lim_area: Shapely Polygon or a list of them + :param plot: if to plot the resulting geometry object + :type plot: bool + :return: Isolated solid geometry + :rtype: + """ + + log.debug("ToolIsolation.combine_rest()") + + total_solid_geometry = [] + + iso_name = iso_obj.options["name"] + '_iso_rest' + work_geo = iso_obj.solid_geometry if iso2geo is None else iso2geo + + sorted_tools = [] + for k, v in self.iso_tools.items(): + sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia'])))) + + order = self.order_radio.get_value() + if order == 'fwd': + sorted_tools.sort(reverse=False) + elif order == 'rev': + sorted_tools.sort(reverse=True) + else: + pass + + for sorted_tool in sorted_tools: + for tool in tools_storage: + if float('%.*f' % (self.decimals, tools_storage[tool]['tooldia'])) == sorted_tool: + + tool_dia = tools_storage[tool]['tooldia'] + tool_type = tools_storage[tool]['tool_type'] + tool_data = tools_storage[tool]['data'] + + iso_t = { + 'ext': 0, + 'int': 1, + 'full': 2 + }[tool_data['tools_iso_isotype']] + + passes = tool_data['tools_iso_passes'] + overlap = tool_data['tools_iso_overlap'] + overlap /= 100.0 + + milling_type = tool_data['tools_iso_milling_type'] + # if milling type is climb then the move is counter-clockwise around features + mill_dir = True if milling_type == 'cl' else False + + iso_except = self.except_cb.get_value() + + outname = "%s_%.*f" % (iso_obj.options["name"], self.decimals, float(tool_dia)) + + internal_name = outname + "_iso" + if iso_t == 0: + internal_name = outname + "_ext_iso" + elif iso_t == 1: + internal_name = outname + "_int_iso" + + tool_data.update({ + "name": internal_name, + }) + + solid_geo, work_geo = self.generate_rest_geometry(geometry=work_geo, tooldia=tool_dia, + passes=passes, overlap=overlap, invert=mill_dir, + env_iso_type=iso_t) + + # ############################################################ + # ########## AREA SUBTRACTION ################################ + # ############################################################ + if iso_except: + self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo")) + solid_geo = self.area_subtraction(solid_geo) + + if lim_area: + self.app.proc_container.update_view_text(' %s' % _("Intersecting Geo")) + solid_geo = self.area_intersection(solid_geo, intersection_geo=lim_area) + + tools_storage.update({ + tool: { + 'tooldia': float(tool_dia), + 'offset': 'Path', + 'offset_value': 0.0, + 'type': _('Rough'), + 'tool_type': tool_type, + 'data': tool_data, + 'solid_geometry': deepcopy(solid_geo) + } + }) + + total_solid_geometry += solid_geo + + # if the geometry is all isolated + if not work_geo: + break + + def iso_init(geo_obj, app_obj): + geo_obj.options["cnctooldia"] = str(tool_dia) + + geo_obj.tools = dict(tools_storage) + geo_obj.solid_geometry = total_solid_geometry + # even if combine is checked, one pass is still single-geo + + # remove the tools that have no geometry + for geo_tool in list(geo_obj.tools.keys()): + if not geo_obj.tools[geo_tool]['solid_geometry']: + geo_obj.tools.pop(geo_tool, None) + + if len(tools_storage) > 1: + geo_obj.multigeo = True + else: + for ky in tools_storage.keys(): + passes_no = float(tools_storage[ky]['data']['tools_iso_passes']) + geo_obj.multigeo = True if passes_no > 1 else False + break + + # detect if solid_geometry is empty and this require list flattening which is "heavy" + # or just looking in the lists (they are one level depth) and if any is not empty + # proceed with object creation, if there are empty and the number of them is the length + # of the list then we have an empty solid_geometry which should raise a Custom Exception + empty_cnt = 0 + if not isinstance(geo_obj.solid_geometry, list) and \ + not isinstance(geo_obj.solid_geometry, MultiPolygon): + geo_obj.solid_geometry = [geo_obj.solid_geometry] + + for g in geo_obj.solid_geometry: + if g: + break + else: + empty_cnt += 1 + + if empty_cnt == len(geo_obj.solid_geometry): + app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Empty Geometry in"), geo_obj.options["name"])) + return 'fail' + else: + app_obj.inform.emit('[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"])) + + self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot) + + # the tools are finished but the isolation is not finished therefore it failed + if work_geo: + self.app.inform.emit("[WARNING] %s" % _("Partial failure. The geometry was processed with all tools.\n" + "But there are still un-isolated geometry elements. " + "Try to include a tool with smaller diameter.")) + self.app.shell_message(msg=_("The following are coordinates for the copper features " + "that could not be isolated:")) + msg = '' + for geo in work_geo: + pt = geo.representative_point() + coords = '(%s, %s), ' % (str(pt.x), str(pt.y)) + msg += coords + self.app.shell_message(msg=msg) + + def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, plot=True): + """ + + :param iso_obj: the isolated Gerber object + :type iso_obj: AppObjects.FlatCAMGerber.GerberObject + :param iso2geo: specific geometry to isolate + :type iso2geo: list of Shapely Polygon + :param tools_storage: a dictionary that holds the tools and geometry + :type tools_storage: dict + :param lim_area: if not None restrict isolation to this area + :type lim_area: Shapely Polygon or a list of them + :param plot: if to plot the resulting geometry object + :type plot: bool + :return: Isolated solid geometry + :rtype: + """ + log.debug("ToolIsolation.combined_normal()") + + total_solid_geometry = [] + + iso_name = iso_obj.options["name"] + '_iso_combined' + geometry = iso2geo + + for tool in tools_storage: + tool_dia = tools_storage[tool]['tooldia'] + tool_type = tools_storage[tool]['tool_type'] + tool_data = tools_storage[tool]['data'] + + to_follow = tool_data['tools_iso_follow'] + + # TODO what to do when the iso2geo param is not None but the Follow cb is checked + # for the case when limited area is used .... the follow geo should be clipped too + work_geo = geometry + if work_geo is None: + work_geo = iso_obj.follow_geometry if to_follow else iso_obj.solid_geometry + + iso_t = { + 'ext': 0, + 'int': 1, + 'full': 2 + }[tool_data['tools_iso_isotype']] + + passes = tool_data['tools_iso_passes'] + overlap = tool_data['tools_iso_overlap'] + overlap /= 100.0 + + milling_type = tool_data['tools_iso_milling_type'] + + iso_except = self.except_cb.get_value() + + outname = "%s_%.*f" % (iso_obj.options["name"], self.decimals, float(tool_dia)) + + internal_name = outname + "_iso" + if iso_t == 0: + internal_name = outname + "_ext_iso" + elif iso_t == 1: + internal_name = outname + "_int_iso" + + tool_data.update({ + "name": internal_name, + }) + + solid_geo = [] + for nr_pass in range(passes): + iso_offset = tool_dia * ((2 * nr_pass + 1) / 2.0000001) - (nr_pass * overlap * tool_dia) + + # if milling type is climb then the move is counter-clockwise around features + mill_dir = 1 if milling_type == 'cl' else 0 + + iso_geo = self.generate_envelope(iso_offset, mill_dir, geometry=work_geo, env_iso_type=iso_t, + follow=to_follow, nr_passes=nr_pass) + if iso_geo == 'fail': + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated.")) + continue + try: + for geo in iso_geo: + solid_geo.append(geo) + except TypeError: + solid_geo.append(iso_geo) + + # ############################################################ + # ########## AREA SUBTRACTION ################################ + # ############################################################ + if iso_except: + self.app.proc_container.update_view_text(' %s' % _("Subtracting Geo")) + solid_geo = self.area_subtraction(solid_geo) + + if lim_area: + self.app.proc_container.update_view_text(' %s' % _("Intersecting Geo")) + solid_geo = self.area_intersection(solid_geo, intersection_geo=lim_area) + + tools_storage.update({ + tool: { + 'tooldia': float(tool_dia), + 'offset': 'Path', + 'offset_value': 0.0, + 'type': _('Rough'), + 'tool_type': tool_type, + 'data': tool_data, + 'solid_geometry': deepcopy(solid_geo) + } + }) + + total_solid_geometry += solid_geo + + def iso_init(geo_obj, app_obj): + geo_obj.options["cnctooldia"] = str(tool_dia) + + geo_obj.tools = dict(tools_storage) + geo_obj.solid_geometry = total_solid_geometry + # even if combine is checked, one pass is still single-geo + + if len(tools_storage) > 1: + geo_obj.multigeo = True + else: + if to_follow: + geo_obj.multigeo = False + else: + passes_no = 1 + for ky in tools_storage.keys(): + passes_no = float(tools_storage[ky]['data']['tools_iso_passes']) + geo_obj.multigeo = True if passes_no > 1 else False + break + geo_obj.multigeo = True if passes_no > 1 else False + + # detect if solid_geometry is empty and this require list flattening which is "heavy" + # or just looking in the lists (they are one level depth) and if any is not empty + # proceed with object creation, if there are empty and the number of them is the length + # of the list then we have an empty solid_geometry which should raise a Custom Exception + empty_cnt = 0 + if not isinstance(geo_obj.solid_geometry, list) and \ + not isinstance(geo_obj.solid_geometry, MultiPolygon): + geo_obj.solid_geometry = [geo_obj.solid_geometry] + + for g in geo_obj.solid_geometry: + if g: + break + else: + empty_cnt += 1 + + if empty_cnt == len(geo_obj.solid_geometry): + app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Empty Geometry in"), geo_obj.options["name"])) + return 'fail' + else: + app_obj.inform.emit('[success] %s: %s' % (_("Isolation geometry created"), geo_obj.options["name"])) + + self.app.app_obj.new_object("geometry", iso_name, iso_init, plot=plot) + + def area_subtraction(self, geo, subtraction_geo=None): + """ + Subtracts the subtraction_geo (if present else self.solid_geometry) from the geo + + :param geo: target geometry from which to subtract + :param subtraction_geo: geometry that acts as subtraction geo + :return: + """ + new_geometry = [] + target_geo = geo + + if subtraction_geo: + sub_union = cascaded_union(subtraction_geo) + else: + name = self.exc_obj_combo.currentText() + subtractor_obj = self.app.collection.get_by_name(name) + sub_union = cascaded_union(subtractor_obj.solid_geometry) + + try: + for geo_elem in target_geo: + if isinstance(geo_elem, Polygon): + for ring in self.poly2rings(geo_elem): + new_geo = ring.difference(sub_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiPolygon): + for poly in geo_elem: + for ring in self.poly2rings(poly): + new_geo = ring.difference(sub_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, LineString) or isinstance(geo_elem, LinearRing): + new_geo = geo_elem.difference(sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiLineString): + for line_elem in geo_elem: + new_geo = line_elem.difference(sub_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + except TypeError: + if isinstance(target_geo, Polygon): + for ring in self.poly2rings(target_geo): + new_geo = ring.difference(sub_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(target_geo, LineString) or isinstance(target_geo, LinearRing): + new_geo = target_geo.difference(sub_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(target_geo, MultiLineString): + for line_elem in target_geo: + new_geo = line_elem.difference(sub_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + return new_geometry + + def area_intersection(self, geo, intersection_geo=None): + """ + Return the intersection geometry between geo and intersection_geo + + :param geo: target geometry + :param intersection_geo: second geometry + :return: + """ + new_geometry = [] + target_geo = geo + + intersect_union = cascaded_union(intersection_geo) + + try: + for geo_elem in target_geo: + if isinstance(geo_elem, Polygon): + for ring in self.poly2rings(geo_elem): + new_geo = ring.intersection(intersect_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiPolygon): + for poly in geo_elem: + for ring in self.poly2rings(poly): + new_geo = ring.intersection(intersect_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, LineString) or isinstance(geo_elem, LinearRing): + new_geo = geo_elem.intersection(intersect_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiLineString): + for line_elem in geo_elem: + new_geo = line_elem.intersection(intersect_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + except TypeError: + if isinstance(target_geo, Polygon): + for ring in self.poly2rings(target_geo): + new_geo = ring.intersection(intersect_union) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(target_geo, LineString) or isinstance(target_geo, LinearRing): + new_geo = target_geo.intersection(intersect_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(target_geo, MultiLineString): + for line_elem in target_geo: + new_geo = line_elem.intersection(intersect_union) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + return new_geometry + + def on_poly_mouse_click_release(self, event): + if self.app.is_legacy is False: + event_pos = event.pos + right_button = 2 + self.app.event_is_dragging = self.app.event_is_dragging + else: + event_pos = (event.xdata, event.ydata) + right_button = 3 + self.app.event_is_dragging = self.app.ui.popMenu.mouse_is_panning + + try: + x = float(event_pos[0]) + y = float(event_pos[1]) + except TypeError: + return + + event_pos = (x, y) + curr_pos = self.app.plotcanvas.translate_coords(event_pos) + if self.app.grid_status(): + curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1]) + else: + curr_pos = (curr_pos[0], curr_pos[1]) + + if event.button == 1: + clicked_poly = self.find_polygon(point=(curr_pos[0], curr_pos[1]), geoset=self.grb_obj.solid_geometry) + + if self.app.selection_type is not None: + self.selection_area_handler(self.app.pos, curr_pos, self.app.selection_type) + self.app.selection_type = None + elif clicked_poly: + if clicked_poly not in self.poly_dict.values(): + shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, shape=clicked_poly, + color=self.app.defaults['global_sel_draw_color'] + 'AF', + face_color=self.app.defaults['global_sel_draw_color'] + 'AF', + visible=True) + self.poly_dict[shape_id] = clicked_poly + self.app.inform.emit( + '%s: %d. %s' % (_("Added polygon"), int(len(self.poly_dict)), + _("Click to add next polygon or right click to start isolation.")) + ) + else: + try: + for k, v in list(self.poly_dict.items()): + if v == clicked_poly: + self.app.tool_shapes.remove(k) + self.poly_dict.pop(k) + break + except TypeError: + return + self.app.inform.emit( + '%s. %s' % (_("Removed polygon"), + _("Click to add/remove next polygon or right click to start isolation.")) + ) + + self.app.tool_shapes.redraw() + else: + self.app.inform.emit(_("No polygon detected under click position.")) + elif event.button == right_button and self.app.event_is_dragging is False: + # restore the Grid snapping if it was active before + if self.grid_status_memory is True: + self.app.ui.grid_snap_btn.trigger() + + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_poly_mouse_click_release) + self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_pres) + else: + self.app.plotcanvas.graph_event_disconnect(self.mr) + self.app.plotcanvas.graph_event_disconnect(self.kp) + + self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', + self.app.on_mouse_click_release_over_plot) + + # disconnect flags + self.poly_sel_disconnect_flag = False + + self.app.tool_shapes.clear(update=True) + + if self.poly_dict: + poly_list = deepcopy(list(self.poly_dict.values())) + self.isolate(isolated_obj=self.grb_obj, geometry=poly_list) + self.poly_dict.clear() + else: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("List of single polygons is empty. Aborting.")) + + def 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 + :return: + """ + poly_selection = Polygon([start_pos, (end_pos[0], start_pos[1]), end_pos, (start_pos[0], end_pos[1])]) + + # delete previous selection shape + self.app.delete_selection_shape() + + added_poly_count = 0 + try: + for geo in self.solid_geometry: + if geo not in self.poly_dict.values(): + if sel_type is True: + if geo.within(poly_selection): + shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, + shape=geo, + color=self.app.defaults['global_sel_draw_color'] + 'AF', + face_color=self.app.defaults[ + 'global_sel_draw_color'] + 'AF', + visible=True) + self.poly_dict[shape_id] = geo + added_poly_count += 1 + else: + if poly_selection.intersects(geo): + shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, + shape=geo, + color=self.app.defaults['global_sel_draw_color'] + 'AF', + face_color=self.app.defaults[ + 'global_sel_draw_color'] + 'AF', + visible=True) + self.poly_dict[shape_id] = geo + added_poly_count += 1 + except TypeError: + if self.solid_geometry not in self.poly_dict.values(): + if sel_type is True: + if self.solid_geometry.within(poly_selection): + shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, + shape=self.solid_geometry, + color=self.app.defaults['global_sel_draw_color'] + 'AF', + face_color=self.app.defaults[ + 'global_sel_draw_color'] + 'AF', + visible=True) + self.poly_dict[shape_id] = self.solid_geometry + added_poly_count += 1 + else: + if poly_selection.intersects(self.solid_geometry): + shape_id = self.app.tool_shapes.add(tolerance=self.drawing_tolerance, layer=0, + shape=self.solid_geometry, + color=self.app.defaults['global_sel_draw_color'] + 'AF', + face_color=self.app.defaults[ + 'global_sel_draw_color'] + 'AF', + visible=True) + self.poly_dict[shape_id] = self.solid_geometry + added_poly_count += 1 + + if added_poly_count > 0: + self.app.tool_shapes.redraw() + self.app.inform.emit( + '%s: %d. %s' % (_("Added polygon"), + int(added_poly_count), + _("Click to add next polygon or right click to start isolation.")) + ) + else: + self.app.inform.emit(_("No polygon in selection.")) + + # To be called after clicking on the plot. + def on_mouse_release(self, event): + if self.app.is_legacy is False: + event_pos = event.pos + # event_is_dragging = event.is_dragging + right_button = 2 + else: + event_pos = (event.xdata, event.ydata) + # event_is_dragging = self.app.plotcanvas.is_dragging + right_button = 3 + + event_pos = self.app.plotcanvas.translate_coords(event_pos) + if self.app.grid_status(): + curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1]) + else: + curr_pos = (event_pos[0], event_pos[1]) + + x1, y1 = curr_pos[0], curr_pos[1] + + shape_type = self.area_shape_radio.get_value() + + # do clear area only for left mouse clicks + if event.button == 1: + if shape_type == "square": + if self.first_click is False: + self.first_click = True + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the paint area.")) + + self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos) + if self.app.grid_status(): + self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1]) + else: + self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish.")) + self.app.delete_selection_shape() + + x0, y0 = self.cursor_pos[0], self.cursor_pos[1] + + pt1 = (x0, y0) + pt2 = (x1, y0) + pt3 = (x1, y1) + pt4 = (x0, y1) + + new_rectangle = Polygon([pt1, pt2, pt3, pt4]) + self.sel_rect.append(new_rectangle) + + # add a temporary shape on canvas + self.draw_tool_selection_shape(old_coords=(x0, y0), coords=(x1, y1)) + + self.first_click = False + return + else: + self.points.append((x1, y1)) + + if len(self.points) > 1: + self.poly_drawn = True + self.app.inform.emit(_("Click on next Point or click right mouse button to complete ...")) + + return "" + elif event.button == right_button and self.mouse_is_dragging is False: + + shape_type = self.area_shape_radio.get_value() + + if shape_type == "square": + self.first_click = False + else: + # if we finish to add a polygon + if self.poly_drawn is True: + try: + # try to add the point where we last clicked if it is not already in the self.points + last_pt = (x1, y1) + if last_pt != self.points[-1]: + self.points.append(last_pt) + except IndexError: + pass + + # we need to add a Polygon and a Polygon can be made only from at least 3 points + if len(self.points) > 2: + self.delete_moving_selection_shape() + pol = Polygon(self.points) + # do not add invalid polygons even if they are drawn by utility geometry + if pol.is_valid: + self.sel_rect.append(pol) + self.draw_selection_shape_polygon(points=self.points) + self.app.inform.emit( + _("Zone added. Click to start adding next zone or right click to finish.")) + + self.points = [] + self.poly_drawn = False + return + + self.delete_tool_selection_shape() + + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) + self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) + self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press) + else: + self.app.plotcanvas.graph_event_disconnect(self.mr) + self.app.plotcanvas.graph_event_disconnect(self.mm) + self.app.plotcanvas.graph_event_disconnect(self.kp) + + self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', + self.app.on_mouse_click_over_plot) + self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move', + self.app.on_mouse_move_over_plot) + self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', + self.app.on_mouse_click_release_over_plot) + + # disconnect flags + self.area_sel_disconnect_flag = False + + if len(self.sel_rect) == 0: + return + + self.sel_rect = cascaded_union(self.sel_rect) + self.isolate(isolated_obj=self.grb_obj, limited_area=self.sel_rect, plot=True) + self.sel_rect = [] + + # called on mouse move + def on_mouse_move(self, event): + shape_type = self.area_shape_radio.get_value() + + if self.app.is_legacy is False: + event_pos = event.pos + event_is_dragging = event.is_dragging + # right_button = 2 + else: + event_pos = (event.xdata, event.ydata) + event_is_dragging = self.app.plotcanvas.is_dragging + # right_button = 3 + + curr_pos = self.app.plotcanvas.translate_coords(event_pos) + + # detect mouse dragging motion + if event_is_dragging is True: + self.mouse_is_dragging = True + else: + self.mouse_is_dragging = False + + # update the cursor position + if self.app.grid_status(): + # Update cursor + curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1]) + + self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]), + symbol='++', edge_color=self.app.cursor_color_3D, + edge_width=self.app.defaults["global_cursor_width"], + size=self.app.defaults["global_cursor_size"]) + + if self.cursor_pos is None: + self.cursor_pos = (0, 0) + + self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) + self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) + + # # update the positions on status bar + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) + + # draw the utility geometry + if shape_type == "square": + if self.first_click: + self.app.delete_selection_shape() + self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]), + coords=(curr_pos[0], curr_pos[1])) + else: + self.delete_moving_selection_shape() + self.draw_moving_selection_shape_poly(points=self.points, data=(curr_pos[0], curr_pos[1])) + + def on_key_press(self, event): + # modifiers = QtWidgets.QApplication.keyboardModifiers() + # matplotlib_key_flag = False + + # events out of the self.app.collection view (it's about Project Tab) are of type int + if type(event) is int: + key = event + # events from the GUI are of type QKeyEvent + elif type(event) == QtGui.QKeyEvent: + key = event.key() + elif isinstance(event, mpl_key_event): # MatPlotLib key events are trickier to interpret than the rest + # matplotlib_key_flag = True + + key = event.key + key = QtGui.QKeySequence(key) + + # check for modifiers + key_string = key.toString().lower() + if '+' in key_string: + mod, __, key_text = key_string.rpartition('+') + if mod.lower() == 'ctrl': + # modifiers = QtCore.Qt.ControlModifier + pass + elif mod.lower() == 'alt': + # modifiers = QtCore.Qt.AltModifier + pass + elif mod.lower() == 'shift': + # modifiers = QtCore.Qt.ShiftModifier + pass + else: + # modifiers = QtCore.Qt.NoModifier + pass + key = QtGui.QKeySequence(key_text) + + # events from Vispy are of type KeyEvent + else: + key = event.key + + if key == QtCore.Qt.Key_Escape or key == 'Escape': + + if self.area_sel_disconnect_flag is True: + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) + self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) + self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press) + else: + self.app.plotcanvas.graph_event_disconnect(self.mr) + self.app.plotcanvas.graph_event_disconnect(self.mm) + self.app.plotcanvas.graph_event_disconnect(self.kp) + + self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', + self.app.on_mouse_click_over_plot) + self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move', + self.app.on_mouse_move_over_plot) + self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', + self.app.on_mouse_click_release_over_plot) + + if self.poly_sel_disconnect_flag is False: + # restore the Grid snapping if it was active before + if self.grid_status_memory is True: + self.app.ui.grid_snap_btn.trigger() + + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_poly_mouse_click_release) + self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_pres) + else: + self.app.plotcanvas.graph_event_disconnect(self.mr) + self.app.plotcanvas.graph_event_disconnect(self.kp) + + self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', + self.app.on_mouse_click_release_over_plot) + + self.points = [] + self.poly_drawn = False + self.delete_moving_selection_shape() + self.delete_tool_selection_shape() + + @staticmethod + def poly2rings(poly): + return [poly.exterior] + [interior for interior in poly.interiors] + + @staticmethod + def poly2ext(poly): + return [poly.exterior] + + @staticmethod + def poly2ints(poly): + return [interior for interior in poly.interiors] + + def generate_envelope(self, offset, invert, geometry=None, env_iso_type=2, follow=None, nr_passes=0): + """ + Isolation_geometry produces an envelope that is going on the left of the geometry + (the copper features). To leave the least amount of burrs on the features + the tool needs to travel on the right side of the features (this is called conventional milling) + the first pass is the one cutting all of the features, so it needs to be reversed + the other passes overlap preceding ones and cut the left over copper. It is better for them + to cut on the right side of the left over copper i.e on the left side of the features. + + :param offset: Offset distance to be passed to the obj.isolation_geometry() method + :type offset: float + :param invert: If to invert the direction of geometry (CW to CCW or reverse) + :type invert: int + :param geometry: Shapely Geometry for which t ogenerate envelope + :type geometry: + :param env_iso_type: type of isolation, can be 0 = exteriors or 1 = interiors or 2 = both (complete) + :type env_iso_type: int + :param follow: If the kind of isolation is a "follow" one + :type follow: bool + :param nr_passes: Number of passes + :type nr_passes: int + :return: The buffered geometry + :rtype: MultiPolygon or Polygon + """ + + if follow: + geom = self.grb_obj.isolation_geometry(offset, geometry=geometry, follow=follow) + return geom + else: + try: + geom = self.grb_obj.isolation_geometry(offset, geometry=geometry, iso_type=env_iso_type, + passes=nr_passes) + except Exception as e: + log.debug('ToolIsolation.generate_envelope() --> %s' % str(e)) + return 'fail' + + if invert: + try: + pl = [] + for p in geom: + if p is not None: + if isinstance(p, Polygon): + pl.append(Polygon(p.exterior.coords[::-1], p.interiors)) + elif isinstance(p, LinearRing): + pl.append(Polygon(p.coords[::-1])) + geom = MultiPolygon(pl) + except TypeError: + if isinstance(geom, Polygon) and geom is not None: + geom = Polygon(geom.exterior.coords[::-1], geom.interiors) + elif isinstance(geom, LinearRing) and geom is not None: + geom = Polygon(geom.coords[::-1]) + else: + log.debug("ToolIsolation.generate_envelope() Error --> Unexpected Geometry %s" % + type(geom)) + except Exception as e: + log.debug("ToolIsolation.generate_envelope() Error --> %s" % str(e)) + return 'fail' + return geom + + @staticmethod + def generate_rest_geometry(geometry, tooldia, passes, overlap, invert, env_iso_type=2): + """ + Will try to isolate the geometry and return a tuple made of list of paths made through isolation + and a list of Shapely Polygons that could not be isolated + + :param geometry: A list of Shapely Polygons to be isolated + :type geometry: list + :param tooldia: The tool diameter used to do the isolation + :type tooldia: float + :param passes: Number of passes that will made the isolation + :type passes: int + :param overlap: How much to overlap the previous pass; in percentage [0.00, 99.99]% + :type overlap: float + :param invert: If to invert the direction of the resulting isolated geometries + :type invert: bool + :param env_iso_type: can be either 0 = keep exteriors or 1 = keep interiors or 2 = keep all paths + :type env_iso_type: int + :return: Tuple made from list of isolating paths and list of not isolated Polygons + :rtype: tuple + """ + + isolated_geo = [] + not_isolated_geo = [] + + work_geo = [] + + for idx, geo in enumerate(geometry): + good_pass_iso = [] + start_idx = idx + 1 + + for nr_pass in range(passes): + iso_offset = tooldia * ((2 * nr_pass + 1) / 2.0) - (nr_pass * overlap * tooldia) + buf_chek = iso_offset * 2 + check_geo = geo.buffer(buf_chek) + + intersect_flag = False + # find if current pass for current geo is valid (no intersection with other geos)) + for geo_search_idx in range(idx): + if check_geo.intersects(geometry[geo_search_idx]): + intersect_flag = True + break + + if intersect_flag is False: + for geo_search_idx in range(start_idx, len(geometry)): + if check_geo.intersects(geometry[geo_search_idx]): + intersect_flag = True + break + + # if we had an intersection do nothing, else add the geo to the good pass isolations + if intersect_flag is False: + good_pass_iso.append(geo.buffer(iso_offset)) + + if good_pass_iso: + work_geo += good_pass_iso + else: + not_isolated_geo.append(geo) + + if invert: + try: + pl = [] + for p in work_geo: + if p is not None: + if isinstance(p, Polygon): + pl.append(Polygon(p.exterior.coords[::-1], p.interiors)) + elif isinstance(p, LinearRing): + pl.append(Polygon(p.coords[::-1])) + work_geo = MultiPolygon(pl) + except TypeError: + if isinstance(work_geo, Polygon) and work_geo is not None: + work_geo = [Polygon(work_geo.exterior.coords[::-1], work_geo.interiors)] + elif isinstance(work_geo, LinearRing) and work_geo is not None: + work_geo = [Polygon(work_geo.coords[::-1])] + else: + log.debug("ToolIsolation.generate_rest_geometry() Error --> Unexpected Geometry %s" % + type(work_geo)) + except Exception as e: + log.debug("ToolIsolation.generate_rest_geometry() Error --> %s" % str(e)) + return 'fail', 'fail' + + if env_iso_type == 0: # exterior + for geo in work_geo: + isolated_geo.append(geo.exterior) + elif env_iso_type == 1: # interiors + for geo in work_geo: + isolated_geo += [interior for interior in geo.interiors] + else: # exterior + interiors + for geo in work_geo: + isolated_geo += [geo.exterior] + [interior for interior in geo.interiors] + + return isolated_geo, not_isolated_geo + + def on_iso_tool_add_from_db_executed(self, tool): + """ + Here add the tool from DB in the selected geometry object + :return: + """ + tool_from_db = deepcopy(tool) + + res = self.on_tool_from_db_inserted(tool=tool_from_db) + + for idx in range(self.app.ui.plot_tab_area.count()): + if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"): + wdg = self.app.ui.plot_tab_area.widget(idx) + wdg.deleteLater() + self.app.ui.plot_tab_area.removeTab(idx) + + if res == 'fail': + return + self.app.inform.emit('[success] %s' % _("Tool from DB added in Tool Table.")) + + # select last tool added + toolid = res + for row in range(self.tools_table.rowCount()): + if int(self.tools_table.item(row, 3).text()) == toolid: + self.tools_table.selectRow(row) + self.on_row_selection_change() + + def on_tool_from_db_inserted(self, tool): + """ + Called from the Tools DB object through a App method when adding a tool from Tools Database + :param tool: a dict with the tool data + :return: None + """ + + self.ui_disconnect() + self.units = self.app.defaults['units'].upper() + + tooldia = float(tool['tooldia']) + + # construct a list of all 'tooluid' in the self.tools + tool_uid_list = [] + for tooluid_key in self.iso_tools: + tool_uid_item = int(tooluid_key) + tool_uid_list.append(tool_uid_item) + + # find maximum from the temp_uid, add 1 and this is the new 'tooluid' + if not tool_uid_list: + max_uid = 0 + else: + max_uid = max(tool_uid_list) + tooluid = max_uid + 1 + + tooldia = float('%.*f' % (self.decimals, tooldia)) + + tool_dias = [] + for k, v in self.iso_tools.items(): + for tool_v in v.keys(): + if tool_v == 'tooldia': + tool_dias.append(float('%.*f' % (self.decimals, (v[tool_v])))) + + if float('%.*f' % (self.decimals, tooldia)) in tool_dias: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Tool already in Tool Table.")) + self.ui_connect() + return 'fail' + + self.iso_tools.update({ + tooluid: { + 'tooldia': float('%.*f' % (self.decimals, tooldia)), + 'offset': tool['offset'], + 'offset_value': tool['offset_value'], + 'type': tool['type'], + 'tool_type': tool['tool_type'], + 'data': deepcopy(tool['data']), + 'solid_geometry': [] + } + }) + + self.iso_tools[tooluid]['data']['name'] = '_iso' + + self.app.inform.emit('[success] %s' % _("New tool added to Tool Table.")) + + self.ui_connect() + self.build_ui() + + # if self.tools_table.rowCount() != 0: + # self.param_frame.setDisabled(False) + + def on_tool_add_from_db_clicked(self): + """ + Called when the user wants to add a new tool from Tools Database. It will create the Tools Database object + and display the Tools Database tab in the form needed for the Tool adding + :return: None + """ + + # if the Tools Database is already opened focus on it + for idx in range(self.app.ui.plot_tab_area.count()): + if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"): + self.app.ui.plot_tab_area.setCurrentWidget(self.app.tools_db_tab) + break + self.app.on_tools_database(source='iso') + self.app.tools_db_tab.ok_to_add = True + self.app.tools_db_tab.buttons_frame.hide() + self.app.tools_db_tab.add_tool_from_db.show() + self.app.tools_db_tab.cancel_tool_from_db.show() + + def reset_fields(self): + self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + + def reset_usage(self): + self.obj_name = "" + self.grb_obj = None + + self.first_click = False + self.cursor_pos = None + self.mouse_is_dragging = False + + prog_plot = True if self.app.defaults["tools_iso_plotting"] == 'progressive' else False + if prog_plot: + self.temp_shapes.clear(update=True) + + self.sel_rect = [] diff --git a/flatcamTools/ToolMove.py b/AppTools/ToolMove.py similarity index 97% rename from flatcamTools/ToolMove.py rename to AppTools/ToolMove.py index 18d6a6e0..e02fed57 100644 --- a/flatcamTools/ToolMove.py +++ b/AppTools/ToolMove.py @@ -6,13 +6,13 @@ # ########################################################## from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.VisPyVisuals import * +from AppTool import AppTool +from AppGUI.VisPyVisuals import * from copy import copy import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -22,13 +22,13 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolMove(FlatCAMTool): +class ToolMove(AppTool): toolName = _("Move") replot_signal = QtCore.pyqtSignal(list) def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.decimals = self.app.decimals @@ -50,7 +50,7 @@ class ToolMove(FlatCAMTool): if self.app.is_legacy is False: self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) else: - from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.sel_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name="move") self.mm = None @@ -60,7 +60,7 @@ class ToolMove(FlatCAMTool): self.replot_signal[list].connect(self.replot) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='M', **kwargs) + AppTool.install(self, icon, separator, shortcut='M', **kwargs) def run(self, toggle): self.app.defaults.report_usage("ToolMove()") diff --git a/flatcamTools/ToolNCC.py b/AppTools/ToolNCC.py similarity index 90% rename from flatcamTools/ToolNCC.py rename to AppTools/ToolNCC.py index 01b879b9..c1aa6551 100644 --- a/flatcamTools/ToolNCC.py +++ b/AppTools/ToolNCC.py @@ -7,12 +7,12 @@ from PyQt5 import QtWidgets, QtCore, QtGui -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog, FCButton,\ +from AppTool import AppTool +from AppGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCInputDialog, FCButton,\ FCComboBox, OptionalInputSection -from flatcamParsers.ParseGerber import Gerber +from AppParsers.ParseGerber import Gerber -from FlatCAMCommon import GracefulException as grace +from camlib import grace from copy import deepcopy @@ -27,7 +27,7 @@ from matplotlib.backend_bases import KeyEvent as mpl_key_event import logging import traceback import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -37,7 +37,7 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class NonCopperClear(FlatCAMTool, Gerber): +class NonCopperClear(AppTool, Gerber): toolName = _("Non-Copper Clearing") @@ -45,7 +45,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app = app self.decimals = self.app.decimals - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) Gerber.__init__(self, steps_per_circle=self.app.defaults["gerber_circle_steps"]) self.tools_frame = QtWidgets.QFrame() @@ -428,7 +428,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.ncc_method_combo = FCComboBox() self.ncc_method_combo.addItems( - [_("Standard"), _("Seed"), _("Lines")] + [_("Standard"), _("Seed"), _("Lines"), _("Combo")] ) self.ncc_method_combo.setObjectName("n_method") @@ -509,7 +509,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.grid3.addWidget(self.gen_param_label, 24, 0, 1, 2) # Rest Machining - self.ncc_rest_cb = FCCheckBox('%s' % _("Rest Machining")) + self.ncc_rest_cb = FCCheckBox('%s' % _("Rest")) self.ncc_rest_cb.setObjectName("n_rest_machining") self.ncc_rest_cb.setToolTip( @@ -936,7 +936,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.inform.emit('[WARNING_NOTCL] %s...' % _("Adding Tool cancelled")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+N', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+N', **kwargs) def run(self, toggle=True): self.app.defaults.report_usage("ToolNonCopperClear()") @@ -961,7 +961,7 @@ class NonCopperClear(FlatCAMTool, Gerber): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() # reset those objects on a new run @@ -1603,10 +1603,11 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(self.obj_name))) return - # use the selected tools in the tool table; get diameters for non-copper clear + # use the selected tools in the tool table; get diameters for isolation self.iso_dia_list = [] # use the selected tools in the tool table; get diameters for non-copper clear self.ncc_dia_list = [] + if self.tools_table.selectedItems(): for x in self.tools_table.selectedItems(): try: @@ -1620,10 +1621,12 @@ class NonCopperClear(FlatCAMTool, Gerber): "use a number.")) continue - if self.op_radio.get_value() == _("Isolation"): - self.iso_dia_list.append(self.tooldia) - else: - self.ncc_dia_list.append(self.tooldia) + for uid_k, uid_v in self.ncc_tools.items(): + if round(uid_v['tooldia'], self.decimals) == round(self.tooldia, self.decimals): + if uid_v['data']['tools_nccoperation'] == "iso": + self.iso_dia_list.append(self.tooldia) + else: + self.ncc_dia_list.append(self.tooldia) else: self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table.")) return @@ -1825,16 +1828,22 @@ class NonCopperClear(FlatCAMTool, Gerber): edge_width=self.app.defaults["global_cursor_width"], size=self.app.defaults["global_cursor_size"]) - # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) if self.cursor_pos is None: self.cursor_pos = (0, 0) self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + + # # update the positions on status bar + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) # draw the utility geometry if shape_type == "square": @@ -1905,13 +1914,15 @@ class NonCopperClear(FlatCAMTool, Gerber): self.delete_moving_selection_shape() self.delete_tool_selection_shape() - def envelope_object(self, ncc_obj, ncc_select, box_obj=None): + def calculate_bounding_box(self, ncc_obj, ncc_select, box_obj=None): """ + Will return a geometry that dictate the total extent of the area to be copper cleared - :param ncc_obj: - :param box_obj: - :param ncc_select: - :return: + :param ncc_obj: The object to be copper cleared + :param box_obj: The object whose geometry will be used as delimitation for copper clearing - if selected + :param ncc_select: String that choose what kind of reference to be used for copper clearing extent + :return: The geometry that surrounds the area to be cleared and the kind of object from which the + geometry originated (string: "gerber", "geometry" or None) """ box_kind = box_obj.kind if box_obj is not None else None @@ -1929,7 +1940,7 @@ class NonCopperClear(FlatCAMTool, Gerber): env_obj = cascaded_union(geo_n) env_obj = env_obj.convex_hull except Exception as e: - log.debug("NonCopperClear.envelope_object() 'itself' --> %s" % str(e)) + log.debug("NonCopperClear.calculate_bounding_box() 'itself' --> %s" % str(e)) self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available.")) return None elif ncc_select == _("Area Selection"): @@ -1960,12 +1971,12 @@ class NonCopperClear(FlatCAMTool, Gerber): return env_obj, box_kind - def envelope_object_to_tool_bounding_box(self, env_obj, box_kind, ncc_select, ncc_margin): + def apply_margin_to_bounding_box(self, bbox, box_kind, ncc_select, ncc_margin): """ Prepare non-copper polygons. - Create the bounding box area from which the copper features will be subtracted + Apply a margin to the bounding box area from which the copper features will be subtracted - :param env_obj: the Geometry to be used as bounding box after applying the ncc_margin + :param bbox: the Geometry to be used as bounding box after applying the ncc_margin :param box_kind: "geometry" or "gerber" :param ncc_select: the kind of area to be copper cleared :param ncc_margin: the margin around the area to be copper cleared @@ -1975,44 +1986,44 @@ class NonCopperClear(FlatCAMTool, Gerber): log.debug("NCC Tool. Preparing non-copper polygons.") self.app.inform.emit(_("NCC Tool. Preparing non-copper polygons.")) - if env_obj is None: - log.debug("NonCopperClear.envelope_object_to_tool_bounding_box() --> The object is None") + if bbox is None: + log.debug("NonCopperClear.apply_margin_to_bounding_box() --> The object is None") return 'fail' - bounding_box = None + new_bounding_box = None if ncc_select == _('Itself'): try: - bounding_box = env_obj.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre) + new_bounding_box = bbox.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre) except Exception as e: - log.debug("NonCopperClear.envelope_object_to_tool_bounding_box() 'itself' --> %s" % str(e)) + log.debug("NonCopperClear.apply_margin_to_bounding_box() 'itself' --> %s" % str(e)) self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available.")) return 'fail' elif ncc_select == _("Area Selection"): geo_buff_list = [] - for poly in env_obj: + for poly in bbox: if self.app.abort_flag: # graceful abort requested by the user raise grace geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)) - bounding_box = cascaded_union(geo_buff_list) + new_bounding_box = cascaded_union(geo_buff_list) elif ncc_select == _("Reference Object"): if box_kind == 'geometry': geo_buff_list = [] - for poly in env_obj: + for poly in bbox: if self.app.abort_flag: # graceful abort requested by the user raise grace geo_buff_list.append(poly.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre)) - bounding_box = cascaded_union(geo_buff_list) + new_bounding_box = cascaded_union(geo_buff_list) elif box_kind == 'gerber': - bounding_box = env_obj.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre) + new_bounding_box = bbox.buffer(distance=ncc_margin, join_style=base.JOIN_STYLE.mitre) else: self.app.inform.emit('[ERROR_NOTCL] %s' % _("The reference object type is not supported.")) return 'fail' log.debug("NCC Tool. Finished non-copper polygons.") - return bounding_box + return new_bounding_box def get_tool_empty_area(self, name, ncc_obj, geo_obj, isotooldia, has_offset, ncc_offset, ncc_margin, bounding_box, tools_storage): @@ -2066,6 +2077,7 @@ class NonCopperClear(FlatCAMTool, Gerber): # unfortunately for this function to work time efficient, # if the Gerber was loaded without buffering then it require the buffering now. + # TODO 'buffering status' should be a property of the object not the project property if self.app.defaults['gerber_buffering'] == 'no': self.solid_geometry = ncc_obj.solid_geometry.buffer(0) else: @@ -2083,81 +2095,84 @@ class NonCopperClear(FlatCAMTool, Gerber): isolated_geo = self.generate_envelope(tool_iso / 2, 0) if isolated_geo == 'fail': - self.app.inform.emit('[ERROR_NOTCL] %s' % _("Isolation geometry could not be generated.")) - else: - if ncc_margin < tool_iso: - self.app.inform.emit('[WARNING_NOTCL] %s' % _("Isolation geometry is broken. Margin is less " - "than isolation tool diameter.")) - try: - for geo_elem in isolated_geo: - # provide the app with a way to process the GUI events when in a blocking loop - QtWidgets.QApplication.processEvents() + self.app.inform.emit('[ERROR_NOTCL] %s %s' % + (_("Isolation geometry could not be generated."), str(tool_iso))) + continue - if self.app.abort_flag: - # graceful abort requested by the user - raise grace + if ncc_margin < tool_iso: + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Isolation geometry is broken. Margin is less " + "than isolation tool diameter.")) + try: + for geo_elem in isolated_geo: + # provide the app with a way to process the GUI events when in a blocking loop + QtWidgets.QApplication.processEvents() - if isinstance(geo_elem, Polygon): - for ring in self.poly2rings(geo_elem): + if self.app.abort_flag: + # graceful abort requested by the user + raise grace + + if isinstance(geo_elem, Polygon): + for ring in self.poly2rings(geo_elem): + new_geo = ring.intersection(bounding_box) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiPolygon): + for poly in geo_elem: + for ring in self.poly2rings(poly): new_geo = ring.intersection(bounding_box) if new_geo and not new_geo.is_empty: new_geometry.append(new_geo) - elif isinstance(geo_elem, MultiPolygon): - for poly in geo_elem: - for ring in self.poly2rings(poly): - new_geo = ring.intersection(bounding_box) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(geo_elem, LineString): - new_geo = geo_elem.intersection(bounding_box) - if new_geo: - if not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(geo_elem, MultiLineString): - for line_elem in geo_elem: - new_geo = line_elem.intersection(bounding_box) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - except TypeError: - if isinstance(isolated_geo, Polygon): - for ring in self.poly2rings(isolated_geo): - new_geo = ring.intersection(bounding_box) - if new_geo: - if not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(isolated_geo, LineString): - new_geo = isolated_geo.intersection(bounding_box) - if new_geo and not new_geo.is_empty: - new_geometry.append(new_geo) - elif isinstance(isolated_geo, MultiLineString): - for line_elem in isolated_geo: + elif isinstance(geo_elem, LineString): + new_geo = geo_elem.intersection(bounding_box) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(geo_elem, MultiLineString): + for line_elem in geo_elem: new_geo = line_elem.intersection(bounding_box) if new_geo and not new_geo.is_empty: new_geometry.append(new_geo) + except TypeError: + if isinstance(isolated_geo, Polygon): + for ring in self.poly2rings(isolated_geo): + new_geo = ring.intersection(bounding_box) + if new_geo: + if not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(isolated_geo, LineString): + new_geo = isolated_geo.intersection(bounding_box) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) + elif isinstance(isolated_geo, MultiLineString): + for line_elem in isolated_geo: + new_geo = line_elem.intersection(bounding_box) + if new_geo and not new_geo.is_empty: + new_geometry.append(new_geo) - # a MultiLineString geometry element will show that the isolation is broken for this tool - for geo_e in new_geometry: - if type(geo_e) == MultiLineString: - warning_flag += 1 - break + # a MultiLineString geometry element will show that the isolation is broken for this tool + for geo_e in new_geometry: + if type(geo_e) == MultiLineString: + warning_flag += 1 + break - current_uid = 0 - for k, v in tools_storage.items(): - if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, - tool_iso)): - current_uid = int(k) - # add the solid_geometry to the current too in self.paint_tools dictionary - # and then reset the temporary list that stored that solid_geometry - v['solid_geometry'] = deepcopy(new_geometry) - v['data']['name'] = name - break - geo_obj.tools[current_uid] = dict(tools_storage[current_uid]) + current_uid = 0 + for k, v in tools_storage.items(): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, + tool_iso)): + current_uid = int(k) + # add the solid_geometry to the current too in self.paint_tools dictionary + # and then reset the temporary list that stored that solid_geometry + v['solid_geometry'] = deepcopy(new_geometry) + v['data']['name'] = name + break + geo_obj.tools[current_uid] = dict(tools_storage[current_uid]) sol_geo = cascaded_union(isolated_geo) if has_offset is True: self.app.inform.emit('[WARNING_NOTCL] %s ...' % _("Buffering")) sol_geo = sol_geo.buffer(distance=ncc_offset) self.app.inform.emit('[success] %s ...' % _("Buffering finished")) + empty = self.get_ncc_empty_area(target=sol_geo, boundary=bounding_box) if empty == 'fail': return 'fail' @@ -2192,28 +2207,98 @@ class NonCopperClear(FlatCAMTool, Gerber): return empty, warning_flag - def clear_copper(self, ncc_obj, - sel_obj=None, - ncctooldia=None, - isotooldia=None, - outname=None, - order=None, - tools_storage=None, - run_threaded=True): + def clear_polygon_worker(self, pol, tooldia, ncc_method, ncc_overlap, ncc_connect, ncc_contour, prog_plot): + + cp = None + + if ncc_method == _("Standard"): + try: + cp = self.clear_polygon(pol, tooldia, + steps_per_circle=self.grb_circle_steps, + overlap=ncc_overlap, contour=ncc_contour, + connect=ncc_connect, + prog_plot=prog_plot) + except grace: + return "fail" + except Exception as ee: + log.debug("NonCopperClear.clear_polygon_worker() Standard --> %s" % str(ee)) + elif ncc_method == _("Seed"): + try: + cp = self.clear_polygon2(pol, tooldia, + steps_per_circle=self.grb_circle_steps, + overlap=ncc_overlap, contour=ncc_contour, + connect=ncc_connect, + prog_plot=prog_plot) + except grace: + return "fail" + except Exception as ee: + log.debug("NonCopperClear.clear_polygon_worker() Seed --> %s" % str(ee)) + elif ncc_method == _("Lines"): + try: + cp = self.clear_polygon3(pol, tooldia, + steps_per_circle=self.grb_circle_steps, + overlap=ncc_overlap, contour=ncc_contour, + connect=ncc_connect, + prog_plot=prog_plot) + except grace: + return "fail" + except Exception as ee: + log.debug("NonCopperClear.clear_polygon_worker() Lines --> %s" % str(ee)) + elif ncc_method == _("Combo"): + try: + self.app.inform.emit(_("Clearing polygon with method: lines.")) + cp = self.clear_polygon3(pol, tooldia, + steps_per_circle=self.grb_circle_steps, + overlap=ncc_overlap, contour=ncc_contour, + connect=ncc_connect, + prog_plot=prog_plot) + + if cp and cp.objects: + pass + else: + self.app.inform.emit(_("Failed. Clearing polygon with method: seed.")) + cp = self.clear_polygon2(pol, tooldia, + steps_per_circle=self.grb_circle_steps, + overlap=ncc_overlap, contour=ncc_contour, + connect=ncc_connect, + prog_plot=prog_plot) + if cp and cp.objects: + pass + else: + self.app.inform.emit(_("Failed. Clearing polygon with method: standard.")) + cp = self.clear_polygon(pol, tooldia, + steps_per_circle=self.grb_circle_steps, + overlap=ncc_overlap, contour=ncc_contour, + connect=ncc_connect, + prog_plot=prog_plot) + except grace: + return "fail" + except Exception as ee: + log.debug("NonCopperClear.clear_polygon_worker() Combo --> %s" % str(ee)) + + if cp and cp.objects: + return list(cp.get_objects()) + else: + self.app.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be cleared completely')) + return None + + def clear_copper(self, ncc_obj, sel_obj=None, ncctooldia=None, isotooldia=None, outname=None, order=None, + tools_storage=None, run_threaded=True): """ Clear the excess copper from the entire object. - :param ncc_obj: ncc cleared object + :param ncc_obj: ncc cleared object :param sel_obj: - :param ncctooldia: a tuple or single element made out of diameters of the tools to be used to ncc clear - :param isotooldia: a tuple or single element made out of diameters of the tools to be used for isolation - :param outname: name of the resulting object - :param order: - :param tools_storage: whether to use the current tools_storage self.ncc_tools or a different one. - Usage of the different one is related to when this function is called from a TcL command. + :param ncctooldia: a tuple or single element made out of diameters of the tools to be used to ncc clear + :param isotooldia: a tuple or single element made out of diameters of the tools to be used for isolation + :param outname: name of the resulting object + :param order: Tools order + :param tools_storage: whether to use the current tools_storage self.ncc_tools or a different one. + Usage of the different one is related to when this function is called + from a TcL command. - :param run_threaded: If True the method will be run in a threaded way suitable for GUI usage; if False it will - run non-threaded for TclShell usage + :param run_threaded: If True the method will be run in a threaded way suitable for GUI usage; if False + it will run non-threaded for TclShell usage :return: """ log.debug("Executing the handler ...") @@ -2224,9 +2309,9 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.proc_container.view.set_busy(_("Non-Copper clearing ...")) QtWidgets.QApplication.processEvents() - # ##################################################################### - # ####### Read the parameters ######################################### - # ##################################################################### + # ###################################################################################################### + # ######################### Read the parameters ######################################################## + # ###################################################################################################### units = self.app.defaults['units'] order = order if order else self.ncc_order_radio.get_value() @@ -2240,22 +2325,25 @@ class NonCopperClear(FlatCAMTool, Gerber): # ###################################################################################################### # # Read the tooldia parameter and create a sorted list out them - they may be more than one diameter ## # ###################################################################################################### - sorted_tools = [] + sorted_clear_tools = [] if ncctooldia is not None: try: - sorted_tools = [float(eval(dia)) for dia in ncctooldia.split(",") if dia != ''] + sorted_clear_tools = [float(eval(dia)) for dia in ncctooldia.split(",") if dia != ''] except AttributeError: if not isinstance(ncctooldia, list): - sorted_tools = [float(ncctooldia)] + sorted_clear_tools = [float(ncctooldia)] else: - sorted_tools = ncctooldia + sorted_clear_tools = ncctooldia else: # for row in range(self.tools_table.rowCount()): # if self.tools_table.cellWidget(row, 1).currentText() == 'clear_op': - # sorted_tools.append(float(self.tools_table.item(row, 1).text())) + # sorted_clear_tools.append(float(self.tools_table.item(row, 1).text())) for tooluid in self.ncc_tools: if self.ncc_tools[tooluid]['data']['tools_nccoperation'] == 'clear': - sorted_tools.append(self.ncc_tools[tooluid]['tooldia']) + sorted_clear_tools.append(self.ncc_tools[tooluid]['tooldia']) + + if not sorted_clear_tools: + return 'fail' # ######################################################################################################## # set the name for the future Geometry object @@ -2263,76 +2351,75 @@ class NonCopperClear(FlatCAMTool, Gerber): # ######################################################################################################## name = outname if outname is not None else self.obj_name + "_ncc" - # ########################################################################################## - # Initializes the new geometry object ###################################################### - # ########################################################################################## + # ######################################################################################################## + # ######### #####Initializes the new geometry object ##################################################### + # ######################################################################################################## def gen_clear_area(geo_obj, app_obj): - assert geo_obj.kind == 'geometry', \ - "Initializer expected a GeometryObject, got %s" % type(geo_obj) + log.debug("NCC Tool. Normal copper clearing task started.") + self.app.inform.emit(_("NCC Tool. Finished non-copper polygons. Normal copper clearing task started.")) # provide the app with a way to process the GUI events when in a blocking loop if not run_threaded: QtWidgets.QApplication.processEvents() - log.debug("NCC Tool. Normal copper clearing task started.") - self.app.inform.emit(_("NCC Tool. Finished non-copper polygons. Normal copper clearing task started.")) - # a flag to signal that the isolation is broken by the bounding box in 'area' and 'box' cases # will store the number of tools for which the isolation is broken warning_flag = 0 + tool = None + if order == 'fwd': - sorted_tools.sort(reverse=False) + sorted_clear_tools.sort(reverse=False) elif order == 'rev': - sorted_tools.sort(reverse=True) + sorted_clear_tools.sort(reverse=True) else: pass cleared_geo = [] - # Already cleared area - cleared = MultiPolygon() - - # flag for polygons not cleared - app_obj.poly_not_cleared = False + cleared = MultiPolygon() # Already cleared area + app_obj.poly_not_cleared = False # flag for polygons not cleared # Generate area for each tool - offset = sum(sorted_tools) + offset = sum(sorted_clear_tools) current_uid = int(1) - try: - tool = eval(self.app.defaults["tools_ncctools"])[0] - except TypeError: - tool = eval(self.app.defaults["tools_ncctools"]) + # try: + # tool = eval(self.app.defaults["tools_ncctools"])[0] + # except TypeError: + # tool = eval(self.app.defaults["tools_ncctools"]) if ncc_select == _("Reference Object"): - env_obj, box_obj_kind = self.envelope_object(ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select) + bbox_geo, bbox_kind = self.calculate_bounding_box( + ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select) else: - env_obj, box_obj_kind = self.envelope_object(ncc_obj=ncc_obj, ncc_select=ncc_select) + bbox_geo, bbox_kind = self.calculate_bounding_box(ncc_obj=ncc_obj, ncc_select=ncc_select) - if env_obj is None and box_obj_kind is None: + if bbox_geo is None and bbox_kind is None: self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box.")) return "fail" - # COPPER CLEARING # - for tool in sorted_tools: + # COPPER CLEARING with tools marked for CLEAR# + for tool in sorted_clear_tools: log.debug("Starting geometry processing for tool: %s" % str(tool)) if self.app.abort_flag: # graceful abort requested by the user raise grace # provide the app with a way to process the GUI events when in a blocking loop - QtWidgets.QApplication.processEvents() + if not run_threaded: + QtWidgets.QApplication.processEvents() app_obj.inform.emit('[success] %s = %s%s %s' % ( _('NCC Tool clearing with tool diameter'), str(tool), units.lower(), _('started.')) ) app_obj.proc_container.update_view_text(' %d%%' % 0) - tool_uid = 0 + tool_uid = 0 # find the current tool_uid for k, v in self.ncc_tools.items(): if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool)): tool_uid = int(k) break + # parameters that are particular to the current tool ncc_overlap = float(self.ncc_tools[tool_uid]["data"]["tools_nccoverlap"]) / 100.0 ncc_margin = float(self.ncc_tools[tool_uid]["data"]["tools_nccmargin"]) ncc_method = self.ncc_tools[tool_uid]["data"]["tools_nccmethod"] @@ -2341,21 +2428,18 @@ class NonCopperClear(FlatCAMTool, Gerber): has_offset = self.ncc_tools[tool_uid]["data"]["tools_ncc_offset_choice"] ncc_offset = float(self.ncc_tools[tool_uid]["data"]["tools_ncc_offset_value"]) - cleared_geo[:] = [] - # Get remaining tools offset offset -= (tool - 1e-12) # Bounding box for current tool - bbox = self.envelope_object_to_tool_bounding_box(env_obj=env_obj, box_kind=box_obj_kind, - ncc_select=ncc_select, ncc_margin=ncc_margin) + bbox = self.apply_margin_to_bounding_box(bbox=bbox_geo, box_kind=bbox_kind, + ncc_select=ncc_select, ncc_margin=ncc_margin) # Area to clear empty, warning_flag = self.get_tool_empty_area(name=name, ncc_obj=ncc_obj, geo_obj=geo_obj, - isotooldia=isotooldia, + isotooldia=isotooldia, ncc_margin=ncc_margin, has_offset=has_offset, ncc_offset=ncc_offset, - ncc_margin=ncc_margin, tools_storage=tools_storage, - bounding_box=bbox) + tools_storage=tools_storage, bounding_box=bbox) area = empty.buffer(-offset) try: @@ -2364,7 +2448,7 @@ class NonCopperClear(FlatCAMTool, Gerber): continue # Transform area to MultiPolygon - if type(area) is Polygon: + if isinstance(area, Polygon): area = MultiPolygon([area]) # variables to display the percentage of work done @@ -2373,12 +2457,14 @@ class NonCopperClear(FlatCAMTool, Gerber): old_disp_number = 0 log.warning("Total number of polygons to be cleared. %s" % str(geo_len)) + cleared_geo[:] = [] if area.geoms: if len(area.geoms) > 0: pol_nr = 0 for p in area.geoms: # provide the app with a way to process the GUI events when in a blocking loop - QtWidgets.QApplication.processEvents() + if not run_threaded: + QtWidgets.QApplication.processEvents() if self.app.abort_flag: # graceful abort requested by the user @@ -2388,72 +2474,40 @@ class NonCopperClear(FlatCAMTool, Gerber): p = p.buffer(0) if p is not None and p.is_valid: - poly_processed = [] + poly_failed = 0 try: for pol in p: if pol is not None and isinstance(pol, Polygon): - if ncc_method == _("Standard"): - cp = self.clear_polygon(pol, tool, - self.grb_circle_steps, - overlap=ncc_overlap, contour=ncc_contour, - connect=ncc_connect, - prog_plot=prog_plot) - elif ncc_method == _("Seed"): - cp = self.clear_polygon2(pol, tool, - self.grb_circle_steps, - overlap=ncc_overlap, contour=ncc_contour, - connect=ncc_connect, - prog_plot=prog_plot) + res = self.clear_polygon_worker(pol=pol, tooldia=tool, + ncc_method=ncc_method, + ncc_overlap=ncc_overlap, + ncc_connect=ncc_connect, + ncc_contour=ncc_contour, + prog_plot=prog_plot) + if res is not None: + cleared_geo += res else: - cp = self.clear_polygon3(pol, tool, - self.grb_circle_steps, - overlap=ncc_overlap, contour=ncc_contour, - connect=ncc_connect, - prog_plot=prog_plot) - if cp: - cleared_geo += list(cp.get_objects()) - poly_processed.append(True) - else: - poly_processed.append(False) - log.warning("Polygon in MultiPolygon can not be cleared.") + poly_failed += 1 else: - log.warning("Geo in Iterable can not be cleared because it is not Polygon. " - "It is: %s" % str(type(pol))) + log.warning("Expected geo is a Polygon. Instead got a %s" % str(type(pol))) except TypeError: if isinstance(p, Polygon): - if ncc_method == _("Standard"): - cp = self.clear_polygon(p, tool, self.grb_circle_steps, - overlap=ncc_overlap, contour=ncc_contour, - connect=ncc_connect, - prog_plot=prog_plot) - elif ncc_method == _("Seed"): - cp = self.clear_polygon2(p, tool, self.grb_circle_steps, - overlap=ncc_overlap, contour=ncc_contour, - connect=ncc_connect, - prog_plot=prog_plot) + res = self.clear_polygon_worker(pol=p, tooldia=tool, + ncc_method=ncc_method, + ncc_overlap=ncc_overlap, + ncc_connect=ncc_connect, + ncc_contour=ncc_contour, + prog_plot=prog_plot) + if res is not None: + cleared_geo += res else: - cp = self.clear_polygon3(p, tool, self.grb_circle_steps, - overlap=ncc_overlap, contour=ncc_contour, - connect=ncc_connect, - prog_plot=prog_plot) - if cp: - cleared_geo += list(cp.get_objects()) - poly_processed.append(True) - else: - poly_processed.append(False) - log.warning("Polygon can not be cleared.") + poly_failed += 1 else: - log.warning("Geo can not be cleared because it is: %s" % str(type(p))) + log.warning("Expected geo is a Polygon. Instead got a %s" % str(type(p))) - p_cleared = poly_processed.count(True) - p_not_cleared = poly_processed.count(False) - - if p_not_cleared: + if poly_failed > 0: app_obj.poly_not_cleared = True - if p_cleared == 0: - continue - pol_nr += 1 disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) # log.debug("Polygons cleared: %d" % pol_nr) @@ -2463,11 +2517,10 @@ class NonCopperClear(FlatCAMTool, Gerber): old_disp_number = disp_number # log.debug("Polygons cleared: %d. Percentage done: %d%%" % (pol_nr, disp_number)) - # check if there is a geometry at all in the cleared geometry + # check if there is a geometry at all in the cleared geometry if cleared_geo: - # Overall cleared area - cleared = empty.buffer(-offset * (1 + ncc_overlap)).buffer(-tool / 1.999999).buffer( - tool / 1.999999) + cleared = empty.buffer(-offset * (1 + ncc_overlap)) # Overall cleared area + cleared = cleared.buffer(-tool / 1.999999).buffer(tool / 1.999999) # clean-up cleared geo cleared = cleared.buffer(0) @@ -2487,7 +2540,6 @@ class NonCopperClear(FlatCAMTool, Gerber): geo_obj.tools[current_uid] = dict(tools_storage[current_uid]) else: log.debug("There are no geometries in the cleared polygon.") - # clean the progressive plotted shapes if it was used if self.app.defaults["tools_ncc_plotting"] == 'progressive': self.temp_shapes.clear(update=True) @@ -2569,7 +2621,7 @@ class NonCopperClear(FlatCAMTool, Gerber): # will store the number of tools for which the isolation is broken warning_flag = 0 - sorted_tools.sort(reverse=True) + sorted_clear_tools.sort(reverse=True) cleared_geo = [] cleared_by_last_tool = [] @@ -2584,9 +2636,10 @@ class NonCopperClear(FlatCAMTool, Gerber): app_obj.poly_not_cleared = True if ncc_select == _("Reference Object"): - env_obj, box_obj_kind = self.envelope_object(ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select) + env_obj, box_obj_kind = self.calculate_bounding_box( + ncc_obj=ncc_obj, box_obj=sel_obj, ncc_select=ncc_select) else: - env_obj, box_obj_kind = self.envelope_object(ncc_obj=ncc_obj, ncc_select=ncc_select) + env_obj, box_obj_kind = self.calculate_bounding_box(ncc_obj=ncc_obj, ncc_select=ncc_select) if env_obj is None and box_obj_kind is None: self.app.inform.emit("[ERROR_NOTCL] %s" % _("NCC Tool failed creating bounding box.")) @@ -2596,7 +2649,7 @@ class NonCopperClear(FlatCAMTool, Gerber): app_obj.inform.emit("NCC Tool. Calculate 'empty' area.") # Generate area for each tool - while sorted_tools: + while sorted_clear_tools: log.debug("Starting geometry processing for tool: %s" % str(tool)) if self.app.abort_flag: # graceful abort requested by the user @@ -2610,7 +2663,7 @@ class NonCopperClear(FlatCAMTool, Gerber): ) app_obj.proc_container.update_view_text(' %d%%' % 0) - tool = sorted_tools.pop(0) + tool = sorted_clear_tools.pop(0) tool_uid = 0 for k, v in self.ncc_tools.items(): @@ -2630,8 +2683,8 @@ class NonCopperClear(FlatCAMTool, Gerber): cleared_geo[:] = [] # Bounding box for current tool - bbox = self.envelope_object_to_tool_bounding_box(env_obj=env_obj, box_kind=box_obj_kind, - ncc_select=ncc_select, ncc_margin=ncc_margin) + bbox = self.apply_margin_to_bounding_box(bbox=env_obj, box_kind=box_obj_kind, + ncc_select=ncc_select, ncc_margin=ncc_margin) # Area to clear empty, warning_flag = self.get_tool_empty_area(name=name, ncc_obj=ncc_obj, geo_obj=geo_obj, @@ -2835,12 +2888,12 @@ class NonCopperClear(FlatCAMTool, Gerber): # ########################################################################################### # Create the Job function and send it to the worker to be processed in another thread ####### # ########################################################################################### - def job_thread(app_obj): + def job_thread(a_obj): try: if rest_machining_choice is True: - app_obj.new_object("geometry", name, gen_clear_area_rest) + a_obj.app_obj.new_object("geometry", name, gen_clear_area_rest) else: - app_obj.new_object("geometry", name, gen_clear_area) + a_obj.app_obj.new_object("geometry", name, gen_clear_area) except grace: if run_threaded: proc.done() @@ -2854,7 +2907,7 @@ class NonCopperClear(FlatCAMTool, Gerber): if run_threaded: proc.done() else: - app_obj.proc_container.view.set_idle() + a_obj.proc_container.view.set_idle() # focus on Selected Tab self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab) @@ -2868,48 +2921,34 @@ class NonCopperClear(FlatCAMTool, Gerber): else: job_thread(app_obj=self.app) - def clear_copper_tcl(self, ncc_obj, - sel_obj=None, - ncctooldia=None, - isotooldia=None, - margin=None, - has_offset=None, - offset=None, - select_method=None, - outname=None, - overlap=None, - connect=None, - contour=None, - order=None, - method=None, - rest=None, - tools_storage=None, - plot=True, - run_threaded=False): + def clear_copper_tcl(self, ncc_obj, sel_obj=None, ncctooldia=None, isotooldia=None, margin=None, has_offset=None, + offset=None, select_method=None, outname=None, overlap=None, connect=None, contour=None, + order=None, method=None, rest=None, tools_storage=None, plot=True, run_threaded=False): """ Clear the excess copper from the entire object. To be used only in a TCL command. - :param ncc_obj: ncc cleared object + :param ncc_obj: ncc cleared object :param sel_obj: - :param ncctooldia: a tuple or single element made out of diameters of the tools to be used to ncc clear - :param isotooldia: a tuple or single element made out of diameters of the tools to be used for isolation - :param overlap: value by which the paths will overlap - :param order: if the tools are ordered and how - :param select_method: if to do ncc on the whole object, on an defined area or on an area defined by - another object - :param has_offset: True if an offset is needed - :param offset: distance from the copper features where the copper clearing is stopping - :param margin: a border around cleared area - :param outname: name of the resulting object - :param connect: Connect lines to avoid tool lifts. - :param contour: Paint around the edges. - :param method: choice out of 'seed', 'normal', 'lines' - :param rest: True if to use rest-machining - :param tools_storage: whether to use the current tools_storage self.ncc_tools or a different one. - Usage of the different one is related to when this function is called from a TcL command. - :param plot: if True after the job is finished the result will be plotted, else it will not. - :param run_threaded: If True the method will be run in a threaded way suitable for GUI usage; if False it will - run non-threaded for TclShell usage + :param ncctooldia: a tuple or single element made out of diameters of the tools to be used to ncc clear + :param isotooldia: a tuple or single element made out of diameters of the tools to be used for isolation + :param overlap: value by which the paths will overlap + :param order: if the tools are ordered and how + :param select_method: if to do ncc on the whole object, on an defined area or on an area defined by + another object + :param has_offset: True if an offset is needed + :param offset: distance from the copper features where the copper clearing is stopping + :param margin: a border around cleared area + :param outname: name of the resulting object + :param connect: Connect lines to avoid tool lifts. + :param contour: Clear around the edges. + :param method: choice out of 'seed', 'normal', 'lines' + :param rest: True if to use rest-machining + :param tools_storage: whether to use the current tools_storage self.ncc_tools or a different one. + Usage of the different one is related to when this function is called from a + TcL command. + :param plot: if True after the job is finished the result will be plotted, else it will not. + :param run_threaded: If True the method will be run in a threaded way suitable for GUI usage; + if False it will run non-threaded for TclShell usage :return: """ if run_threaded: @@ -2957,6 +2996,9 @@ class NonCopperClear(FlatCAMTool, Gerber): else: sorted_tools = ncctooldia + if not sorted_tools: + return 'fail' + # ############################################################################################################## # Prepare non-copper polygons. Create the bounding box area from which the copper features will be subtracted ## # ############################################################################################################## @@ -3835,9 +3877,9 @@ class NonCopperClear(FlatCAMTool, Gerber): def job_thread(app_obj): try: if rest_machining_choice is True: - app_obj.new_object("geometry", name, gen_clear_area_rest, plot=plot) + app_obj.app_obj.new_object("geometry", name, gen_clear_area_rest, plot=plot) else: - app_obj.new_object("geometry", name, gen_clear_area, plot=plot) + app_obj.app_obj.new_object("geometry", name, gen_clear_area, plot=plot) except grace: if run_threaded: proc.done() @@ -3870,6 +3912,11 @@ class NonCopperClear(FlatCAMTool, Gerber): Returns the complement of target geometry within the given boundary polygon. If not specified, it defaults to the rectangular bounding box of target geometry. + + :param target: The geometry that is to be 'inverted' + :param boundary: A polygon that surrounds the entire solid geometry and from which we subtract in order to + create a "negative" geometry (geometry to be emptied of copper) + :return: """ if isinstance(target, Polygon): geo_len = 1 @@ -3882,6 +3929,7 @@ class NonCopperClear(FlatCAMTool, Gerber): boundary = target.envelope else: boundary = boundary + try: ret_val = boundary.difference(target) except Exception: @@ -3889,10 +3937,10 @@ class NonCopperClear(FlatCAMTool, Gerber): for el in target: # provide the app with a way to process the GUI events when in a blocking loop QtWidgets.QApplication.processEvents() - if self.app.abort_flag: # graceful abort requested by the user raise grace + boundary = boundary.difference(el) pol_nr += 1 disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) diff --git a/flatcamTools/ToolOptimal.py b/AppTools/ToolOptimal.py similarity index 98% rename from flatcamTools/ToolOptimal.py rename to AppTools/ToolOptimal.py index de46045e..1733edc6 100644 --- a/flatcamTools/ToolOptimal.py +++ b/AppTools/ToolOptimal.py @@ -7,9 +7,9 @@ from PyQt5 import QtWidgets, QtCore, QtGui -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import OptionalHideInputSection, FCTextArea, FCEntry, FCSpinner, FCCheckBox, FCComboBox -from FlatCAMCommon import GracefulException as grace +from AppTool import AppTool +from AppGUI.GUIElements import OptionalHideInputSection, FCTextArea, FCEntry, FCSpinner, FCCheckBox, FCComboBox +from camlib import grace from shapely.geometry import MultiPolygon from shapely.ops import nearest_points @@ -18,7 +18,7 @@ import numpy as np import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -28,7 +28,7 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolOptimal(FlatCAMTool): +class ToolOptimal(AppTool): toolName = _("Optimal Tool") @@ -36,7 +36,7 @@ class ToolOptimal(FlatCAMTool): update_sec_distances = QtCore.pyqtSignal(dict) def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.units = self.app.defaults['units'].upper() self.decimals = self.app.decimals @@ -277,7 +277,7 @@ class ToolOptimal(FlatCAMTool): self.reset_button.clicked.connect(self.set_tool_ui) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+O', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+O', **kwargs) def run(self, toggle=True): self.app.defaults.report_usage("ToolOptimal()") @@ -301,7 +301,7 @@ class ToolOptimal(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Optimal Tool")) diff --git a/AppTools/ToolPDF.py b/AppTools/ToolPDF.py new file mode 100644 index 00000000..59f4f7a6 --- /dev/null +++ b/AppTools/ToolPDF.py @@ -0,0 +1,361 @@ +# ########################################################## +# FlatCAM: 2D Post-processing for Manufacturing # +# File Author: Marius Adrian Stanciu (c) # +# Date: 4/23/2019 # +# MIT Licence # +# ########################################################## + +from PyQt5 import QtWidgets, QtCore + +from AppTool import AppTool + +from AppParsers.ParsePDF import PdfParser, grace +from shapely.geometry import Point, MultiPolygon +from shapely.ops import unary_union + +from copy import deepcopy + +import zlib +import re +import time +import logging +import traceback + +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + +log = logging.getLogger('base') + + +class ToolPDF(AppTool): + """ + Parse a PDF file. + Reference here: https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdfs/pdf_reference_archives/PDFReference.pdf + Return a list of geometries + """ + toolName = _("PDF Import Tool") + + def __init__(self, app): + AppTool.__init__(self, app) + self.app = app + self.decimals = self.app.decimals + + self.stream_re = re.compile(b'.*?FlateDecode.*?stream(.*?)endstream', re.S) + + self.pdf_decompressed = {} + + # key = file name and extension + # value is a dict to store the parsed content of the PDF + self.pdf_parsed = {} + + # QTimer for periodic check + self.check_thread = QtCore.QTimer() + + # Every time a parser is started we add a promise; every time a parser finished we remove a promise + # when empty we start the layer rendering + self.parsing_promises = [] + + self.parser = PdfParser(app=self.app) + + def run(self, toggle=True): + self.app.defaults.report_usage("ToolPDF()") + + self.set_tool_ui() + self.on_open_pdf_click() + + def install(self, icon=None, separator=None, **kwargs): + AppTool.install(self, icon, separator, shortcut='Ctrl+Q', **kwargs) + + def set_tool_ui(self): + pass + + def on_open_pdf_click(self): + """ + File menu callback for opening an PDF file. + + :return: None + """ + + self.app.defaults.report_usage("ToolPDF.on_open_pdf_click()") + self.app.log.debug("ToolPDF.on_open_pdf_click()") + + _filter_ = "Adobe PDF Files (*.pdf);;" \ + "All Files (*.*)" + + try: + filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), + directory=self.app.get_last_folder(), + filter=_filter_) + except TypeError: + filenames, _f = QtWidgets.QFileDialog.getOpenFileNames(caption=_("Open PDF"), filter=_filter_) + + if len(filenames) == 0: + self.app.inform.emit('[WARNING_NOTCL] %s.' % _("Open PDF cancelled")) + else: + # start the parsing timer with a period of 1 second + self.periodic_check(1000) + + for filename in filenames: + if filename != '': + self.app.worker_task.emit({'fcn': self.open_pdf, + 'params': [filename]}) + + def open_pdf(self, filename): + short_name = filename.split('/')[-1].split('\\')[-1] + self.parsing_promises.append(short_name) + + self.pdf_parsed[short_name] = {} + self.pdf_parsed[short_name]['pdf'] = {} + self.pdf_parsed[short_name]['filename'] = filename + + self.pdf_decompressed[short_name] = '' + + if self.app.abort_flag: + # graceful abort requested by the user + raise grace + + with self.app.proc_container.new(_("Parsing PDF file ...")): + with open(filename, "rb") as f: + pdf = f.read() + + stream_nr = 0 + for s in re.findall(self.stream_re, pdf): + if self.app.abort_flag: + # graceful abort requested by the user + raise grace + + stream_nr += 1 + log.debug("PDF STREAM: %d\n" % stream_nr) + s = s.strip(b'\r\n') + try: + self.pdf_decompressed[short_name] += (zlib.decompress(s).decode('UTF-8') + '\r\n') + except Exception as e: + self.app.inform.emit('[ERROR_NOTCL] %s: %s\n%s' % (_("Failed to open"), str(filename), str(e))) + log.debug("ToolPDF.open_pdf().obj_init() --> %s" % str(e)) + return + + self.pdf_parsed[short_name]['pdf'] = self.parser.parse_pdf(pdf_content=self.pdf_decompressed[short_name]) + # we used it, now we delete it + self.pdf_decompressed[short_name] = '' + + # removal from list is done in a multithreaded way therefore not always the removal can be done + # try to remove until it's done + try: + while True: + self.parsing_promises.remove(short_name) + time.sleep(0.1) + except Exception as e: + log.debug("ToolPDF.open_pdf() --> %s" % str(e)) + self.app.inform.emit('[success] %s: %s' % (_("Opened"), str(filename))) + + def layer_rendering_as_excellon(self, filename, ap_dict, layer_nr): + outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr) + + # store the points here until reconstitution: + # keys are diameters and values are list of (x,y) coords + points = {} + + def obj_init(exc_obj, app_obj): + clear_geo = [geo_el['clear'] for geo_el in ap_dict['0']['geometry']] + + for geo in clear_geo: + xmin, ymin, xmax, ymax = geo.bounds + center = (((xmax - xmin) / 2) + xmin, ((ymax - ymin) / 2) + ymin) + + # for drill bits, even in INCH, it's enough 3 decimals + correction_factor = 0.974 + dia = (xmax - xmin) * correction_factor + dia = round(dia, 3) + if dia in points: + points[dia].append(center) + else: + points[dia] = [center] + + sorted_dia = sorted(points.keys()) + + name_tool = 0 + for dia in sorted_dia: + name_tool += 1 + + # create tools dictionary + spec = {"C": dia, 'solid_geometry': []} + exc_obj.tools[str(name_tool)] = spec + + # create drill list of dictionaries + for dia_points in points: + if dia == dia_points: + for pt in points[dia_points]: + exc_obj.drills.append({'point': Point(pt), 'tool': str(name_tool)}) + break + + ret = exc_obj.create_geometry() + if ret == 'fail': + log.debug("Could not create geometry for Excellon object.") + return "fail" + for tool in exc_obj.tools: + if exc_obj.tools[tool]['solid_geometry']: + return + app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("No geometry found in file"), outname)) + return "fail" + + with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)): + + ret_val = self.app.app_obj.new_object("excellon", outname, obj_init, autoselected=False) + if ret_val == 'fail': + self.app.inform.emit('[ERROR_NOTCL] %s' % _('Open PDF file failed.')) + return + # Register recent file + self.app.file_opened.emit("excellon", filename) + # GUI feedback + self.app.inform.emit('[success] %s: %s' % (_("Rendered"), outname)) + + def layer_rendering_as_gerber(self, filename, ap_dict, layer_nr): + outname = filename.split('/')[-1].split('\\')[-1] + "_%s" % str(layer_nr) + + def obj_init(grb_obj, app_obj): + + grb_obj.apertures = ap_dict + + poly_buff = [] + follow_buf = [] + for ap in grb_obj.apertures: + for k in grb_obj.apertures[ap]: + if k == 'geometry': + for geo_el in ap_dict[ap][k]: + if 'solid' in geo_el: + poly_buff.append(geo_el['solid']) + if 'follow' in geo_el: + follow_buf.append(geo_el['follow']) + poly_buff = unary_union(poly_buff) + + if '0' in grb_obj.apertures: + global_clear_geo = [] + if 'geometry' in grb_obj.apertures['0']: + for geo_el in ap_dict['0']['geometry']: + if 'clear' in geo_el: + global_clear_geo.append(geo_el['clear']) + + if global_clear_geo: + solid = [] + for apid in grb_obj.apertures: + if 'geometry' in grb_obj.apertures[apid]: + for elem in grb_obj.apertures[apid]['geometry']: + if 'solid' in elem: + solid_geo = deepcopy(elem['solid']) + for clear_geo in global_clear_geo: + # Make sure that the clear_geo is within the solid_geo otherwise we loose + # the solid_geometry. We want for clear_geometry just to cut into solid_geometry + # not to delete it + if clear_geo.within(solid_geo): + solid_geo = solid_geo.difference(clear_geo) + if solid_geo.is_empty: + solid_geo = elem['solid'] + try: + for poly in solid_geo: + solid.append(poly) + except TypeError: + solid.append(solid_geo) + poly_buff = deepcopy(MultiPolygon(solid)) + + follow_buf = unary_union(follow_buf) + + try: + poly_buff = poly_buff.buffer(0.0000001) + except ValueError: + pass + try: + poly_buff = poly_buff.buffer(-0.0000001) + except ValueError: + pass + + grb_obj.solid_geometry = deepcopy(poly_buff) + grb_obj.follow_geometry = deepcopy(follow_buf) + + with self.app.proc_container.new(_("Rendering PDF layer #%d ...") % int(layer_nr)): + + ret = self.app.app_obj.new_object('gerber', outname, obj_init, autoselected=False) + if ret == 'fail': + self.app.inform.emit('[ERROR_NOTCL] %s' % _('Open PDF file failed.')) + return + # Register recent file + self.app.file_opened.emit('gerber', filename) + # GUI feedback + self.app.inform.emit('[success] %s: %s' % (_("Rendered"), outname)) + + def periodic_check(self, check_period): + """ + This function starts an QTimer and it will periodically check if parsing was done + + :param check_period: time at which to check periodically if all plots finished to be plotted + :return: + """ + + # self.plot_thread = threading.Thread(target=lambda: self.check_plot_finished(check_period)) + # self.plot_thread.start() + log.debug("ToolPDF --> Periodic Check started.") + + try: + self.check_thread.stop() + except TypeError: + pass + + self.check_thread.setInterval(check_period) + try: + self.check_thread.timeout.disconnect(self.periodic_check_handler) + except (TypeError, AttributeError): + pass + + self.check_thread.timeout.connect(self.periodic_check_handler) + self.check_thread.start(QtCore.QThread.HighPriority) + + def periodic_check_handler(self): + """ + If the parsing worker finished then start multithreaded rendering + :return: + """ + # log.debug("checking parsing --> %s" % str(self.parsing_promises)) + + try: + if not self.parsing_promises: + self.check_thread.stop() + log.debug("PDF --> start rendering") + # parsing finished start the layer rendering + if self.pdf_parsed: + obj_to_delete = [] + for object_name in self.pdf_parsed: + if self.app.abort_flag: + # graceful abort requested by the user + raise grace + + filename = deepcopy(self.pdf_parsed[object_name]['filename']) + pdf_content = deepcopy(self.pdf_parsed[object_name]['pdf']) + obj_to_delete.append(object_name) + for k in pdf_content: + if self.app.abort_flag: + # graceful abort requested by the user + raise grace + + ap_dict = pdf_content[k] + print(k, ap_dict) + if ap_dict: + layer_nr = k + if k == 0: + self.app.worker_task.emit({'fcn': self.layer_rendering_as_excellon, + 'params': [filename, ap_dict, layer_nr]}) + else: + self.app.worker_task.emit({'fcn': self.layer_rendering_as_gerber, + 'params': [filename, ap_dict, layer_nr]}) + # delete the object already processed so it will not be processed again for other objects + # that were opened at the same time; like in drag & drop on AppGUI + for obj_name in obj_to_delete: + if obj_name in self.pdf_parsed: + self.pdf_parsed.pop(obj_name) + + log.debug("ToolPDF --> Periodic check finished.") + except Exception: + traceback.print_exc() diff --git a/flatcamTools/ToolPaint.py b/AppTools/ToolPaint.py similarity index 98% rename from flatcamTools/ToolPaint.py rename to AppTools/ToolPaint.py index 4dda2d36..b2b68625 100644 --- a/flatcamTools/ToolPaint.py +++ b/AppTools/ToolPaint.py @@ -8,13 +8,12 @@ from PyQt5 import QtWidgets, QtGui, QtCore from PyQt5.QtCore import Qt -from FlatCAMTool import FlatCAMTool +from AppTool import AppTool from copy import deepcopy # from ObjectCollection import * -from flatcamParsers.ParseGerber import Gerber -from camlib import Geometry, FlatCAMRTreeStorage -from flatcamGUI.GUIElements import FCTable, FCDoubleSpinner, FCCheckBox, FCInputDialog, RadioSet, FCButton, FCComboBox -from FlatCAMCommon import GracefulException as grace +from AppParsers.ParseGerber import Gerber +from camlib import Geometry, FlatCAMRTreeStorage, grace +from AppGUI.GUIElements import FCTable, FCDoubleSpinner, FCCheckBox, FCInputDialog, RadioSet, FCButton, FCComboBox from shapely.geometry import base, Polygon, MultiPolygon, LinearRing, Point from shapely.ops import cascaded_union, unary_union, linemerge @@ -28,7 +27,7 @@ import traceback import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -38,7 +37,7 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolPaint(FlatCAMTool, Gerber): +class ToolPaint(AppTool, Gerber): toolName = _("Paint Tool") @@ -46,7 +45,7 @@ class ToolPaint(FlatCAMTool, Gerber): self.app = app self.decimals = self.app.decimals - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) Geometry.__init__(self, geo_steps_per_circle=self.app.defaults["geometry_circle_steps"]) # ## Title @@ -439,7 +438,7 @@ class ToolPaint(FlatCAMTool, Gerber): ) grid4.addWidget(self.gen_param_label, 15, 0, 1, 2) - self.rest_cb = FCCheckBox('%s' % _("Rest Machining")) + self.rest_cb = FCCheckBox('%s' % _("Rest")) self.rest_cb.setObjectName('p_rest_machining') self.rest_cb.setToolTip( _("If checked, use 'rest machining'.\n" @@ -483,7 +482,7 @@ class ToolPaint(FlatCAMTool, Gerber): self.selectmethod_combo = FCComboBox() self.selectmethod_combo.addItems( - [_("Polygon Selection"), _("Area Selection"), _("All Polygons"), _("Reference Object")] + [_("Polygon Selection"), _("Area Selection"), _("All"), _("Reference Object")] ) self.selectmethod_combo.setObjectName('p_selection') @@ -707,7 +706,7 @@ class ToolPaint(FlatCAMTool, Gerber): }[self.reference_type_combo.get_value()] def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+P', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+P', **kwargs) def run(self, toggle=True): self.app.defaults.report_usage("ToolPaint()") @@ -732,7 +731,7 @@ class ToolPaint(FlatCAMTool, Gerber): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Paint Tool")) @@ -1424,7 +1423,7 @@ class ToolPaint(FlatCAMTool, Gerber): self.app.inform.emit('[ERROR_NOTCL] %s' % _("No selected tools in Tool Table.")) return - if self.select_method == _("All Polygons"): + if self.select_method == _("All"): self.paint_poly_all(self.paint_obj, tooldia=self.tooldia_list, outname=self.o_name) @@ -1724,16 +1723,22 @@ class ToolPaint(FlatCAMTool, Gerber): edge_width=self.app.defaults["global_cursor_width"], size=self.app.defaults["global_cursor_size"]) - # update the positions on status bar - self.app.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (curr_pos[0], curr_pos[1])) if self.cursor_pos is None: self.cursor_pos = (0, 0) self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) - self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.app.dx, self.app.dy)) + + # # update the positions on status bar + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) # draw the utility geometry if shape_type == "square": @@ -2309,7 +2314,7 @@ class ToolPaint(FlatCAMTool, Gerber): def job_thread(app_obj): try: - ret = app_obj.new_object("geometry", name, job_init, plot=plot) + ret = app_obj.app_obj.new_object("geometry", name, job_init, plot=plot) except grace: proc.done() return @@ -2817,9 +2822,9 @@ class ToolPaint(FlatCAMTool, Gerber): def job_thread(app_obj): try: if self.rest_cb.isChecked(): - ret = app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot) + ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot) else: - ret = app_obj.new_object("geometry", name, gen_paintarea, plot=plot) + ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea, plot=plot) except grace: proc.done() return @@ -3314,9 +3319,9 @@ class ToolPaint(FlatCAMTool, Gerber): def job_thread(app_obj): try: if self.rest_cb.isChecked(): - ret = app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot) + ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea_rest_machining, plot=plot) else: - ret = app_obj.new_object("geometry", name, gen_paintarea, plot=plot) + ret = app_obj.app_obj.new_object("geometry", name, gen_paintarea, plot=plot) except grace: proc.done() return diff --git a/flatcamTools/ToolPanelize.py b/AppTools/ToolPanelize.py similarity index 96% rename from flatcamTools/ToolPanelize.py rename to AppTools/ToolPanelize.py index 8995da05..be41ba5e 100644 --- a/flatcamTools/ToolPanelize.py +++ b/AppTools/ToolPanelize.py @@ -6,10 +6,11 @@ # ########################################################## from PyQt5 import QtWidgets, QtGui, QtCore -from FlatCAMTool import FlatCAMTool +from AppTool import AppTool + +from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection, FCComboBox +from camlib import grace -from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection, FCComboBox -from FlatCAMCommon import GracefulException as grace from copy import deepcopy import numpy as np @@ -18,7 +19,7 @@ from shapely.ops import unary_union from shapely.geometry import LineString import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins import logging @@ -29,14 +30,14 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class Panelize(FlatCAMTool): +class Panelize(AppTool): toolName = _("Panelize PCB") def __init__(self, app): self.decimals = app.decimals - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) # ## Title title_label = QtWidgets.QLabel("%s" % self.toolName) @@ -49,8 +50,6 @@ class Panelize(FlatCAMTool): """) self.layout.addWidget(title_label) - self.layout.addWidget(QtWidgets.QLabel('')) - self.object_label = QtWidgets.QLabel('%s:' % _("Source Object")) self.object_label.setToolTip( _("Specify the type of object to be panelized\n" @@ -90,7 +89,6 @@ class Panelize(FlatCAMTool): "be duplicated in an array of rows and columns.") ) form_layout_0.addRow(self.object_combo) - form_layout_0.addRow(QtWidgets.QLabel("")) # Form Layout form_layout = QtWidgets.QFormLayout() @@ -142,7 +140,11 @@ class Panelize(FlatCAMTool): "selected object that is to be panelized.") ) form_layout.addRow(self.box_combo) - form_layout.addRow(QtWidgets.QLabel("")) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + form_layout.addRow(separator_line) panel_data_label = QtWidgets.QLabel("%s:" % _("Panel Data")) panel_data_label.setToolTip( @@ -198,7 +200,11 @@ class Panelize(FlatCAMTool): _("Number of rows of the desired panel") ) form_layout.addRow(self.rows_label, self.rows) - form_layout.addRow(QtWidgets.QLabel("")) + + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + form_layout.addRow(separator_line) # Type of resulting Panel object self.panel_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'gerber'}, @@ -248,6 +254,11 @@ class Panelize(FlatCAMTool): self.constrain_sel = OptionalInputSection( self.constrain_cb, [self.x_width_lbl, self.x_width_entry, self.y_height_lbl, self.y_height_entry]) + separator_line = QtWidgets.QFrame() + separator_line.setFrameShape(QtWidgets.QFrame.HLine) + separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) + form_layout.addRow(separator_line) + # Buttons self.panelize_object_button = QtWidgets.QPushButton(_("Panelize Object")) self.panelize_object_button.setToolTip( @@ -316,13 +327,13 @@ class Panelize(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Panel. Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+Z', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+Z', **kwargs) def set_tool_ui(self): self.reset_fields() @@ -784,9 +795,9 @@ class Panelize(FlatCAMTool): self.app.inform.emit('%s: %d' % (_("Generating panel... Spawning copies"), (int(rows * columns)))) if panel_source_obj.kind == 'excellon': - self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True) + self.app.app_obj.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True) else: - self.app.new_object(panel_type, self.outname, job_init_geometry, plot=True, autoselected=True) + self.app.app_obj.new_object(panel_type, self.outname, job_init_geometry, plot=True, autoselected=True) if self.constrain_flag is False: self.app.inform.emit('[success] %s' % _("Panel done...")) diff --git a/flatcamTools/ToolPcbWizard.py b/AppTools/ToolPcbWizard.py similarity index 97% rename from flatcamTools/ToolPcbWizard.py rename to AppTools/ToolPcbWizard.py index 47d3a196..a9fc6075 100644 --- a/flatcamTools/ToolPcbWizard.py +++ b/AppTools/ToolPcbWizard.py @@ -7,8 +7,8 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCSpinner, FCButton, FCTable +from AppTool import AppTool +from AppGUI.GUIElements import RadioSet, FCSpinner, FCButton, FCTable import re import os @@ -16,7 +16,7 @@ from datetime import datetime from io import StringIO import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -24,14 +24,14 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class PcbWizard(FlatCAMTool): +class PcbWizard(AppTool): file_loaded = QtCore.pyqtSignal(str, str) toolName = _("PcbWizard Import Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.decimals = self.app.decimals @@ -191,13 +191,13 @@ class PcbWizard(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("PCBWizard Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, **kwargs) + AppTool.install(self, icon, separator, **kwargs) def set_tool_ui(self): self.units = 'INCH' @@ -452,7 +452,7 @@ class PcbWizard(FlatCAMTool): # Object name name = self.outname - ret_val = self.app.new_object("excellon", name, obj_init, autoselected=False) + ret_val = self.app.app_obj.new_object("excellon", name, obj_init, autoselected=False) if ret_val == 'fail': self.app.inform.emit('[ERROR_NOTCL] %s' % _('Import Excellon file failed.')) return diff --git a/flatcamTools/ToolProperties.py b/AppTools/ToolProperties.py similarity index 98% rename from flatcamTools/ToolProperties.py rename to AppTools/ToolProperties.py index e1cc1c25..59d73a8c 100644 --- a/flatcamTools/ToolProperties.py +++ b/AppTools/ToolProperties.py @@ -6,8 +6,8 @@ # ########################################################## from PyQt5 import QtGui, QtCore, QtWidgets -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCTree +from AppTool import AppTool +from AppGUI.GUIElements import FCTree from shapely.geometry import MultiPolygon, Polygon from shapely.ops import cascaded_union @@ -17,7 +17,7 @@ import math import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -27,13 +27,13 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class Properties(FlatCAMTool): +class Properties(AppTool): toolName = _("Properties") calculations_finished = QtCore.pyqtSignal(float, float, float, float, float, object) def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored) @@ -97,13 +97,13 @@ class Properties(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.properties() def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='P', **kwargs) + AppTool.install(self, icon, separator, shortcut='P', **kwargs) def set_tool_ui(self): # this reset the TreeWidget diff --git a/flatcamTools/ToolPunchGerber.py b/AppTools/ToolPunchGerber.py similarity index 94% rename from flatcamTools/ToolPunchGerber.py rename to AppTools/ToolPunchGerber.py index f0c6a9e3..819d9407 100644 --- a/flatcamTools/ToolPunchGerber.py +++ b/AppTools/ToolPunchGerber.py @@ -7,15 +7,15 @@ from PyQt5 import QtCore, QtWidgets -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox +from AppTool import AppTool +from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox from copy import deepcopy import logging from shapely.geometry import MultiPolygon, Point import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -25,12 +25,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolPunchGerber(FlatCAMTool): +class ToolPunchGerber(AppTool): toolName = _("Punch Gerber") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.decimals = self.app.decimals @@ -418,14 +418,14 @@ class ToolPunchGerber(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Punch Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+H', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+H', **kwargs) def set_tool_ui(self): self.reset_fields() @@ -591,7 +591,7 @@ class ToolPunchGerber(FlatCAMTool): new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None, local_use=new_obj, use_thread=False) - self.app.new_object('gerber', outname, init_func) + self.app.app_obj.new_object('gerber', outname, init_func) elif punch_method == 'fixed': punch_size = float(self.dia_entry.get_value()) @@ -599,32 +599,31 @@ class ToolPunchGerber(FlatCAMTool): self.app.inform.emit('[WARNING_NOTCL] %s' % _("The value of the fixed diameter is 0.0. Aborting.")) return 'fail' + fail_msg = _("Could not generate punched hole Gerber because the punch hole size is bigger than" + " some of the apertures in the Gerber object.") + punching_geo = [] for apid in grb_obj.apertures: if grb_obj.apertures[apid]['type'] == 'C' and self.circular_cb.get_value(): - if punch_size >= float(grb_obj.apertures[apid]['size']): - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Could not generate punched hole Gerber because the punch hole size" - " is bigger than some of the apertures in the Gerber object.")) - return 'fail' - else: - for elem in grb_obj.apertures[apid]['geometry']: - if 'follow' in elem: - if isinstance(elem['follow'], Point): - punching_geo.append(elem['follow'].buffer(punch_size / 2)) + for elem in grb_obj.apertures[apid]['geometry']: + if 'follow' in elem: + if isinstance(elem['follow'], Point): + if punch_size >= float(grb_obj.apertures[apid]['size']): + self.app.inform.emit('[ERROR_NOTCL] %s' % fail_msg) + return 'fail' + punching_geo.append(elem['follow'].buffer(punch_size / 2)) elif grb_obj.apertures[apid]['type'] == 'R': - if punch_size >= float(grb_obj.apertures[apid]['width']) or \ - punch_size >= float(grb_obj.apertures[apid]['height']): - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Could not generate punched hole Gerber because the punch hole size" - " is bigger than some of the apertures in the Gerber object.")) - return 'fail' - elif round(float(grb_obj.apertures[apid]['width']), self.decimals) == \ + + if round(float(grb_obj.apertures[apid]['width']), self.decimals) == \ round(float(grb_obj.apertures[apid]['height']), self.decimals) and \ self.square_cb.get_value(): for elem in grb_obj.apertures[apid]['geometry']: if 'follow' in elem: if isinstance(elem['follow'], Point): + if punch_size >= float(grb_obj.apertures[apid]['width']) or \ + punch_size >= float(grb_obj.apertures[apid]['height']): + self.app.inform.emit('[ERROR_NOTCL] %s' % fail_msg) + return 'fail' punching_geo.append(elem['follow'].buffer(punch_size / 2)) elif round(float(grb_obj.apertures[apid]['width']), self.decimals) != \ round(float(grb_obj.apertures[apid]['height']), self.decimals) and \ @@ -632,16 +631,26 @@ class ToolPunchGerber(FlatCAMTool): for elem in grb_obj.apertures[apid]['geometry']: if 'follow' in elem: if isinstance(elem['follow'], Point): + if punch_size >= float(grb_obj.apertures[apid]['width']) or \ + punch_size >= float(grb_obj.apertures[apid]['height']): + self.app.inform.emit('[ERROR_NOTCL] %s' % fail_msg) + return 'fail' punching_geo.append(elem['follow'].buffer(punch_size / 2)) elif grb_obj.apertures[apid]['type'] == 'O' and self.oblong_cb.get_value(): for elem in grb_obj.apertures[apid]['geometry']: if 'follow' in elem: if isinstance(elem['follow'], Point): + if punch_size >= float(grb_obj.apertures[apid]['size']): + self.app.inform.emit('[ERROR_NOTCL] %s' % fail_msg) + return 'fail' punching_geo.append(elem['follow'].buffer(punch_size / 2)) elif grb_obj.apertures[apid]['type'] not in ['C', 'R', 'O'] and self.other_cb.get_value(): for elem in grb_obj.apertures[apid]['geometry']: if 'follow' in elem: if isinstance(elem['follow'], Point): + if punch_size >= float(grb_obj.apertures[apid]['size']): + self.app.inform.emit('[ERROR_NOTCL] %s' % fail_msg) + return 'fail' punching_geo.append(elem['follow'].buffer(punch_size / 2)) punching_geo = MultiPolygon(punching_geo) @@ -705,7 +714,7 @@ class ToolPunchGerber(FlatCAMTool): new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None, local_use=new_obj, use_thread=False) - self.app.new_object('gerber', outname, init_func) + self.app.app_obj.new_object('gerber', outname, init_func) elif punch_method == 'ring': circ_r_val = self.circular_ring_entry.get_value() oblong_r_val = self.oblong_ring_entry.get_value() @@ -847,7 +856,7 @@ class ToolPunchGerber(FlatCAMTool): new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None, local_use=new_obj, use_thread=False) - self.app.new_object('gerber', outname, init_func) + self.app.app_obj.new_object('gerber', outname, init_func) elif punch_method == 'prop': prop_factor = self.factor_entry.get_value() / 100.0 @@ -986,7 +995,7 @@ class ToolPunchGerber(FlatCAMTool): new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None, local_use=new_obj, use_thread=False) - self.app.new_object('gerber', outname, init_func) + self.app.app_obj.new_object('gerber', outname, init_func) def reset_fields(self): self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) diff --git a/flatcamTools/ToolQRCode.py b/AppTools/ToolQRCode.py similarity index 98% rename from flatcamTools/ToolQRCode.py rename to AppTools/ToolQRCode.py index 8d54fedd..28ea7c9f 100644 --- a/flatcamTools/ToolQRCode.py +++ b/AppTools/ToolQRCode.py @@ -8,9 +8,9 @@ from PyQt5 import QtWidgets, QtCore, QtGui from PyQt5.QtCore import Qt -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox, FCComboBox, FCFileSaveDialog -from flatcamParsers.ParseSVG import * +from AppTool import AppTool +from AppGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox, FCComboBox, FCFileSaveDialog +from AppParsers.ParseSVG import * from shapely.geometry.base import * from shapely.ops import unary_union @@ -28,7 +28,7 @@ import qrcode.image.pil from lxml import etree as ET import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -38,12 +38,12 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class QRCode(FlatCAMTool): +class QRCode(AppTool): toolName = _("QRCode Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.app = app self.canvas = self.app.plotcanvas @@ -375,14 +375,14 @@ class QRCode(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("QRCode Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+Q', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+Q', **kwargs) def set_tool_ui(self): self.units = self.app.defaults['units'] @@ -781,9 +781,9 @@ class QRCode(FlatCAMTool): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export PNG"), directory=self.app.get_last_save_folder() + '/' + str(name) + '_png', - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export PNG"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export PNG"), ext_filter=_filter) filename = str(filename) @@ -828,9 +828,9 @@ class QRCode(FlatCAMTool): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export SVG"), directory=self.app.get_last_save_folder() + '/' + str(name) + '_svg', - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export SVG"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export SVG"), ext_filter=_filter) filename = str(filename) diff --git a/flatcamTools/ToolRulesCheck.py b/AppTools/ToolRulesCheck.py similarity index 98% rename from flatcamTools/ToolRulesCheck.py rename to AppTools/ToolRulesCheck.py index fe07b683..ee32824c 100644 --- a/flatcamTools/ToolRulesCheck.py +++ b/AppTools/ToolRulesCheck.py @@ -7,18 +7,18 @@ from PyQt5 import QtWidgets -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCComboBox +from AppTool import AppTool +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCComboBox from copy import deepcopy -from FlatCAMPool import * +from AppPool import * # from os import getpid from shapely.ops import nearest_points from shapely.geometry import MultiPolygon, Polygon import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -28,7 +28,7 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class RulesCheck(FlatCAMTool): +class RulesCheck(AppTool): toolName = _("Check Rules") @@ -37,7 +37,7 @@ class RulesCheck(FlatCAMTool): def __init__(self, app): self.decimals = app.decimals - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) # ## Title title_label = QtWidgets.QLabel("%s" % self.toolName) @@ -610,13 +610,13 @@ class RulesCheck(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Rules Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+R', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+R', **kwargs) def set_tool_ui(self): @@ -951,7 +951,7 @@ class RulesCheck(FlatCAMTool): geo = geo_el['solid'] pt = geo.representative_point() points_list.append((pt.x, pt.y)) - except Exception as e: + except Exception: # An exception will be raised for the 'size' key in case of apertures of type AM (macro) which does # not have the size key pass @@ -1137,7 +1137,7 @@ class RulesCheck(FlatCAMTool): copper_list.append(elem_dict) copper_name_2 = self.copper_b_object.currentText() - if copper_name_2 !='' and self.copper_b_cb.get_value(): + if copper_name_2 != '' and self.copper_b_cb.get_value(): elem_dict = {} elem_dict['name'] = deepcopy(copper_name_2) elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_2).apertures) @@ -1363,12 +1363,12 @@ class RulesCheck(FlatCAMTool): top_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_top).apertures) silk_bottom = self.ss_b_object.currentText() - if silk_bottom != '' and self.ss_b_cb.get_value(): + if silk_bottom != '' and self.ss_b_cb.get_value(): bottom_dict['name'] = deepcopy(silk_bottom) bottom_dict['apertures'] = deepcopy(self.app.collection.get_by_name(silk_bottom).apertures) copper_outline = self.outline_object.currentText() - if copper_outline != '' and self.out_cb.get_value(): + if copper_outline != '' and self.out_cb.get_value(): outline_dict['name'] = deepcopy(copper_outline) outline_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_outline).apertures) @@ -1421,7 +1421,7 @@ class RulesCheck(FlatCAMTool): if self.sm_t_cb.get_value(): solder_obj = self.sm_t_object.currentText() - if solder_obj != '': + if solder_obj != '': sm_dict['name'] = deepcopy(solder_obj) sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures) @@ -1431,7 +1431,7 @@ class RulesCheck(FlatCAMTool): _("TOP -> Minimum Solder Mask Sliver")))) if self.sm_b_cb.get_value(): solder_obj = self.sm_b_object.currentText() - if solder_obj != '': + if solder_obj != '': sm_dict['name'] = deepcopy(solder_obj) sm_dict['apertures'] = deepcopy(self.app.collection.get_by_name(solder_obj).apertures) @@ -1625,7 +1625,7 @@ class RulesCheck(FlatCAMTool): new_obj.source_file = txt new_obj.read_only = True - self.app.new_object('document', name='Rules Check results', initialize=init, plot=False) + self.app.app_obj.new_object('document', name='Rules_check_results', initialize=init, plot=False) def reset_fields(self): # self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) diff --git a/flatcamTools/ToolShell.py b/AppTools/ToolShell.py similarity index 95% rename from flatcamTools/ToolShell.py rename to AppTools/ToolShell.py index f68a9da4..246ffd24 100644 --- a/flatcamTools/ToolShell.py +++ b/AppTools/ToolShell.py @@ -10,7 +10,7 @@ from PyQt5.QtCore import Qt from PyQt5.QtGui import QTextCursor from PyQt5.QtWidgets import QVBoxLayout, QWidget -from flatcamGUI.GUIElements import _BrowserTextEdit, _ExpandableTextEdit +from AppGUI.GUIElements import _BrowserTextEdit, _ExpandableTextEdit import html import sys import traceback @@ -19,7 +19,7 @@ import tkinter as tk import tclCommands import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -267,6 +267,7 @@ class FCShell(TermWidget): self.app = app self.tcl_commands_storage = {} + self.tcl = None self.init_tcl() @@ -345,11 +346,11 @@ class FCShell(TermWidget): def is_command_complete(self, text): - def skipQuotes(txt): - quote = txt[0] - text_val = txt[1:] - endIndex = str(text_val).index(quote) - return text[endIndex:] + # def skipQuotes(txt): + # quote = txt[0] + # text_val = txt[1:] + # endIndex = str(text_val).index(quote) + # return text[endIndex:] # I'm disabling this because I need to be able to load paths that have spaces by # enclosing them in quotes --- Marius Stanciu @@ -371,10 +372,10 @@ class FCShell(TermWidget): Handles input from the shell. See FlatCAMApp.setup_shell for shell commands. Also handles execution in separated threads - :param text: FlatCAM TclCommand with parameters - :param no_echo: If True it will not try to print to the Shell because most likely the shell is hidden and it - will create crashes of the _Expandable_Edit widget - :return: output if there was any + :param text: FlatCAM TclCommand with parameters + :param no_echo: If True it will not try to print to the Shell because most likely the shell is hidden and it + will create crashes of the _Expandable_Edit widget + :return: output if there was any """ self.app.defaults.report_usage('exec_command') @@ -404,12 +405,11 @@ class FCShell(TermWidget): self.append_output(result + '\n') except tk.TclError as e: - # This will display more precise answer if something in TCL shell fails result = self.tcl.eval("set errorInfo") - self.app.log.error("Exec command Exception: %s" % (result + '\n')) + self.app.log.error("Exception on Tcl Command execution: %s" % (result + '\n')) if no_echo is False: - self.append_error('ERROR: ' + result + '\n') + self.append_error('ERROR Report: ' + result + '\n') # Show error in console and just return or in test raise exception if reraise: raise e diff --git a/flatcamTools/ToolSolderPaste.py b/AppTools/ToolSolderPaste.py similarity index 98% rename from flatcamTools/ToolSolderPaste.py rename to AppTools/ToolSolderPaste.py index 93cb4744..b2b3ed99 100644 --- a/flatcamTools/ToolSolderPaste.py +++ b/AppTools/ToolSolderPaste.py @@ -5,13 +5,13 @@ # MIT Licence # # ########################################################## -from FlatCAMTool import FlatCAMTool -from FlatCAMCommon import LoudDict -from flatcamGUI.GUIElements import FCComboBox, FCEntry, FCTable, \ +from AppTool import AppTool +from Common import LoudDict +from AppGUI.GUIElements import FCComboBox, FCEntry, FCTable, \ FCInputDialog, FCDoubleSpinner, FCSpinner, FCFileSaveDialog -from FlatCAMApp import log +from App_Main import log from camlib import distance -from flatcamEditors.FlatCAMTextEditor import TextEditor +from AppEditors.FlatCAMTextEditor import TextEditor from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5.QtCore import Qt @@ -25,7 +25,7 @@ import traceback from io import StringIO import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -33,11 +33,11 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class SolderPaste(FlatCAMTool): +class SolderPaste(AppTool): toolName = _("Solder Paste Tool") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) # Number of decimals to be used for tools/nozzles in this FlatCAM Tool self.decimals = self.app.decimals @@ -546,14 +546,14 @@ class SolderPaste(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.build_ui() self.app.ui.notebook.setTabText(2, _("SolderPaste Tool")) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+K', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+K', **kwargs) def on_add_tool_by_key(self): tool_add_popup = FCInputDialog(title='%s...' % _("New Tool"), @@ -1257,7 +1257,7 @@ class SolderPaste(FlatCAMTool): if use_thread: def job_thread(app_obj): try: - app_obj.new_object("geometry", name + "_solderpaste", geo_init) + app_obj.app_obj.new_object("geometry", name + "_solderpaste", geo_init) except Exception as e: log.error("SolderPaste.on_create_geo() --> %s" % str(e)) proc.done() @@ -1271,7 +1271,7 @@ class SolderPaste(FlatCAMTool): # Background self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: - self.app.new_object("geometry", name + "_solderpaste", geo_init) + self.app.app_obj.new_object("geometry", name + "_solderpaste", geo_init) def on_create_gcode_click(self, signal): """ @@ -1331,7 +1331,7 @@ class SolderPaste(FlatCAMTool): self.app.inform.emit(msg) return - # Object initialization function for app.new_object() + # Object initialization function for app.app_obj.new_object() # RUNNING ON SEPARATE THREAD! def job_init(job_obj): assert job_obj.kind == 'cncjob', \ @@ -1388,7 +1388,7 @@ class SolderPaste(FlatCAMTool): # To be run in separate thread def job_thread(app_obj): with self.app.proc_container.new("Generating CNC Code"): - if app_obj.new_object("cncjob", name, job_init) != 'fail': + if app_obj.app_obj.new_object("cncjob", name, job_init) != 'fail': app_obj.inform.emit('[success] [success] %s: %s' % (_("ToolSolderPaste CNCjob created"), name)) # Create a promise with the name @@ -1396,7 +1396,7 @@ class SolderPaste(FlatCAMTool): # Send to worker self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: - self.app.new_object("cncjob", name, job_init) + self.app.app_obj.new_object("cncjob", name, job_init) def on_view_gcode(self): """ @@ -1493,10 +1493,11 @@ class SolderPaste(FlatCAMTool): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export GCode ..."), directory=dir_file_to_save, - filter=_filter_ + ext_filter=_filter_ ) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Machine Code ..."), filter=_filter_) + filename, _f = FCFileSaveDialog.get_saved_filename( + caption=_("Export Machine Code ..."), ext_filter=_filter_) if filename == '': self.app.inform.emit('[WARNING_NOTCL] %s' % diff --git a/flatcamTools/ToolSub.py b/AppTools/ToolSub.py similarity index 78% rename from flatcamTools/ToolSub.py rename to AppTools/ToolSub.py index 5409fa76..9c5e1457 100644 --- a/flatcamTools/ToolSub.py +++ b/AppTools/ToolSub.py @@ -7,8 +7,8 @@ from PyQt5 import QtWidgets, QtCore -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCCheckBox, FCButton, FCComboBox +from AppTool import AppTool +from AppGUI.GUIElements import FCCheckBox, FCButton, FCComboBox from shapely.geometry import Polygon, MultiPolygon, MultiLineString, LineString from shapely.ops import cascaded_union @@ -18,7 +18,7 @@ from copy import deepcopy import time import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -28,17 +28,22 @@ if '_' not in builtins.__dict__: log = logging.getLogger('base') -class ToolSub(FlatCAMTool): +class ToolSub(AppTool): job_finished = QtCore.pyqtSignal(bool) + # the string param is the outname and the list is a list of tuples each being formed from the new_aperture_geometry + # list and the second element is also a list with possible geometry that needs to be added to the '0' aperture + # meaning geometry that was deformed + aperture_processing_finished = QtCore.pyqtSignal(str, list) + toolName = _("Subtract Tool") def __init__(self, app): self.app = app self.decimals = self.app.decimals - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.tools_frame = QtWidgets.QFrame() self.tools_frame.setContentsMargins(0, 0, 0, 0) @@ -218,22 +223,18 @@ class ToolSub(FlatCAMTool): self.sub_union = [] - try: - self.intersect_btn.clicked.disconnect(self.on_grb_intersection_click) - except (TypeError, AttributeError): - pass - self.intersect_btn.clicked.connect(self.on_grb_intersection_click) + # multiprocessing + self.pool = self.app.pool + self.results = [] - try: - self.intersect_geo_btn.clicked.disconnect() - except (TypeError, AttributeError): - pass + self.intersect_btn.clicked.connect(self.on_grb_intersection_click) self.intersect_geo_btn.clicked.connect(self.on_geo_intersection_click) self.job_finished.connect(self.on_job_finished) + self.aperture_processing_finished.connect(self.new_gerber_object) self.reset_button.clicked.connect(self.set_tool_ui) def install(self, icon=None, separator=None, **kwargs): - FlatCAMTool.install(self, icon, separator, shortcut='Alt+W', **kwargs) + AppTool.install(self, icon, separator, shortcut='Alt+W', **kwargs) def run(self, toggle=True): self.app.defaults.report_usage("ToolSub()") @@ -257,7 +258,7 @@ class ToolSub(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.run(self) self.set_tool_ui() self.app.ui.notebook.setTabText(2, _("Sub Tool")) @@ -310,138 +311,141 @@ class ToolSub(FlatCAMTool): # crate the new_apertures dict structure for apid in self.target_grb_obj.apertures: self.new_apertures[apid] = {} - self.new_apertures[apid]['type'] = 'C' - self.new_apertures[apid]['size'] = self.target_grb_obj.apertures[apid]['size'] - self.new_apertures[apid]['geometry'] = [] + for key in self.target_grb_obj.apertures[apid]: + if key == 'geometry': + self.new_apertures[apid]['geometry'] = [] + else: + self.new_apertures[apid][key] = self.target_grb_obj.apertures[apid][key] - geo_solid_union_list = [] - geo_follow_union_list = [] - geo_clear_union_list = [] + def worker_job(app_obj): + for apid in self.target_grb_obj.apertures: + target_geo = self.target_grb_obj.apertures[apid]['geometry'] - for apid1 in self.sub_grb_obj.apertures: - if 'geometry' in self.sub_grb_obj.apertures[apid1]: - for elem in self.sub_grb_obj.apertures[apid1]['geometry']: - if 'solid' in elem: - geo_solid_union_list.append(elem['solid']) - if 'follow' in elem: - geo_follow_union_list.append(elem['follow']) - if 'clear' in elem: - geo_clear_union_list.append(elem['clear']) + sub_geometry = {} + sub_geometry['solid'] = [] + sub_geometry['clear'] = [] + for s_apid in self.sub_grb_obj.apertures: + for s_el in self.sub_grb_obj.apertures[s_apid]['geometry']: + if "solid" in s_el: + sub_geometry['solid'].append(s_el["solid"]) + if "clear" in s_el: + sub_geometry['clear'].append(s_el["clear"]) - self.app.inform.emit('%s' % _("Processing geometry from Subtractor Gerber object.")) - self.sub_solid_union = cascaded_union(geo_solid_union_list) - self.sub_follow_union = cascaded_union(geo_follow_union_list) - self.sub_clear_union = cascaded_union(geo_clear_union_list) + self.results.append( + self.pool.apply_async(self.aperture_intersection, args=(apid, target_geo, sub_geometry)) + ) - # add the promises - for apid in self.target_grb_obj.apertures: - self.promises.append(apid) + output = [] + for p in self.results: + res = p.get() + output.append(res) + app_obj.inform.emit('%s: %s...' % (_("Finished parsing geometry for aperture"), str(res[0]))) - # start the QTimer to check for promises with 0.5 second period check - self.periodic_check(500, reset=True) + app_obj.inform.emit("%s" % _("Subtraction aperture processing finished.")) - for apid in self.target_grb_obj.apertures: - geo = self.target_grb_obj.apertures[apid]['geometry'] - self.app.worker_task.emit({'fcn': self.aperture_intersection, 'params': [apid, geo]}) + outname = self.target_gerber_combo.currentText() + '_sub' + self.aperture_processing_finished.emit(outname, output) - def aperture_intersection(self, apid, geo): - new_geometry = [] + self.app.worker_task.emit({'fcn': worker_job, 'params': [self.app]}) - log.debug("Working on promise: %s" % str(apid)) + @staticmethod + def aperture_intersection(apid, target_geo, sub_geometry): + """ - with self.app.proc_container.new('%s: %s...' % (_("Parsing geometry for aperture"), str(apid))): + :param apid: the aperture id for which we process geometry + :type apid: str + :param target_geo: the geometry list that holds the geometry from which we subtract + :type target_geo: list + :param sub_geometry: the apertures dict that holds all the geometry that is subtracted + :type sub_geometry: dict + :return: (apid, unaffected_geometry lsit, affected_geometry list) + :rtype: tuple + """ - for geo_el in geo: - new_el = {} + unafected_geo = [] + affected_geo = [] - if 'solid' in geo_el: - work_geo = geo_el['solid'] - if self.sub_solid_union: - if work_geo.intersects(self.sub_solid_union): - new_geo = work_geo.difference(self.sub_solid_union) - new_geo = new_geo.buffer(0) - if new_geo: - if not new_geo.is_empty: - new_el['solid'] = new_geo - else: - new_el['solid'] = work_geo - else: - new_el['solid'] = work_geo - else: - new_el['solid'] = work_geo - else: - new_el['solid'] = work_geo + is_modified = False + for geo_el in target_geo: + new_geo_el = {} + if "solid" in geo_el: + for sub_solid_geo in sub_geometry["solid"]: + if geo_el["solid"].intersects(sub_solid_geo): + new_geo = geo_el["solid"].difference(sub_solid_geo) + if not new_geo.is_empty: + geo_el["solid"] = new_geo + is_modified = True - if 'follow' in geo_el: - work_geo = geo_el['follow'] - if self.sub_follow_union: - if work_geo.intersects(self.sub_follow_union): - new_geo = work_geo.difference(self.sub_follow_union) - new_geo = new_geo.buffer(0) - if new_geo: - if not new_geo.is_empty: - new_el['follow'] = new_geo - else: - new_el['follow'] = work_geo - else: - new_el['follow'] = work_geo - else: - new_el['follow'] = work_geo - else: - new_el['follow'] = work_geo + new_geo_el["solid"] = deepcopy(geo_el["solid"]) - if 'clear' in geo_el: - work_geo = geo_el['clear'] - if self.sub_clear_union: - if work_geo.intersects(self.sub_clear_union): - new_geo = work_geo.difference(self.sub_clear_union) - new_geo = new_geo.buffer(0) - if new_geo: - if not new_geo.is_empty: - new_el['clear'] = new_geo - else: - new_el['clear'] = work_geo - else: - new_el['clear'] = work_geo - else: - new_el['clear'] = work_geo - else: - new_el['clear'] = work_geo + if "clear" in geo_el: + for sub_solid_geo in sub_geometry["clear"]: + if geo_el["clear"].intersects(sub_solid_geo): + new_geo = geo_el["clear"].difference(sub_solid_geo) + if not new_geo.is_empty: + geo_el["clear"] = new_geo + is_modified = True - new_geometry.append(deepcopy(new_el)) + new_geo_el["clear"] = deepcopy(geo_el["clear"]) - self.app.inform.emit('%s: %s...' % (_("Finished parsing geometry for aperture"), str(apid))) + if is_modified: + affected_geo.append(new_geo_el) + else: + unafected_geo.append(geo_el) - if new_geometry: - while not self.new_apertures[apid]['geometry']: - self.new_apertures[apid]['geometry'] = deepcopy(new_geometry) - time.sleep(0.5) + return apid, unafected_geo, affected_geo - while True: - # removal from list is done in a multithreaded way therefore not always the removal can be done - # so we keep trying until it's done - if apid not in self.promises: - break + def new_gerber_object(self, outname, output): + """ - self.promises.remove(apid) - time.sleep(0.5) - - log.debug("Promise fulfilled: %s" % str(apid)) - - def new_gerber_object(self, outname): + :param outname: name for the new Gerber object + :type outname: str + :param output: a list made of tuples in format: + (aperture id in the target Gerber, unaffected_geometry list, affected_geometry list) + :type output: list + :return: + :rtype: + """ def obj_init(grb_obj, app_obj): grb_obj.apertures = deepcopy(self.new_apertures) + if '0' not in grb_obj.apertures: + grb_obj.apertures['0'] = {} + grb_obj.apertures['0']['type'] = 'REG' + grb_obj.apertures['0']['size'] = 0.0 + grb_obj.apertures['0']['geometry'] = [] + + for apid, apid_val in list(grb_obj.apertures.items()): + for t in output: + new_apid = t[0] + if apid == new_apid: + surving_geo = t[1] + modified_geo = t[2] + if surving_geo: + apid_val['geometry'] = deepcopy(surving_geo) + else: + grb_obj.apertures.pop(apid, None) + + if modified_geo: + grb_obj.apertures['0']['geometry'] += modified_geo + + # delete the '0' aperture if it has no geometry + if not grb_obj.apertures['0']['geometry']: + grb_obj.apertures.pop('0', None) + poly_buff = [] follow_buff = [] - for ap in self.new_apertures: - for elem in self.new_apertures[ap]['geometry']: - poly_buff.append(elem['solid']) - follow_buff.append(elem['follow']) + for ap in grb_obj.apertures: + for elem in grb_obj.apertures[ap]['geometry']: + if 'solid' in elem: + solid_geo = elem['solid'] + poly_buff.append(solid_geo) + if 'follow' in elem: + follow_buff.append(elem['follow']) - work_poly_buff = cascaded_union(poly_buff) + work_poly_buff = MultiPolygon(poly_buff) try: poly_buff = work_poly_buff.buffer(0.0000001) except ValueError: @@ -454,25 +458,22 @@ class ToolSub(FlatCAMTool): grb_obj.solid_geometry = deepcopy(poly_buff) grb_obj.follow_geometry = deepcopy(follow_buff) + grb_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None, + local_use=grb_obj, use_thread=False) with self.app.proc_container.new(_("Generating new object ...")): - ret = self.app.new_object('gerber', outname, obj_init, autoselected=False) + ret = self.app.app_obj.new_object('gerber', outname, obj_init, autoselected=False) if ret == 'fail': - self.app.inform.emit('[ERROR_NOTCL] %s' % - _('Generating new object failed.')) + self.app.inform.emit('[ERROR_NOTCL] %s' % _('Generating new object failed.')) return # GUI feedback - self.app.inform.emit('[success] %s: %s' % - (_("Created"), outname)) + self.app.inform.emit('[success] %s: %s' % (_("Created"), outname)) # cleanup self.new_apertures.clear() self.new_solid_geometry[:] = [] - try: - self.sub_union[:] = [] - except TypeError: - self.sub_union = [] + self.results = [] def on_geo_intersection_click(self): # reset previous values @@ -659,7 +660,7 @@ class ToolSub(FlatCAMTool): geo_obj.multigeo = False with self.app.proc_container.new(_("Generating new object ...")): - ret = self.app.new_object('geometry', outname, obj_init, autoselected=False) + ret = self.app.app_obj.new_object('geometry', outname, obj_init, autoselected=False) if ret == 'fail': self.app.inform.emit('[ERROR_NOTCL] %s' % _('Generating new object failed.')) @@ -739,11 +740,9 @@ class ToolSub(FlatCAMTool): outname = self.target_geo_combo.currentText() + '_sub' # intersection jobs finished, start the creation of solid_geometry - self.app.worker_task.emit({'fcn': self.new_geo_object, - 'params': [outname]}) + self.app.worker_task.emit({'fcn': self.new_geo_object, 'params': [outname]}) else: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _('Generating new object failed.')) + self.app.inform.emit('[ERROR_NOTCL] %s' % _('Generating new object failed.')) def reset_fields(self): self.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) diff --git a/flatcamTools/ToolTransform.py b/AppTools/ToolTransform.py similarity index 97% rename from flatcamTools/ToolTransform.py rename to AppTools/ToolTransform.py index 1db4fb93..41e6ff91 100644 --- a/flatcamTools/ToolTransform.py +++ b/AppTools/ToolTransform.py @@ -6,11 +6,11 @@ # ########################################################## from PyQt5 import QtWidgets -from FlatCAMTool import FlatCAMTool -from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, EvalEntry2 +from AppTool import AppTool +from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, FCEntry import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -18,7 +18,7 @@ if '_' not in builtins.__dict__: _ = gettext.gettext -class ToolTransform(FlatCAMTool): +class ToolTransform(AppTool): toolName = _("Object Transform") rotateName = _("Rotate") @@ -29,7 +29,7 @@ class ToolTransform(FlatCAMTool): bufferName = _("Buffer") def __init__(self, app): - FlatCAMTool.__init__(self, app) + AppTool.__init__(self, app) self.decimals = self.app.decimals # ## Title @@ -300,7 +300,7 @@ class ToolTransform(FlatCAMTool): "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_entry = EvalEntry2("(0, 0)") + self.flip_ref_entry = FCEntry() # self.flip_ref_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) # self.flip_ref_entry.setFixedWidth(70) @@ -454,13 +454,13 @@ class ToolTransform(FlatCAMTool): if self.app.ui.splitter.sizes()[0] == 0: self.app.ui.splitter.setSizes([1, 1]) - FlatCAMTool.run(self) + AppTool.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) + AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs) def set_tool_ui(self): self.rotate_button.set_value(_("Rotate")) @@ -533,7 +533,7 @@ class ToolTransform(FlatCAMTool): 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)) + self.flip_ref_entry.set_value("0, 0") if self.app.defaults["tools_transform_buffer_dis"]: self.buffer_entry.set_value(self.app.defaults["tools_transform_buffer_dis"]) @@ -702,7 +702,7 @@ class ToolTransform(FlatCAMTool): self.app.inform.emit(_("CNCJob objects can't be rotated.")) else: sel_obj.rotate(-num, point=(px, py)) - self.app.object_changed.emit(sel_obj) + self.app.app_obj.object_changed.emit(sel_obj) # add information to the object that it was changed and how much sel_obj.options['rotate'] = num @@ -776,7 +776,7 @@ class ToolTransform(FlatCAMTool): else: sel_obj.options['mirror_x'] = True self.app.inform.emit('[success] %s...' % _('Flip on the X axis done')) - self.app.object_changed.emit(sel_obj) + self.app.app_obj.object_changed.emit(sel_obj) sel_obj.plot() except Exception as e: self.app.inform.emit('[ERROR_NOTCL] %s %s, %s.' % @@ -825,7 +825,7 @@ class ToolTransform(FlatCAMTool): sel_obj.skew(0, num, point=(xminimal, yminimal)) # add information to the object that it was changed and how much sel_obj.options['skew_y'] = num - self.app.object_changed.emit(sel_obj) + self.app.app_obj.object_changed.emit(sel_obj) sel_obj.plot() self.app.inform.emit('[success] %s %s %s...' % (_('Skew on the'), str(axis), _("axis done"))) except Exception as e: @@ -878,7 +878,7 @@ class ToolTransform(FlatCAMTool): # add information to the object that it was changed and how much sel_obj.options['scale_x'] = xfactor sel_obj.options['scale_y'] = yfactor - self.app.object_changed.emit(sel_obj) + self.app.app_obj.object_changed.emit(sel_obj) sel_obj.plot() self.app.inform.emit('[success] %s %s %s...' % (_('Scale on the'), str(axis), _('axis done'))) @@ -908,7 +908,7 @@ class ToolTransform(FlatCAMTool): sel_obj.offset((0, num)) # add information to the object that it was changed and how much sel_obj.options['offset_y'] = num - self.app.object_changed.emit(sel_obj) + self.app.app_obj.object_changed.emit(sel_obj) sel_obj.plot() self.app.inform.emit('[success] %s %s %s...' % (_('Offset on the'), str(axis), _('axis done'))) @@ -942,7 +942,7 @@ class ToolTransform(FlatCAMTool): elif sel_obj.kind.lower() == 'geometry': sel_obj.buffer(value, join, factor) - self.app.object_changed.emit(sel_obj) + self.app.app_obj.object_changed.emit(sel_obj) sel_obj.plot() self.app.inform.emit('[success] %s...' % _('Buffer done')) diff --git a/AppTools/__init__.py b/AppTools/__init__.py new file mode 100644 index 00000000..e2699a42 --- /dev/null +++ b/AppTools/__init__.py @@ -0,0 +1,45 @@ + +from AppTools.ToolCalculators import ToolCalculator +from AppTools.ToolCalibration import ToolCalibration + +from AppTools.ToolDblSided import DblSidedTool +from AppTools.ToolExtractDrills import ToolExtractDrills +from AppTools.ToolAlignObjects import AlignObjects + +from AppTools.ToolFilm import Film + +from AppTools.ToolImage import ToolImage + +from AppTools.ToolDistance import Distance +from AppTools.ToolDistanceMin import DistanceMin + +from AppTools.ToolMove import ToolMove + +from AppTools.ToolCutOut import CutOut +from AppTools.ToolNCC import NonCopperClear +from AppTools.ToolPaint import ToolPaint +from AppTools.ToolIsolation import ToolIsolation + +from AppTools.ToolOptimal import ToolOptimal + +from AppTools.ToolPanelize import Panelize +from AppTools.ToolPcbWizard import PcbWizard +from AppTools.ToolPDF import ToolPDF +from AppTools.ToolProperties import Properties + +from AppTools.ToolQRCode import QRCode +from AppTools.ToolRulesCheck import RulesCheck + +from AppTools.ToolCopperThieving import ToolCopperThieving +from AppTools.ToolFiducials import ToolFiducials + +from AppTools.ToolShell import FCShell +from AppTools.ToolSolderPaste import SolderPaste +from AppTools.ToolSub import ToolSub + +from AppTools.ToolTransform import ToolTransform +from AppTools.ToolPunchGerber import ToolPunchGerber + +from AppTools.ToolInvertGerber import ToolInvertGerber +from AppTools.ToolCorners import ToolCorners +from AppTools.ToolEtchCompensation import ToolEtchCompensation \ No newline at end of file diff --git a/FlatCAMTranslation.py b/AppTranslation.py similarity index 98% rename from FlatCAMTranslation.py rename to AppTranslation.py index 4e8edbf3..8101d72e 100644 --- a/FlatCAMTranslation.py +++ b/AppTranslation.py @@ -106,6 +106,8 @@ def on_language_apply_click(app, restart=False): (_("Are you sure do you want to change the current language to"), name.capitalize())) msgbox.setWindowTitle(_("Apply Language ...")) msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/language32.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + bt_yes = msgbox.addButton(_("Yes"), QtWidgets.QMessageBox.YesRole) bt_no = msgbox.addButton(_("No"), QtWidgets.QMessageBox.NoRole) @@ -186,8 +188,7 @@ def restart_program(app, ask=None): # try to quit the Socket opened by ArgsThread class try: - app.new_launch.thread_exit = True - app.new_launch.listener.close() + app.new_launch.stop.emit() except Exception as err: log.debug("FlatCAMTranslation.restart_program() --> %s" % str(err)) @@ -204,6 +205,8 @@ def restart_program(app, ask=None): "Do you want to Save the project?")) msgbox.setWindowTitle(_("Save changes")) msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/save_as.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) diff --git a/FlatCAMWorker.py b/AppWorker.py similarity index 100% rename from FlatCAMWorker.py rename to AppWorker.py diff --git a/FlatCAMWorkerStack.py b/AppWorkerStack.py similarity index 97% rename from FlatCAMWorkerStack.py rename to AppWorkerStack.py index 3ce56011..fc8cd6d2 100644 --- a/FlatCAMWorkerStack.py +++ b/AppWorkerStack.py @@ -1,5 +1,5 @@ from PyQt5 import QtCore -from FlatCAMWorker import Worker +from AppWorker import Worker import multiprocessing diff --git a/FlatCAMApp.py b/App_Main.py similarity index 85% rename from FlatCAMApp.py rename to App_Main.py index 274752a9..c708399b 100644 --- a/FlatCAMApp.py +++ b/App_Main.py @@ -4,6 +4,7 @@ # Author: Juan Pablo Caram (c) # # Date: 2/5/2014 # # MIT Licence # +# Modified by Marius Stanciu (2019) # # ########################################################### import urllib.request @@ -13,14 +14,13 @@ import urllib.error import getopt import random import simplejson as json -import lzma import shutil +import lzma from datetime import datetime import time import ctypes import traceback -from PyQt5.QtCore import pyqtSlot, Qt from shapely.geometry import Point, MultiPolygon from io import StringIO @@ -42,58 +42,58 @@ import socket # ################################### Imports part of FlatCAM ############################################# # #################################################################################################################### -# Diverse -from FlatCAMCommon import LoudDict, color_variant -from FlatCAMBookmark import BookmarkManager -from FlatCAMDB import ToolsDB2 +# Various +from Common import LoudDict +from Common import color_variant +from Common import ExclusionAreas + +from Bookmark import BookmarkManager +from AppDatabase import ToolsDB2 from vispy.gloo.util import _screenshot from vispy.io import write_png -# FlatCAM Objects +# FlatCAM defaults (preferences) from defaults import FlatCAMDefaults -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI -from flatcamGUI.preferences.PreferencesUIManager import PreferencesUIManager -from flatcamObjects.ObjectCollection import * -from flatcamObjects.FlatCAMObj import FlatCAMObj -from flatcamObjects.FlatCAMCNCJob import CNCJobObject -from flatcamObjects.FlatCAMDocument import DocumentObject -from flatcamObjects.FlatCAMExcellon import ExcellonObject -from flatcamObjects.FlatCAMGeometry import GeometryObject -from flatcamObjects.FlatCAMGerber import GerberObject -from flatcamObjects.FlatCAMScript import ScriptObject + +# FlatCAM Objects +from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI +from AppGUI.preferences.PreferencesUIManager import PreferencesUIManager +from AppObjects.ObjectCollection import * +from AppObjects.FlatCAMObj import FlatCAMObj +from AppObjects.AppObject import AppObject # FlatCAM Parsing files -from flatcamParsers.ParseExcellon import Excellon -from flatcamParsers.ParseGerber import Gerber +from AppParsers.ParseExcellon import Excellon +from AppParsers.ParseGerber import Gerber from camlib import to_dict, dict2obj, ET, ParseError, Geometry, CNCjob -# FlatCAM GUI -from flatcamGUI.PlotCanvas import * -from flatcamGUI.PlotCanvasLegacy import * -from flatcamGUI.FlatCAMGUI import * -from flatcamGUI.GUIElements import FCFileSaveDialog +# FlatCAM AppGUI +from AppGUI.PlotCanvas import * +from AppGUI.PlotCanvasLegacy import * +from AppGUI.MainGUI import * +from AppGUI.GUIElements import FCFileSaveDialog, message_dialog, FlatCAMSystemTray # FlatCAM Pre-processors -from FlatCAMPostProc import load_preprocessors +from AppPreProcessor import load_preprocessors -# FlatCAM Editors -from flatcamEditors.FlatCAMGeoEditor import FlatCAMGeoEditor -from flatcamEditors.FlatCAMExcEditor import FlatCAMExcEditor -from flatcamEditors.FlatCAMGrbEditor import FlatCAMGrbEditor -from flatcamEditors.FlatCAMTextEditor import TextEditor -from flatcamParsers.ParseHPGL2 import HPGL2 +# FlatCAM AppEditors +from AppEditors.FlatCAMGeoEditor import FlatCAMGeoEditor +from AppEditors.FlatCAMExcEditor import FlatCAMExcEditor +from AppEditors.FlatCAMGrbEditor import FlatCAMGrbEditor +from AppEditors.FlatCAMTextEditor import TextEditor +from AppParsers.ParseHPGL2 import HPGL2 # FlatCAM Workers -from FlatCAMProcess import * -from FlatCAMWorkerStack import WorkerStack +from AppProcess import * +from AppWorkerStack import WorkerStack # FlatCAM Tools -from flatcamTools import * +from AppTools import * # FlatCAM Translation import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins if sys.platform == 'win32': @@ -107,7 +107,7 @@ if '_' not in builtins.__dict__: class App(QtCore.QObject): """ - The main application class. The constructor starts the GUI. + The main application class. The constructor starts the GUI and all other classes used by the program. """ # ############################################################################################################### @@ -204,7 +204,7 @@ class App(QtCore.QObject): # Inform the user # Handled by: # * App.info() --> Print on the status bar - inform = QtCore.pyqtSignal(str) + inform = QtCore.pyqtSignal([str], [str, bool]) app_quit = QtCore.pyqtSignal() @@ -227,20 +227,6 @@ class App(QtCore.QObject): # Percentage of progress progress = QtCore.pyqtSignal(int) - plots_updated = QtCore.pyqtSignal() - - # Emitted by new_object() and passes the new object as argument, plot flag. - # on_object_created() adds the object to the collection, plots on appropriate flag - # and emits new_object_available. - object_created = QtCore.pyqtSignal(object, bool, bool) - - # Emitted when a object has been changed (like scaled, mirrored) - object_changed = QtCore.pyqtSignal(object) - - # Emitted after object has been plotted. - # Calls 'on_zoom_fit' method to fit object in scene view in main thread to prevent drawing glitches. - object_plotted = QtCore.pyqtSignal(object) - # Emitted when a new object has been added or deleted from/to the collection object_status_changed = QtCore.pyqtSignal(object, str, str) @@ -285,6 +271,8 @@ class App(QtCore.QObject): :rtype: App """ + super().__init__() + App.log.info("FlatCAM Starting...") self.main_thread = QtWidgets.QApplication.instance().thread() @@ -303,7 +291,7 @@ class App(QtCore.QObject): self.new_launch.start.emit() # ############################################################################################################ - # # ######################################## OS-specific ##################################################### + # ########################################## OS-specific ##################################################### # ############################################################################################################ portable = False @@ -415,13 +403,12 @@ class App(QtCore.QObject): json.dump([], fp) fp.close() - # Application directory. CHDIR to it. Otherwise, trying to load - # GUI icons will fail as their path is relative. + # Application directory. CHDIR to it. Otherwise, trying to load GUI icons will fail as their path is relative. # This will fail under cx_freeze ... self.app_home = os.path.dirname(os.path.realpath(__file__)) - App.log.debug("Application path is " + self.app_home) - App.log.debug("Started in " + os.getcwd()) + log.debug("Application path is " + self.app_home) + log.debug("Started in " + os.getcwd()) # cx_freeze workaround if os.path.isfile(self.app_home): @@ -465,7 +452,6 @@ class App(QtCore.QObject): # ########################################################################################################### # ###################################### Setting the Splash Screen ########################################## # ########################################################################################################### - splash_settings = QSettings("Open Source", "FlatCAM") if splash_settings.contains("splash_screen"): show_splash = splash_settings.value("splash_screen") @@ -504,10 +490,6 @@ class App(QtCore.QObject): self.FC_light_blue = '#a5a5ffbf' self.FC_dark_blue = '#0000ffbf' - QtCore.QObject.__init__(self) - - self.ui = FlatCAMGUI(self) - theme_settings = QtCore.QSettings("Open Source", "FlatCAM") if theme_settings.contains("theme"): theme = theme_settings.value('theme', type=str) @@ -522,14 +504,10 @@ class App(QtCore.QObject): else: self.cursor_color_3D = 'gray' - self.ui.geom_update[int, int, int, int, int].connect(self.save_geometry) - self.ui.final_save.connect(self.final_save) + # update the defaults dict with the setting in QSetting + self.defaults['global_theme'] = theme - # restore the toolbar view - self.restore_toolbar_view() - - # restore the GUI geometry - self.restore_main_win_geom() + self.ui = MainGUI(self) # set FlatCAM units in the Status bar self.set_screen_units(self.defaults['units']) @@ -543,52 +521,15 @@ class App(QtCore.QObject): self.save_project_auto_update() self.autosave_timer.timeout.connect(self.save_project_auto) - # ########################################################################################################### - # ##################################### UPDATE PREFERENCES GUI FORMS ######################################## - # ########################################################################################################### - - self.preferencesUiManager = PreferencesUIManager(defaults=self.defaults, data_path=self.data_path, ui=self.ui, - inform=self.inform) - self.preferencesUiManager.defaults_write_form() - - # When the self.defaults dictionary changes will update the Preferences GUI forms - self.defaults.set_change_callback(self.on_defaults_dict_change) - - # ########################################################################################################### - # ##################################### FIRST RUN SECTION ################################################### - # ################################ It's done only once after install ##################################### - # ########################################################################################################### - - if self.defaults["first_run"] is True: - - # ONLY AT FIRST STARTUP INIT THE GUI LAYOUT TO 'COMPACT' - initial_lay = 'minimal' - self.ui.general_defaults_form.general_gui_group.on_layout(lay=initial_lay) - - # Set the combobox in Preferences to the current layout - idx = self.ui.general_defaults_form.general_gui_group.layout_combo.findText(initial_lay) - self.ui.general_defaults_form.general_gui_group.layout_combo.setCurrentIndex(idx) - - # after the first run, this object should be False - self.defaults["first_run"] = False - self.preferencesUiManager.save_defaults(silent=True) - - # ########################################################################################################### - # ############################################ Data ######################################################### - # ########################################################################################################### - - self.recent = [] - self.recent_projects = [] - - self.clipboard = QtWidgets.QApplication.clipboard() - - self.project_filename = None - self.toggle_units_ignore = False - # ########################################################################################################### # #################################### LOAD PREPROCESSORS ################################################### # ########################################################################################################### + # ----------------------------------------- WARNING -------------------------------------------------------- + # Preprocessors need to be loaded before the Preferences Manager builds the Preferences + # That's because the number of preprocessors can vary and here the comboboxes are populated + # ----------------------------------------------------------------------------------------------------------- + # a dictionary that have as keys the name of the preprocessor files and the value is the class from # the preprocessor file self.preprocessors = load_preprocessors(self) @@ -621,6 +562,46 @@ class App(QtCore.QObject): self.ui.excellon_defaults_form.excellon_opt_group.pp_excellon_name_cb.addItem(name) + # ########################################################################################################### + # ##################################### UPDATE PREFERENCES GUI FORMS ######################################## + # ########################################################################################################### + + self.preferencesUiManager = PreferencesUIManager(defaults=self.defaults, data_path=self.data_path, ui=self.ui, + inform=self.inform) + self.preferencesUiManager.defaults_write_form() + + # When the self.defaults dictionary changes will update the Preferences GUI forms + self.defaults.set_change_callback(self.on_defaults_dict_change) + + # ########################################################################################################### + # ##################################### FIRST RUN SECTION ################################################### + # ################################ It's done only once after install ##################################### + # ########################################################################################################### + if self.defaults["first_run"] is True: + # ONLY AT FIRST STARTUP INIT THE GUI LAYOUT TO 'COMPACT' + initial_lay = 'minimal' + self.ui.general_defaults_form.general_gui_group.on_layout(lay=initial_lay) + + # Set the combobox in Preferences to the current layout + idx = self.ui.general_defaults_form.general_gui_group.layout_combo.findText(initial_lay) + self.ui.general_defaults_form.general_gui_group.layout_combo.setCurrentIndex(idx) + + # after the first run, this object should be False + self.defaults["first_run"] = False + self.preferencesUiManager.save_defaults(silent=True) + + # ########################################################################################################### + # ############################################ Data ######################################################### + # ########################################################################################################### + + self.recent = [] + self.recent_projects = [] + + self.clipboard = QtWidgets.QApplication.clipboard() + + self.project_filename = None + self.toggle_units_ignore = False + # ########################################################################################################### # ########################################## LOAD LANGUAGES ################################################ # ########################################################################################################### @@ -675,9 +656,11 @@ class App(QtCore.QObject): # #################################### SETUP OBJECT COLLECTION ############################################## # ########################################################################################################### - self.collection = ObjectCollection(self) + self.collection = ObjectCollection(app=self) self.ui.project_tab_layout.addWidget(self.collection.view) + self.app_obj = AppObject(app=self) + # ### Adjust tabs width ## ## # self.collection.view.setMinimumWidth(self.ui.options_scroll_area.widget().sizeHint().width() + # self.ui.options_scroll_area.verticalScrollBar().sizeHint().width()) @@ -774,18 +757,14 @@ class App(QtCore.QObject): # ########################################## Custom signals ################################################ # signal for displaying messages in status bar - self.inform.connect(self.info) + self.inform[str].connect(self.info) + self.inform[str, bool].connect(self.info) + # signal to be called when the app is quiting self.app_quit.connect(self.quit_application, type=Qt.QueuedConnection) - self.message.connect(self.message_dialog) + self.message.connect(lambda: message_dialog(parent=self.ui)) # self.progress.connect(self.set_progress_bar) - # signals that are emitted when object state changes - self.object_created.connect(self.on_object_created) - self.object_changed.connect(self.on_object_changed) - self.object_plotted.connect(self.on_object_plotted) - self.plots_updated.connect(self.on_plots_updated) - # signals emitted when file state change self.file_opened.connect(self.register_recent) self.file_opened.connect(lambda kind, filename: self.register_folder(filename)) @@ -794,10 +773,10 @@ class App(QtCore.QObject): # ########################################## Standard signals ############################################### # ### Menu self.ui.menufilenewproject.triggered.connect(self.on_file_new_click) - self.ui.menufilenewgeo.triggered.connect(self.new_geometry_object) - self.ui.menufilenewgrb.triggered.connect(self.new_gerber_object) - self.ui.menufilenewexc.triggered.connect(self.new_excellon_object) - self.ui.menufilenewdoc.triggered.connect(self.new_document_object) + self.ui.menufilenewgeo.triggered.connect(self.app_obj.new_geometry_object) + self.ui.menufilenewgrb.triggered.connect(self.app_obj.new_gerber_object) + self.ui.menufilenewexc.triggered.connect(self.app_obj.new_excellon_object) + self.ui.menufilenewdoc.triggered.connect(self.app_obj.new_document_object) self.ui.menufileopengerber.triggered.connect(self.on_fileopengerber) self.ui.menufileopenexcellon.triggered.connect(self.on_fileopenexcellon) @@ -889,18 +868,21 @@ class App(QtCore.QObject): self.ui.menuview_replot.triggered.connect(self.plot_all) self.ui.menuview_toggle_code_editor.triggered.connect(self.on_toggle_code_editor) - self.ui.menuview_toggle_fscreen.triggered.connect(self.on_fullscreen) - self.ui.menuview_toggle_parea.triggered.connect(self.on_toggle_plotarea) - self.ui.menuview_toggle_notebook.triggered.connect(self.on_toggle_notebook) - self.ui.menu_toggle_nb.triggered.connect(self.on_toggle_notebook) - self.ui.menuview_toggle_grid.triggered.connect(self.on_toggle_grid) - self.ui.menuview_toggle_grid_lines.triggered.connect(self.on_toggle_grid_lines) - self.ui.menuview_toggle_axis.triggered.connect(self.on_toggle_axis) + self.ui.menuview_toggle_fscreen.triggered.connect(self.ui.on_fullscreen) + self.ui.menuview_toggle_parea.triggered.connect(self.ui.on_toggle_plotarea) + self.ui.menuview_toggle_notebook.triggered.connect(self.ui.on_toggle_notebook) + self.ui.menu_toggle_nb.triggered.connect(self.ui.on_toggle_notebook) + self.ui.menuview_toggle_grid.triggered.connect(self.ui.on_toggle_grid) self.ui.menuview_toggle_workspace.triggered.connect(self.on_workspace_toggle) - self.ui.menutoolshell.triggered.connect(self.toggle_shell) + self.ui.menuview_toggle_grid_lines.triggered.connect(self.plotcanvas.on_toggle_grid_lines) + self.ui.menuview_toggle_axis.triggered.connect(self.plotcanvas.on_toggle_axis) + self.ui.menuview_toggle_hud.triggered.connect(self.plotcanvas.on_toggle_hud) + + self.ui.menutoolshell.triggered.connect(self.ui.toggle_shell_ui) self.ui.menuhelp_about.triggered.connect(self.on_about) + self.ui.menuhelp_readme.triggered.connect(self.on_readme) self.ui.menuhelp_manual.triggered.connect(lambda: webbrowser.open(self.manual_url)) self.ui.menuhelp_report_bug.triggered.connect(lambda: webbrowser.open(self.bug_report_url)) self.ui.menuhelp_exc_spec.triggered.connect(lambda: webbrowser.open(self.excellon_spec_url)) @@ -933,11 +915,11 @@ class App(QtCore.QObject): # Context Menu self.ui.popmenu_disable.triggered.connect(lambda: self.toggle_plots(self.collection.get_selected())) - self.ui.popmenu_panel_toggle.triggered.connect(self.on_toggle_notebook) + self.ui.popmenu_panel_toggle.triggered.connect(self.ui.on_toggle_notebook) - self.ui.popmenu_new_geo.triggered.connect(self.new_geometry_object) - self.ui.popmenu_new_grb.triggered.connect(self.new_gerber_object) - self.ui.popmenu_new_exc.triggered.connect(self.new_excellon_object) + self.ui.popmenu_new_geo.triggered.connect(self.app_obj.new_geometry_object) + self.ui.popmenu_new_grb.triggered.connect(self.app_obj.new_gerber_object) + self.ui.popmenu_new_exc.triggered.connect(self.app_obj.new_excellon_object) self.ui.popmenu_new_prj.triggered.connect(self.on_file_new) self.ui.zoomfit.triggered.connect(self.on_zoom_fit) @@ -975,43 +957,15 @@ class App(QtCore.QObject): # ########################################################################################################### # ######################################## GUI SETTINGS SIGNALS ############################################# # ########################################################################################################### - self.ui.general_defaults_form.general_app_group.ge_radio.activated_custom.connect(self.on_app_restart) self.ui.general_defaults_form.general_app_set_group.cursor_radio.activated_custom.connect(self.on_cursor_type) # ######################################## Tools related signals ############################################ - # Film Tool - self.ui.tools_defaults_form.tools_film_group.film_color_entry.editingFinished.connect( - self.on_film_color_entry) - self.ui.tools_defaults_form.tools_film_group.film_color_button.clicked.connect( - self.on_film_color_button) - - # QRCode Tool - self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.editingFinished.connect( - self.on_qrcode_fill_color_entry) - self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.clicked.connect( - self.on_qrcode_fill_color_button) - self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.editingFinished.connect( - self.on_qrcode_back_color_entry) - self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.clicked.connect( - self.on_qrcode_back_color_button) # portability changed signal self.ui.general_defaults_form.general_app_group.portability_cb.stateChanged.connect(self.on_portable_checked) # Object list - self.collection.view.activated.connect(self.on_row_activated) - self.collection.item_selected.connect(self.on_row_selected) - - self.object_status_changed.connect(self.on_collection_updated) - - # Make sure that when the Excellon loading parameters are changed, the change is reflected in the - # Export Excellon parameters. - self.ui.excellon_defaults_form.excellon_gen_group.update_excellon_cb.stateChanged.connect( - self.on_update_exc_export - ) - - # call it once to make sure it is updated at startup - self.on_update_exc_export(state=self.defaults["excellon_update"]) + self.object_status_changed.connect(self.collection.on_collection_updated) # when there are arguments at application startup this get launched self.args_at_startup[list].connect(self.on_startup_args) @@ -1068,6 +1022,15 @@ class App(QtCore.QObject): self.ui.util_defaults_form.kw_group.del_btn.clicked.connect( lambda: self.del_extension(ext_type='keyword')) + # ########################################################################################################### + # ########################################### GUI SIGNALS ################################################### + # ########################################################################################################### + self.ui.hud_label.clicked.connect(self.plotcanvas.on_toggle_hud) + self.ui.axis_status_label.clicked.connect(self.plotcanvas.on_toggle_axis) + + # ########################################################################################################### + # ####################################### VARIOUS SIGNALS ################################################### + # ########################################################################################################### # connect the abort_all_tasks related slots to the related signals self.proc_container.idle_flag.connect(self.app_is_idle) @@ -1087,7 +1050,7 @@ class App(QtCore.QObject): # ########################################## Other setups ################################################### # ########################################################################################################### - # to use for tools like Distance tool who depends on the event sources who are changed inside the Editors + # to use for tools like Distance tool who depends on the event sources who are changed inside the AppEditors # depending on from where those tools are called different actions can be done self.call_source = 'app' @@ -1365,10 +1328,13 @@ class App(QtCore.QObject): self.rules_tool = None self.sub_tool = None self.move_tool = None + self.cutout_tool = None self.ncclear_tool = None - self.optimal_tool = None self.paint_tool = None + self.isolation_tool = None + + self.optimal_tool = None self.transform_tool = None self.properties_tool = None self.pdf_tool = None @@ -1382,12 +1348,14 @@ class App(QtCore.QObject): self.align_objects_tool = None self.punch_tool = None self.invert_tool = None + self.corners_tool = None + self.etch_tool = None # always install tools only after the shell is initialized because the self.inform.emit() depends on shell try: self.install_tools() except AttributeError as e: - log.debug("App.__init__() install tools() --> %s" % str(e)) + log.debug("App.__init__() install_tools() --> %s" % str(e)) # ########################################################################################################### # ############################################ SETUP RECENT ITEMS ########################################### @@ -1483,15 +1451,6 @@ class App(QtCore.QObject): # holds the key modifier if pressed (CTRL, SHIFT or ALT) self.key_modifiers = None - # Variable to hold the status of the axis - self.toggle_axis = True - - # Variable to hold the status of the grid lines - self.toggle_grid_lines = True - - # Variable to store the status of the fullscreen event - self.toggle_fscreen = False - # Variable to store the status of the code editor self.toggle_codeeditor = False @@ -1562,7 +1521,7 @@ class App(QtCore.QObject): except AttributeError: self.tool_shapes = None else: - from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.tool_shapes = ShapeCollectionLegacy(obj=self, app=self, name="tool") # used in the delayed shutdown self.start_delayed_quit() method @@ -1598,6 +1557,11 @@ class App(QtCore.QObject): self.ui.excellon_defaults_form.excellon_gen_group.excellon_optimization_radio.set_value('T') self.ui.excellon_defaults_form.excellon_gen_group.excellon_optimization_radio.setDisabled(True) + # ########################################################################################################### + # ########################################### EXCLUSION AREAS ############################################### + # ########################################################################################################### + self.exc_areas = ExclusionAreas(app=self) + # ########################################################################################################### # ##################################### Finished the CONSTRUCTOR ############################################ # ########################################################################################################### @@ -1712,7 +1676,7 @@ class App(QtCore.QObject): try: shutil.copytree(from_path, to_path) except FileNotFoundError: - from_new_path = os.path.dirname(os.path.realpath(__file__)) + '\\flatcamGUI\\VisPyData\\data' + from_new_path = os.path.dirname(os.path.realpath(__file__)) + '\\AppGUI\\VisPyData\\data' shutil.copytree(from_new_path, to_path) def on_startup_args(self, args, silent=False): @@ -1947,6 +1911,10 @@ class App(QtCore.QObject): self.paint_tool.install(icon=QtGui.QIcon(self.resource_location + '/paint16.png'), pos=self.ui.menutool, before=self.sub_tool.menuAction, separator=True) + self.isolation_tool = ToolIsolation(self) + self.isolation_tool.install(icon=QtGui.QIcon(self.resource_location + '/iso_16.png'), pos=self.ui.menutool, + before=self.sub_tool.menuAction, separator=True) + self.copper_thieving_tool = ToolCopperThieving(self) self.copper_thieving_tool.install(icon=QtGui.QIcon(self.resource_location + '/copperfill32.png'), pos=self.ui.menutool) @@ -1965,6 +1933,12 @@ class App(QtCore.QObject): self.invert_tool = ToolInvertGerber(self) self.invert_tool.install(icon=QtGui.QIcon(self.resource_location + '/invert32.png'), pos=self.ui.menutool) + self.corners_tool = ToolCorners(self) + self.corners_tool.install(icon=QtGui.QIcon(self.resource_location + '/corners_32.png'), pos=self.ui.menutool) + + self.etch_tool = ToolEtchCompensation(self) + self.etch_tool.install(icon=QtGui.QIcon(self.resource_location + '/etch_32.png'), pos=self.ui.menutool) + self.transform_tool = ToolTransform(self) self.transform_tool.install(icon=QtGui.QIcon(self.resource_location + '/transform.png'), pos=self.ui.menuoptions, separator=True) @@ -2030,7 +2004,7 @@ class App(QtCore.QObject): # re-add the TCL Shell action to the Tools menu and reconnect it to ist slot function self.ui.menutoolshell = self.ui.menutool.addAction(QtGui.QIcon(self.resource_location + '/shell16.png'), '&Command Line\tS') - self.ui.menutoolshell.triggered.connect(self.toggle_shell) + self.ui.menutoolshell.triggered.connect(self.ui.toggle_shell_ui) # third install all of them try: @@ -2053,21 +2027,22 @@ class App(QtCore.QObject): """ # Toolbar + + # File Toolbar Signals # self.ui.file_new_btn.triggered.connect(self.on_file_new) self.ui.file_open_btn.triggered.connect(self.on_file_openproject) self.ui.file_save_btn.triggered.connect(self.on_file_saveproject) self.ui.file_open_gerber_btn.triggered.connect(self.on_fileopengerber) self.ui.file_open_excellon_btn.triggered.connect(self.on_fileopenexcellon) + # View Toolbar Signals self.ui.clear_plot_btn.triggered.connect(self.clear_plots) self.ui.replot_btn.triggered.connect(self.plot_all) self.ui.zoom_fit_btn.triggered.connect(self.on_zoom_fit) self.ui.zoom_in_btn.triggered.connect(lambda: self.plotcanvas.zoom(1 / 1.5)) self.ui.zoom_out_btn.triggered.connect(lambda: self.plotcanvas.zoom(1.5)) - self.ui.newgeo_btn.triggered.connect(self.new_geometry_object) - self.ui.newgrb_btn.triggered.connect(self.new_gerber_object) - self.ui.newexc_btn.triggered.connect(self.new_excellon_object) + # Edit Toolbar Signals self.ui.editgeo_btn.triggered.connect(self.object2editor) self.ui.update_obj_btn.triggered.connect(lambda: self.editor2object()) self.ui.copy_btn.triggered.connect(self.on_copy_command) @@ -2081,7 +2056,8 @@ class App(QtCore.QObject): self.ui.jmp_btn.triggered.connect(self.on_jump_to) self.ui.locate_btn.triggered.connect(lambda: self.on_locate(obj=self.collection.get_active())) - self.ui.shell_btn.triggered.connect(self.toggle_shell) + # Scripting Toolbar Signals + self.ui.shell_btn.triggered.connect(self.ui.toggle_shell_ui) self.ui.new_script_btn.triggered.connect(self.on_filenewscript) self.ui.open_script_btn.triggered.connect(self.on_fileopenscript) self.ui.run_script_btn.triggered.connect(self.on_filerunscript) @@ -2095,6 +2071,7 @@ class App(QtCore.QObject): self.ui.cutout_btn.triggered.connect(lambda: self.cutout_tool.run(toggle=True)) self.ui.ncc_btn.triggered.connect(lambda: self.ncclear_tool.run(toggle=True)) self.ui.paint_btn.triggered.connect(lambda: self.paint_tool.run(toggle=True)) + self.ui.isolation_btn.triggered.connect(lambda: self.isolation_tool.run(toggle=True)) self.ui.panelize_btn.triggered.connect(lambda: self.panelize_tool.run(toggle=True)) self.ui.film_btn.triggered.connect(lambda: self.film_tool.run(toggle=True)) @@ -2110,25 +2087,27 @@ class App(QtCore.QObject): self.ui.fiducials_btn.triggered.connect(lambda: self.fiducial_tool.run(toggle=True)) self.ui.punch_btn.triggered.connect(lambda: self.punch_tool.run(toggle=True)) self.ui.invert_btn.triggered.connect(lambda: self.invert_tool.run(toggle=True)) + self.ui.corners_tool_btn.triggered.connect(lambda: self.corners_tool.run(toggle=True)) + self.ui.etch_btn.triggered.connect(lambda: self.etch_tool.run(toggle=True)) def object2editor(self): """ - Send the current Geometry or Excellon object (if any) into the it's editor. + Send the current Geometry, Gerber, Excellon object or CNCJob (if any) into the it's editor. :return: None """ self.defaults.report_usage("object2editor()") - # disable the objects menu as it may interfere with the Editors + # disable the objects menu as it may interfere with the AppEditors self.ui.menuobjects.setDisabled(True) edited_object = self.collection.get_active() if isinstance(edited_object, GerberObject) or isinstance(edited_object, GeometryObject) or \ - isinstance(edited_object, ExcellonObject): + isinstance(edited_object, ExcellonObject) or isinstance(edited_object, CNCJobObject): pass else: - self.inform.emit('[WARNING_NOTCL] %s' % _("Select a Geometry, Gerber or Excellon Object to edit.")) + self.inform.emit('[WARNING_NOTCL] %s' % _("Select a Geometry, Gerber, Excellon or CNCJob Object to edit.")) return if isinstance(edited_object, GeometryObject): @@ -2192,6 +2171,14 @@ class App(QtCore.QObject): edited_object.ui_build = False edited_object.build_aperture_storage = False + elif isinstance(edited_object, CNCJobObject): + + if self.ui.splitter.sizes()[0] == 0: + self.ui.splitter.setSizes([1, 1]) + + edited_object.on_edit_code_click() + return + # make sure that we can't select another object while in Editor Mode: # self.collection.view.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) self.ui.project_frame.setDisabled(True) @@ -2225,6 +2212,7 @@ class App(QtCore.QObject): msgbox.setText(_("Do you want to save the edited object?")) msgbox.setWindowTitle(_("Close Editor")) msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/save_as.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) @@ -2242,14 +2230,11 @@ class App(QtCore.QObject): if isinstance(edited_obj, GeometryObject): obj_type = "Geometry" - if cleanup is None: - self.geo_editor.update_fcgeometry(edited_obj) - # self.geo_editor.update_options(edited_obj) - - self.geo_editor.deactivate() + self.geo_editor.update_fcgeometry(edited_obj) + # self.geo_editor.update_options(edited_obj) # restore GUI to the Selected TAB - # Remove anything else in the GUI + # Remove anything else in the AppGUI self.ui.tool_scroll_area.takeWidget() # update the geo object options so it is including the bounding box values @@ -2264,21 +2249,19 @@ class App(QtCore.QObject): log.debug("App.editor2object() --> Geometry --> %s" % str(e)) edited_obj.build_ui() + edited_obj.plot() self.inform.emit('[success] %s' % _("Editor exited. Editor content saved.")) elif isinstance(edited_obj, GerberObject): obj_type = "Gerber" - if cleanup is None: - self.grb_editor.update_fcgerber() - self.grb_editor.update_options(edited_obj) - self.grb_editor.deactivate_grb_editor() + self.grb_editor.update_fcgerber() + # self.grb_editor.update_options(edited_obj) # delete the old object (the source object) if it was an empty one try: if len(edited_obj.solid_geometry) == 0: old_name = edited_obj.options['name'] - self.collection.set_active(old_name) - self.collection.delete_active() + self.collection.delete_by_name(old_name) except TypeError: # if the solid_geometry is a single Polygon the len() will not work # in any case, falling here means that we have something in the solid_geometry, even if only @@ -2288,19 +2271,16 @@ class App(QtCore.QObject): self.inform.emit('[success] %s' % _("Editor exited. Editor content saved.")) # restore GUI to the Selected TAB - # Remove anything else in the GUI + # Remove anything else in the AppGUI self.ui.selected_scroll_area.takeWidget() elif isinstance(edited_obj, ExcellonObject): obj_type = "Excellon" - if cleanup is None: - self.exc_editor.update_fcexcellon(edited_obj) - # self.exc_editor.update_options(edited_obj) - - self.exc_editor.deactivate() + self.exc_editor.update_fcexcellon(edited_obj) + # self.exc_editor.update_options(edited_obj) # restore GUI to the Selected TAB - # Remove anything else in the GUI + # Remove anything else in the AppGUI self.ui.tool_scroll_area.takeWidget() # delete the old object (the source object) if it was an empty one @@ -2326,6 +2306,7 @@ class App(QtCore.QObject): if isinstance(edited_obj, GeometryObject): self.geo_editor.deactivate() edited_obj.build_ui() + edited_obj.plot() elif isinstance(edited_obj, GerberObject): self.grb_editor.deactivate_grb_editor() edited_obj.build_ui() @@ -2342,7 +2323,7 @@ class App(QtCore.QObject): # edited_obj.set_ui(edited_obj.ui_type(decimals=self.decimals)) # edited_obj.build_ui() # Switch notebook to Selected page - self.ui.notebook.setCurrentWidget(self.ui.selected_tab) + # self.ui.notebook.setCurrentWidget(self.ui.selected_tab) else: if isinstance(edited_obj, GeometryObject): self.geo_editor.deactivate() @@ -2362,7 +2343,7 @@ class App(QtCore.QObject): # restore the call_source to app self.call_source = 'app' - edited_obj.plot() + # edited_obj.plot() self.ui.plot_tab_area.setTabText(0, "Plot Area") self.ui.plot_tab_area.protectTab(0) @@ -2389,12 +2370,17 @@ class App(QtCore.QObject): loc = os.path.dirname(__file__) return loc - def info(self, msg): + @QtCore.pyqtSlot(str) + @QtCore.pyqtSlot(str, bool) + def info(self, msg, shell_echo=True): """ Informs the user. Normally on the status bar, optionally also on the shell. - :param msg: Text to write. + :param msg: Text to write. + :type msg: str + :param shell_echo: Control if to display the message msg in the Shell + :type shell_echo: bool :return: None """ @@ -2405,88 +2391,35 @@ class App(QtCore.QObject): msg_ = match.group(2) self.ui.fcinfo.set_status(str(msg_), level=level) - if level.lower() == "error": - self.shell_message(msg, error=True, show=True) - elif level.lower() == "warning": - self.shell_message(msg, warning=True, show=True) + if shell_echo is True: + if level.lower() == "error": + self.shell_message(msg, error=True, show=True) + elif level.lower() == "warning": + self.shell_message(msg, warning=True, show=True) - elif level.lower() == "error_notcl": - self.shell_message(msg, error=True, show=False) + elif level.lower() == "error_notcl": + self.shell_message(msg, error=True, show=False) - elif level.lower() == "warning_notcl": - self.shell_message(msg, warning=True, show=False) + elif level.lower() == "warning_notcl": + self.shell_message(msg, warning=True, show=False) - elif level.lower() == "success": - self.shell_message(msg, success=True, show=False) + elif level.lower() == "success": + self.shell_message(msg, success=True, show=False) - elif level.lower() == "selected": - self.shell_message(msg, selected=True, show=False) + elif level.lower() == "selected": + self.shell_message(msg, selected=True, show=False) - else: - self.shell_message(msg, show=False) + else: + self.shell_message(msg, show=False) else: self.ui.fcinfo.set_status(str(msg), level="info") # make sure that if the message is to clear the infobar with a space # is not printed over and over on the shell - if msg != '': + if msg != '' and shell_echo is True: self.shell_message(msg) - def restore_toolbar_view(self): - """ - Some toolbars may be hidden by user and here we restore the state of the toolbars visibility that - was saved in the defaults dictionary. - - :return: None - """ - tb = self.defaults["global_toolbar_view"] - - if tb & 1: - self.ui.toolbarfile.setVisible(True) - else: - self.ui.toolbarfile.setVisible(False) - - if tb & 2: - self.ui.toolbargeo.setVisible(True) - else: - self.ui.toolbargeo.setVisible(False) - - if tb & 4: - self.ui.toolbarview.setVisible(True) - else: - self.ui.toolbarview.setVisible(False) - - if tb & 8: - self.ui.toolbartools.setVisible(True) - else: - self.ui.toolbartools.setVisible(False) - - if tb & 16: - self.ui.exc_edit_toolbar.setVisible(True) - else: - self.ui.exc_edit_toolbar.setVisible(False) - - if tb & 32: - self.ui.geo_edit_toolbar.setVisible(True) - else: - self.ui.geo_edit_toolbar.setVisible(False) - - if tb & 64: - self.ui.grb_edit_toolbar.setVisible(True) - else: - self.ui.grb_edit_toolbar.setVisible(False) - - if tb & 128: - self.ui.snap_toolbar.setVisible(True) - else: - self.ui.snap_toolbar.setVisible(False) - - if tb & 256: - self.ui.toolbarshell.setVisible(True) - else: - self.ui.toolbarshell.setVisible(False) - def on_import_preferences(self): """ Loads the application default settings from a saved file into @@ -2538,10 +2471,11 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export FlatCAM Preferences"), directory=self.data_path + '/preferences_' + date, - filter=filter__ + ext_filter=filter__ ) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export FlatCAM Preferences"), filter=filter__) + filename, _f = FCFileSaveDialog.get_saved_filename( + caption=_("Export FlatCAM Preferences"), ext_filter=filter__) filename = str(filename) if filename == "": self.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled.")) @@ -2577,16 +2511,16 @@ class App(QtCore.QObject): self.date = self.date.replace(' ', '_') filter__ = "HTML File .html (*.html);;TXT File .txt (*.txt);;All Files (*.*)" - path_to_save = self.defaults["global_last_save_folder"] if\ + path_to_save = self.defaults["global_last_save_folder"] if \ self.defaults["global_last_save_folder"] is not None else self.data_path try: filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Save to file"), directory=path_to_save + '/file_' + self.date, - filter=filter__ + ext_filter=filter__ ) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save to file"), filter=filter__) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save to file"), ext_filter=filter__) filename = str(filename) @@ -2628,52 +2562,6 @@ class App(QtCore.QObject): self.inform.emit('[success] %s: %s' % (_("Exported file to"), filename)) - def save_geometry(self, x, y, width, height, notebook_width): - """ - Will save the application geometry and positions in the defaults discitionary to be restored at the next - launch of the application. - - :param x: X position of the main window - :param y: Y position of the main window - :param width: width of the main window - :param height: height of the main window - :param notebook_width: the notebook width is adjustable so it get saved here, too. - - :return: None - """ - self.defaults["global_def_win_x"] = x - self.defaults["global_def_win_y"] = y - self.defaults["global_def_win_w"] = width - self.defaults["global_def_win_h"] = height - self.defaults["global_def_notebook_width"] = notebook_width - self.preferencesUiManager.save_defaults() - - def restore_main_win_geom(self): - try: - self.ui.setGeometry(self.defaults["global_def_win_x"], - self.defaults["global_def_win_y"], - self.defaults["global_def_win_w"], - self.defaults["global_def_win_h"]) - self.ui.splitter.setSizes([self.defaults["global_def_notebook_width"], 0]) - except KeyError as e: - log.debug("App.restore_main_win_geom() --> %s" % str(e)) - - def message_dialog(self, title, message, kind="info"): - """ - Builds and show a custom QMessageBox to be used in FlatCAM. - - :param title: title of the QMessageBox - :param message: message to be displayed - :param kind: type of QMessageBox; will display a specific icon. - :return: - """ - icon = {"info": QtWidgets.QMessageBox.Information, - "warning": QtWidgets.QMessageBox.Warning, - "error": QtWidgets.QMessageBox.Critical}[str(kind)] - dlg = QtWidgets.QMessageBox(icon, title, message, parent=self.ui) - dlg.setText(message) - dlg.exec_() - def register_recent(self, kind, filename): """ Will register the files opened into record dictionaries. The FlatCAM projects has it's own @@ -2729,344 +2617,6 @@ class App(QtCore.QObject): # Re-build the recent items menu self.setup_recent_items() - def new_object(self, kind, name, initialize, plot=True, autoselected=True): - """ - Creates a new specialized FlatCAMObj and attaches it to the application, - this is, updates the GUI accordingly, any other records and plots it. - This method is thread-safe. - - Notes: - * If the name is in use, the self.collection will modify it - when appending it to the collection. There is no need to handle - name conflicts here. - - :param kind: The kind of object to create. One of 'gerber', 'excellon', 'cncjob' and 'geometry'. - :type kind: str - :param name: Name for the object. - :type name: str - :param initialize: Function to run after creation of the object but before it is attached to the application. - The function is called with 2 parameters: the new object and the App instance. - :type initialize: function - :param plot: If to plot the resulting object - :param autoselected: if the resulting object is autoselected in the Project tab and therefore in the - self.collection - :return: None - :rtype: None - """ - - App.log.debug("new_object()") - obj_plot = plot - obj_autoselected = autoselected - - t0 = time.time() # Debug - - # ## Create object - classdict = { - "gerber": GerberObject, - "excellon": ExcellonObject, - "cncjob": CNCJobObject, - "geometry": GeometryObject, - "script": ScriptObject, - "document": DocumentObject - } - - App.log.debug("Calling object constructor...") - - # Object creation/instantiation - obj = classdict[kind](name) - - obj.units = self.options["units"] - - # IMPORTANT - # The key names in defaults and options dictionary's are not random: - # they have to have in name first the type of the object (geometry, excellon, cncjob and gerber) or how it's - # called here, the 'kind' followed by an underline. Above the App default values from self.defaults are - # copied to self.options. After that, below, depending on the type of - # object that is created, it will strip the name of the object and the underline (if the original key was - # let's say "excellon_toolchange", it will strip the excellon_) and to the obj.options the key will become - # "toolchange" - - for option in self.options: - if option.find(kind + "_") == 0: - oname = option[len(kind) + 1:] - obj.options[oname] = self.options[option] - - obj.isHovering = False - obj.notHovering = True - - # Initialize as per user request - # User must take care to implement initialize - # in a thread-safe way as is is likely that we - # have been invoked in a separate thread. - t1 = time.time() - self.log.debug("%f seconds before initialize()." % (t1 - t0)) - try: - return_value = initialize(obj, self) - except Exception as e: - msg = '[ERROR_NOTCL] %s' % _("An internal error has occurred. See shell.\n") - msg += _("Object ({kind}) failed because: {error} \n\n").format(kind=kind, error=str(e)) - msg += traceback.format_exc() - self.inform.emit(msg) - return "fail" - - t2 = time.time() - self.log.debug("%f seconds executing initialize()." % (t2 - t1)) - - if return_value == 'fail': - log.debug("Object (%s) parsing and/or geometry creation failed." % kind) - return "fail" - - # Check units and convert if necessary - # This condition CAN be true because initialize() can change obj.units - if self.options["units"].upper() != obj.units.upper(): - self.inform.emit('%s: %s' % (_("Converting units to "), self.options["units"])) - obj.convert_units(self.options["units"]) - t3 = time.time() - self.log.debug("%f seconds converting units." % (t3 - t2)) - - # Create the bounding box for the object and then add the results to the obj.options - # But not for Scripts or for Documents - if kind != 'document' and kind != 'script': - try: - xmin, ymin, xmax, ymax = obj.bounds() - obj.options['xmin'] = xmin - obj.options['ymin'] = ymin - obj.options['xmax'] = xmax - obj.options['ymax'] = ymax - except Exception as e: - log.warning("App.new_object() -> The object has no bounds properties. %s" % str(e)) - return "fail" - - try: - if kind == 'excellon': - obj.fill_color = self.defaults["excellon_plot_fill"] - obj.outline_color = self.defaults["excellon_plot_line"] - - if kind == 'gerber': - obj.fill_color = self.defaults["gerber_plot_fill"] - obj.outline_color = self.defaults["gerber_plot_line"] - except Exception as e: - log.warning("App.new_object() -> setting colors error. %s" % str(e)) - - # update the KeyWords list with the name of the file - self.myKeywords.append(obj.options['name']) - - log.debug("Moving new object back to main thread.") - - # Move the object to the main thread and let the app know that it is available. - obj.moveToThread(self.main_thread) - self.object_created.emit(obj, obj_plot, obj_autoselected) - - return obj - - def new_excellon_object(self): - """ - Creates a new, blank Excellon object. - - :return: None - """ - self.defaults.report_usage("new_excellon_object()") - - self.new_object('excellon', 'new_exc', lambda x, y: None, plot=False) - - def new_geometry_object(self): - """ - Creates a new, blank and single-tool Geometry object. - - :return: None - """ - self.defaults.report_usage("new_geometry_object()") - - def initialize(obj, app): - obj.multitool = False - - self.new_object('geometry', 'new_geo', initialize, plot=False) - - def new_gerber_object(self): - """ - Creates a new, blank Gerber object. - - :return: None - """ - self.defaults.report_usage("new_gerber_object()") - - def initialize(grb_obj, app): - grb_obj.multitool = False - grb_obj.source_file = [] - grb_obj.multigeo = False - grb_obj.follow = False - grb_obj.apertures = {} - grb_obj.solid_geometry = [] - - try: - grb_obj.options['xmin'] = 0 - grb_obj.options['ymin'] = 0 - grb_obj.options['xmax'] = 0 - grb_obj.options['ymax'] = 0 - except KeyError: - pass - - self.new_object('gerber', 'new_grb', initialize, plot=False) - - def new_script_object(self): - """ - Creates a new, blank TCL Script object. - - :return: None - """ - self.defaults.report_usage("new_script_object()") - - # commands_list = "# AddCircle, AddPolygon, AddPolyline, AddRectangle, AlignDrill, " \ - # "AlignDrillGrid, Bbox, Bounds, ClearShell, CopperClear,\n" \ - # "# Cncjob, Cutout, Delete, Drillcncjob, ExportDXF, ExportExcellon, ExportGcode,\n" \ - # "# ExportGerber, ExportSVG, Exteriors, Follow, GeoCutout, GeoUnion, GetNames,\n" \ - # "# GetSys, ImportSvg, Interiors, Isolate, JoinExcellon, JoinGeometry, " \ - # "ListSys, MillDrills,\n" \ - # "# MillSlots, Mirror, New, NewExcellon, NewGeometry, NewGerber, Nregions, " \ - # "Offset, OpenExcellon, OpenGCode, OpenGerber, OpenProject,\n" \ - # "# Options, Paint, Panelize, PlotAl, PlotObjects, SaveProject, " \ - # "SaveSys, Scale, SetActive, SetSys, SetOrigin, Skew, SubtractPoly,\n" \ - # "# SubtractRectangle, Version, WriteGCode\n" - - new_source_file = '# %s\n' % _('CREATE A NEW FLATCAM TCL SCRIPT') + \ - '# %s:\n' % _('TCL Tutorial is here') + \ - '# https://www.tcl.tk/man/tcl8.5/tutorial/tcltutorial.html\n' + '\n\n' + \ - '# %s:\n' % _("FlatCAM commands list") - new_source_file += '# %s\n\n' % _("Type >help< followed by Run Code for a list of FlatCAM Tcl Commands " - "(displayed in Tcl Shell).") - - def initialize(obj, app): - obj.source_file = deepcopy(new_source_file) - - outname = 'new_script' - self.new_object('script', outname, initialize, plot=False) - - def new_document_object(self): - """ - Creates a new, blank Document object. - - :return: None - """ - self.defaults.report_usage("new_document_object()") - - def initialize(obj, app): - obj.source_file = "" - - self.new_object('document', 'new_document', initialize, plot=False) - - def on_object_created(self, obj, plot, auto_select): - """ - Event callback for object creation. - It will add the new object to the collection. After that it will plot the object in a threaded way - - :param obj: The newly created FlatCAM object. - :param plot: if the newly create object t obe plotted - :param auto_select: if the newly created object to be autoselected after creation - :return: None - """ - t0 = time.time() # DEBUG - self.log.debug("on_object_created()") - - # The Collection might change the name if there is a collision - self.collection.append(obj) - - # after adding the object to the collection always update the list of objects that are in the collection - self.all_objects_list = self.collection.get_list() - - # self.inform.emit('[selected] %s created & selected: %s' % - # (str(obj.kind).capitalize(), str(obj.options['name']))) - if obj.kind == 'gerber': - self.inform.emit('[selected] {kind} {tx}: {name}'.format( - kind=obj.kind.capitalize(), - color='green', - name=str(obj.options['name']), tx=_("created/selected")) - ) - elif obj.kind == 'excellon': - self.inform.emit('[selected] {kind} {tx}: {name}'.format( - kind=obj.kind.capitalize(), - color='brown', - name=str(obj.options['name']), tx=_("created/selected")) - ) - elif obj.kind == 'cncjob': - self.inform.emit('[selected] {kind} {tx}: {name}'.format( - kind=obj.kind.capitalize(), - color='blue', - name=str(obj.options['name']), tx=_("created/selected")) - ) - elif obj.kind == 'geometry': - self.inform.emit('[selected] {kind} {tx}: {name}'.format( - kind=obj.kind.capitalize(), - color='red', - name=str(obj.options['name']), tx=_("created/selected")) - ) - elif obj.kind == 'script': - self.inform.emit('[selected] {kind} {tx}: {name}'.format( - kind=obj.kind.capitalize(), - color='orange', - name=str(obj.options['name']), tx=_("created/selected")) - ) - elif obj.kind == 'document': - self.inform.emit('[selected] {kind} {tx}: {name}'.format( - kind=obj.kind.capitalize(), - color='darkCyan', - name=str(obj.options['name']), tx=_("created/selected")) - ) - - # update the SHELL auto-completer model with the name of the new object - self.shell._edit.set_model_data(self.myKeywords) - - if auto_select: - # select the just opened object but deselect the previous ones - self.collection.set_all_inactive() - self.collection.set_active(obj.options["name"]) - else: - self.collection.set_all_inactive() - - # here it is done the object plotting - def worker_task(t_obj): - with self.proc_container.new(_("Plotting")): - if isinstance(t_obj, CNCJobObject): - t_obj.plot(kind=self.defaults["cncjob_plot_kind"]) - else: - t_obj.plot() - t1 = time.time() # DEBUG - self.log.debug("%f seconds adding object and plotting." % (t1 - t0)) - self.object_plotted.emit(t_obj) - - # Send to worker - # self.worker.add_task(worker_task, [self]) - if plot is True: - self.worker_task.emit({'fcn': worker_task, 'params': [obj]}) - - def on_object_changed(self, obj): - """ - Called whenever the geometry of the object was changed in some way. - This require the update of it's bounding values so it can be the selected on canvas. - Update the bounding box data from obj.options - - :param obj: the object that was changed - :return: None - """ - - xmin, ymin, xmax, ymax = obj.bounds() - obj.options['xmin'] = xmin - obj.options['ymin'] = ymin - obj.options['xmax'] = xmax - obj.options['ymax'] = ymax - - log.debug("Object changed, updating the bounding box data on self.options") - # delete the old selection shape - self.delete_selection_shape() - self.should_we_save = True - - def on_object_plotted(self): - """ - Callback called whenever the plotted object needs to be fit into the viewport (canvas) - - :return: None - """ - self.on_zoom_fit(None) - def on_about(self): """ Displays the "about" dialog found in the Menu --> Help. @@ -3251,7 +2801,7 @@ class App(QtCore.QObject): self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % _("E-mail")), 0, 2) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Juan Pablo Caram"), 1, 0) - self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Program Author"), 1, 1) + self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % _("Program Author")), 1, 1) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "<>"), 1, 2) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Denis Hayrullin"), 2, 0) self.prog_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Kamil Sopko"), 3, 0) @@ -3347,7 +2897,7 @@ class App(QtCore.QObject): self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Italian"), 4, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Golfetto Massimiliano"), 4, 1) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % " "), 4, 2) - self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "pcb@golfetto.eu"), 4, 3) + self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % ""), 4, 3) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "German"), 5, 0) self.translator_grid_lay.addWidget(QtWidgets.QLabel('%s' % "Marius Stanciu (Google-Tr)"), 5, 1) @@ -3384,6 +2934,83 @@ class App(QtCore.QObject): AboutDialog(app=self, parent=self.ui).exec_() + def on_readme(self): + class AboutDialog(QtWidgets.QDialog): + def __init__(self, app, parent=None): + QtWidgets.QDialog.__init__(self, parent) + + self.app = app + + open_source_link = "Open Source" + new_features_link = "new features" + + bugs_link = "report bugs" + donation_link = "donation" + + # Icon and title + self.setWindowIcon(parent.app_icon) + self.setWindowTitle(_("Important Information's")) + self.resize(720, 330) + + logo = QtWidgets.QLabel() + logo.setPixmap(QtGui.QPixmap(self.app.resource_location + '/contribute256.png')) + + content = QtWidgets.QLabel( + "This program is %s and free in a very wide meaning of the word.
" + "Yet it cannot evolve without contributions.

" + "If you want to see this application evolve and grow, or if you make money with it,
" + "you can contribute to the development yourself by:
" + "" + "There are no strings attached.
" + "You don't have to make a %s, and it is totally optional but:" + "" % + (open_source_link, new_features_link, bugs_link, donation_link, donation_link) + ) + content.setOpenExternalLinks(True) + closebtn = QtWidgets.QPushButton(_("Close")) + + # layouts + layout1 = QtWidgets.QVBoxLayout() + self.setLayout(layout1) + + pal = QtGui.QPalette() + pal.setColor(QtGui.QPalette.Background, Qt.white) + + self.grid_lay = QtWidgets.QGridLayout() + self.grid_lay.setHorizontalSpacing(20) + self.grid_lay.setColumnStretch(0, 0) + self.grid_lay.setColumnStretch(1, 1) + + content_widget = QtWidgets.QWidget() + content_widget.setLayout(self.grid_lay) + scroll_area = QtWidgets.QScrollArea() + scroll_area.setWidget(content_widget) + scroll_area.setWidgetResizable(True) + scroll_area.setFrameShape(QtWidgets.QFrame.NoFrame) + scroll_area.setPalette(pal) + + self.grid_lay.addWidget(logo, 0, 0) + self.grid_lay.addWidget(content, 0, 1) + layout1.addWidget(scroll_area) + + layout2 = QtWidgets.QHBoxLayout() + layout1.addLayout(layout2) + layout2.addStretch() + layout2.addWidget(closebtn) + + closebtn.clicked.connect(self.accept) + + AboutDialog(app=self, parent=self.ui).exec_() + def install_bookmarks(self, book_dict=None): """ Install the bookmarks actions in the Help menu -> Bookmarks @@ -3465,7 +3092,7 @@ class App(QtCore.QObject): # delete the absolute and relative position and messages in the infobar self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") # Switch plot_area to preferences page self.ui.plot_tab_area.setCurrentWidget(self.book_dialog_tab) @@ -3481,6 +3108,8 @@ class App(QtCore.QObject): msgbox.setWindowTitle(_("Alternative website")) msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/globe16.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + bt_yes = msgbox.addButton(_('Close'), QtWidgets.QMessageBox.YesRole) msgbox.setDefaultButton(bt_yes) @@ -3515,6 +3144,8 @@ class App(QtCore.QObject): "Do you want to Save the project?")) msgbox.setWindowTitle(_("Save changes")) msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/save_as.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) bt_cancel = msgbox.addButton(_('Cancel'), QtWidgets.QMessageBox.RejectRole) @@ -3574,6 +3205,11 @@ class App(QtCore.QObject): 'textbox_font_size', self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value() ) + stgs.setValue( + 'hud_font_size', + self.ui.general_defaults_form.general_app_set_group.hud_font_size_spinner.get_value() + ) + stgs.setValue('toolbar_lock', self.ui.lock_action.isChecked()) stgs.setValue( 'machinist', @@ -3587,14 +3223,13 @@ class App(QtCore.QObject): # try to quit the Socket opened by ArgsThread class try: - self.new_launch.thread_exit = True - self.new_launch.listener.close() + self.new_launch.stop.emit() except Exception as err: log.debug("App.quit_application() --> %s" % str(err)) # try to quit the QThread that run ArgsThread class try: - self.th.terminate() + self.th.quit() except Exception as e: log.debug("App.quit_application() --> %s" % str(e)) @@ -3615,7 +3250,6 @@ class App(QtCore.QObject): @staticmethod def kill_app(): - # QtCore.QCoreApplication.quit() QtWidgets.qApp.quit() # When the main event loop is not started yet in which case the qApp.quit() will do nothing # we use the following command @@ -4034,7 +3668,7 @@ class App(QtCore.QObject): for v in geo_obj.tools.values(): v['data']['name'] = obj_name_multi - self.new_object("geometry", obj_name_multi, initialize) + self.app_obj.new_object("geometry", obj_name_multi, initialize) else: def initialize(geo_obj, app): GeometryObject.merge(geo_list=objs, geo_final=geo_obj, multigeo=False) @@ -4044,7 +3678,7 @@ class App(QtCore.QObject): for v in geo_obj.tools.values(): v['data']['name'] = obj_name_single - self.new_object("geometry", obj_name_single, initialize) + self.app_obj.new_object("geometry", obj_name_single, initialize) self.should_we_save = True @@ -4073,7 +3707,7 @@ class App(QtCore.QObject): ExcellonObject.merge(exc_list=objs, exc_final=exc_obj, decimals=self.decimals) app.inform.emit('[success] %s.' % _("Excellon merging finished")) - self.new_object("excellon", 'Combo_Excellon', initialize) + self.app_obj.new_object("excellon", 'Combo_Excellon', initialize) self.should_we_save = True def on_edit_join_grb(self): @@ -4101,7 +3735,7 @@ class App(QtCore.QObject): GerberObject.merge(grb_list=objs, grb_final=grb_obj) app.inform.emit('[success] %s.' % _("Gerber merging finished")) - self.new_object("gerber", 'Combo_Gerber', initialize) + self.app_obj.new_object("gerber", 'Combo_Gerber', initialize) self.should_we_save = True def on_convert_singlegeo_to_multigeo(self): @@ -4235,13 +3869,14 @@ class App(QtCore.QObject): # If option is the same, then ignore if new_units == self.defaults["units"].upper(): - self.log.debug("on_toggle_units(): Same as defaults, so ignoring.") + self.log.debug("on_toggle_units(): Same as previous, ignoring.") return - # Options to scale - dimensions = ['gerber_isotooldia', 'gerber_noncoppermargin', 'gerber_bboxmargin', - "gerber_editor_newsize", "gerber_editor_lin_pitch", "gerber_editor_buff_f", "gerber_vtipdia", - "gerber_vcutz", "gerber_editor_newdim", "gerber_editor_ma_low", + # Keys in self.defaults for which to scale their values + dimensions = ['tools_iso_tooldia', 'gerber_noncoppermargin', 'gerber_bboxmargin', + "gerber_editor_newsize", "gerber_editor_lin_pitch", "gerber_editor_buff_f", + "tools_iso_tool_vtipdia", + "tools_iso_tool_cutz", "gerber_editor_newdim", "gerber_editor_ma_low", "gerber_editor_ma_high", 'excellon_cutz', 'excellon_travelz', "excellon_toolchangexy", 'excellon_offset', @@ -4312,149 +3947,62 @@ class App(QtCore.QObject): def scale_defaults(sfactor): for dim in dimensions: - - if dim == 'gerber_editor_newdim': - if self.defaults["gerber_editor_newdim"] is None or self.defaults["gerber_editor_newdim"] == '': - continue - coordinates = self.defaults["gerber_editor_newdim"].split(",") - coords_xy = [float(eval(a)) for a in coordinates if a != ''] - coords_xy[0] *= sfactor - coords_xy[1] *= sfactor - self.defaults['gerber_editor_newdim'] = "%.*f, %.*f" % (self.decimals, coords_xy[0], - self.decimals, coords_xy[1]) - if dim == 'excellon_toolchangexy': - if self.defaults["excellon_toolchangexy"] is None or self.defaults["excellon_toolchangexy"] == '': - continue - coordinates = self.defaults["excellon_toolchangexy"].split(",") - coords_xy = [float(eval(a)) for a in coordinates if a != ''] - coords_xy[0] *= sfactor - coords_xy[1] *= sfactor - self.defaults['excellon_toolchangexy'] = "%.*f, %.*f" % (self.decimals, coords_xy[0], - self.decimals, coords_xy[1]) - elif dim == 'geometry_toolchangexy': - if self.defaults["geometry_toolchangexy"] is None or self.defaults["geometry_toolchangexy"] == '': - continue - coordinates = self.defaults["geometry_toolchangexy"].split(",") - coords_xy = [float(eval(a)) for a in coordinates if a != ''] - coords_xy[0] *= sfactor - coords_xy[1] *= sfactor - self.defaults['geometry_toolchangexy'] = "%.*f, %.*f" % (self.decimals, coords_xy[0], - self.decimals, coords_xy[1]) - elif dim == 'excellon_endxy': - if self.defaults["excellon_endxy"] is None or self.defaults["excellon_endxy"] == '': + if dim in [ + 'gerber_editor_newdim', 'excellon_toolchangexy', 'geometry_toolchangexy', 'excellon_endxy', + 'geometry_endxy', 'tools_solderpaste_xy_toolchange', 'tools_cal_toolchange_xy', + 'tools_transform_mirror_point' + ]: + if self.defaults[dim] is None or self.defaults[dim] == '': continue - coordinates = self.defaults["excellon_endxy"].split(",") - end_coords_xy = [float(eval(a)) for a in coordinates if a != ''] - end_coords_xy[0] *= sfactor - end_coords_xy[1] *= sfactor - self.defaults['excellon_endxy'] = "%.*f, %.*f" % (self.decimals, end_coords_xy[0], - self.decimals, end_coords_xy[1]) - elif dim == 'geometry_endxy': - if self.defaults["geometry_endxy"] is None or self.defaults["geometry_endxy"] == '': - continue - coordinates = self.defaults["geometry_endxy"].split(",") - end_coords_xy = [float(eval(a)) for a in coordinates if a != ''] - end_coords_xy[0] *= sfactor - end_coords_xy[1] *= sfactor - self.defaults['geometry_endxy'] = "%.*f, %.*f" % (self.decimals, end_coords_xy[0], - self.decimals, end_coords_xy[1]) + try: + coordinates = self.defaults[dim].split(",") + coords_xy = [float(eval(a)) for a in coordinates if a != ''] + coords_xy[0] *= sfactor + coords_xy[1] *= sfactor + self.defaults[dim] = "%.*f, %.*f" % ( + self.decimals, coords_xy[0], self.decimals, coords_xy[1]) + except Exception as e: + log.debug("App.on_toggle_units.scale_defaults() --> 'string tuples': %s" % str(e)) - elif dim == 'geometry_cnctooldia': - if self.defaults["geometry_cnctooldia"] is None or self.defaults["geometry_cnctooldia"] == '': + elif dim in [ + 'geometry_cnctooldia', 'tools_ncctools', 'tools_solderpaste_tools' + ]: + if self.defaults[dim] is None or self.defaults[dim] == '': continue - if type(self.defaults["geometry_cnctooldia"]) is float: - tools_diameters = [self.defaults["geometry_cnctooldia"]] - else: + + try: + self.defaults[dim] = float(self.defaults[dim]) + tools_diameters = [self.defaults[dim]] + except ValueError: try: - tools_string = self.defaults["geometry_cnctooldia"].split(",") + tools_string = self.defaults[dim].split(",") tools_diameters = [eval(a) for a in tools_string if a != ''] except Exception as e: log.debug("App.on_toggle_units().scale_options() --> %s" % str(e)) continue - self.defaults['geometry_cnctooldia'] = '' - for t in range(len(tools_diameters)): - tools_diameters[t] *= sfactor - self.defaults['geometry_cnctooldia'] += "%.*f," % (self.decimals, tools_diameters[t]) - elif dim == 'tools_ncctools': - if self.defaults["tools_ncctools"] is None or self.defaults["tools_ncctools"] == '': - continue - if type(self.defaults["tools_ncctools"]) == float: - ncctools = [self.defaults["tools_ncctools"]] + self.defaults[dim] = '' + td_len = len(tools_diameters) + if td_len > 1: + for t in range(td_len): + tools_diameters[t] *= sfactor + self.defaults[dim] += "%.*f," % (self.decimals, tools_diameters[t]) else: - try: - tools_string = self.defaults["tools_ncctools"].split(",") - ncctools = [eval(a) for a in tools_string if a != ''] - except Exception as e: - log.debug("App.on_toggle_units().scale_options() --> %s" % str(e)) - continue + tools_diameters[0] *= sfactor + self.defaults[dim] += "%.*f" % (self.decimals, tools_diameters[0]) - self.defaults['tools_ncctools'] = '' - for t in range(len(ncctools)): - ncctools[t] *= sfactor - self.defaults['tools_ncctools'] += "%.*f," % (self.decimals, ncctools[t]) - elif dim == 'tools_solderpaste_tools': - if self.defaults["tools_solderpaste_tools"] is None or \ - self.defaults["tools_solderpaste_tools"] == '': - continue - if type(self.defaults["tools_solderpaste_tools"]) == float: - sptools = [self.defaults["tools_solderpaste_tools"]] - else: - try: - tools_string = self.defaults["tools_solderpaste_tools"].split(",") - sptools = [eval(a) for a in tools_string if a != ''] - except Exception as e: - log.debug("App.on_toggle_units().scale_options() --> %s" % str(e)) - continue - - self.defaults['tools_solderpaste_tools'] = "" - for t in range(len(sptools)): - sptools[t] *= sfactor - self.defaults['tools_solderpaste_tools'] += "%.*f," % (self.decimals, sptools[t]) - elif dim == 'tools_solderpaste_xy_toolchange': - if self.defaults["tools_solderpaste_xy_toolchange"] is None or \ - self.defaults["tools_solderpaste_xy_toolchange"] == '': - continue + elif dim in ['global_gridx', 'global_gridy']: + # format the number of decimals to the one specified in self.decimals try: - coordinates = self.defaults["tools_solderpaste_xy_toolchange"].split(",") - sp_coords = [float(eval(a)) for a in coordinates if a != ''] - sp_coords[0] *= sfactor - sp_coords[1] *= sfactor - self.defaults['tools_solderpaste_xy_toolchange'] = "%.*f, %.*f" % (self.decimals, sp_coords[0], - self.decimals, sp_coords[1]) + val = float(self.defaults[dim]) * sfactor except Exception as e: - log.debug("App.on_toggle_units().scale_options() --> %s" % str(e)) + log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e)) continue - elif dim == 'tools_cal_toolchange_xy': - if self.defaults["tools_cal_toolchange_xy"] is None or \ - self.defaults["tools_cal_toolchange_xy"] == '': - continue - coordinates = self.defaults["tools_cal_toolchange_xy"].split(",") - end_coords_xy = [float(eval(a)) for a in coordinates if a != ''] - end_coords_xy[0] *= sfactor - end_coords_xy[1] *= sfactor - self.defaults['tools_cal_toolchange_xy'] = "%.*f, %.*f" % (self.decimals, end_coords_xy[0], - self.decimals, end_coords_xy[1]) - elif dim == 'global_gridx' or dim == 'global_gridy': - if new_units == 'IN': - try: - val = float(self.defaults[dim]) * sfactor - except Exception as e: - log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e)) - continue - - self.defaults[dim] = float('%.*f' % (self.decimals, val)) - else: - try: - val = float(self.defaults[dim]) * sfactor - except Exception as e: - log.debug('App.on_toggle_units().scale_defaults() --> %s' % str(e)) - continue - - self.defaults[dim] = float('%.*f' % (self.decimals, val)) + self.defaults[dim] = float('%.*f' % (self.decimals, val)) else: + # the number of decimals for the rest is kept unchanged if self.defaults[dim]: try: val = float(self.defaults[dim]) * sfactor @@ -4471,6 +4019,8 @@ class App(QtCore.QObject): msgbox = QtWidgets.QMessageBox() msgbox.setWindowTitle(_("Toggle Units")) msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/toggle_units32.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + msgbox.setText(_("Changing the units of the project\n" "will scale all objects.\n\n" "Do you want to continue?")) @@ -4497,7 +4047,7 @@ class App(QtCore.QObject): obj.convert_units(new_units) # make that the properties stored in the object are also updated - self.object_changed.emit(obj) + self.app_obj.object_changed.emit(obj) # rebuild the object UI obj.build_ui() @@ -4517,10 +4067,11 @@ class App(QtCore.QObject): # replot all objects self.plot_all() + # set the status labels to reflect the current FlatCAM units self.set_screen_units(new_units) - # signal to the app that we changed the object properties and it shoud save the project + # signal to the app that we changed the object properties and it should save the project self.should_we_save = True self.inform.emit('[success] %s: %s' % (_("Converted units to"), new_units)) @@ -4549,373 +4100,6 @@ class App(QtCore.QObject): self.ui.grid_gap_x_entry.set_value(val_x, decimals=self.decimals) self.ui.grid_gap_y_entry.set_value(val_y, decimals=self.decimals) - def on_fullscreen(self, disable=False): - self.defaults.report_usage("on_fullscreen()") - - flags = self.ui.windowFlags() - if self.toggle_fscreen is False and disable is False: - # self.ui.showFullScreen() - self.ui.setWindowFlags(flags | Qt.FramelessWindowHint) - a = self.ui.geometry() - self.x_pos = a.x() - self.y_pos = a.y() - self.width = a.width() - self.height = a.height() - - # set new geometry to full desktop rect - # Subtracting and adding the pixels below it's hack to bypass a bug in Qt5 and OpenGL that made that a - # window drawn with OpenGL in fullscreen will not show any other windows on top which means that menus and - # everything else will not work without this hack. This happen in Windows. - # https://bugreports.qt.io/browse/QTBUG-41309 - desktop = QtWidgets.QApplication.desktop() - screen = desktop.screenNumber(QtGui.QCursor.pos()) - - rec = desktop.screenGeometry(screen) - x = rec.x() - 1 - y = rec.y() - 1 - h = rec.height() + 2 - w = rec.width() + 2 - self.ui.setGeometry(x, y, w, h) - self.ui.show() - - for tb in self.ui.findChildren(QtWidgets.QToolBar): - tb.setVisible(False) - self.ui.splitter_left.setVisible(False) - self.toggle_fscreen = True - elif self.toggle_fscreen is True or disable is True: - self.ui.setWindowFlags(flags & ~Qt.FramelessWindowHint) - self.ui.setGeometry(self.x_pos, self.y_pos, self.width, self.height) - self.ui.showNormal() - self.restore_toolbar_view() - self.ui.splitter_left.setVisible(True) - self.toggle_fscreen = False - - def on_toggle_plotarea(self): - self.defaults.report_usage("on_toggle_plotarea()") - - try: - name = self.ui.plot_tab_area.widget(0).objectName() - except AttributeError: - self.ui.plot_tab_area.addTab(self.ui.plot_tab, "Plot Area") - # remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON - self.ui.plot_tab_area.protectTab(0) - return - - if name != 'plotarea_tab': - self.ui.plot_tab_area.insertTab(0, self.ui.plot_tab, "Plot Area") - # remove the close button from the Plot Area tab (first tab index = 0) as this one will always be ON - self.ui.plot_tab_area.protectTab(0) - else: - self.ui.plot_tab_area.closeTab(0) - - def on_toggle_notebook(self): - if self.ui.splitter.sizes()[0] == 0: - self.ui.splitter.setSizes([1, 1]) - self.ui.menu_toggle_nb.setChecked(True) - else: - self.ui.splitter.setSizes([0, 1]) - self.ui.menu_toggle_nb.setChecked(False) - - def on_toggle_axis(self): - self.defaults.report_usage("on_toggle_axis()") - - if self.toggle_axis is False: - if self.is_legacy is False: - self.plotcanvas.v_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 1.0), vertical=True, - parent=self.plotcanvas.view.scene) - - self.plotcanvas.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 1.0), vertical=False, - parent=self.plotcanvas.view.scene) - else: - if self.plotcanvas.h_line not in self.plotcanvas.axes.lines and \ - self.plotcanvas.v_line not in self.plotcanvas.axes.lines: - self.plotcanvas.h_line = self.plotcanvas.axes.axhline(color=(0.70, 0.3, 0.3), linewidth=2) - self.plotcanvas.v_line = self.plotcanvas.axes.axvline(color=(0.70, 0.3, 0.3), linewidth=2) - self.plotcanvas.canvas.draw() - - self.toggle_axis = True - else: - if self.is_legacy is False: - self.plotcanvas.v_line.parent = None - self.plotcanvas.h_line.parent = None - else: - if self.plotcanvas.h_line in self.plotcanvas.axes.lines and \ - self.plotcanvas.v_line in self.plotcanvas.axes.lines: - self.plotcanvas.axes.lines.remove(self.plotcanvas.h_line) - self.plotcanvas.axes.lines.remove(self.plotcanvas.v_line) - self.plotcanvas.canvas.draw() - self.toggle_axis = False - - def on_toggle_grid(self): - self.defaults.report_usage("on_toggle_grid()") - - self.ui.grid_snap_btn.trigger() - self.ui.on_grid_snap_triggered(state=True) - - def on_toggle_grid_lines(self): - self.defaults.report_usage("on_toggle_grd_lines()") - - tt_settings = QtCore.QSettings("Open Source", "FlatCAM") - if tt_settings.contains("theme"): - theme = tt_settings.value('theme', type=str) - else: - theme = 'white' - - if self.toggle_grid_lines is False: - if self.is_legacy is False: - if theme == 'white': - self.plotcanvas.grid._grid_color_fn['color'] = Color('dimgray').rgba - else: - self.plotcanvas.grid._grid_color_fn['color'] = Color('#dededeff').rgba - else: - self.plotcanvas.axes.grid(True) - try: - self.plotcanvas.canvas.draw() - except IndexError: - pass - pass - self.toggle_grid_lines = True - else: - if self.is_legacy is False: - if theme == 'white': - self.plotcanvas.grid._grid_color_fn['color'] = Color('#ffffffff').rgba - else: - self.plotcanvas.grid._grid_color_fn['color'] = Color('#000000FF').rgba - else: - self.plotcanvas.axes.grid(False) - try: - self.plotcanvas.canvas.draw() - except IndexError: - pass - self.toggle_grid_lines = False - - if self.is_legacy is False: - # HACK: enabling/disabling the cursor seams to somehow update the shapes on screen - # - perhaps is a bug in VisPy implementation - if self.grid_status() is True: - self.app_cursor.enabled = False - self.app_cursor.enabled = True - else: - self.app_cursor.enabled = True - self.app_cursor.enabled = False - - def on_update_exc_export(self, state): - """ - This is handling the update of Excellon Export parameters based on the ones in the Excellon General but only - if the update_excellon_cb checkbox is checked - - :param state: state of the checkbox whose signals is tied to his slot - :return: - """ - if state: - # first try to disconnect - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_in_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_mm_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_mm_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio.activated_custom. \ - disconnect(self.on_excellon_zeros_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_units_radio.activated_custom. \ - disconnect(self.on_excellon_zeros_changed) - except TypeError: - pass - - # the connect them - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry.returnPressed.connect( - self.on_excellon_format_changed) - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_in_entry.returnPressed.connect( - self.on_excellon_format_changed) - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_mm_entry.returnPressed.connect( - self.on_excellon_format_changed) - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_mm_entry.returnPressed.connect( - self.on_excellon_format_changed) - self.ui.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio.activated_custom.connect( - self.on_excellon_zeros_changed) - self.ui.excellon_defaults_form.excellon_gen_group.excellon_units_radio.activated_custom.connect( - self.on_excellon_units_changed) - else: - # disconnect the signals - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_in_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_mm_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_mm_entry.returnPressed. \ - disconnect(self.on_excellon_format_changed) - except TypeError: - pass - - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio.activated_custom. \ - disconnect(self.on_excellon_zeros_changed) - except TypeError: - pass - try: - self.ui.excellon_defaults_form.excellon_gen_group.excellon_units_radio.activated_custom. \ - disconnect(self.on_excellon_zeros_changed) - except TypeError: - pass - - def on_excellon_format_changed(self): - """ - Slot activated when the user changes the Excellon format values in Preferences -> Excellon -> Excellon General - :return: None - """ - if self.ui.excellon_defaults_form.excellon_gen_group.excellon_units_radio.get_value().upper() == 'METRIC': - self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value( - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_mm_entry.get_value() - ) - self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value( - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_mm_entry.get_value() - ) - else: - self.ui.excellon_defaults_form.excellon_exp_group.format_whole_entry.set_value( - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry.get_value() - ) - self.ui.excellon_defaults_form.excellon_exp_group.format_dec_entry.set_value( - self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_lower_in_entry.get_value() - ) - - def on_excellon_zeros_changed(self): - """ - Slot activated when the user changes the Excellon zeros values in Preferences -> Excellon -> Excellon General - :return: None - """ - self.ui.excellon_defaults_form.excellon_exp_group.zeros_radio.set_value( - self.ui.excellon_defaults_form.excellon_gen_group.excellon_zeros_radio.get_value() + 'Z' - ) - - def on_excellon_units_changed(self): - """ - Slot activated when the user changes the Excellon unit values in Preferences -> Excellon -> Excellon General - :return: None - """ - self.ui.excellon_defaults_form.excellon_exp_group.excellon_units_radio.set_value( - self.ui.excellon_defaults_form.excellon_gen_group.excellon_units_radio.get_value() - ) - self.on_excellon_format_changed() - - def on_film_color_entry(self): - self.defaults['tools_film_color'] = \ - self.ui.tools_defaults_form.tools_film_group.film_color_entry.get_value() - self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet( - "background-color:%s;" - "border-color: dimgray" % str(self.defaults['tools_film_color']) - ) - - def on_film_color_button(self): - current_color = QtGui.QColor(self.defaults['tools_film_color']) - - c_dialog = QtWidgets.QColorDialog() - film_color = c_dialog.getColor(initial=current_color) - - if film_color.isValid() is False: - return - - # if new color is different then mark that the Preferences are changed - if film_color != current_color: - self.preferencesUiManager.on_preferences_edited() - - self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet( - "background-color:%s;" - "border-color: dimgray" % str(film_color.name()) - ) - new_val_sel = str(film_color.name()) - self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(new_val_sel) - self.defaults['tools_film_color'] = new_val_sel - - def on_qrcode_fill_color_entry(self): - self.defaults['tools_qrcode_fill_color'] = \ - self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.get_value() - self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet( - "background-color:%s;" - "border-color: dimgray" % str(self.defaults['tools_qrcode_fill_color']) - ) - - def on_qrcode_fill_color_button(self): - current_color = QtGui.QColor(self.defaults['tools_qrcode_fill_color']) - - c_dialog = QtWidgets.QColorDialog() - fill_color = c_dialog.getColor(initial=current_color) - - if fill_color.isValid() is False: - return - - # if new color is different then mark that the Preferences are changed - if fill_color != current_color: - self.preferencesUiManager.on_preferences_edited() - - self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_button.setStyleSheet( - "background-color:%s;" - "border-color: dimgray" % str(fill_color.name()) - ) - - new_val_sel = str(fill_color.name()) - self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value(new_val_sel) - self.defaults['tools_qrcode_fill_color'] = new_val_sel - - def on_qrcode_back_color_entry(self): - self.defaults['tools_qrcode_back_color'] = \ - self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.get_value() - self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet( - "background-color:%s;" - "border-color: dimgray" % str(self.defaults['tools_qrcode_back_color']) - ) - - def on_qrcode_back_color_button(self): - current_color = QtGui.QColor(self.defaults['tools_qrcode_back_color']) - - c_dialog = QtWidgets.QColorDialog() - back_color = c_dialog.getColor(initial=current_color) - - if back_color.isValid() is False: - return - - # if new color is different then mark that the Preferences are changed - if back_color != current_color: - self.preferencesUiManager.on_preferences_edited() - - self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_button.setStyleSheet( - "background-color:%s;" - "border-color: dimgray" % str(back_color.name()) - ) - - new_val_sel = str(back_color.name()) - self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value(new_val_sel) - self.defaults['tools_qrcode_back_color'] = new_val_sel - def on_tab_rmb_click(self, checked): self.ui.notebook.set_detachable(val=checked) self.defaults["global_tabs_detachable"] = checked @@ -4946,16 +4130,18 @@ class App(QtCore.QObject): def on_workspace_modified(self): # self.save_defaults(silent=True) - if self.is_legacy is True: - self.plotcanvas.delete_workspace() + + self.plotcanvas.delete_workspace() self.preferencesUiManager.defaults_read_form() self.plotcanvas.draw_workspace(workspace_size=self.defaults['global_workspaceT']) def on_workspace(self): if self.ui.general_defaults_form.general_app_set_group.workspace_cb.get_value(): self.plotcanvas.draw_workspace(workspace_size=self.defaults['global_workspaceT']) + self.inform[str, bool].emit(_("Workspace enabled."), False) else: self.plotcanvas.delete_workspace() + self.inform[str, bool].emit(_("Workspace disabled."), False) self.preferencesUiManager.defaults_read_form() # self.save_defaults(silent=True) @@ -4965,6 +4151,7 @@ class App(QtCore.QObject): self.ui.general_defaults_form.general_app_set_group.workspace_cb.stateChanged.disconnect(self.on_workspace) except TypeError: pass + self.ui.general_defaults_form.general_app_set_group.workspace_cb.set_value(state) self.ui.general_defaults_form.general_app_set_group.workspace_cb.stateChanged.connect(self.on_workspace) self.on_workspace() @@ -5022,6 +4209,8 @@ class App(QtCore.QObject): "Go to Preferences -> General - Show Advanced Options.")) msgbox.setWindowTitle("Tool adding ...") msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/warning.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Warning) + bt_ok = msgbox.addButton(_('Ok'), QtWidgets.QMessageBox.AcceptRole) msgbox.setDefaultButton(bt_ok) @@ -5068,6 +4257,10 @@ class App(QtCore.QObject): # and only if the tool is Solder Paste Dispensing Tool elif tool_widget == self.paste_tool.toolName: self.paste_tool.on_tool_delete() + + # and only if the tool is Isolation Tool + elif tool_widget == self.isolation_tool.toolName: + self.isolation_tool.on_tool_delete() else: self.on_delete() @@ -5096,6 +4289,8 @@ class App(QtCore.QObject): msgbox = QtWidgets.QMessageBox() msgbox.setWindowTitle(_("Delete objects")) msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/deleteshape32.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + # msgbox.setText("%s" % _("Change project units ...")) msgbox.setText(_("Are you sure you want to permanently delete\n" "the selected objects?")) @@ -5115,13 +4310,13 @@ class App(QtCore.QObject): for obj_active in self.collection.get_selected(): # if the deleted object is GerberObject then make sure to delete the possible mark shapes - if isinstance(obj_active, GerberObject): + if obj_active.kind == 'gerber': for el in obj_active.mark_shapes: obj_active.mark_shapes[el].clear(update=True) obj_active.mark_shapes[el].enabled = False # obj_active.mark_shapes[el] = None del el - elif isinstance(obj_active, CNCJobObject): + elif obj_active.kind == 'cncjob': try: obj_active.text_col.enabled = False del obj_active.text_col @@ -5135,9 +4330,13 @@ class App(QtCore.QObject): while self.collection.get_selected(): self.delete_first_selected() - self.inform.emit('%s...' % _("Object(s) deleted")) # make sure that the selection shape is deleted, too self.delete_selection_shape() + + # if there are no longer objects delete also the exclusion areas shapes + if not self.collection.get_list(): + self.exc_areas.clear_shapes() + self.inform.emit('%s...' % _("Object(s) deleted")) else: self.inform.emit('[ERROR_NOTCL] %s' % _("Failed. No object(s) selected...")) else: @@ -5224,7 +4423,7 @@ class App(QtCore.QObject): for obj in obj_list: obj.offset((x, y)) - self.object_changed.emit(obj) + self.app_obj.object_changed.emit(obj) # Update the object bounding box options a, b, c, d = obj.bounds() @@ -5261,7 +4460,7 @@ class App(QtCore.QObject): self.should_we_save = True return - if event.button == 1: + if event is not None and event.button == 1: if self.is_legacy is False: event_pos = event.pos else: @@ -5312,7 +4511,7 @@ class App(QtCore.QObject): for obj in obj_list: obj.offset((-x, -y)) - self.object_changed.emit(obj) + self.app_obj.object_changed.emit(obj) # Update the object bounding box options a, b, c, d = obj.bounds() @@ -5451,14 +4650,20 @@ class App(QtCore.QObject): edge_width=self.defaults["global_cursor_width"], size=self.defaults["global_cursor_size"]) - # Set the position label - self.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (location[0], location[1])) # Set the relative position label dx = location[0] - float(self.rel_point1[0]) dy = location[1] - float(self.rel_point1[1]) - self.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (dx, dy)) + self.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (location[0], location[1])) + # # Set the position label + # + # self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (dx, dy)) + + units = self.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + dx, units, dy, units, location[0], units, location[1], units) self.inform.emit('[success] %s' % _("Done.")) return location @@ -5600,14 +4805,19 @@ class App(QtCore.QObject): edge_width=self.defaults["global_cursor_width"], size=self.defaults["global_cursor_size"]) - # Set the position label - self.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (location[0], location[1])) # Set the relative position label self.dx = location[0] - float(self.rel_point1[0]) self.dy = location[1] - float(self.rel_point1[1]) - self.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.dx, self.dy)) + # Set the position label + self.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (location[0], location[1])) + # self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.dx, self.dy)) + + units = self.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.dx, units, self.dy, units, location[0], units, location[1], units) self.inform.emit('[success] %s' % _("Done.")) return location @@ -5620,6 +4830,16 @@ class App(QtCore.QObject): self.defaults.report_usage("on_copy_command()") def initialize(obj_init, app): + """ + + :param obj_init: the new object + :type obj_init: class + :param app: An instance of the App class + :type app: App + :return: None + :rtype: + """ + obj_init.solid_geometry = deepcopy(obj.solid_geometry) try: obj_init.follow_geometry = deepcopy(obj.follow_geometry) @@ -5664,15 +4884,15 @@ class App(QtCore.QObject): try: if isinstance(obj, ExcellonObject): - self.new_object("excellon", str(obj_name) + "_copy", initialize_excellon) + self.app_obj.new_object("excellon", str(obj_name) + "_copy", initialize_excellon) elif isinstance(obj, GerberObject): - self.new_object("gerber", str(obj_name) + "_copy", initialize) + self.app_obj.new_object("gerber", str(obj_name) + "_copy", initialize) elif isinstance(obj, GeometryObject): - self.new_object("geometry", str(obj_name) + "_copy", initialize) + self.app_obj.new_object("geometry", str(obj_name) + "_copy", initialize) elif isinstance(obj, ScriptObject): - self.new_object("script", str(obj_name) + "_copy", initialize_script) + self.app_obj.new_object("script", str(obj_name) + "_copy", initialize_script) elif isinstance(obj, DocumentObject): - self.new_object("document", str(obj_name) + "_copy", initialize_document) + self.app_obj.new_object("document", str(obj_name) + "_copy", initialize_document) except Exception as e: return "Operation failed: %s" % str(e) @@ -5713,11 +4933,11 @@ class App(QtCore.QObject): obj_name = obj.options["name"] try: if isinstance(obj, ExcellonObject): - self.new_object("excellon", str(obj_name) + custom_name, initialize_excellon) + self.app_obj.new_object("excellon", str(obj_name) + custom_name, initialize_excellon) elif isinstance(obj, GerberObject): - self.new_object("gerber", str(obj_name) + custom_name, initialize_gerber) + self.app_obj.new_object("gerber", str(obj_name) + custom_name, initialize_gerber) elif isinstance(obj, GeometryObject): - self.new_object("geometry", str(obj_name) + custom_name, initialize_geometry) + self.app_obj.new_object("geometry", str(obj_name) + custom_name, initialize_geometry) except Exception as er: return "Operation failed: %s" % str(er) @@ -5784,9 +5004,9 @@ class App(QtCore.QObject): try: if isinstance(obj, ExcellonObject): - self.new_object("geometry", str(obj_name) + "_conv", initialize_excellon) + self.app_obj.new_object("geometry", str(obj_name) + "_conv", initialize_excellon) else: - self.new_object("geometry", str(obj_name) + "_conv", initialize) + self.app_obj.new_object("geometry", str(obj_name) + "_conv", initialize) except Exception as e: return "Operation failed: %s" % str(e) @@ -5862,9 +5082,9 @@ class App(QtCore.QObject): try: if isinstance(obj, ExcellonObject): - self.new_object("gerber", str(obj_name) + "_conv", initialize_excellon) + self.app_obj.new_object("gerber", str(obj_name) + "_conv", initialize_excellon) elif isinstance(obj, GeometryObject): - self.new_object("gerber", str(obj_name) + "_conv", initialize_geometry) + self.app_obj.new_object("gerber", str(obj_name) + "_conv", initialize_geometry) else: log.warning("App.convert_any2gerber --> This is no vaild object for conversion.") @@ -5917,7 +5137,7 @@ class App(QtCore.QObject): # delete the absolute and relative position and messages in the infobar self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") # Switch plot_area to preferences page self.ui.plot_tab_area.setCurrentWidget(self.ui.preferences_tab) @@ -6003,7 +5223,13 @@ class App(QtCore.QObject): callback_on_edited=self.on_tools_db_edited, callback_on_tool_request=self.paint_tool.on_paint_tool_add_from_db_executed ) - + elif source == 'iso': + self.tools_db_tab = ToolsDB2( + app=self, + parent=self.ui, + callback_on_edited=self.on_tools_db_edited, + callback_on_tool_request=self.isolation_tool.on_iso_tool_add_from_db_executed + ) # add the tab if it was closed try: self.ui.plot_tab_area.addTab(self.tools_db_tab, _("Tools Database")) @@ -6014,7 +5240,7 @@ class App(QtCore.QObject): # delete the absolute and relative position and messages in the infobar self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") # Switch plot_area to preferences page self.ui.plot_tab_area.setCurrentWidget(self.tools_db_tab) @@ -6030,7 +5256,7 @@ class App(QtCore.QObject): :return: """ - self.inform.emit('[WARNING_NOTCL] %s' % _("Tools in Tools Database edited but not saved.")) + self.inform[str, bool].emit('[WARNING_NOTCL] %s' % _("Tools in Tools Database edited but not saved."), False) for idx in range(self.ui.plot_tab_area.count()): if self.ui.plot_tab_area.tabText(idx) == _("Tools Database"): @@ -6048,9 +5274,19 @@ class App(QtCore.QObject): tool_from_db = deepcopy(tool) obj = self.collection.get_active() - if isinstance(obj, GeometryObject): + if obj.kind == 'geometry': obj.on_tool_from_db_inserted(tool=tool_from_db) + # close the tab and delete it + for idx in range(self.ui.plot_tab_area.count()): + if self.ui.plot_tab_area.tabText(idx) == _("Tools Database"): + wdg = self.ui.plot_tab_area.widget(idx) + wdg.deleteLater() + self.ui.plot_tab_area.removeTab(idx) + self.inform.emit('[success] %s' % _("Tool from DB added in Tool Table.")) + elif obj.kind == 'gerber': + self.isolation_tool.on_tool_from_db_inserted(tool=tool_from_db) + # close the tab and delete it for idx in range(self.ui.plot_tab_area.count()): if self.ui.plot_tab_area.tabText(idx) == _("Tools Database"): @@ -6081,6 +5317,7 @@ class App(QtCore.QObject): "Do you want to update the Tools Database?")) msgbox.setWindowTitle(_("Save Tools Database")) msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/save_as.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) @@ -6156,7 +5393,7 @@ class App(QtCore.QObject): for obj in obj_list: obj.mirror('X', [px, py]) obj.plot() - self.object_changed.emit(obj) + self.app_obj.object_changed.emit(obj) self.inform.emit('[success] %s' % _("Flip on Y axis done.")) except Exception as e: @@ -6204,7 +5441,7 @@ class App(QtCore.QObject): for obj in obj_list: obj.mirror('Y', [px, py]) obj.plot() - self.object_changed.emit(obj) + self.app_obj.object_changed.emit(obj) self.inform.emit('[success] %s' % _("Flip on X axis done.")) except Exception as e: @@ -6260,9 +5497,8 @@ class App(QtCore.QObject): for sel_obj in obj_list: sel_obj.rotate(-float(num), point=(px, py)) sel_obj.plot() - self.object_changed.emit(sel_obj) - self.inform.emit('[success] %s' % - _("Rotation done.")) + self.app_obj.object_changed.emit(sel_obj) + self.inform.emit('[success] %s' % _("Rotation done.")) except Exception as e: self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Rotation movement was not executed."), str(e))) return @@ -6281,8 +5517,7 @@ class App(QtCore.QObject): yminlist = [] if not obj_list: - self.inform.emit('[WARNING_NOTCL] %s' % - _("No object selected to Skew/Shear on X axis.")) + self.inform.emit('[WARNING_NOTCL] %s' % _("No object selected to Skew/Shear on X axis.")) else: skewxbox = FCInputDialog(title=_("Transform"), text=_("Enter the Angle value:"), min=-360, max=360, decimals=4, @@ -6302,9 +5537,8 @@ class App(QtCore.QObject): for obj in obj_list: obj.skew(num, 0, point=(xminimal, yminimal)) obj.plot() - self.object_changed.emit(obj) - self.inform.emit('[success] %s' % - _("Skew on X axis done.")) + self.app_obj.object_changed.emit(obj) + self.inform.emit('[success] %s' % _("Skew on X axis done.")) def on_skewy(self): """ @@ -6320,8 +5554,7 @@ class App(QtCore.QObject): yminlist = [] if not obj_list: - self.inform.emit('[WARNING_NOTCL] %s' % - _("No object selected to Skew/Shear on Y axis.")) + self.inform.emit('[WARNING_NOTCL] %s' % _("No object selected to Skew/Shear on Y axis.")) else: skewybox = FCInputDialog(title=_("Transform"), text=_("Enter the Angle value:"), min=-360, max=360, decimals=4, @@ -6341,9 +5574,8 @@ class App(QtCore.QObject): for obj in obj_list: obj.skew(0, num, point=(xminimal, yminimal)) obj.plot() - self.object_changed.emit(obj) - self.inform.emit('[success] %s' % - _("Skew on Y axis done.")) + self.app_obj.object_changed.emit(obj) + self.inform.emit('[success] %s' % _("Skew on Y axis done.")) def on_plots_updated(self): """ @@ -6357,7 +5589,7 @@ class App(QtCore.QObject): else: self.plotcanvas.auto_adjust_axes() - self.on_zoom_fit(None) + self.on_zoom_fit() self.collection.update_view() # self.inform.emit(_("Plots updated ...")) @@ -6368,7 +5600,6 @@ class App(QtCore.QObject): :return: None """ - self.defaults.report_usage("on_toolbar_replot") self.log.debug("on_toolbar_replot()") try: @@ -6379,207 +5610,8 @@ class App(QtCore.QObject): self.plot_all() - def on_row_activated(self, index): - if index.isValid(): - if index.internalPointer().parent_item != self.collection.root_item: - self.ui.notebook.setCurrentWidget(self.ui.selected_tab) - self.collection.on_item_activated(index) - - def on_row_selected(self, obj_name): - """ - This is a special string; when received it will make all Menu -> Objects entries unchecked - It mean we clicked outside of the items and deselected all - - :param obj_name: - :return: - """ - if obj_name == 'none': - for act in self.ui.menuobjects.actions(): - act.setChecked(False) - return - - # get the name of the selected objects and add them to a list - name_list = [] - for obj in self.collection.get_selected(): - name_list.append(obj.options['name']) - - # set all actions as unchecked but the ones selected make them checked - for act in self.ui.menuobjects.actions(): - act.setChecked(False) - if act.text() in name_list: - act.setChecked(True) - - def on_collection_updated(self, obj, state, old_name): - """ - Create a menu from the object loaded in the collection. - - :param obj: object that was changed (added, deleted, renamed) - :param state: what was done with the object. Can be: added, deleted, delete_all, renamed - :param old_name: the old name of the object before the action that triggered this slot happened - :return: None - """ - icon_files = { - "gerber": self.resource_location + "/flatcam_icon16.png", - "excellon": self.resource_location + "/drill16.png", - "cncjob": self.resource_location + "/cnc16.png", - "geometry": self.resource_location + "/geometry16.png", - "script": self.resource_location + "/script_new16.png", - "document": self.resource_location + "/notes16_1.png" - } - - if state == 'append': - for act in self.ui.menuobjects.actions(): - try: - act.triggered.disconnect() - except TypeError: - pass - self.ui.menuobjects.clear() - - gerber_list = [] - exc_list = [] - cncjob_list = [] - geo_list = [] - script_list = [] - doc_list = [] - - for name in self.collection.get_names(): - obj_named = self.collection.get_by_name(name) - if obj_named.kind == 'gerber': - gerber_list.append(name) - elif obj_named.kind == 'excellon': - exc_list.append(name) - elif obj_named.kind == 'cncjob': - cncjob_list.append(name) - elif obj_named.kind == 'geometry': - geo_list.append(name) - elif obj_named.kind == 'script': - script_list.append(name) - elif obj_named.kind == 'document': - doc_list.append(name) - - def add_act(o_name): - obj_for_icon = self.collection.get_by_name(o_name) - add_action = QtWidgets.QAction(parent=self.ui.menuobjects) - add_action.setCheckable(True) - add_action.setText(o_name) - add_action.setIcon(QtGui.QIcon(icon_files[obj_for_icon.kind])) - add_action.triggered.connect( - lambda: self.collection.set_active(o_name) if add_action.isChecked() is True else - self.collection.set_inactive(o_name)) - self.ui.menuobjects.addAction(add_action) - - for name in gerber_list: - add_act(name) - self.ui.menuobjects.addSeparator() - - for name in exc_list: - add_act(name) - self.ui.menuobjects.addSeparator() - - for name in cncjob_list: - add_act(name) - self.ui.menuobjects.addSeparator() - - for name in geo_list: - add_act(name) - self.ui.menuobjects.addSeparator() - - for name in script_list: - add_act(name) - self.ui.menuobjects.addSeparator() - - for name in doc_list: - add_act(name) - - self.ui.menuobjects.addSeparator() - self.ui.menuobjects_selall = self.ui.menuobjects.addAction( - QtGui.QIcon(self.resource_location + '/select_all.png'), - _('Select All') - ) - self.ui.menuobjects_unselall = self.ui.menuobjects.addAction( - QtGui.QIcon(self.resource_location + '/deselect_all32.png'), - _('Deselect All') - ) - self.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True)) - self.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False)) - - elif state == 'delete': - for act in self.ui.menuobjects.actions(): - if act.text() == obj.options['name']: - try: - act.triggered.disconnect() - except TypeError: - pass - self.ui.menuobjects.removeAction(act) - break - elif state == 'rename': - for act in self.ui.menuobjects.actions(): - if act.text() == old_name: - add_action = QtWidgets.QAction(parent=self.ui.menuobjects) - add_action.setText(obj.options['name']) - add_action.setIcon(QtGui.QIcon(icon_files[obj.kind])) - add_action.triggered.connect( - lambda: self.collection.set_active(obj.options['name']) if add_action.isChecked() is True else - self.collection.set_inactive(obj.options['name'])) - - self.ui.menuobjects.insertAction(act, add_action) - - try: - act.triggered.disconnect() - except TypeError: - pass - self.ui.menuobjects.removeAction(act) - break - elif state == 'delete_all': - for act in self.ui.menuobjects.actions(): - try: - act.triggered.disconnect() - except TypeError: - pass - self.ui.menuobjects.clear() - - self.ui.menuobjects.addSeparator() - self.ui.menuobjects_selall = self.ui.menuobjects.addAction( - QtGui.QIcon(self.resource_location + '/select_all.png'), - _('Select All') - ) - self.ui.menuobjects_unselall = self.ui.menuobjects.addAction( - QtGui.QIcon(self.resource_location + '/deselect_all32.png'), - _('Deselect All') - ) - self.ui.menuobjects_selall.triggered.connect(lambda: self.on_objects_selection(True)) - self.ui.menuobjects_unselall.triggered.connect(lambda: self.on_objects_selection(False)) - - def on_objects_selection(self, on_off): - obj_list = self.collection.get_names() - - if on_off is True: - self.collection.set_all_active() - for act in self.ui.menuobjects.actions(): - try: - act.setChecked(True) - except Exception: - pass - if obj_list: - self.inform.emit('[selected] %s' % _("All objects are selected.")) - else: - self.collection.set_all_inactive() - for act in self.ui.menuobjects.actions(): - try: - act.setChecked(False) - except Exception: - pass - - if obj_list: - self.inform.emit('%s' % _("Objects selection is cleared.")) - else: - self.inform.emit('') - def grid_status(self): - if self.ui.grid_snap_btn.isChecked(): - return True - else: - return False + return True if self.ui.grid_snap_btn.isChecked() else False def populate_cmenu_grids(self): units = self.defaults['units'].lower() @@ -6635,14 +5667,11 @@ class App(QtCore.QObject): else: if val not in self.defaults["global_grid_context_menu"][str(units)]: self.defaults["global_grid_context_menu"][str(units)].append(val) - self.inform.emit('[success] %s...' % - _("New Grid added")) + self.inform.emit('[success] %s...' % _("New Grid added")) else: - self.inform.emit('[WARNING_NOTCL] %s...' % - _("Grid already exists")) + self.inform.emit('[WARNING_NOTCL] %s...' % _("Grid already exists")) else: - self.inform.emit('[WARNING_NOTCL] %s...' % - _("Adding New Grid cancelled")) + self.inform.emit('[WARNING_NOTCL] %s...' % _("Adding New Grid cancelled")) def on_grid_delete(self): # ## Current application units in lower Case @@ -6663,14 +5692,11 @@ class App(QtCore.QObject): try: self.defaults["global_grid_context_menu"][str(units)].remove(val) except ValueError: - self.inform.emit('[ERROR_NOTCL]%s...' % - _(" Grid Value does not exist")) + self.inform.emit('[ERROR_NOTCL]%s...' % _(" Grid Value does not exist")) return - self.inform.emit('[success] %s...' % - _("Grid Value deleted")) + self.inform.emit('[success] %s...' % _("Grid Value deleted")) else: - self.inform.emit('[WARNING_NOTCL] %s...' % - _("Delete Grid value cancelled")) + self.inform.emit('[WARNING_NOTCL] %s...' % _("Delete Grid value cancelled")) def on_shortcut_list(self): self.defaults.report_usage("on_shortcut_list()") @@ -6680,7 +5706,7 @@ class App(QtCore.QObject): # delete the absolute and relative position and messages in the infobar self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") # Switch plot_area to preferences page self.ui.plot_tab_area.setCurrentWidget(self.ui.shortcuts_tab) @@ -6811,6 +5837,9 @@ class App(QtCore.QObject): try: # May fail in case mouse not within axes pos_canvas = self.plotcanvas.translate_coords(event_pos) + if pos_canvas[0] is None or pos_canvas[1] is None: + return + if self.grid_status(): pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1]) @@ -6822,13 +5851,19 @@ class App(QtCore.QObject): else: pos = (pos_canvas[0], pos_canvas[1]) - self.ui.position_label.setText("    X: %.4f   " - "Y: %.4f" % (pos[0], pos[1])) - self.dx = pos[0] - float(self.rel_point1[0]) self.dy = pos[1] - float(self.rel_point1[1]) - self.ui.rel_position_label.setText("Dx: %.4f   Dy: " - "%.4f    " % (self.dx, self.dy)) + + self.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (pos[0], pos[1])) + # self.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.dx, self.dy)) + + units = self.defaults["units"].lower() + self.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.dx, units, self.dy, units, pos[0], units, pos[1], units) + self.mouse = [pos[0], pos[1]] # if the mouse is moved and the LMB is clicked then the action is a selection @@ -6846,7 +5881,7 @@ class App(QtCore.QObject): else: self.selection_type = None - # hover effect - enabled in Preferences -> General -> GUI Settings + # hover effect - enabled in Preferences -> General -> AppGUI Settings if self.defaults['global_hover']: for obj in self.collection.get_list(): try: @@ -6877,9 +5912,10 @@ class App(QtCore.QObject): # In this case poly_obj creation (see above) will fail pass - except Exception: + except Exception as e: + log.debug("App.on_mouse_move_over_plot() - rel_point1 is not None -> %s" % str(e)) self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") self.mouse = None def on_mouse_click_release_over_plot(self, event): @@ -7044,7 +6080,7 @@ class App(QtCore.QObject): for obj in self.all_objects_list: # ScriptObject and DocumentObject objects can't be selected - if isinstance(obj, ScriptObject) or isinstance(obj, DocumentObject): + if obj.kind == 'script' or obj.kind == 'document': continue if key == 'multisel' and obj.options['name'] in self.objects_under_the_click_list: @@ -7073,7 +6109,7 @@ class App(QtCore.QObject): curr_sel_obj.selection_shape_drawn = True elif curr_sel_obj.options['name'] not in self.objects_under_the_click_list: - self.on_objects_selection(False) + self.collection.on_objects_selection(False) self.delete_selection_shape() curr_sel_obj.selection_shape_drawn = False @@ -7091,7 +6127,7 @@ class App(QtCore.QObject): self.draw_selection_shape(curr_sel_obj) curr_sel_obj.selection_shape_drawn = True else: - self.on_objects_selection(False) + self.collection.on_objects_selection(False) self.delete_selection_shape() if self.call_source != 'app': @@ -7133,7 +6169,7 @@ class App(QtCore.QObject): else: # deselect everything - self.on_objects_selection(False) + self.collection.on_objects_selection(False) # delete the possible selection box around a possible selected object self.delete_selection_shape() @@ -7342,6 +6378,8 @@ class App(QtCore.QObject): "Do you want to Save the project?")) msgbox.setWindowTitle(_("Save changes")) msgbox.setWindowIcon(QtGui.QIcon(self.resource_location + '/save_as.png')) + msgbox.setIcon(QtWidgets.QMessageBox.Question) + bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole) bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole) bt_cancel = msgbox.addButton(_('Cancel'), QtWidgets.QMessageBox.RejectRole) @@ -7405,6 +6443,9 @@ class App(QtCore.QObject): except AttributeError: pass + # delete the exclusion areas + self.exc_areas.clear_shapes() + # tcl needs to be reinitialized, otherwise old shell variables etc remains self.shell.init_tcl() @@ -7429,7 +6470,7 @@ class App(QtCore.QObject): # Init FlatCAMTools self.init_tools() - # Try to close all tabs in the PlotArea but only if the GUI is active (CLI is None) + # Try to close all tabs in the PlotArea but only if the AppGUI is active (CLI is None) if cli is None: # we need to go in reverse because once we remove a tab then the index changes # meaning that removing the first tab (idx = 0) then the tab at former idx = 1 will assume idx = 0 @@ -7724,6 +6765,8 @@ class App(QtCore.QObject): self.inform.emit('[WARNING_NOTCL] %s' % _("No object selected.")) msg = _("Please Select a Geometry object to export") msgbox = QtWidgets.QMessageBox() + msgbox.setIcon(QtWidgets.QMessageBox.Warning) + msgbox.setInformativeText(msg) bt_ok = msgbox.addButton(_('Ok'), QtWidgets.QMessageBox.AcceptRole) msgbox.setDefaultButton(bt_ok) @@ -7738,6 +6781,8 @@ class App(QtCore.QObject): msg = '[ERROR_NOTCL] %s' % \ _("Only Geometry, Gerber and CNCJob objects can be used.") msgbox = QtWidgets.QMessageBox() + msgbox.setIcon(QtWidgets.QMessageBox.Warning) + msgbox.setInformativeText(msg) bt_ok = msgbox.addButton(_('Ok'), QtWidgets.QMessageBox.AcceptRole) msgbox.setDefaultButton(bt_ok) @@ -7751,9 +6796,9 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export SVG"), directory=self.get_last_save_folder() + '/' + str(name) + '_svg', - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export SVG"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export SVG"), ext_filter=_filter) filename = str(filename) @@ -7774,8 +6819,9 @@ class App(QtCore.QObject): self.date = ''.join(c for c in self.date if c not in ':-') self.date = self.date.replace(' ', '_') + data = None if self.is_legacy is False: - image = _screenshot() + image = _screenshot(alpha=None) data = np.asarray(image) if not data.ndim == 3 and data.shape[-1] in (3, 4): self.inform.emit('[[WARNING_NOTCL]] %s' % _('Data must be a 3D array with last dimension 3 or 4')) @@ -7786,9 +6832,9 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export PNG Image"), directory=self.get_last_save_folder() + '/png_' + self.date, - filter=filter_) + ext_filter=filter_) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export PNG Image"), filter=filter_) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export PNG Image"), ext_filter=filter_) filename = str(filename) @@ -7831,9 +6877,9 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption="Save Gerber source file", directory=self.get_last_save_folder() + '/' + name, - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Gerber source file"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Gerber source file"), ext_filter=_filter) filename = str(filename) @@ -7872,9 +6918,9 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption="Save Script source file", directory=self.get_last_save_folder() + '/' + name, - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Script source file"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Script source file"), ext_filter=_filter) filename = str(filename) @@ -7913,9 +6959,10 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption="Save Document source file", directory=self.get_last_save_folder() + '/' + name, - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Document source file"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Document source file"), + ext_filter=_filter) filename = str(filename) @@ -7954,9 +7001,10 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Save Excellon source file"), directory=self.get_last_save_folder() + '/' + name, - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Excellon source file"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename( + caption=_("Save Excellon source file"), ext_filter=_filter) filename = str(filename) @@ -7995,9 +7043,9 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export Excellon"), directory=self.get_last_save_folder() + '/' + name, - filter=_filter) + ext_filter=_filter) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Excellon"), filter=_filter) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Excellon"), ext_filter=_filter) filename = str(filename) @@ -8039,9 +7087,9 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export Gerber"), directory=self.get_last_save_folder() + '/' + name, - filter=_filter_) + ext_filter=_filter_) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Gerber"), filter=_filter_) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Gerber"), ext_filter=_filter_) filename = str(filename) @@ -8071,6 +7119,8 @@ class App(QtCore.QObject): self.inform.emit('[WARNING_NOTCL] %s' % _("No object selected.")) msg = _("Please Select a Geometry object to export") msgbox = QtWidgets.QMessageBox() + msgbox.setIcon(QtWidgets.QMessageBox.Warning) + msgbox.setInformativeText(msg) bt_ok = msgbox.addButton(_('Ok'), QtWidgets.QMessageBox.AcceptRole) msgbox.setDefaultButton(bt_ok) @@ -8081,6 +7131,8 @@ class App(QtCore.QObject): if not isinstance(obj, GeometryObject): msg = '[ERROR_NOTCL] %s' % _("Only Geometry objects can be used.") msgbox = QtWidgets.QMessageBox() + msgbox.setIcon(QtWidgets.QMessageBox.Warning) + msgbox.setInformativeText(msg) bt_ok = msgbox.addButton(_('Ok'), QtWidgets.QMessageBox.AcceptRole) msgbox.setDefaultButton(bt_ok) @@ -8095,9 +7147,9 @@ class App(QtCore.QObject): filename, _f = FCFileSaveDialog.get_saved_filename( caption=_("Export DXF"), directory=self.get_last_save_folder() + '/' + name, - filter=_filter_) + ext_filter=_filter_) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export DXF"), filter=_filter_) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export DXF"), ext_filter=_filter_) filename = str(filename) @@ -8186,7 +7238,7 @@ class App(QtCore.QObject): # delete the absolute and relative position and messages in the infobar self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") # first clear previous text in text editor (if any) self.text_editor_tab.code_editor.clear() @@ -8237,7 +7289,7 @@ class App(QtCore.QObject): # delete the absolute and relative position and messages in the infobar self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") # first clear previous text in text editor (if any) self.source_editor_tab.code_editor.clear() @@ -8352,8 +7404,6 @@ class App(QtCore.QObject): Will create a new script file and open it in the Code Editor :param silent: if True will not display status messages - :param name: if specified will be the name of the new script - :param text: pass a source file to the newly created script to be loaded in it :return: None """ if silent is False: @@ -8361,9 +7411,9 @@ class App(QtCore.QObject): # delete the absolute and relative position and messages in the infobar self.ui.position_label.setText("") - self.ui.rel_position_label.setText("") + # self.ui.rel_position_label.setText("") - self.new_script_object() + self.app_obj.new_script_object() # script_text = script_obj.source_file # @@ -8549,10 +7599,10 @@ class App(QtCore.QObject): caption=_("Save Project As ..."), directory='{l_save}/{proj}_{date}'.format(l_save=str(self.get_last_save_folder()), date=self.date, proj=_("Project")), - filter=filter_ + ext_filter=filter_ ) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Project As ..."), filter=filter_) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Project As ..."), ext_filter=filter_) filename = str(filename) @@ -8604,10 +7654,10 @@ class App(QtCore.QObject): directory='{l_save}/{obj_name}_{date}'.format(l_save=str(self.get_last_save_folder()), obj_name=obj_name, date=self.date), - filter=filter_ + ext_filter=filter_ ) except TypeError: - filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Object as PDF ..."), filter=filter_) + filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Save Object as PDF ..."), ext_filter=filter_) filename = str(filename) @@ -9284,7 +8334,7 @@ class App(QtCore.QObject): # Object name name = outname or filename.split('/')[-1].split('\\')[-1] - ret = self.new_object(obj_type, name, obj_init, autoselected=False, plot=plot) + ret = self.app_obj.new_object(obj_type, name, obj_init, autoselected=False, plot=plot) if ret == 'fail': self.inform.emit('[ERROR_NOTCL]%s' % _('Import failed.')) @@ -9293,7 +8343,7 @@ class App(QtCore.QObject): # Register recent file self.file_opened.emit("svg", filename) - # GUI feedback + # AppGUI feedback self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) def import_dxf(self, filename, geo_type='geometry', outname=None, plot=True): @@ -9330,7 +8380,7 @@ class App(QtCore.QObject): # Object name name = outname or filename.split('/')[-1].split('\\')[-1] - ret = self.new_object(obj_type, name, obj_init, autoselected=False, plot=plot) + ret = self.app_obj.new_object(obj_type, name, obj_init, autoselected=False, plot=plot) if ret == 'fail': self.inform.emit('[ERROR_NOTCL]%s' % _('Import failed.')) @@ -9339,7 +8389,7 @@ class App(QtCore.QObject): # Register recent file self.file_opened.emit("dxf", filename) - # GUI feedback + # AppGUI feedback self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) def open_gerber(self, filename, outname=None, plot=True, from_tcl=False): @@ -9391,11 +8441,11 @@ class App(QtCore.QObject): name = outname or filename.split('/')[-1].split('\\')[-1] # # ## Object creation # ## - ret_val = self.new_object("gerber", name, obj_init, autoselected=False, plot=plot) + ret_val = self.app_obj.new_object("gerber", name, obj_init, autoselected=False, plot=plot) if ret_val == 'fail': if from_tcl: filename = self.defaults['global_tcl_path'] + '/' + name - ret_val = self.new_object("gerber", name, obj_init, autoselected=False, plot=plot) + ret_val = self.app_obj.new_object("gerber", name, obj_init, autoselected=False, plot=plot) if ret_val == 'fail': self.inform.emit('[ERROR_NOTCL]%s' % _('Open Gerber failed. Probable not a Gerber file.')) return 'fail' @@ -9403,7 +8453,7 @@ class App(QtCore.QObject): # Register recent file self.file_opened.emit("gerber", filename) - # GUI feedback + # AppGUI feedback self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) def open_excellon(self, filename, outname=None, plot=True, from_tcl=False): @@ -9457,11 +8507,11 @@ class App(QtCore.QObject): with self.proc_container.new(_("Opening Excellon.")): # Object name name = outname or filename.split('/')[-1].split('\\')[-1] - ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot) + ret_val = self.app_obj.new_object("excellon", name, obj_init, autoselected=False, plot=plot) if ret_val == 'fail': if from_tcl: filename = self.defaults['global_tcl_path'] + '/' + name - ret_val = self.new_object("excellon", name, obj_init, autoselected=False, plot=plot) + ret_val = self.app_obj.new_object("excellon", name, obj_init, autoselected=False, plot=plot) if ret_val == 'fail': self.inform.emit('[ERROR_NOTCL] %s' % _('Open Excellon file failed. Probable not an Excellon file.')) @@ -9470,7 +8520,7 @@ class App(QtCore.QObject): # Register recent file self.file_opened.emit("excellon", filename) - # GUI feedback + # AppGUI feedback self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) def open_gcode(self, filename, outname=None, force_parsing=None, plot=True, from_tcl=False): @@ -9520,11 +8570,11 @@ class App(QtCore.QObject): name = outname or filename.split('/')[-1].split('\\')[-1] # New object creation and file processing - ret_val = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot) + ret_val = self.app_obj.new_object("cncjob", name, obj_init, autoselected=False, plot=plot) if ret_val == 'fail': if from_tcl: filename = self.defaults['global_tcl_path'] + '/' + name - ret_val = self.new_object("cncjob", name, obj_init, autoselected=False, plot=plot) + ret_val = self.app_obj.new_object("cncjob", name, obj_init, autoselected=False, plot=plot) if ret_val == 'fail': self.inform.emit('[ERROR_NOTCL] %s' % _("Failed to create CNCJob Object. Probable not a GCode file. " @@ -9536,7 +8586,7 @@ class App(QtCore.QObject): # Register recent file self.file_opened.emit("cncjob", filename) - # GUI feedback + # AppGUI feedback self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) def open_hpgl2(self, filename, outname=None): @@ -9593,7 +8643,7 @@ class App(QtCore.QObject): name = outname or filename.split('/')[-1].split('\\')[-1] # # ## Object creation # ## - ret = self.new_object("geometry", name, obj_init, autoselected=False) + ret = self.app_obj.new_object("geometry", name, obj_init, autoselected=False) if ret == 'fail': self.inform.emit('[ERROR_NOTCL]%s' % _(' Open HPGL2 failed. Probable not a HPGL2 file.')) return 'fail' @@ -9601,7 +8651,7 @@ class App(QtCore.QObject): # Register recent file self.file_opened.emit("geometry", filename) - # GUI feedback + # AppGUI feedback self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) def open_script(self, filename, outname=None, silent=False): @@ -9647,10 +8697,10 @@ class App(QtCore.QObject): script_name = outname or filename.split('/')[-1].split('\\')[-1] # Object creation - ret_val = self.new_object("script", script_name, obj_init, autoselected=False, plot=False) + ret_val = self.app_obj.new_object("script", script_name, obj_init, autoselected=False, plot=False) if ret_val == 'fail': filename = self.defaults['global_tcl_path'] + '/' + script_name - ret_val = self.new_object("script", script_name, obj_init, autoselected=False, plot=False) + ret_val = self.app_obj.new_object("script", script_name, obj_init, autoselected=False, plot=False) if ret_val == 'fail': self.inform.emit('[ERROR_NOTCL]%s' % _('Failed to open TCL Script.')) return 'fail' @@ -9658,7 +8708,7 @@ class App(QtCore.QObject): # Register recent file self.file_opened.emit("script", filename) - # GUI feedback + # AppGUI feedback self.inform.emit('[success] %s: %s' % (_("Opened"), filename)) def open_config_file(self, filename, run_from_arg=None): @@ -9713,7 +8763,7 @@ class App(QtCore.QObject): 2) Registers the file as recently opened. 3) Calls on_file_new() 4) Updates options - 5) Calls new_object() with the object's from_dict() as init method. + 5) Calls app_obj.new_object() with the object's from_dict() as init method. 6) Calls plot_all() if plot=True :param filename: Name of the file from which to load. @@ -9815,7 +8865,7 @@ class App(QtCore.QObject): ) ) - self.new_object(obj['kind'], obj['options']['name'], obj_init, plot=plot) + self.app_obj.new_object(obj['kind'], obj['options']['name'], obj_init, plot=plot) except Exception as e: print('App.open_project() --> ' + str(e)) @@ -9834,23 +8884,25 @@ class App(QtCore.QObject): App.log.debug(" **************** Finished PROJECT loading... **************** ") - def plot_all(self, fit_view=True, use_thread=True): + def plot_all(self, fit_view=True, muted=False, use_thread=True): """ Re-generates all plots from all objects. :param fit_view: if True will plot the objects and will adjust the zoom to fit all plotted objects into view + :param muted: if True don't print messages :param use_thread: if True will use threading for plotting the objects :return: None """ self.log.debug("Plot_all()") - self.inform.emit('[success] %s...' % _("Redrawing all objects")) + if muted is not True: + self.inform.emit('[success] %s...' % _("Redrawing all objects")) for plot_obj in self.collection.get_list(): def worker_task(obj): with self.proc_container.new("Plotting"): obj.plot(kind=self.defaults["cncjob_plot_kind"]) if fit_view is True: - self.object_plotted.emit(obj) + self.app_obj.object_plotted.emit(obj) if use_thread is True: # Send to worker @@ -10085,11 +9137,11 @@ class App(QtCore.QObject): #
  • Loat/Import a Gerber, Excellon, Gcode, DXF, Raster Image or SVG # file into # FlatCAM using either the menu's, toolbars, key shortcuts or - # even dragging and dropping the files on the GUI.
    + # even dragging and dropping the files on the AppGUI.
    #
    # You can also load a FlatCAM project by double clicking on the project file, drag & # drop of the - # file into the FLATCAM GUI or through the menu/toolbar links offered within the app.

    + # file into the FLATCAM AppGUI or through the menu/toolbar links offered within the app.
    #  
  • #
  • Once an object is available in the Project Tab, by selecting it # and then @@ -10148,9 +9200,9 @@ class App(QtCore.QObject): s1=_("The normal flow when working in FlatCAM is the following:"), s2=_("Load/Import a Gerber, Excellon, Gcode, DXF, Raster Image or SVG file into FlatCAM " "using either the toolbars, key shortcuts or even dragging and dropping the " - "files on the GUI."), + "files on the AppGUI."), s3=_("You can also load a FlatCAM project by double clicking on the project file, " - "drag and drop of the file into the FLATCAM GUI or through the menu (or toolbar) " + "drag and drop of the file into the FLATCAM AppGUI or through the menu (or toolbar) " "actions offered within the app."), s4=_("Once an object is available in the Project Tab, by selecting it and then focusing " "on SELECTED TAB (more simpler is to double click the object name in the Project Tab, " @@ -10219,8 +9271,8 @@ class App(QtCore.QObject): # no_stats dict; just so it won't break things on website no_ststs_dict = {} no_ststs_dict["global_ststs"] = {} - full_url = App.version_url + "?s=" + str(self.defaults['global_serial']) + "&v=" + str(self.version) + \ - "&os=" + str(self.os) + "&" + urllib.parse.urlencode(no_ststs_dict["global_ststs"]) + full_url = App.version_url + "?s=" + str(self.defaults['global_serial']) + "&v=" + str(self.version) + full_url += "&os=" + str(self.os) + "&" + urllib.parse.urlencode(no_ststs_dict["global_ststs"]) App.log.debug("Checking for updates @ %s" % full_url) # ## Get the data @@ -10323,13 +9375,12 @@ class App(QtCore.QObject): # will use the default Matplotlib axes self.hover_shapes = ShapeCollectionLegacy(obj=self, app=self, name='hover') - def on_zoom_fit(self, event): + def on_zoom_fit(self): """ Callback for zoom-fit request. This can be either from the corresponding toolbar button or the '1' key when the canvas is focused. Calls ``self.adjust_axes()`` with axes limits from the geometry bounds of all objects. - :param event: Ignored. :return: None """ if self.is_legacy is False: @@ -10435,8 +9486,6 @@ class App(QtCore.QObject): self.worker_task.emit({'fcn': worker_task, 'params': [objects]}) - # self.plots_updated.emit() - def disable_plots(self, objects): """ Disables plots @@ -10475,7 +9524,6 @@ class App(QtCore.QObject): except Exception as e: log.debug("App.disable_plots() --> %s" % str(e)) - # self.plots_updated.emit() def worker_task(objs): with self.proc_container.new(_("Disabling plots ...")): for plot_obj in objs: @@ -10506,7 +9554,7 @@ class App(QtCore.QObject): obj.options['plot'] = True else: obj.options['plot'] = False - self.plots_updated.emit() + self.app_obj.plots_updated.emit() def clear_plots(self): """ @@ -10809,44 +9857,16 @@ class App(QtCore.QObject): self.preferencesUiManager.defaults_read_form() self.options.update(self.defaults) - def toggle_shell(self): - """ - Toggle shell: if is visible close it, if it is closed then open it - :return: None - """ - - self.defaults.report_usage("toggle_shell()") - - if self.ui.shell_dock.isVisible(): - self.ui.shell_dock.hide() - self.plotcanvas.native.setFocus() - else: - self.ui.shell_dock.show() - - # I want to take the focus and give it to the Tcl Shell when the Tcl Shell is run - # self.shell._edit.setFocus() - QtCore.QTimer.singleShot(0, lambda: self.ui.shell_dock.widget()._edit.setFocus()) - - # HACK - simulate a mouse click - alternative - # no_km = QtCore.Qt.KeyboardModifier(QtCore.Qt.NoModifier) # no KB modifier - # pos = QtCore.QPoint((self.shell._edit.width() - 40), (self.shell._edit.height() - 2)) - # e = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, - # no_km) - # QtWidgets.qApp.sendEvent(self.shell._edit, e) - # f = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonRelease, pos, QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, - # no_km) - # QtWidgets.qApp.sendEvent(self.shell._edit, f) - def shell_message(self, msg, show=False, error=False, warning=False, success=False, selected=False): """ Shows a message on the FlatCAM Shell - :param msg: Message to display. - :param show: Opens the shell. - :param error: Shows the message as an error. - :param warning: Shows the message as an warning. - :param success: Shows the message as an success. - :param selected: Indicate that something was selected on canvas + :param msg: Message to display. + :param show: Opens the shell. + :param error: Shows the message as an error. + :param warning: Shows the message as an warning. + :param success: Shows the message as an success. + :param selected: Indicate that something was selected on canvas :return: None """ if show: @@ -10869,6 +9889,7 @@ class App(QtCore.QObject): class ArgsThread(QtCore.QObject): open_signal = pyqtSignal(list) start = pyqtSignal() + stop = pyqtSignal() if sys.platform == 'win32': address = (r'\\.\pipe\NPtest', 'AF_PIPE') @@ -10881,6 +9902,7 @@ class ArgsThread(QtCore.QObject): self.thread_exit = False self.start.connect(self.run) + self.stop.connect(self.close_listener) def my_loop(self, address): try: @@ -10924,4 +9946,9 @@ class ArgsThread(QtCore.QObject): def run(self): self.my_loop(self.address) + @pyqtSlot() + def close_listener(self): + self.thread_exit = True + self.listener.close() + # end of file diff --git a/FlatCAMBookmark.py b/Bookmark.py similarity index 98% rename from FlatCAMBookmark.py rename to Bookmark.py index b9884fe2..ebdb2404 100644 --- a/FlatCAMBookmark.py +++ b/Bookmark.py @@ -1,5 +1,5 @@ from PyQt5 import QtGui, QtCore, QtWidgets -from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCFileSaveDialog +from AppGUI.GUIElements import FCTable, FCEntry, FCButton, FCFileSaveDialog import sys import webbrowser @@ -7,7 +7,7 @@ import webbrowser from copy import deepcopy from datetime import datetime import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -292,7 +292,7 @@ class BookmarkManager(QtWidgets.QWidget): l_save=str(self.app.get_last_save_folder()), n=_("Bookmarks"), date=date), - filter=filter__) + ext_filter=filter__) filename = str(filename) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17a2795b..1dac8877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,222 @@ CHANGELOG for FlatCAM beta ================================================= +30.05.2020 + +- made confirmation messages for the values that are modified not to be printed in the Shell +- Isolation Tool: working on the Rest machining: almost there, perhaps I will use multiprocessing +- Isolation Tool: removed the tools that have empty geometry in case of rest machining +- Isolation Tool: solved some naming issues +- Isolation Tool: updated the tools dict with the common parameters value on isolating +- Fixed a recent change that made the edited Geometry objects in the Geometry Editor not to be plotted after saving changes +- modified the Tool Database such that when a tool shape is selected as 'V' any change in the Vdia or Vangle or CutZ parameters will update the tool diameter value +- In Tool Isolation made sure that the use of ESC key while some processes are active will disconnect the mouse events that may be connected, correctly +- optimized the Gerber UI +- added a Multi-color checkbox for the Geometry UI (will color differently tool geometry when the geometry is multitool) +- added a Multi-color checkbox for the Excellon UI (this way colors for each tool are easier to differentiate especially when the diameter is close) +- made the Shell Dock always show docked +- fixed NCC Tool behavior when selecting tools for Isolation operation + +29.05.2020 + +- fixed the Tool Isolation when using the 'follow' parameter +- in Isolation Tool when the Rest machining is checked the combine parameter is set True automatically because the rest machining concept make sense only when all tools are used together +- some changes in the UI; added in the status bar an icon to control the Shell Dock +- clicking on the activity icon will replot all objects +- optimized UI in Tool Isolation +- overloaded the App inform signal to allow not printing to shell if a second bool parameter is given; modified some GUI messages to use this feature +- fixed the shell status label status on shell dock close from close button +- refactored some methods from App class and moved them to plotcanvas (plotcanvaslegacy) class +- added an label with icon in the status bar, clicking it will toggle (show status) of the X-Y axis on cavnas +- optimized the UI, added to status bar an icon to toggle the axis +- updated the Etch Compensation Tool by adding a new possibility to compensate the lateral etch (manual value) +- updated the Etch Compensation Tool such that the resulting Gerber object will have the apertures attributes ('size', 'width', 'height') updated to the changes + +28.05.2020 + +- made the visibility change (when using the Spacebar key in Project Tab) to be not threaded and to use the enabled property of the ShapesCollection which should be faster +- updated the Tool Database class to have the Isolation Tool data +- Isolation Tool - made to work the adding of tools from database +- fixed some issues related to using the new Numerical... GUI elements +- fixed issues in the Tool Subtract +- remade Tool Subtract to use multiprocessing when processing geometry +- the resulting Gerber file from Tool Subtract has now the attribute source_file populated + +27.05.2020 + +- working on Isolation Tool: made to work the Isolation with multiple tools without rest machining + +26.05.2020 + +- working on Isolation Tool: made to work the tool parameters data to GUI and GUI to data +- Isolation Tool: reworked the GUI +- if there is a Gerber object selected then in Isolation Tool the Gerber object combobox will show that object name as current +- made the Project Tree items not editable by clicking on selected Tree items (the object rename can still be done in the Selected tab) +- working on Isolation Tool: added a Preferences section in Edit -> Preferences and updated their usage within the Isolation tool +- fixed milling drills not plotting the resulting Geometry object +- all tuple entries in the Preferences UI are now protected against letter entry +- all entries in the Preferences UI that have numerical entry are protected now against letters +- cleaned the Preferences UI in the Gerber area +- minor UI changes + +25.05.2020 + +- updated the GUI fields for the Scale and Offset in the Object UI to allow only numeric values and operators in the list [/,*,+,-], spaces, dots and comma +- modified the Etch Compensation Tool and added conversion utilities from Oz thickenss and mils to microns +- added a Toggle All checkbox to Corner Markers Tool +- added an Icon to the MessageBox that asks for saving if the user try to close the app and there is some unsaved work +- changed and added some icons +- fixed the Shortcuts Tab to reflect the actual current shortcut keys +- started to work on moving the Isolation Routing from the Gerber Object UI to it's own tool +- created a new tool: Isolation Routing Tool: work in progress +- some fixes in NCC Tool +- added a dialog in Menu -> Help -> ReadMe? + +24.05.2020 + +- changes some icons +- added a new GUI element which is a evaluated LineEdit that accepts only float numbers and /,*,+,-,% chars +- finished the Etch Compensation Tool +- fixed unreliable work of Gerber Editor and optimized the App.editor2object() method +- updated the Gerber parser such that it will parse correctly Gerber files that have only one solid polygon inside with multiple clear polygons (like those generated by the Invert Tool) +- fixed a small bug in the Geometry UI that made updating the storage from GUI not to work +- some small changes in Gerber Editor + +23.05.2020 + +- fixed a issue when testing for Exclusion areas overlap over the Geometry object solid_geometry + +22.05.2020 + +- fixed the algorithm for calculating closest points in the Exclusion areas +- added the Exclusion zones processing to Geometry GCode generation + +21.05.2020 + +- added the Exclusion zones processing to Excellon GCode generation +- fixed a non frequent plotting problem for CNCJob objects made out of Excellon objects + +19.05.2020 + +- updated the Italian language (translation incomplete) +- updated all the language strings to the latest changes; updated the POT file +- fixed a possible malfunction in Tool Punch Gerber + +18.05.2020 + +- fixed the PDF Tool when importing as Gerber objects +- moved all the parsing out of the PDF Tool to a new file ParsePDF in the flatcamParsers folder +- trying to fix the pixmap load crash when running a FlatCAMScript +- made the workspace label in the status bar clickable and also added a status bar message on status toggle for workspace +- modified the GUI for Film and Panelize Tools +- moved some of the GUI related methods from FlatCAMApp.App to the flatcamGUI.MainGUI class +- moved Shortcuts Tab creation in it's own class +- renamed classes to have shorter names and grouped +- removed reference to postprocessors and replaced it with preprocessors +- more refactoring class names +- moved some of the methods from the App class to the ObjectCollection class +- moved all the new_object related methods in their own class AppObjects.AppObject +- more refactoring; solved some issues introduced by the refactoring +- solved a circular import +- updated the language translation files to the latest changes (no translation) +- working on a new Tool: Etch Compensation Tool -> installed the tool and created the GUI and class template +- moved more methods out of App_Main class +- added confirmation messages for toggle of HUD, Grid, Grid Snap, Axis +- added icon in status bar for HUD; clicking on it will toggle the HUD (heads up display) +- fixes due of recent changes +- fixed issue #417 + +17.05.2020 + +- added new FlatCAM Tool: Corner Markers Tool which will add line markers in the selected corners of the bounding box of the targeted Gerber object +- added a menu entry in Menu -> View for Toggle HUD +- solved the issue with the GUI in the Notebook being expanded too much in width due of the FCDoubleSpinner and FCSpinner sizeHint by setting the sizePolicy to Ignored value +- fixed the workspace being always A4 +- added a label in the status bar to show if the workplace is active and what size it is +- now the Edit command (either from Menu Edit ->Edit Object) or through the shortcut key (E key) or project tab context menu works also for the CNCJob objects (will open a text Editor with the GCode) +- fixed the object collection methods that return a list of objects or names of objects such that they have a parameter now to allow adding to those lists (or not) for the objects of type Script or Document. Thus fixing some of the Tcl commands such Set Origin +- reverted the previous changes to object collection; it is better to create empty methods in FlatCAMScript and FlatCAMDocument objects + +16.05.2020 + +- worked on the NCC Tool; added a new clear method named 'Combo' which will go through all methods until the clear is done +- added a Preferences parameter for font size used in HUD + +13.05.2020 + +- updated the French translation strings, made by @micmac (Michel) + +12.05.2020 + +- fixed recent issues introduced in Tcl command Drillcncjob +- updated the Cncjob to use the 'endxy' parameter which dictates the x,y position at the end of the job +- now the Tcl commands Drillcncjob and Cncjob can use the toolchangexy and endxy parameters with or without parenthesis (but no spaces allowed) +- modified the Tcl command Paint "single" parameter. Now it's value is a tuple with the x,y coordinates of the single polygon to be painted. +- the HUD display state is now persistent between app restarts +- updated the Distance Tool such that the right click of the mouse will cancel the tool unless it was a panning move +- modified the PlotCanvasLegacy to decide if there is a mouse drag based on the distance between the press event position and the release event position. If the distance is smaller than a delta distance then it is not a drag move. + +11.05.2020 + +- removed the labels in status bar that display X,Y positions and replaced it with a HUD display on canvas (combo key SHIFT+H) will toggle the display of the HUD +- made the HUD work in Legacy2D mode +- fixed situation when the mouse cursor is outside of the canvas and no therefore returning None values +- remade the Snap Toolbar presence; now it is always active and situated in the Status Bar +- Snap Toolbar is now visible in Fullscreen +- in Fullscreen now the Notebook is available but it will be hidden on Fullscreen launch +- fixed some minor issues (in the HUD added a separating line, missing an icon in toolbars on first launch) +- made sure that the corner snap buttons are shown only in Editors +- changed the HUD color when using Dark theme +- fix issue in Legacy2D graphic mode where the snap function was not accessible when the PlotCanvasLegacy class was created +- modified the HUD in Legacy2D when using Dark Theme to use different colors +- modified how the graphic engine change act in Preferences: now only by clicking Apply(or Save) the change will happen. And there is also a message asking for confirmation +- re-added the position labels in the status bar; they will be useful if HUD is Off (Altium does the same :) so learn from the best) +- fixed the Tcl command Cncjob: there was a problem reported as issue #416. The command did not work due of the dpp parameter +- modified the Tcl command Cncjob such that if some of the parameters are not used then the default values will be used (set with set_sys) +- modified the Tcl command Drillcncjob to use the defaults when some of the parameters are not used + +10.05.2020 + +- fixed the problem with using comma as decimal separator in Grid Snap fields + +9.05.2020 + +- modified the GUI for Exclusion areas; now the shapes are displayed in a Table where they can be selected and deleted. Modification applied for Geometry Objects only (for now). +- fixed an error when converting units, error that acted when in those fields that accept lists of tools only one tool was added +- finished the GUI for exclusion areas both in the Excellon and Geometry Objects. Need to think if to make it visible only in Advanced Mode + +8.05.2020 + +- added a parameter to the FlatCAMDefaults class, whenever a value in the self.defaults dict change it will call a callback function and send to it the modified key +- optimized and fixed some issues in the self.on_toggle_units() method +- the Exclusion areas will have all the orange color but the color of the outline will differ according to the type of the object from where it was added (cosmetic use only as the Exclusion areas will be applied globally) +- removed the Apply theme button in the Preferences; it is now replaced by the more general buttons (either Save or Apply) +- added a confirmation/warning message when applying a new theme + +7.05.2020 + +- added a fix so the app close is now clean, with exit code 0 as set +- added the ability to add exclusion areas from the Excellon object too. Now there is a difference in color to differentiate from which type of object the exclusion areas were added but they all serve the same purpose + +6.05.2020 + +- wip in adding Exclusion areas in Geometry object; each Geometry object has now a storage for shapes (exclusion shapes, should I make them more general?) +- changed the above: too many shapes collections and the performance will go down. Created a class ExclusionAreas that holds all the require properties and the Object UI elements will connect to it's methods. This way I can apply this feature to Excellon object too (who is a special type of Geometry Object) +- handled the New project event and the object deletion (when all objects are deleted then the exclusion areas will be deleted too) +- solved issue with new parameter end_xy when it is None +- solved issue with applying theme and not making the change in the Preferences UI. In Preferences UI the theme radio is always Light (white) +- now the annotations will invert the selected color in the Preferences, when selecting Dark theme + +5.05.2020 + +- fixed an issue that made the preprocessors combo boxes in Preferences not to load and display the saved value fro the file +- some PEP8 corrections + +4.05.2020 + +- in detachable tabs, Linux loose the reference of the detached tab and on close of the detachable tabs will gave a 'segmentation fault' error. Solved it by not deleting the reference in case of Unix-like systems +- some strings added to translation strings + 3.05.2020 - small changes to allow making the x86 installer that is made from a Python 3.5 run FlatCAM beta @@ -24,7 +240,7 @@ CHANGELOG for FlatCAM beta 2.05.2020 - changed the icons for the grid snap in the status bar -- moved some of the methods from FlatCAMApp.App to flatcamGUI.FlatCAMGUI class +- moved some of the methods from FlatCAMApp.App to flatcamGUI.MainGUI class - fixed bug in Gerber Editor in which the units conversion wasn't calculated correct - fixed bug in Gerber Editor in which the QThread that is started on object edit was not stopped at clean up stage - fixed bug in Gerber Editor that kept all the apertures (including the geometry) of a previously edited object that was not saved after edit @@ -1292,7 +1508,7 @@ CHANGELOG for FlatCAM beta - optimized Rules Check Tool so it runs faster when doing Copper 2 Copper rule - small GUI changes in Optimal Tool and in Film Tool - some PEP8 corrections -- some code annotations to make it easier to navigate in the FlatCAMGUI.py +- some code annotations to make it easier to navigate in the MainGUI.py - fixed exit FullScreen with Escape key - added a new menu category in the MenuBar named 'Objects'. It will hold the objects found in the Project tab. Useful when working in FullScreen - disabled a log.debug in ObjectColection.get_by_name() @@ -2731,7 +2947,7 @@ CHANGELOG for FlatCAM beta - fix for issue #262: when doing Edit-> Save & Close Editor on a Geometry that is not generated through first entering into an Editor, the geometry disappear - finished preparing for internationalization for the files: camlib and objectCollection - fixed tools shortcuts not working anymore due of the new toggle parameter for the .run(). -- finished preparing for internationalization for the files: FlatCAMEditor, FlatCAMGUI +- finished preparing for internationalization for the files: FlatCAMEditor, MainGUI - finished preparing for internationalization for the files: FlatCAMObj, ObjectUI - sorted the languages in the Preferences combobox @@ -3053,7 +3269,7 @@ CHANGELOG for FlatCAM beta - fixed the name self-insert in save dialog file for GCode; added protection in case the save path is None - fixed FlatCAM crash when trying to make drills GCode out of a file that have only slots. - changed the messages for Units Conversion -- all key shortcuts work across the entire application; moved all the shortcuts definitions in FlatCAMGUI.keyPressEvent() +- all key shortcuts work across the entire application; moved all the shortcuts definitions in MainGUI.keyPressEvent() - renamed the theme to layout because it is really a layout change - added plot kind for CNC Job in the App Preferences - combined the geocutout and cutout_any TCL commands - work in progress @@ -3574,7 +3790,7 @@ For now they are used only for Excellon objects who do have toolchange events - fixed a reported bug generated by a typo for feedrate_z object in camlib.py. Because of that, the project could not be saved. - fixed a G01 usage (should be G1) in Marlin preprocessor. -- changed the position of the Tool Dia entry in the Object UI and in FlatCAMGUI +- changed the position of the Tool Dia entry in the Object UI and in MainGUI - fixed issues in the installer 30.10.2018 @@ -4134,7 +4350,7 @@ still copper leftovers. - modified generate_milling method which had issues from the Python3 port (it could not sort the tools due of dict to dict comparison no longer possible). - modified the 'default' preprocessor in order to include a space between the value of Xcoord and the following Y - made optional the using of threads for the milling command; by default it is OFF (False) because in the current configuration it creates issues when it is using threads -- modified the Panelize function and Tcl command Panelize. It was having issues due to multithreading (kept trying to modify a dictionary in redraw() method)and automatically selecting the last created object (feature introduced by me). I've added a parameter to the new_object method, named autoselected (by default it is True) and in the panelize method I initialized it with False. +- modified the Panelize function and Tcl command Panelize. It was having issues due to multithreading (kept trying to modify a dictionary in redraw() method)and automatically selecting the last created object (feature introduced by me). I've added a parameter to the app_obj.new_object method, named autoselected (by default it is True) and in the panelize method I initialized it with False. By initializing the plot parameter with False for the temporary objects, I have increased dramatically the generation speed of the panel because now the temporary object are no longer ploted which consumed time. - replaced log.warn() with log.warning() in camlib.py. Reason: deprecated - fixed the issue that the "Defaults" button was having no effect when clicked and Options Combo was in Project Options diff --git a/Common.py b/Common.py new file mode 100644 index 00000000..03574b86 --- /dev/null +++ b/Common.py @@ -0,0 +1,854 @@ +# ########################################################## +# FlatCAM: 2D Post-processing for Manufacturing # +# http://flatcam.org # +# Author: Juan Pablo Caram (c) # +# Date: 2/5/2014 # +# MIT Licence # +# ########################################################## + +# ########################################################## +# File Modified (major mod): Marius Adrian Stanciu # +# Date: 11/4/2019 # +# ########################################################## +from PyQt5 import QtCore + +from shapely.geometry import Polygon, Point, LineString +from shapely.ops import unary_union + +from AppGUI.VisPyVisuals import ShapeCollection +from AppTool import AppTool + +from copy import deepcopy + +import numpy as np + +import gettext +import AppTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + + +class GracefulException(Exception): + """ + Graceful Exception raised when the user is requesting to cancel the current threaded task + """ + def __init__(self): + super().__init__() + + def __str__(self): + return '\n\n%s' % _("The user requested a graceful exit of the current task.") + + +class LoudDict(dict): + """ + A Dictionary with a callback for item changes. + """ + + def __init__(self, *args, **kwargs): + dict.__init__(self, *args, **kwargs) + self.callback = lambda x: None + + def __setitem__(self, key, value): + """ + Overridden __setitem__ method. Will emit 'changed(QString)' if the item was changed, with key as parameter. + """ + if key in self and self.__getitem__(key) == value: + return + + dict.__setitem__(self, key, value) + self.callback(key) + + def update(self, *args, **kwargs): + if len(args) > 1: + raise TypeError("update expected at most 1 arguments, got %d" % len(args)) + other = dict(*args, **kwargs) + for key in other: + self[key] = other[key] + + def set_change_callback(self, callback): + """ + Assigns a function as callback on item change. The callback + will receive the key of the object that was changed. + + :param callback: Function to call on item change. + :type callback: func + :return: None + """ + + self.callback = callback + + +class FCSignal: + """ + Taken from here: https://blog.abstractfactory.io/dynamic-signals-in-pyqt/ + """ + + def __init__(self): + self.__subscribers = [] + + def emit(self, *args, **kwargs): + for subs in self.__subscribers: + subs(*args, **kwargs) + + def connect(self, func): + self.__subscribers.append(func) + + def disconnect(self, func): + try: + self.__subscribers.remove(func) + except ValueError: + print('Warning: function %s not removed ' + 'from signal %s' % (func, self)) + + +def color_variant(hex_color, bright_factor=1): + """ + Takes a color in HEX format #FF00FF and produces a lighter or darker variant + + :param hex_color: color to change + :type hex_color: str + :param bright_factor: factor to change the color brightness [0 ... 1] + :type bright_factor: float + :return: Modified color + :rtype: str + """ + + if len(hex_color) != 7: + print("Color is %s, but needs to be in #FF00FF format. Returning original color." % hex_color) + return hex_color + + if bright_factor > 1.0: + bright_factor = 1.0 + if bright_factor < 0.0: + bright_factor = 0.0 + + rgb_hex = [hex_color[x:x + 2] for x in [1, 3, 5]] + new_rgb = [] + for hex_value in rgb_hex: + # adjust each color channel and turn it into a INT suitable as argument for hex() + mod_color = round(int(hex_value, 16) * bright_factor) + # make sure that each color channel has two digits without the 0x prefix + mod_color_hex = str(hex(mod_color)[2:]).zfill(2) + new_rgb.append(mod_color_hex) + + return "#" + "".join([i for i in new_rgb]) + + +class ExclusionAreas(QtCore.QObject): + """ + Functionality for adding Exclusion Areas for the Excellon and Geometry FlatCAM Objects + """ + e_shape_modified = QtCore.pyqtSignal() + + def __init__(self, app): + super().__init__() + + self.app = app + + # Storage for shapes, storage that can be used by FlatCAm tools for utility geometry + # VisPy visuals + if self.app.is_legacy is False: + try: + self.exclusion_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) + except AttributeError: + self.exclusion_shapes = None + else: + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy + self.exclusion_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name="exclusion") + + # Event signals disconnect id holders + self.mr = None + self.mm = None + self.kp = None + + # variables to be used in area exclusion + self.cursor_pos = (0, 0) + self.first_click = False + self.points = [] + self.poly_drawn = False + + ''' + Here we store the exclusion shapes and some other information's + Each list element is a dictionary with the format: + + { + "obj_type": string ("excellon" or "geometry") <- self.obj_type + "shape": Shapely polygon + "strategy": string ("over" or "around") <- self.strategy_button + "overz": float <- self.over_z_button + } + ''' + self.exclusion_areas_storage = [] + + self.mouse_is_dragging = False + + self.solid_geometry = [] + self.obj_type = None + + self.shape_type_button = None + self.over_z_button = None + self.strategy_button = None + self.cnc_button = None + + def on_add_area_click(self, shape_button, overz_button, strategy_radio, cnc_button, solid_geo, obj_type): + """ + + :param shape_button: a FCButton that has the value for the shape + :param overz_button: a FCDoubleSpinner that holds the Over Z value + :param strategy_radio: a RadioSet button with the strategy_button value + :param cnc_button: a FCButton in Object UI that when clicked the CNCJob is created + We have a reference here so we can change the color signifying that exclusion areas are + available. + :param solid_geo: reference to the object solid geometry for which we add exclusion areas + :param obj_type: Type of FlatCAM object that called this method. String: "excellon" or "geometry" + :type obj_type: str + :return: None + """ + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area.")) + self.app.call_source = 'geometry' + + self.shape_type_button = shape_button + + # TODO use the self.app.defaults when made general (not in Geo object Pref UI) + # self.shape_type_button.set_value('square') + + self.over_z_button = overz_button + self.strategy_button = strategy_radio + self.cnc_button = cnc_button + + self.solid_geometry = solid_geo + self.obj_type = obj_type + + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot) + self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot) + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot) + else: + self.app.plotcanvas.graph_event_disconnect(self.app.mp) + self.app.plotcanvas.graph_event_disconnect(self.app.mm) + self.app.plotcanvas.graph_event_disconnect(self.app.mr) + + self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release) + self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move) + # self.kp = self.app.plotcanvas.graph_event_connect('key_press', self.on_key_press) + + # To be called after clicking on the plot. + def on_mouse_release(self, event): + """ + Called on mouse click release. + + :param event: Mouse event + :type event: + :return: None + :rtype: + """ + if self.app.is_legacy is False: + event_pos = event.pos + # event_is_dragging = event.is_dragging + right_button = 2 + else: + event_pos = (event.xdata, event.ydata) + # event_is_dragging = self.app.plotcanvas.is_dragging + right_button = 3 + + event_pos = self.app.plotcanvas.translate_coords(event_pos) + if self.app.grid_status(): + curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1]) + else: + curr_pos = (event_pos[0], event_pos[1]) + + x1, y1 = curr_pos[0], curr_pos[1] + + # shape_type_button = self.ui.area_shape_radio.get_value() + + # do clear area only for left mouse clicks + if event.button == 1: + if self.shape_type_button.get_value() == "square": + if self.first_click is False: + self.first_click = True + self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area.")) + + self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos) + if self.app.grid_status(): + self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1]) + else: + self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish.")) + self.app.delete_selection_shape() + + x0, y0 = self.cursor_pos[0], self.cursor_pos[1] + + pt1 = (x0, y0) + pt2 = (x1, y0) + pt3 = (x1, y1) + pt4 = (x0, y1) + + new_rectangle = Polygon([pt1, pt2, pt3, pt4]) + + # { + # "obj_type": string("excellon" or "geometry") < - self.obj_type + # "shape": Shapely polygon + # "strategy_button": string("over" or "around") < - self.strategy_button + # "overz": float < - self.over_z_button + # } + new_el = { + "obj_type": self.obj_type, + "shape": new_rectangle, + "strategy": self.strategy_button.get_value(), + "overz": self.over_z_button.get_value() + } + self.exclusion_areas_storage.append(new_el) + + if self.obj_type == 'excellon': + color = "#FF7400" + face_color = "#FF7400BF" + else: + color = "#098a8f" + face_color = "#FF7400BF" + + # add a temporary shape on canvas + AppTool.draw_tool_selection_shape( + self, old_coords=(x0, y0), coords=(x1, y1), + color=color, + face_color=face_color, + shapes_storage=self.exclusion_shapes) + + self.first_click = False + return + else: + self.points.append((x1, y1)) + + if len(self.points) > 1: + self.poly_drawn = True + self.app.inform.emit(_("Click on next Point or click right mouse button to complete ...")) + + return "" + elif event.button == right_button and self.mouse_is_dragging is False: + + shape_type = self.shape_type_button.get_value() + + if shape_type == "square": + self.first_click = False + else: + # if we finish to add a polygon + if self.poly_drawn is True: + try: + # try to add the point where we last clicked if it is not already in the self.points + last_pt = (x1, y1) + if last_pt != self.points[-1]: + self.points.append(last_pt) + except IndexError: + pass + + # we need to add a Polygon and a Polygon can be made only from at least 3 points + if len(self.points) > 2: + AppTool.delete_moving_selection_shape(self) + pol = Polygon(self.points) + # do not add invalid polygons even if they are drawn by utility geometry + if pol.is_valid: + """ + { + "obj_type": string("excellon" or "geometry") < - self.obj_type + "shape": Shapely polygon + "strategy": string("over" or "around") < - self.strategy_button + "overz": float < - self.over_z_button + } + """ + new_el = { + "obj_type": self.obj_type, + "shape": pol, + "strategy": self.strategy_button.get_value(), + "overz": self.over_z_button.get_value() + } + self.exclusion_areas_storage.append(new_el) + + if self.obj_type == 'excellon': + color = "#FF7400" + face_color = "#FF7400BF" + else: + color = "#098a8f" + face_color = "#FF7400BF" + + AppTool.draw_selection_shape_polygon( + self, points=self.points, + color=color, + face_color=face_color, + shapes_storage=self.exclusion_shapes) + self.app.inform.emit( + _("Zone added. Click to start adding next zone or right click to finish.")) + + self.points = [] + self.poly_drawn = False + return + + # AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes) + + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) + self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) + # self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press) + else: + self.app.plotcanvas.graph_event_disconnect(self.mr) + self.app.plotcanvas.graph_event_disconnect(self.mm) + # self.app.plotcanvas.graph_event_disconnect(self.kp) + + self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', + self.app.on_mouse_click_over_plot) + self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move', + self.app.on_mouse_move_over_plot) + self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', + self.app.on_mouse_click_release_over_plot) + + self.app.call_source = 'app' + + if len(self.exclusion_areas_storage) == 0: + return + + # since the exclusion areas should apply to all objects in the app collection, this check is limited to + # only the current object therefore it will not guarantee success + self.app.inform.emit("%s" % _("Exclusion areas added. Checking overlap with the object geometry ...")) + for el in self.exclusion_areas_storage: + if el["shape"].intersects(unary_union(self.solid_geometry)): + self.on_clear_area_click() + self.app.inform.emit( + "[ERROR_NOTCL] %s" % _("Failed. Exclusion areas intersects the object geometry ...")) + return + + self.app.inform.emit( + "[success] %s" % _("Exclusion areas added.")) + self.cnc_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + color: orange; + } + """) + self.cnc_button.setToolTip( + '%s %s' % (_("Generate the CNC Job object."), _("With Exclusion areas.")) + ) + + self.e_shape_modified.emit() + + def area_disconnect(self): + """ + Will do the cleanup. Will disconnect the mouse events for the custom handlers in this class and initialize + certain class attributes. + + :return: None + :rtype: + """ + if self.app.is_legacy is False: + self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) + self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) + else: + self.app.plotcanvas.graph_event_disconnect(self.mr) + self.app.plotcanvas.graph_event_disconnect(self.mm) + self.app.plotcanvas.graph_event_disconnect(self.kp) + + self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', + self.app.on_mouse_click_over_plot) + self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move', + self.app.on_mouse_move_over_plot) + self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', + self.app.on_mouse_click_release_over_plot) + self.points = [] + self.poly_drawn = False + self.exclusion_areas_storage = [] + + AppTool.delete_moving_selection_shape(self) + # AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes) + + self.app.call_source = "app" + self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted.")) + + def on_mouse_move(self, event): + """ + Called on mouse move + + :param event: mouse event + :type event: + :return: None + :rtype: + """ + shape_type = self.shape_type_button.get_value() + + if self.app.is_legacy is False: + event_pos = event.pos + event_is_dragging = event.is_dragging + # right_button = 2 + else: + event_pos = (event.xdata, event.ydata) + event_is_dragging = self.app.plotcanvas.is_dragging + # right_button = 3 + + curr_pos = self.app.plotcanvas.translate_coords(event_pos) + + # detect mouse dragging motion + if event_is_dragging is True: + self.mouse_is_dragging = True + else: + self.mouse_is_dragging = False + + # update the cursor position + if self.app.grid_status(): + # Update cursor + curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1]) + + self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]), + symbol='++', edge_color=self.app.cursor_color_3D, + edge_width=self.app.defaults["global_cursor_width"], + size=self.app.defaults["global_cursor_size"]) + + # update the positions on status bar + if self.cursor_pos is None: + self.cursor_pos = (0, 0) + + self.app.dx = curr_pos[0] - float(self.cursor_pos[0]) + self.app.dy = curr_pos[1] - float(self.cursor_pos[1]) + self.app.ui.position_label.setText(" X: %.4f   " + "Y: %.4f " % (curr_pos[0], curr_pos[1])) + # self.app.ui.rel_position_label.setText("Dx: %.4f   Dy: " + # "%.4f    " % (self.app.dx, self.app.dy)) + + units = self.app.defaults["units"].lower() + self.app.plotcanvas.text_hud.text = \ + 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( + self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) + + if self.obj_type == 'excellon': + color = "#FF7400" + face_color = "#FF7400BF" + else: + color = "#098a8f" + face_color = "#FF7400BF" + + # draw the utility geometry + if shape_type == "square": + if self.first_click: + self.app.delete_selection_shape() + + self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]), + color=color, + face_color=face_color, + coords=(curr_pos[0], curr_pos[1])) + else: + AppTool.delete_moving_selection_shape(self) + AppTool.draw_moving_selection_shape_poly( + self, points=self.points, + color=color, + face_color=face_color, + data=(curr_pos[0], curr_pos[1])) + + def on_clear_area_click(self): + """ + Slot for clicking the button for Deleting all the Exclusion areas. + + :return: None + :rtype: + """ + self.clear_shapes() + + # restore the default StyleSheet + self.cnc_button.setStyleSheet("") + # update the StyleSheet + self.cnc_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object.")) + + def clear_shapes(self): + """ + Will delete all the Exclusion areas; will delete on canvas any possible selection box for the Exclusion areas. + + :return: None + :rtype: + """ + self.exclusion_areas_storage.clear() + AppTool.delete_moving_selection_shape(self) + self.app.delete_selection_shape() + AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes) + self.app.inform.emit('%s' % _("All exclusion zones deleted.")) + + def delete_sel_shapes(self, idxs): + """ + + :param idxs: list of indexes in self.exclusion_areas_storage list to be deleted + :type idxs: list + :return: None + """ + + # delete all plotted shapes + AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes) + + # delete shapes + for idx in sorted(idxs, reverse=True): + del self.exclusion_areas_storage[idx] + + # re-add what's left after deletion in first step + if self.obj_type == 'excellon': + color = "#FF7400" + face_color = "#FF7400BF" + else: + color = "#098a8f" + face_color = "#FF7400BF" + + face_alpha = 0.3 + color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:] + + for geo_el in self.exclusion_areas_storage: + if isinstance(geo_el['shape'], Polygon): + self.exclusion_shapes.add( + geo_el['shape'], color=color, face_color=color_t, update=True, layer=0, tolerance=None) + if self.app.is_legacy is True: + self.exclusion_shapes.redraw() + + if self.exclusion_areas_storage: + self.app.inform.emit('[success] %s' % _("Selected exclusion zones deleted.")) + else: + # restore the default StyleSheet + self.cnc_button.setStyleSheet("") + # update the StyleSheet + self.cnc_button.setStyleSheet(""" + QPushButton + { + font-weight: bold; + } + """) + self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object.")) + + self.app.inform.emit('%s' % _("All exclusion zones deleted.")) + + def travel_coordinates(self, start_point, end_point, tooldia): + """ + WIll create a path the go around the exclusion areas on the shortest path when travelling (at a Z above the + material). + + :param start_point: X,Y coordinates for the start point of the travel line + :type start_point: tuple + :param end_point: X,Y coordinates for the destination point of the travel line + :type end_point: tuple + :param tooldia: THe tool diameter used and which generates the travel lines + :type tooldia float + :return: A list of x,y tuples that describe the avoiding path + :rtype: list + """ + + ret_list = [] + + # Travel lines: rapids. Should not pass through Exclusion areas + travel_line = LineString([start_point, end_point]) + origin_point = Point(start_point) + + buffered_storage = [] + # add a little something to the half diameter, to make sure that we really don't enter in the exclusion zones + buffered_distance = (tooldia / 2.0) + (0.1 if self.app.defaults['units'] == 'MM' else 0.00393701) + + for area in self.exclusion_areas_storage: + new_area = deepcopy(area) + new_area['shape'] = area['shape'].buffer(buffered_distance, join_style=2) + buffered_storage.append(new_area) + + # sort the Exclusion areas from the closest to the start_point to the farthest + tmp = [] + for area in buffered_storage: + dist = Point(start_point).distance(area['shape']) + tmp.append((dist, area)) + tmp.sort(key=lambda k: k[0]) + + sorted_area_storage = [k[1] for k in tmp] + + # process the ordered exclusion areas list + for area in sorted_area_storage: + outline = area['shape'].exterior + if travel_line.intersects(outline): + intersection_pts = travel_line.intersection(outline) + + if isinstance(intersection_pts, Point): + # it's just a touch, continue + continue + + entry_pt = nearest_point(origin_point, intersection_pts) + exit_pt = farthest_point(origin_point, intersection_pts) + + if area['strategy'] == 'around': + full_vertex_points = [Point(x) for x in list(outline.coords)] + + # the last coordinate in outline, a LinearRing, is the closing one + # therefore a duplicate of the first one; discard it + vertex_points = full_vertex_points[:-1] + + # dist_from_entry = [(entry_pt.distance(vt), vertex_points.index(vt)) for vt in vertex_points] + # closest_point_entry = nsmallest(1, dist_from_entry, key=lambda x: x[0]) + # start_idx = closest_point_entry[0][1] + # + # dist_from_exit = [(exit_pt.distance(vt), vertex_points.index(vt)) for vt in vertex_points] + # closest_point_exit = nsmallest(1, dist_from_exit, key=lambda x: x[0]) + # end_idx = closest_point_exit[0][1] + + # pts_line_entry = None + # pts_line_exit = None + # for i in range(len(full_vertex_points)): + # try: + # line = LineString( + # [ + # (full_vertex_points[i].x, full_vertex_points[i].y), + # (full_vertex_points[i + 1].x, full_vertex_points[i + 1].y) + # ] + # ) + # except IndexError: + # continue + # + # if entry_pt.within(line) or entry_pt.equals(Point(line.coords[0])) or \ + # entry_pt.equals(Point(line.coords[1])): + # pts_line_entry = [Point(x) for x in line.coords] + # + # if exit_pt.within(line) or exit_pt.equals(Point(line.coords[0])) or \ + # exit_pt.equals(Point(line.coords[1])): + # pts_line_exit = [Point(x) for x in line.coords] + # + # closest_point_entry = nearest_point(entry_pt, pts_line_entry) + # start_idx = vertex_points.index(closest_point_entry) + # + # closest_point_exit = nearest_point(exit_pt, pts_line_exit) + # end_idx = vertex_points.index(closest_point_exit) + + # find all vertexes for which a line from start_point does not cross the Exclusion area polygon + # the same for end_point + # we don't need closest points for which the path leads to crosses of the Exclusion area + + close_start_points = [] + close_end_points = [] + for i in range(len(vertex_points)): + try: + start_line = LineString( + [ + start_point, + (vertex_points[i].x, vertex_points[i].y) + ] + ) + end_line = LineString( + [ + end_point, + (vertex_points[i].x, vertex_points[i].y) + ] + ) + except IndexError: + continue + + if not start_line.crosses(area['shape']): + close_start_points.append(vertex_points[i]) + if not end_line.crosses(area['shape']): + close_end_points.append(vertex_points[i]) + + closest_point_entry = nearest_point(entry_pt, close_start_points) + closest_point_exit = nearest_point(exit_pt, close_end_points) + + start_idx = vertex_points.index(closest_point_entry) + end_idx = vertex_points.index(closest_point_exit) + + # calculate possible paths: one clockwise the other counterclockwise on the exterior of the + # exclusion area outline (Polygon.exterior) + vp_len = len(vertex_points) + if end_idx > start_idx: + path_1 = vertex_points[start_idx:(end_idx + 1)] + path_2 = [vertex_points[start_idx]] + idx = start_idx + for __ in range(vp_len): + idx = idx - 1 if idx > 0 else (vp_len - 1) + path_2.append(vertex_points[idx]) + if idx == end_idx: + break + else: + path_1 = vertex_points[end_idx:(start_idx + 1)] + path_2 = [vertex_points[end_idx]] + idx = end_idx + for __ in range(vp_len): + idx = idx - 1 if idx > 0 else (vp_len - 1) + path_2.append(vertex_points[idx]) + if idx == start_idx: + break + path_1.reverse() + path_2.reverse() + + # choose the one with the lesser length + length_path_1 = 0 + for i in range(len(path_1)): + try: + length_path_1 += path_1[i].distance(path_1[i + 1]) + except IndexError: + pass + + length_path_2 = 0 + for i in range(len(path_2)): + try: + length_path_2 += path_2[i].distance(path_2[i + 1]) + except IndexError: + pass + + path = path_1 if length_path_1 < length_path_2 else path_2 + + # transform the list of Points into a list of Points coordinates + path_coords = [[None, (p.x, p.y)] for p in path] + ret_list += path_coords + + else: + path_coords = [[float(area['overz']), (entry_pt.x, entry_pt.y)], [None, (exit_pt.x, exit_pt.y)]] + ret_list += path_coords + + # create a new LineString to test again for possible other Exclusion zones + last_pt_in_path = path_coords[-1][1] + travel_line = LineString([last_pt_in_path, end_point]) + + ret_list.append([None, end_point]) + return ret_list + + +def farthest_point(origin, points_list): + """ + Calculate the farthest Point in a list from another Point + + :param origin: Reference Point + :type origin: Point + :param points_list: List of Points or a MultiPoint + :type points_list: list + :return: Farthest Point + :rtype: Point + """ + old_dist = 0 + fartherst_pt = None + + for pt in points_list: + dist = abs(origin.distance(pt)) + if dist >= old_dist: + fartherst_pt = pt + old_dist = dist + + return fartherst_pt + + +def nearest_point(origin, points_list): + """ + Calculate the nearest Point in a list from another Point + + :param origin: Reference Point + :type origin: Point + :param points_list: List of Points or a MultiPoint + :type points_list: list + :return: Nearest Point + :rtype: Point + """ + old_dist = np.Inf + nearest_pt = None + + for pt in points_list: + dist = abs(origin.distance(pt)) + if dist <= old_dist: + nearest_pt = pt + old_dist = dist + + return nearest_pt diff --git a/FlatCAM.py b/FlatCAM.py index 3875f0f3..17b56c29 100644 --- a/FlatCAM.py +++ b/FlatCAM.py @@ -3,8 +3,8 @@ import os from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings, Qt -from FlatCAMApp import App -from flatcamGUI import VisPyPatches +from App_Main import App +from AppGUI import VisPyPatches from multiprocessing import freeze_support # import copyreg diff --git a/FlatCAMCommon.py b/FlatCAMCommon.py deleted file mode 100644 index 952ed0cd..00000000 --- a/FlatCAMCommon.py +++ /dev/null @@ -1,121 +0,0 @@ -# ########################################################## -# FlatCAM: 2D Post-processing for Manufacturing # -# http://flatcam.org # -# Author: Juan Pablo Caram (c) # -# Date: 2/5/2014 # -# MIT Licence # -# ########################################################## - -# ########################################################## -# File Modified (major mod): Marius Adrian Stanciu # -# Date: 11/4/2019 # -# ########################################################## - -import gettext -import FlatCAMTranslation as fcTranslate -import builtins - -fcTranslate.apply_language('strings') -if '_' not in builtins.__dict__: - _ = gettext.gettext - - -class GracefulException(Exception): - # Graceful Exception raised when the user is requesting to cancel the current threaded task - def __init__(self): - super().__init__() - - def __str__(self): - return '\n\n%s' % _("The user requested a graceful exit of the current task.") - - -class LoudDict(dict): - """ - A Dictionary with a callback for item changes. - """ - - def __init__(self, *args, **kwargs): - dict.__init__(self, *args, **kwargs) - self.callback = lambda x: None - - def __setitem__(self, key, value): - """ - Overridden __setitem__ method. Will emit 'changed(QString)' if the item was changed, with key as parameter. - """ - if key in self and self.__getitem__(key) == value: - return - - dict.__setitem__(self, key, value) - self.callback(key) - - def update(self, *args, **kwargs): - if len(args) > 1: - raise TypeError("update expected at most 1 arguments, got %d" % len(args)) - other = dict(*args, **kwargs) - for key in other: - self[key] = other[key] - - def set_change_callback(self, callback): - """ - Assigns a function as callback on item change. The callback - will receive the key of the object that was changed. - - :param callback: Function to call on item change. - :type callback: func - :return: None - """ - - self.callback = callback - - -class FCSignal: - """ - Taken from here: https://blog.abstractfactory.io/dynamic-signals-in-pyqt/ - """ - - def __init__(self): - self.__subscribers = [] - - def emit(self, *args, **kwargs): - for subs in self.__subscribers: - subs(*args, **kwargs) - - def connect(self, func): - self.__subscribers.append(func) - - def disconnect(self, func): - try: - self.__subscribers.remove(func) - except ValueError: - print('Warning: function %s not removed ' - 'from signal %s' % (func, self)) - - -def color_variant(hex_color, bright_factor=1): - """ - Takes a color in HEX format #FF00FF and produces a lighter or darker variant - - :param hex_color: color to change - :param bright_factor: factor to change the color brightness [0 ... 1] - :return: modified color - """ - - if len(hex_color) != 7: - print("Color is %s, but needs to be in #FF00FF format. Returning original color." % hex_color) - return hex_color - - if bright_factor > 1.0: - bright_factor = 1.0 - if bright_factor < 0.0: - bright_factor = 0.0 - - rgb_hex = [hex_color[x:x + 2] for x in [1, 3, 5]] - new_rgb = [] - for hex_value in rgb_hex: - # adjust each color channel and turn it into a INT suitable as argument for hex() - mod_color = round(int(hex_value, 16) * bright_factor) - # make sure that each color channel has two digits without the 0x prefix - mod_color_hex = str(hex(mod_color)[2:]).zfill(2) - new_rgb.append(mod_color_hex) - - return "#" + "".join([i for i in new_rgb]) diff --git a/Utils/vispy_example.py b/Utils/vispy_example.py new file mode 100644 index 00000000..980d2b10 --- /dev/null +++ b/Utils/vispy_example.py @@ -0,0 +1,195 @@ +from PyQt5.QtGui import QPalette +from PyQt5 import QtCore, QtWidgets + +import vispy.scene as scene +from vispy.scene.visuals import Rectangle, Text +from vispy.color import Color + +import sys + + +class VisPyCanvas(scene.SceneCanvas): + + def __init__(self, config=None): + super().__init__(config=config, keys=None) + + self.unfreeze() + + # Colors used by the Scene + theme_color = Color('#FFFFFF') + tick_color = Color('#000000') + back_color = str(QPalette().color(QPalette.Window).name()) + + # Central Widget Colors + self.central_widget.bgcolor = back_color + self.central_widget.border_color = back_color + + self.grid_widget = self.central_widget.add_grid(margin=10) + self.grid_widget.spacing = 0 + + # TOP Padding + top_padding = self.grid_widget.add_widget(row=0, col=0, col_span=2) + top_padding.height_max = 0 + + # RIGHT Padding + right_padding = self.grid_widget.add_widget(row=0, col=2, row_span=2) + right_padding.width_max = 0 + + # X Axis + self.xaxis = scene.AxisWidget( + orientation='bottom', axis_color=tick_color, text_color=tick_color, + font_size=8, axis_width=1, + anchors=['center', 'bottom'] + ) + self.xaxis.height_max = 30 + self.grid_widget.add_widget(self.xaxis, row=2, col=1) + + # Y Axis + self.yaxis = scene.AxisWidget( + orientation='left', axis_color=tick_color, text_color=tick_color, + font_size=8, axis_width=1 + ) + self.yaxis.width_max = 55 + self.grid_widget.add_widget(self.yaxis, row=1, col=0) + + # View & Camera + self.view = self.grid_widget.add_view(row=1, col=1, border_color=tick_color, + bgcolor=theme_color) + self.view.camera = scene.PanZoomCamera(aspect=1, rect=(-25, -25, 150, 150)) + + self.xaxis.link_view(self.view) + self.yaxis.link_view(self.view) + + self.grid = scene.GridLines(parent=self.view.scene, color='dimgray') + self.grid.set_gl_state(depth_test=False) + + self.rect = Rectangle(center=(65,30), color=Color('#0000FF10'), border_color=Color('#0000FF10'), + width=120, height=50, radius=[5, 5, 5, 5], parent=self.view) + self.rect.set_gl_state(depth_test=False) + + self.text = Text('', parent=self.view, color='black', pos=(5, 30), method='gpu', anchor_x='left') + self.text.font_size = 8 + self.text.text = 'Coordinates:\nX: %s\nY: %s' % ('0.0000', '0.0000') + + self.freeze() + + # self.measure_fps() + + +class PlotCanvas(QtCore.QObject): + + def __init__(self, container, my_app): + """ + The constructor configures the VisPy figure that + will contain all plots, creates the base axes and connects + events to the plotting area. + + :param container: The parent container in which to draw plots. + :rtype: PlotCanvas + """ + + super().__init__() + + # VisPyCanvas instance + self.vispy_canvas = VisPyCanvas() + + self.vispy_canvas.unfreeze() + + self.my_app = my_app + + # Parent container + self.container = container + + # + self.vispy_canvas.create_native() + self.vispy_canvas.native.setParent(self.my_app.ui) + + # + self.container.addWidget(self.vispy_canvas.native) + + # add two Infinite Lines to act as markers for the X,Y axis + self.v_line = scene.visuals.InfiniteLine( + pos=0, color=(0.0, 0.0, 1.0, 0.3), vertical=True, + parent=self.vispy_canvas.view.scene) + + self.h_line = scene.visuals.InfiniteLine( + pos=0, color=(0.00, 0.0, 1.0, 0.3), vertical=False, + parent=self.vispy_canvas.view.scene) + + self.vispy_canvas.freeze() + + def event_connect(self, event, callback): + getattr(self.vispy_canvas.events, event).connect(callback) + + def event_disconnect(self, event, callback): + getattr(self.vispy_canvas.events, event).disconnect(callback) + + def translate_coords(self, pos): + """ + Translate pixels to canvas units. + """ + tr = self.vispy_canvas.grid.get_transform('canvas', 'visual') + return tr.map(pos) + + +class MyGui(QtWidgets.QMainWindow): + + def __init__(self): + super().__init__() + + self.setWindowTitle("VisPy Test") + + # add Menubar + self.menu = self.menuBar() + self.menufile = self.menu.addMenu("File") + self.menuedit = self.menu.addMenu("Edit") + self.menufhelp = self.menu.addMenu("Help") + + # add a Toolbar + self.file_toolbar = QtWidgets.QToolBar("File Toolbar") + self.addToolBar(self.file_toolbar) + self.button = self.file_toolbar.addAction("Open") + + # add Central Widget + self.c_widget = QtWidgets.QWidget() + self.central_layout = QtWidgets.QVBoxLayout() + self.c_widget.setLayout(self.central_layout) + self.setCentralWidget(self.c_widget) + + # add InfoBar + # self.infobar = self.statusBar() + # self.position_label = QtWidgets.QLabel("Position: X: 0.0000\tY: 0.0000") + # self.infobar.addWidget(self.position_label) + + +class MyApp(QtCore.QObject): + + def __init__(self): + super().__init__() + + self.ui = MyGui() + self.plot = PlotCanvas(container=self.ui.central_layout, my_app=self) + + self.ui.show() + + self.plot.event_connect(event="mouse_move", callback=self.on_mouse_move) + + def on_mouse_move(self, event): + cursor_pos = event.pos + + pos_canvas = self.plot.translate_coords(cursor_pos) + + # we don't need all the info in the tuple returned by the translate_coords() + # only first 2 elements + pos_canvas = [pos_canvas[0], pos_canvas[1]] + self.ui.position_label.setText("Position: X: %.4f\tY: %.4f" % (pos_canvas[0], pos_canvas[1])) + # pos_text = 'Coordinates: \nX: {:<7.4f}\nY: {:<7.4f}'.format(pos_canvas[0], pos_canvas[1]) + pos_text = 'Coordinates: \nX: {:<.4f}\nY: {:<.4f}'.format(pos_canvas[0], pos_canvas[1]) + self.plot.vispy_canvas.text.text = pos_text + + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + + m_app = MyApp() + sys.exit(app.exec_()) diff --git a/assets/linux/flatcam-beta.desktop b/assets/linux/flatcam-beta.desktop old mode 100644 new mode 100755 diff --git a/assets/resources/axis16.png b/assets/resources/axis16.png new file mode 100644 index 00000000..56aef433 Binary files /dev/null and b/assets/resources/axis16.png differ diff --git a/assets/resources/contribute256.png b/assets/resources/contribute256.png new file mode 100644 index 00000000..dd1bc479 Binary files /dev/null and b/assets/resources/contribute256.png differ diff --git a/assets/resources/corners_32.png b/assets/resources/corners_32.png new file mode 100644 index 00000000..b9f87261 Binary files /dev/null and b/assets/resources/corners_32.png differ diff --git a/assets/resources/dark_resources/contribute256.png b/assets/resources/dark_resources/contribute256.png new file mode 100644 index 00000000..ab1739dd Binary files /dev/null and b/assets/resources/dark_resources/contribute256.png differ diff --git a/assets/resources/dark_resources/corners_32.png b/assets/resources/dark_resources/corners_32.png new file mode 100644 index 00000000..c5ad0f00 Binary files /dev/null and b/assets/resources/dark_resources/corners_32.png differ diff --git a/assets/resources/dark_resources/etch_32.png b/assets/resources/dark_resources/etch_32.png new file mode 100644 index 00000000..2f06671b Binary files /dev/null and b/assets/resources/dark_resources/etch_32.png differ diff --git a/assets/resources/dark_resources/iso_16.png b/assets/resources/dark_resources/iso_16.png new file mode 100644 index 00000000..6d0ee71f Binary files /dev/null and b/assets/resources/dark_resources/iso_16.png differ diff --git a/assets/resources/dark_resources/panelize32.png b/assets/resources/dark_resources/panelize32.png index aa3479b2..ff5ff124 100644 Binary files a/assets/resources/dark_resources/panelize32.png and b/assets/resources/dark_resources/panelize32.png differ diff --git a/assets/resources/etch_32.png b/assets/resources/etch_32.png new file mode 100644 index 00000000..dd70dd74 Binary files /dev/null and b/assets/resources/etch_32.png differ diff --git a/assets/resources/grid32.png b/assets/resources/grid32.png index 9cb197bf..b2d7ca1b 100644 Binary files a/assets/resources/grid32.png and b/assets/resources/grid32.png differ diff --git a/assets/resources/grid_lines32.png b/assets/resources/grid_lines32.png new file mode 100644 index 00000000..d2be39cd Binary files /dev/null and b/assets/resources/grid_lines32.png differ diff --git a/assets/resources/hud_32.png b/assets/resources/hud_32.png new file mode 100644 index 00000000..5ea627f7 Binary files /dev/null and b/assets/resources/hud_32.png differ diff --git a/assets/resources/iso_16.png b/assets/resources/iso_16.png new file mode 100644 index 00000000..48db1e65 Binary files /dev/null and b/assets/resources/iso_16.png differ diff --git a/assets/resources/panelize32.png b/assets/resources/panelize32.png index ea74ef36..5445781c 100644 Binary files a/assets/resources/panelize32.png and b/assets/resources/panelize32.png differ diff --git a/assets/resources/shell20.png b/assets/resources/shell20.png new file mode 100644 index 00000000..299cabf4 Binary files /dev/null and b/assets/resources/shell20.png differ diff --git a/camlib.py b/camlib.py index f4dff415..2f134b84 100644 --- a/camlib.py +++ b/camlib.py @@ -39,21 +39,20 @@ from shapely.geometry import shape from descartes.patch import PolygonPatch # --------------------------------------- -import collections from collections import Iterable import rasterio from rasterio.features import shapes import ezdxf -from FlatCAMCommon import GracefulException as grace +from Common import GracefulException as grace -# TODO: Commented for FlatCAM packaging with cx_freeze +# Commented for FlatCAM packaging with cx_freeze # from scipy.spatial import KDTree, Delaunay # from scipy.spatial import Delaunay -from flatcamParsers.ParseSVG import * -from flatcamParsers.ParseDXF import * +from AppParsers.ParseSVG import * +from AppParsers.ParseDXF import * if platform.architecture()[0] == '64bit': from ortools.constraint_solver import pywrapcp @@ -62,7 +61,7 @@ if platform.architecture()[0] == '64bit': import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') @@ -249,7 +248,7 @@ class ApertureMacro: pol, dia, x, y = ApertureMacro.default2zero(4, mods) - return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)} + return {"pol": int(pol), "geometry": Point(x, y).buffer(dia / 2)} @staticmethod def make_vectorline(mods): @@ -262,7 +261,7 @@ class ApertureMacro: pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods) line = LineString([(xs, ys), (xe, ye)]) - box = line.buffer(width/2, cap_style=2) + box = line.buffer(width / 2, cap_style=2) box_rotated = affinity.rotate(box, angle, origin=(0, 0)) return {"pol": int(pol), "geometry": box_rotated} @@ -278,7 +277,7 @@ class ApertureMacro: pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2) + box = shply_box(x - width / 2, y - height / 2, x + width / 2, y + height / 2) box_rotated = affinity.rotate(box, angle, origin=(0, 0)) return {"pol": int(pol), "geometry": box_rotated} @@ -294,7 +293,7 @@ class ApertureMacro: pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - box = shply_box(x, y, x+width, y+height) + box = shply_box(x, y, x + width, y + height) box_rotated = affinity.rotate(box, angle, origin=(0, 0)) return {"pol": int(pol), "geometry": box_rotated} @@ -309,12 +308,12 @@ class ApertureMacro: pol = mods[0] n = mods[1] - points = [(0, 0)]*(n+1) + points = [(0, 0)] * (n + 1) - for i in range(n+1): - points[i] = mods[2*i + 2:2*i + 4] + for i in range(n + 1): + points[i] = mods[2 * i + 2:2 * i + 4] - angle = mods[2*n + 4] + angle = mods[2 * n + 4] poly = Polygon(points) poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) @@ -333,11 +332,11 @@ class ApertureMacro: """ pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods) - points = [(0, 0)]*nverts + points = [(0, 0)] * nverts for i in range(nverts): - points[i] = (x + 0.5 * dia * np.cos(2*np.pi * i/nverts), - y + 0.5 * dia * np.sin(2*np.pi * i/nverts)) + points[i] = (x + 0.5 * dia * np.cos(2 * np.pi * i / nverts), + y + 0.5 * dia * np.sin(2 * np.pi * i / nverts)) poly = Polygon(points) poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) @@ -358,9 +357,9 @@ class ApertureMacro: x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods) - r = dia/2 - thickness/2 - result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy! + r = dia / 2 - thickness / 2 + result = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0) + ring = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0) # Need a copy! i = 1 # Number of rings created so far @@ -370,13 +369,13 @@ class ApertureMacro: r -= thickness + gap if r <= 0: break - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) + ring = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0) result = cascaded_union([result, ring]) i += 1 # ## Crosshair - hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2) - ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2) + hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th / 2.0, cap_style=2) + ver = LineString([(x, y - cross_len), (x, y + cross_len)]).buffer(cross_th / 2.0, cap_style=2) result = cascaded_union([result, hor, ver]) return {"pol": 1, "geometry": result} @@ -394,9 +393,9 @@ class ApertureMacro: x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods) - ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0)) - hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3) - vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3) + ring = Point((x, y)).buffer(dout / 2.0).difference(Point((x, y)).buffer(din / 2.0)) + hline = LineString([(x - dout / 2.0, y), (x + dout / 2.0, y)]).buffer(t / 2.0, cap_style=3) + vline = LineString([(x, y - dout / 2.0), (x, y + dout / 2.0)]).buffer(t / 2.0, cap_style=3) thermal = ring.difference(hline.union(vline)) return {"pol": 1, "geometry": thermal} @@ -502,7 +501,7 @@ class Geometry(object): if self.app.is_legacy is False: self.temp_shapes = self.app.plotcanvas.new_shape_group() else: - from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy + from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.temp_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='camlib.geometry') def plot_temp_shapes(self, element, color='red'): @@ -587,7 +586,8 @@ class Geometry(object): return def is_empty(self): - if isinstance(self.solid_geometry, BaseGeometry): + if isinstance(self.solid_geometry, BaseGeometry) or isinstance(self.solid_geometry, Polygon) or \ + isinstance(self.solid_geometry, MultiPolygon): return self.solid_geometry.is_empty if isinstance(self.solid_geometry, list): @@ -920,14 +920,16 @@ class Geometry(object): Creates contours around geometry at a given offset distance. - :param offset: Offset distance. - :type offset: float - :param iso_type: type of isolation, can be 0 = exteriors or 1 = interiors or 2 = both (complete) - :param corner: type of corner for the isolation: 0 = round; 1 = square; 2= beveled (line that connects the ends) - :param follow: whether the geometry to be isolated is a follow_geometry - :param passes: current pass out of possible multiple passes for which the isolation is done - :return: The buffered geometry. - :rtype: Shapely.MultiPolygon or Shapely.Polygon + :param offset: Offset distance. + :type offset: float + :param geometry The geometry to work with + :param iso_type: type of isolation, can be 0 = exteriors or 1 = interiors or 2 = both (complete) + :param corner: type of corner for the isolation: + 0 = round; 1 = square; 2= beveled (line that connects the ends) + :param follow: whether the geometry to be isolated is a follow_geometry + :param passes: current pass out of possible multiple passes for which the isolation is done + :return: The buffered geometry. + :rtype: Shapely.MultiPolygon or Shapely.Polygon """ if self.app.abort_flag: @@ -963,6 +965,8 @@ class Geometry(object): corner_type = 1 if corner is None else corner geo_iso.append(pol.buffer(offset, int(self.geo_steps_per_circle), join_style=corner_type)) pol_nr += 1 + + # activity view update disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) if old_disp_number < disp_number <= 100: @@ -1102,15 +1106,17 @@ class Geometry(object): """ Imports shapes from an IMAGE file into the object's geometry. - :param filename: Path to the IMAGE file. - :type filename: str - :param flip: Flip the object vertically. - :type flip: bool - :param units: FlatCAM units - :param dpi: dots per inch on the imported image - :param mode: how to import the image: as 'black' or 'color' - :param mask: level of detail for the import - :return: None + :param filename: Path to the IMAGE file. + :type filename: str + :param flip: Flip the object vertically. + :type flip: bool + :param units: FlatCAM units + :type units: str + :param dpi: dots per inch on the imported image + :param mode: how to import the image: as 'black' or 'color' + :type mode: str + :param mask: level of detail for the import + :return: None """ if mask is None: mask = [128, 128, 128, 128] @@ -1194,7 +1200,7 @@ class Geometry(object): return 0 bounds = self.bounds() return bounds[2] - bounds[0], bounds[3] - bounds[1] - + def get_empty_area(self, boundary=None): """ Returns the complement of self.solid_geometry within @@ -1886,6 +1892,7 @@ class Geometry(object): # ## Index first and last points in paths def get_pts(o): return [o.coords[0], o.coords[-1]] + # # storage = FlatCAMRTreeStorage() # storage.get_points = get_pts @@ -1982,10 +1989,10 @@ class Geometry(object): the geometry appropriately. This call ``scale()``. Don't call it again in descendents. - :param units: "IN" or "MM" - :type units: str - :return: Scaling factor resulting from unit change. - :rtype: float + :param obj_units: "IN" or "MM" + :type obj_units: str + :return: Scaling factor resulting from unit change. + :rtype: float """ if obj_units.upper() == self.units.upper(): @@ -2013,8 +2020,8 @@ class Geometry(object): Returns a representation of the object as a dictionary. Attributes to include are listed in ``self.ser_attrs``. - :return: A dictionary-encoded copy of the object. - :rtype: dict + :return: A dictionary-encoded copy of the object. + :rtype: dict """ d = {} for attr in self.ser_attrs: @@ -2030,9 +2037,9 @@ class Geometry(object): be present. Use only for deserializing saved objects. - :param d: Dictionary of attributes to set in the object. - :type d: dict - :return: None + :param d: Dictionary of attributes to set in the object. + :type d: dict + :return: None """ for attr in self.ser_attrs: setattr(self, attr, d[attr]) @@ -2432,7 +2439,7 @@ class CNCjob(Geometry): pp_geometry_name='default', pp_excellon_name='default', depthpercut=0.1, z_pdepth=-0.02, spindlespeed=None, spindledir='CW', dwell=True, dwelltime=1000, - toolchangez=0.787402, toolchange_xy=[0.0, 0.0], + toolchangez=0.787402, toolchange_xy='0.0,0.0', endz=2.0, endxy='', segx=None, segy=None, @@ -2441,7 +2448,8 @@ class CNCjob(Geometry): self.decimals = self.app.decimals # Used when parsing G-code arcs - self.steps_per_circle = int(self.app.defaults['cncjob_steps_per_circle']) + self.steps_per_circle = steps_per_circle if steps_per_circle is not None else \ + int(self.app.defaults['cncjob_steps_per_circle']) Geometry.__init__(self, geo_steps_per_circle=self.steps_per_circle) @@ -2547,9 +2555,23 @@ class CNCjob(Geometry): @property def postdata(self): + """ + This will return all the attributes of the class in the form of a dictionary + + :return: Class attributes + :rtype: dict + """ return self.__dict__ def convert_units(self, units): + """ + Will convert the parameters in the class that are relevant, from metric to imperial and reverse + + :param units: FlatCAM units + :type units: str + :return: conversion factor + :rtype: float + """ log.debug("camlib.CNCJob.convert_units()") factor = Geometry.convert_units(self, units) @@ -2570,6 +2592,17 @@ class CNCjob(Geometry): return self.doformat2(fun, **kwargs) + "\n" def doformat2(self, fun, **kwargs): + """ + This method will call one of the current preprocessor methods having as parameters all the attributes of + current class to which will add the kwargs parameters + + :param fun: One of the methods inside the preprocessor classes which get loaded here in the 'p' object + :type fun: class 'function' + :param kwargs: keyword args which will update attributes of the current class + :type kwargs: dict + :return: Gcode line + :rtype: str + """ attributes = AttrDict() attributes.update(self.postdata) attributes.update(kwargs) @@ -2581,6 +2614,16 @@ class CNCjob(Geometry): return '' def parse_custom_toolchange_code(self, data): + """ + Will parse a text and get a toolchange sequence in text format suitable to be included in a Gcode file. + The '%' symbol is used to surround class variables name and must be removed in the returned string. + After that, the class variables (attributes) are replaced with the current values. The result is returned. + + :param data: Toolchange sequence + :type data: str + :return: Processed toolchange sequence + :rtype: str + """ text = data match_list = self.re_toolchange_custom.findall(text) @@ -2612,6 +2655,13 @@ class CNCjob(Geometry): [2, 3], [2, 4], [3, 4], [3, 3], [3, 2], [3, 1], [3, 0], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4]] >>> optimized_travelling_salesman([[0,0],[10,0],[6,0]]) [[0, 0], [6, 0], [10, 0]] + + :param points: List of tuples with x, y coordinates + :type points: list + :param start: a tuple with a x,y coordinates of the start point + :type start: tuple + :return: List of points ordered in a optimized way + :rtype: list """ if start is None: @@ -2627,16 +2677,17 @@ class CNCjob(Geometry): def generate_from_excellon_by_tool(self, exobj, tools="all", use_ui=False): """ - Creates gcode for this object from an Excellon object + Creates Gcode for this object from an Excellon object for the specified tools. - :param exobj: Excellon object to process - :type exobj: Excellon - :param tools: Comma separated tool names - :type: tools: str - :param use_ui: Bool, if True the method will use parameters set in UI - :return: None - :rtype: None + :param exobj: Excellon object to process + :type exobj: Excellon + :param tools: Comma separated tool names + :type tools: str + :param use_ui: if True the method will use parameters set in UI + :type use_ui: bool + :return: None + :rtype: None """ # create a local copy of the exobj.drills so it can be used for creating drill CCode geometry @@ -2667,8 +2718,12 @@ class CNCjob(Geometry): if self.xy_toolchange == '': self.xy_toolchange = None else: - self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",") if self.xy_toolchange != ''] - if self.xy_toolchange and len(self.xy_toolchange) < 2: + self.xy_toolchange = re.sub('[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None + + if self.xy_toolchange and self.xy_toolchange != '': + self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")] + + if self.xy_toolchange and len(self.xy_toolchange) != 2: self.app.inform.emit('[ERROR]%s' % _("The Toolchange X,Y field in Edit -> Preferences has to be " "in the format (x, y) \nbut now there is only one value, not two. ")) @@ -2677,7 +2732,11 @@ class CNCjob(Geometry): log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> %s" % str(e)) pass - self.xy_end = [float(eval(a)) for a in self.xy_end.split(",") if self.xy_end != ''] + self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) if self.xy_end else None + + if self.xy_end and self.xy_end != '': + self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")] + if self.xy_end and len(self.xy_end) < 2: self.app.inform.emit('[ERROR] %s' % _("The End Move X,Y field in Edit -> Preferences has to be " "in the format (x, y) but now there is only one value, not two.")) @@ -2689,7 +2748,7 @@ class CNCjob(Geometry): log.debug("Creating CNC Job from Excellon...") # Tools - + # sort the tools list by the second item in tuple (here we have a dict with diameter of the tool) # so we actually are sorting the tools by diameter # sorted_tools = sorted(exobj.tools.items(), key=lambda t1: t1['C']) @@ -2700,7 +2759,7 @@ class CNCjob(Geometry): sorted_tools = sorted(sort, key=lambda t1: t1[1]) if tools == "all": - tools = [i[0] for i in sorted_tools] # we get a array of ordered tools + tools = [i[0] for i in sorted_tools] # we get a array of ordered tools log.debug("Tools 'all' and sorted are: %s" % str(tools)) else: selected_tools = [x.strip() for x in tools.split(",")] # we strip spaces and also separate the tools by ',' @@ -2769,7 +2828,7 @@ class CNCjob(Geometry): self.app.inform.emit(_("Creating a list of points to drill...")) - # Points (Group by tool) + # Points (Group by tool): a dictionary of shapely Point geo elements grouped by tool number points = {} for drill in exobj.drills: if self.app.abort_flag: @@ -2784,6 +2843,17 @@ class CNCjob(Geometry): # log.debug("Found %d drills." % len(points)) + # check if there are drill points in the exclusion areas. + # If we find any within the exclusion areas return 'fail' + for tool in points: + for pt in points[tool]: + for area in self.app.exc_areas.exclusion_areas_storage: + pt_buf = pt.buffer(exobj.tools[tool]['C'] / 2.0) + if pt_buf.within(area['shape']) or pt_buf.intersects(area['shape']): + self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed. Drill points inside the exclusion zones.")) + return 'fail' + + # this holds the resulting GCode self.gcode = [] self.f_plunge = self.app.defaults["excellon_f_plunge"] @@ -3004,7 +3074,7 @@ class CNCjob(Geometry): ) # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # APPLY Offset only when using the GUI, for TclCommand this will create an error + # APPLY Offset only when using the AppGUI, for TclCommand this will create an error # because the values for Z offset are created in build_ui() # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! try: @@ -3031,7 +3101,41 @@ class CNCjob(Geometry): locx = locations[k][0] locy = locations[k][1] - gcode += self.doformat(p.rapid_code, x=locx, y=locy) + travels = self.app.exc_areas.travel_coordinates(start_point=(self.oldx, self.oldy), + end_point=(locx, locy), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = exobj.tools[tool]['data']['travelz'] + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = exobj.tools[tool]['data']['travelz'] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.rapid_code, x=locx, y=locy) if self.multidepth and abs(self.z_cut) > abs(self.z_depthpercut): doc = deepcopy(self.z_cut) @@ -3101,7 +3205,7 @@ class CNCjob(Geometry): raise grace self.tool = tool - self.postdata['toolC']=exobj.tools[tool]["C"] + self.postdata['toolC'] = exobj.tools[tool]["C"] self.tooldia = exobj.tools[tool]["C"] if self.use_ui: @@ -3223,7 +3327,7 @@ class CNCjob(Geometry): ) # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # APPLY Offset only when using the GUI, for TclCommand this will create an error + # APPLY Offset only when using the AppGUI, for TclCommand this will create an error # because the values for Z offset are created in build_ui() # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! try: @@ -3249,7 +3353,41 @@ class CNCjob(Geometry): locx = locations[k][0] locy = locations[k][1] - gcode += self.doformat(p.rapid_code, x=locx, y=locy) + travels = self.app.exc_areas.travel_coordinates(start_point=(self.oldx, self.oldy), + end_point=(locx, locy), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = exobj.tools[tool]['data']['travelz'] + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = exobj.tools[tool]['data']['travelz'] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.rapid_code, x=locx, y=locy) if self.multidepth and abs(self.z_cut) > abs(self.z_depthpercut): doc = deepcopy(self.z_cut) @@ -3387,7 +3525,7 @@ class CNCjob(Geometry): ) # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - # APPLY Offset only when using the GUI, for TclCommand this will create an error + # APPLY Offset only when using the AppGUI, for TclCommand this will create an error # because the values for Z offset are created in build_ui() # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! try: @@ -3418,7 +3556,41 @@ class CNCjob(Geometry): locx = point[0] locy = point[1] - gcode += self.doformat(p.rapid_code, x=locx, y=locy) + travels = self.app.exc_areas.travel_coordinates(start_point=(self.oldx, self.oldy), + end_point=(locx, locy), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = exobj.tools[tool]['data']['travelz'] + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = exobj.tools[tool]['data']['travelz'] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.rapid_code, x=locx, y=locy) if self.multidepth and abs(self.z_cut) > abs(self.z_depthpercut): doc = deepcopy(self.z_cut) @@ -3577,7 +3749,11 @@ class CNCjob(Geometry): self.startz = float(startz) if startz is not None else None self.z_end = float(endz) if endz is not None else None - self.xy_end = [float(eval(a)) for a in endxy.split(",") if endxy != ''] + self.xy_end = re.sub('[()\[\]]', '', str(endxy)) if endxy else None + + if self.xy_end and self.xy_end != '': + self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")] + if self.xy_end and len(self.xy_end) < 2: self.app.inform.emit('[ERROR] %s' % _("The End Move X,Y field in Edit -> Preferences has to be " "in the format (x, y) but now there is only one value, not two.")) @@ -3595,7 +3771,11 @@ class CNCjob(Geometry): if toolchangexy == '': self.xy_toolchange = None else: - self.xy_toolchange = [float(eval(a)) for a in toolchangexy.split(",")] + self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) if toolchangexy else None + + if self.xy_toolchange and self.xy_toolchange != '': + self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")] + if len(self.xy_toolchange) < 2: self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y field in Edit -> Preferences has to be " "in the format (x, y) \n" @@ -3693,7 +3873,7 @@ class CNCjob(Geometry): self.gcode = self.doformat(p.start_code) - self.gcode += self.doformat(p.feedrate_code) # sets the feed rate + self.gcode += self.doformat(p.feedrate_code) # sets the feed rate if toolchange is False: self.gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height @@ -3707,19 +3887,19 @@ class CNCjob(Geometry): self.gcode += self.doformat(p.toolchange_code) if 'laser' not in self.pp_geometry_name: - self.gcode += self.doformat(p.spindle_code) # Spindle start + self.gcode += self.doformat(p.spindle_code) # Spindle start else: # for laser this will disable the laser self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time else: if 'laser' not in self.pp_geometry_name: self.gcode += self.doformat(p.spindle_code) # Spindle start if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time total_travel = 0.0 total_cut = 0.0 @@ -3766,7 +3946,9 @@ class CNCjob(Geometry): # calculate the cut distance total_cut = total_cut + geo.length - self.gcode += self.create_gcode_single_pass(geo, extracut, extracut_length, tolerance, + self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, extracut_length, + tolerance, + z_move=z_move, postproc=p, old_point=current_pt) # --------- Multi-pass --------- @@ -3781,14 +3963,16 @@ class CNCjob(Geometry): total_cut += (geo.length * nr_cuts) - self.gcode += self.create_gcode_multi_pass(geo, extracut, extracut_length, tolerance, - postproc=p, old_point=current_pt) + self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, extracut_length, + tolerance, + z_move=z_move, postproc=p, + old_point=current_pt) # calculate the total distance total_travel = total_travel + abs(distance(pt1=current_pt, pt2=pt)) current_pt = geo.coords[-1] - pt, geo = storage.nearest(current_pt) # Next + pt, geo = storage.nearest(current_pt) # Next disp_number = int(np.interp(path_count, [0, geo_len], [0, 100])) if old_disp_number < disp_number <= 100: @@ -3959,10 +4143,16 @@ class CNCjob(Geometry): self.dwell = dwell self.dwelltime = float(dwelltime) if dwelltime is not None else self.app.defaults["geometry_dwelltime"] - self.startz = float(startz) if startz is not None else self.app.defaults["geometry_startz"] + self.startz = float(startz) if startz is not None and startz != '' else self.app.defaults["geometry_startz"] + self.z_end = float(endz) if endz is not None else self.app.defaults["geometry_endz"] - self.xy_end = endxy if endxy != '' else self.app.defaults["geometry_endxy"] - self.xy_end = [float(eval(a)) for a in self.xy_end.split(",") if self.xy_end != ''] + + self.xy_end = endxy if endxy != '' and endxy else self.app.defaults["geometry_endxy"] + self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) if self.xy_end else None + + if self.xy_end is not None and self.xy_end != '': + self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")] + if self.xy_end and len(self.xy_end) < 2: self.app.inform.emit('[ERROR] %s' % _("The End Move X,Y field in Edit -> Preferences has to be " "in the format (x, y) but now there is only one value, not two.")) @@ -3978,7 +4168,11 @@ class CNCjob(Geometry): if toolchangexy == '': self.xy_toolchange = None else: - self.xy_toolchange = [float(eval(a)) for a in toolchangexy.split(",")] + self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) if self.xy_toolchange else None + + if self.xy_toolchange and self.xy_toolchange != '': + self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")] + if len(self.xy_toolchange) < 2: self.app.inform.emit( '[ERROR] %s' % @@ -4074,20 +4268,24 @@ class CNCjob(Geometry): # this is the tool diameter, it is used as such to accommodate the preprocessor who need the tool diameter # given under the name 'toolC' + # this is a fancy way of adding a class attribute (which should be added in the __init__ method) without doing + # it there :) self.postdata['toolC'] = self.tooldia # Initial G-Code self.pp_geometry = self.app.preprocessors[self.pp_geometry_name] + + # the 'p' local attribute is a reference to the current preprocessor class p = self.pp_geometry self.oldx = 0.0 self.oldy = 0.0 self.gcode = self.doformat(p.start_code) - - self.gcode += self.doformat(p.feedrate_code) # sets the feed rate + self.gcode += self.doformat(p.feedrate_code) # sets the feed rate if toolchange is False: + # all the x and y parameters in self.doformat() are used only by some preprocessors not by all self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height self.gcode += self.doformat(p.startz_code, x=self.oldx, y=self.oldy) @@ -4099,19 +4297,19 @@ class CNCjob(Geometry): self.gcode += self.doformat(p.toolchange_code) if 'laser' not in self.pp_geometry_name: - self.gcode += self.doformat(p.spindle_code) # Spindle start + self.gcode += self.doformat(p.spindle_code) # Spindle start else: # for laser this will disable the laser self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time else: if 'laser' not in self.pp_geometry_name: self.gcode += self.doformat(p.spindle_code) # Spindle start if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time total_travel = 0.0 total_cut = 0.0 @@ -4135,6 +4333,9 @@ class CNCjob(Geometry): path_count = 0 current_pt = (0, 0) pt, geo = storage.nearest(current_pt) + + # when nothing is left in the storage a StopIteration exception will be raised therefore stopping + # the whole process including the infinite loop while True below. try: while True: if self.app.abort_flag: @@ -4155,7 +4356,9 @@ class CNCjob(Geometry): if not multidepth: # calculate the cut distance total_cut += geo.length - self.gcode += self.create_gcode_single_pass(geo, extracut, self.extracut_length, tolerance, + self.gcode += self.create_gcode_single_pass(geo, current_tooldia, extracut, self.extracut_length, + tolerance, + z_move=z_move, postproc=p, old_point=current_pt) # --------- Multi-pass --------- @@ -4170,8 +4373,10 @@ class CNCjob(Geometry): total_cut += (geo.length * nr_cuts) - self.gcode += self.create_gcode_multi_pass(geo, extracut, self.extracut_length, tolerance, - postproc=p, old_point=current_pt) + self.gcode += self.create_gcode_multi_pass(geo, current_tooldia, extracut, self.extracut_length, + tolerance, + z_move=z_move, postproc=p, + old_point=current_pt) # calculate the travel distance total_travel += abs(distance(pt1=current_pt, pt2=pt)) @@ -4179,6 +4384,7 @@ class CNCjob(Geometry): pt, geo = storage.nearest(current_pt) # Next + # update the activity counter (lower left side of the app, status bar) disp_number = int(np.interp(path_count, [0, geo_len], [0, 100])) if old_disp_number < disp_number <= 100: self.app.proc_container.update_view_text(' %d%%' % disp_number) @@ -4393,27 +4599,76 @@ class CNCjob(Geometry): gcode += self.doformat(p.lift_code) return gcode - def create_gcode_single_pass(self, geometry, extracut, extracut_length, tolerance, old_point=(0, 0)): + def create_gcode_single_pass(self, geometry, cdia, extracut, extracut_length, tolerance, z_move, postproc, + old_point=(0, 0)): + """ # G-code. Note: self.linear2gcode() and self.point2gcode() will lower and raise the tool every time. + :param geometry: A Shapely Geometry (LineString or LinearRing) which is the path to be cut + :type geometry: LineString, LinearRing + :param cdia: Tool diameter + :type cdia: float + :param extracut: Will add an extra cut over the point where start of the cut is met with the end cut + :type extracut: bool + :param extracut_length: The length of the extra cut: half before the meeting point, half after + :type extracut_length: float + :param tolerance: Tolerance used to simplify the paths (making them mre rough) + :type tolerance: float + :param z_move: Travel Z + :type z_move: float + :param postproc: Preprocessor class + :type postproc: class + :param old_point: Previous point + :type old_point: tuple + :return: Gcode + :rtype: str + """ + # p = postproc + if type(geometry) == LineString or type(geometry) == LinearRing: if extracut is False: - gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point) + gcode_single_pass = self.linear2gcode(geometry, z_move=z_move, dia=cdia, tolerance=tolerance, + old_point=old_point) else: if geometry.is_ring: gcode_single_pass = self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance, + z_move=z_move, dia=cdia, old_point=old_point) else: - gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, old_point=old_point) + gcode_single_pass = self.linear2gcode(geometry, tolerance=tolerance, z_move=z_move, dia=cdia, + old_point=old_point) elif type(geometry) == Point: - gcode_single_pass = self.point2gcode(geometry) + gcode_single_pass = self.point2gcode(geometry, dia=cdia, z_move=z_move, old_point=old_point) else: log.warning("G-code generation not implemented for %s" % (str(type(geometry)))) return return gcode_single_pass - def create_gcode_multi_pass(self, geometry, extracut, extracut_length, tolerance, postproc, old_point=(0, 0)): + def create_gcode_multi_pass(self, geometry, cdia, extracut, extracut_length, tolerance, postproc, z_move, + old_point=(0, 0)): + """ + + :param geometry: A Shapely Geometry (LineString or LinearRing) which is the path to be cut + :type geometry: LineString, LinearRing + :param cdia: Tool diameter + :type cdia: float + :param extracut: Will add an extra cut over the point where start of the cut is met with the end cut + :type extracut: bool + :param extracut_length: The length of the extra cut: half before the meeting point, half after + :type extracut_length: float + :param tolerance: Tolerance used to simplify the paths (making them mre rough) + :type tolerance: float + :param postproc: Preprocessor class + :type postproc: class + :param z_move: Travel Z + :type z_move: float + :param old_point: Previous point + :type old_point: tuple + :return: Gcode + :rtype: str + """ + p = postproc gcode_multi_pass = '' @@ -4443,18 +4698,20 @@ class CNCjob(Geometry): if type(geometry) == LineString or type(geometry) == LinearRing: if extracut is False: gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False, - old_point=old_point) + z_move=z_move, dia=cdia, old_point=old_point) else: if geometry.is_ring: gcode_multi_pass += self.linear2gcode_extra(geometry, extracut_length, tolerance=tolerance, - z_cut=depth, up=False, old_point=old_point) + dia=cdia, z_move=z_move, z_cut=depth, up=False, + old_point=old_point) else: gcode_multi_pass += self.linear2gcode(geometry, tolerance=tolerance, z_cut=depth, up=False, + dia=cdia, z_move=z_move, old_point=old_point) # Ignore multi-pass for points. elif type(geometry) == Point: - gcode_multi_pass += self.point2gcode(geometry, old_point=old_point) + gcode_multi_pass += self.point2gcode(geometry, dia=cdia, z_move=z_move, old_point=old_point) break # Ignoring ... else: log.warning("G-code generation not implemented for %s" % (str(type(geometry)))) @@ -4470,7 +4727,7 @@ class CNCjob(Geometry): geometry.coords = list(geometry.coords)[::-1] # Lift the tool - gcode_multi_pass += self.doformat(postproc.lift_code, x=old_point[0], y=old_point[1]) + gcode_multi_pass += self.doformat(p.lift_code, x=old_point[0], y=old_point[1]) return gcode_multi_pass def codes_split(self, gline): @@ -4478,8 +4735,10 @@ class CNCjob(Geometry): Parses a line of G-Code such as "G01 X1234 Y987" into a dictionary: {'G': 1.0, 'X': 1234.0, 'Y': 987.0} - :param gline: G-Code line string - :return: Dictionary with parsed line. + :param gline: G-Code line string + :type gline: str + :return: Dictionary with parsed line. + :rtype: dict """ command = {} @@ -4548,12 +4807,24 @@ class CNCjob(Geometry): G-Code parser (from self.gcode). Generates dictionary with single-segment LineString's and "kind" indicating cut or travel, fast or feedrate speed. + + Will return a dict in the format: + { + "geom": LineString(path), + "kind": kind + } + where kind can be either ["C", "F"] # T=travel, C=cut, F=fast, S=slow + + :param force_parsing: + :type force_parsing: + :return: + :rtype: dict """ kind = ["C", "F"] # T=travel, C=cut, F=fast, S=slow # Results go here - geometry = [] + geometry = [] # Last known instruction current = {'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'G': 0} @@ -4561,15 +4832,25 @@ class CNCjob(Geometry): # Current path: temporary storage until tool is # lifted or lowered. if self.toolchange_xy_type == "excellon": - if self.app.defaults["excellon_toolchangexy"] == '': + if self.app.defaults["excellon_toolchangexy"] == '' or self.app.defaults["excellon_toolchangexy"] is None: pos_xy = (0, 0) else: - pos_xy = [float(eval(a)) for a in self.app.defaults["excellon_toolchangexy"].split(",")] + pos_xy = self.app.defaults["excellon_toolchangexy"] + try: + pos_xy = [float(eval(a)) for a in pos_xy.split(",")] + except Exception: + if len(pos_xy) != 2: + pos_xy = (0, 0) else: - if self.app.defaults["geometry_toolchangexy"] == '': + if self.app.defaults["geometry_toolchangexy"] == '' or self.app.defaults["geometry_toolchangexy"] is None: pos_xy = (0, 0) else: - pos_xy = [float(eval(a)) for a in self.app.defaults["geometry_toolchangexy"].split(",")] + pos_xy = self.app.defaults["geometry_toolchangexy"] + try: + pos_xy = [float(eval(a)) for a in pos_xy.split(",")] + except Exception: + if len(pos_xy) != 2: + pos_xy = (0, 0) path = [pos_xy] # path = [(0, 0)] @@ -4636,7 +4917,7 @@ class CNCjob(Geometry): kind = ['C', 'F'] geometry.append( { - "geom": Point(current_drill_point_coords).buffer(dia/2.0).exterior, + "geom": Point(current_drill_point_coords).buffer(dia / 2.0).exterior, "kind": kind } ) @@ -4644,14 +4925,14 @@ class CNCjob(Geometry): if 'G' in gobj: current['G'] = int(gobj['G']) - + if 'X' in gobj or 'Y' in gobj: if 'X' in gobj: x = gobj['X'] # current['X'] = x else: x = current['X'] - + if 'Y' in gobj: y = gobj['Y'] else: @@ -4670,7 +4951,7 @@ class CNCjob(Geometry): arcdir = [None, None, "cw", "ccw"] if current['G'] in [2, 3]: # arc center = [gobj['I'] + current['X'], gobj['J'] + current['Y']] - radius = np.sqrt(gobj['I']**2 + gobj['J']**2) + radius = np.sqrt(gobj['I'] ** 2 + gobj['J'] ** 2) start = np.arctan2(-gobj['J'], -gobj['I']) stop = np.arctan2(-center[1] + y, -center[0] + x) path += arc(center, radius, start, stop, arcdir[current['G']], int(self.steps_per_circle)) @@ -4737,16 +5018,28 @@ class CNCjob(Geometry): """ Plots the G-code job onto the given axes. - :param tooldia: Tool diameter. - :param dpi: Not used! - :param margin: Not used! - :param color: Color specification. - :param alpha: Transparency specification. - :param tool_tolerance: Tolerance when drawing the toolshape. - :param obj - :param visible - :param kind - :return: None + :param tooldia: Tool diameter. + :type tooldia: float + :param dpi: Not used! + :type dpi: float + :param margin: Not used! + :type margin: float + :param gcode_parsed: Parsed Gcode + :type gcode_parsed: str + :param color: Color specification. + :type color: str + :param alpha: Transparency specification. + :type alpha: dict + :param tool_tolerance: Tolerance when drawing the toolshape. + :type tool_tolerance: float + :param obj: The object for whih to plot + :type obj: class + :param visible: Visibility status + :type visible: bool + :param kind: Can be: "travel", "cut", "all" + :type kind: str + :return: None + :rtype: """ # units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() @@ -4799,14 +5092,27 @@ class CNCjob(Geometry): # 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(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle) + if geo['kind'][0] == 'T': + # if the geos are travel lines it will enter into Exception + poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), + resolution=self.steps_per_circle) + else: + poly = Polygon(geo['geom']) + poly = poly.simplify(tool_tolerance) except Exception: # deal here with unexpected plot errors due of LineStrings not valid continue + + # try: + # poly = Polygon(geo['geom']) + # except ValueError: + # # if the geos are travel lines it will enter into Exception + # poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle) + # poly = poly.simplify(tool_tolerance) + # except Exception: + # # deal here with unexpected plot errors due of LineStrings not valid + # continue else: # plot the geometry of any objects other than Excellon poly = geo['geom'].buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle) @@ -4864,102 +5170,43 @@ class CNCjob(Geometry): if geo['kind'][0] == 'C': obj.add_shape(shape=poly, color=color['C'][1], face_color=color['C'][0], visible=visible, layer=1) - # current_x = gcode_parsed[0]['geom'].coords[0][0] - # current_y = gcode_parsed[0]['geom'].coords[0][1] - # old_pos = ( - # current_x, - # current_y - # ) - # - # for geo in gcode_parsed: - # if geo['kind'][0] == 'T': - # current_position = ( - # geo['geom'].coords[0][0] + old_pos[0], - # geo['geom'].coords[0][1] + old_pos[1] - # ) - # if current_position not in pos: - # pos.append(current_position) - # path_num += 1 - # text.append(str(path_num)) - # - # delta = ( - # geo['geom'].coords[-1][0] - geo['geom'].coords[0][0], - # geo['geom'].coords[-1][1] - geo['geom'].coords[0][1] - # ) - # current_position = ( - # current_position[0] + geo['geom'].coords[-1][0], - # current_position[1] + geo['geom'].coords[-1][1] - # ) - # if current_position not in pos: - # pos.append(current_position) - # path_num += 1 - # text.append(str(path_num)) - # - # # plot the geometry of Excellon objects - # if self.origin_kind == 'excellon': - # if isinstance(geo['geom'], Point): - # # if geo is Point - # current_position = ( - # current_position[0] + geo['geom'].x, - # current_position[1] + geo['geom'].y - # ) - # poly = Polygon(Point(current_position)) - # elif isinstance(geo['geom'], LineString): - # # if the geos are travel lines (LineStrings) - # new_line_pts = [] - # old_line_pos = deepcopy(current_position) - # for p in list(geo['geom'].coords): - # current_position = ( - # current_position[0] + p[0], - # current_position[1] + p[1] - # ) - # new_line_pts.append(current_position) - # old_line_pos = p - # new_line = LineString(new_line_pts) - # - # poly = new_line.buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle) - # poly = poly.simplify(tool_tolerance) - # else: - # # plot the geometry of any objects other than Excellon - # new_line_pts = [] - # old_line_pos = deepcopy(current_position) - # for p in list(geo['geom'].coords): - # current_position = ( - # current_position[0] + p[0], - # current_position[1] + p[1] - # ) - # new_line_pts.append(current_position) - # old_line_pos = p - # new_line = LineString(new_line_pts) - # - # poly = new_line.buffer(distance=(tooldia / 1.99999999), resolution=self.steps_per_circle) - # poly = poly.simplify(tool_tolerance) - # - # old_pos = deepcopy(current_position) - # - # 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) - # elif kind == 'travel': - # if geo['kind'][0] == 'T': - # obj.add_shape(shape=poly, color=color['T'][1], face_color=color['T'][0], - # visible=visible, layer=2) - # elif kind == 'cut': - # if geo['kind'][0] == 'C': - # obj.add_shape(shape=poly, color=color['C'][1], face_color=color['C'][0], - # visible=visible, layer=1) try: - obj.annotation.set(text=text, pos=pos, visible=obj.options['plot'], - font_size=self.app.defaults["cncjob_annotation_fontsize"], - color=self.app.defaults["cncjob_annotation_fontcolor"]) - except Exception: - pass + if self.app.defaults['global_theme'] == 'white': + obj.annotation.set(text=text, pos=pos, visible=obj.options['plot'], + font_size=self.app.defaults["cncjob_annotation_fontsize"], + color=self.app.defaults["cncjob_annotation_fontcolor"]) + else: + # invert the color + old_color = self.app.defaults["cncjob_annotation_fontcolor"].lower() + new_color = '' + code = {} + l1 = "#;0123456789abcdef" + l2 = "#;fedcba9876543210" + for i in range(len(l1)): + code[l1[i]] = l2[i] + + for x in range(len(old_color)): + new_color += code[old_color[x]] + + obj.annotation.set(text=text, pos=pos, visible=obj.options['plot'], + font_size=self.app.defaults["cncjob_annotation_fontsize"], + color=new_color) + except Exception as e: + log.debug("CNCJob.plot2() --> annotations --> %s" % str(e)) def create_geometry(self): - self.app.inform.emit('%s: %s' % (_("Unifying Geometry from parsed Geometry segments"), - str(len(self.gcode_parsed)))) + """ + It is used by the Excellon objects. Will create the solid_geometry which will be an attribute of the + Excellon object class. + + :return: List of Shapely geometry elements + :rtype: list + """ + # TODO: This takes forever. Too much data? + # self.app.inform.emit('%s: %s' % (_("Unifying Geometry from parsed Geometry segments"), + # str(len(self.gcode_parsed)))) # self.solid_geometry = cascaded_union([geo['geom'] for geo in self.gcode_parsed]) # This is much faster but not so nice to look at as you can see different segments of the geometry @@ -4967,10 +5214,15 @@ class CNCjob(Geometry): return self.solid_geometry - # code snippet added by Lei Zheng in a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ def segment(self, coords): """ - break long linear lines to make it more auto level friendly + Break long linear lines to make it more auto level friendly. + Code snippet added by Lei Zheng in a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ + + :param coords: List of coordinates tuples + :type coords: list + :return: A path; list with the multiple coordinates breaking a line. + :rtype: list """ if len(coords) < 2 or self.segx <= 0 and self.segy <= 0: @@ -5022,7 +5274,7 @@ class CNCjob(Geometry): return path - def linear2gcode(self, linear, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, + def linear2gcode(self, linear, dia, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)): """ @@ -5030,6 +5282,8 @@ class CNCjob(Geometry): :param linear: The path to cut along. :type: Shapely.LinearRing or Shapely.Linear String + :param dia: The tool diameter that is going on the path + :type dia: float :param tolerance: All points in the simplified object will be within the tolerance distance of the original geometry. :type tolerance: float @@ -5089,7 +5343,42 @@ class CNCjob(Geometry): # Move fast to 1st point if not cont: - gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point + current_tooldia = dia + travels = self.app.exc_areas.travel_coordinates(start_point=(old_point[0], old_point[1]), + end_point=(first_x, first_y), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = z_move + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = z_move + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point # Move down to cutting depth if down: @@ -5128,7 +5417,7 @@ class CNCjob(Geometry): gcode += self.doformat(p.lift_code, x=prev_x, y=prev_y, z_move=z_move) # Stop cutting return gcode - def linear2gcode_extra(self, linear, extracut_length, tolerance=0, down=True, up=True, + def linear2gcode_extra(self, linear, dia, extracut_length, tolerance=0, down=True, up=True, z_cut=None, z_move=None, zdownrate=None, feedrate=None, feedrate_z=None, feedrate_rapid=None, cont=False, old_point=(0, 0)): """ @@ -5137,6 +5426,8 @@ class CNCjob(Geometry): :param linear: The path to cut along. :type: Shapely.LinearRing or Shapely.Linear String + :param dia: The tool diameter that is going on the path + :type dia: float :param extracut_length: how much to cut extra over the first point at the end of the path :param tolerance: All points in the simplified object will be within the tolerance distance of the original geometry. @@ -5196,7 +5487,42 @@ class CNCjob(Geometry): # Move fast to 1st point if not cont: - gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point + current_tooldia = dia + travels = self.app.exc_areas.travel_coordinates(start_point=(old_point[0], old_point[1]), + end_point=(first_x, first_y), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = z_move + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = z_move + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.rapid_code, x=first_x, y=first_y) # Move to first point # Move down to cutting depth if down: @@ -5362,7 +5688,20 @@ class CNCjob(Geometry): return gcode - def point2gcode(self, point, old_point=(0, 0)): + def point2gcode(self, point, dia, z_move=None, old_point=(0, 0)): + """ + + :param point: A Shapely Point + :type point: Point + :param dia: The tool diameter that is going on the path + :type dia: float + :param z_move: Travel Z + :type z_move: float + :param old_point: Old point coordinates from which we moved to the 'point' + :type old_point: tuple + :return: G-code to cut on the Point feature. + :rtype: str + """ gcode = "" if self.app.abort_flag: @@ -5386,7 +5725,42 @@ class CNCjob(Geometry): first_x = path[0][0] first_y = path[0][1] - gcode += self.doformat(p.linear_code, x=first_x, y=first_y) # Move to first point + current_tooldia = dia + travels = self.app.exc_areas.travel_coordinates(start_point=(old_point[0], old_point[1]), + end_point=(first_x, first_y), + tooldia=current_tooldia) + prev_z = None + for travel in travels: + locx = travel[1][0] + locy = travel[1][1] + + if travel[0] is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # raise to safe Z (travel[0]) each time because safe Z may be different + self.z_move = travel[0] + gcode += self.doformat(p.lift_code, x=locx, y=locy) + + # restore z_move + self.z_move = z_move + else: + if prev_z is not None: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # we assume that previously the z_move was altered therefore raise to + # the travel_z (z_move) + self.z_move = z_move + gcode += self.doformat(p.lift_code, x=locx, y=locy) + else: + # move to next point + gcode += self.doformat(p.rapid_code, x=locx, y=locy) + + # store prev_z + prev_z = travel[0] + + # gcode += self.doformat(p.linear_code, x=first_x, y=first_y) # Move to first point if self.z_feedrate is not None: gcode += self.doformat(p.z_feedrate_code) @@ -5402,8 +5776,10 @@ class CNCjob(Geometry): """ Exports the CNC Job as a SVG Element - :scale_factor: float - :return: SVG Element string + :param scale_stroke_factor: A factor to scale the SVG geometry + :type scale_stroke_factor: float + :return: SVG Element string + :rtype: str """ # scale_factor is a multiplication factor for the SVG stroke-width used within shapely's svg export # If not specified then try and use the tool diameter @@ -5423,6 +5799,9 @@ class CNCjob(Geometry): # This way we can add different formatting / colors to both cuts = [] travels = [] + cutsgeom = '' + travelsgeom = '' + for g in self.gcode_parsed: if self.app.abort_flag: # graceful abort requested by the user @@ -5460,10 +5839,12 @@ class CNCjob(Geometry): def bounds(self, flatten=None): """ - Returns coordinates of rectangular bounds - of geometry: (xmin, ymin, xmax, ymax). + Returns coordinates of rectangular bounds of geometry: (xmin, ymin, xmax, ymax). :param flatten: Not used, it is here for compatibility with base class method + :type flatten: bool + :return: Bounding values in format (xmin, ymin, xmax, ymax) + :rtype: tuple """ log.debug("camlib.CNCJob.bounds()") @@ -5592,7 +5973,7 @@ class CNCjob(Geometry): new_nr = float(nr) * xfactor # replace the updated string line = line.replace(nr, ('%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_nr)) - ) + ) # this scales all the X and Y and Z and F values and also the Tool Dia in the toolchange message if header_stop is True: @@ -5993,9 +6374,9 @@ def arc(center, radius, start, stop, direction, steps_per_circ): stop += 2 * np.pi if direction == "cw" and stop >= start: stop -= 2 * np.pi - + angle = abs(stop - start) - + # angle = stop-start steps = max([int(np.ceil(angle / (2 * np.pi) * steps_per_circ)), 2]) delta_angle = da_sign[direction] * angle * 1.0 / steps @@ -6578,7 +6959,6 @@ class FlatCAMRTreeStorage(FlatCAMRTree): tidx = super(FlatCAMRTreeStorage, self).nearest(pt) return (tidx.bbox[0], tidx.bbox[1]), self.objects[tidx.object] - # class myO: # def __init__(self, coords): # self.coords = coords diff --git a/defaults.py b/defaults.py index ffd84874..833563cb 100644 --- a/defaults.py +++ b/defaults.py @@ -2,16 +2,16 @@ import os import stat import sys from copy import deepcopy -from FlatCAMCommon import LoudDict +from Common import LoudDict from camlib import to_dict, CNCjob, Geometry import simplejson import logging import gettext -import FlatCAMTranslation as fcTranslate +import AppTranslation as fcTranslate import builtins -from flatcamParsers.ParseExcellon import Excellon -from flatcamParsers.ParseGerber import Gerber +from AppParsers.ParseExcellon import Excellon +from AppParsers.ParseGerber import Gerber fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -43,6 +43,7 @@ class FlatCAMDefaults: # General "global_graphic_engine": '3D', + "global_hud": True, "global_app_level": 'b', "global_portable": False, "global_language": 'English', @@ -171,12 +172,6 @@ class FlatCAMDefaults: "All Files (*.*)", # Gerber Options - "gerber_isotooldia": 0.1, - "gerber_isopasses": 1, - "gerber_isooverlap": 10, - "gerber_milling_type": "cl", - "gerber_combine_passes": False, - "gerber_iso_scope": 'all', "gerber_noncoppermargin": 0.1, "gerber_noncopperrounded": False, "gerber_bboxmargin": 0.1, @@ -187,11 +182,6 @@ class FlatCAMDefaults: "gerber_aperture_scale_factor": 1.0, "gerber_aperture_buffer_factor": 0.0, "gerber_follow": False, - "gerber_tool_type": 'circular', - "gerber_vtipdia": 0.1, - "gerber_vtipangle": 30, - "gerber_vcutz": -0.05, - "gerber_iso_type": "full", "gerber_buffering": "full", "gerber_simplification": False, "gerber_simp_tolerance": 0.0005, @@ -222,6 +212,7 @@ class FlatCAMDefaults: # Excellon General "excellon_plot": True, "excellon_solid": True, + "excellon_multicolored": False, "excellon_format_upper_in": 2, "excellon_format_lower_in": 4, "excellon_format_upper_mm": 3, @@ -263,6 +254,10 @@ class FlatCAMDefaults: "excellon_tooldia": 0.8, "excellon_slot_tooldia": 1.8, "excellon_gcode_type": "drills", + "excellon_area_exclusion": False, + "excellon_area_shape": "polygon", + "excellon_area_strategy": "over", + "excellon_area_overz": 1.0, # Excellon Advanced Options "excellon_offset": 0.0, @@ -306,6 +301,7 @@ class FlatCAMDefaults: # Geometry General "geometry_plot": True, + "geometry_multicolored": False, "geometry_circle_steps": 64, "geometry_cnctooldia": "2.4", "geometry_plot_line": "#FF0000", @@ -386,6 +382,28 @@ class FlatCAMDefaults: "cncjob_annotation_fontsize": 9, "cncjob_annotation_fontcolor": '#990000', + # Isolation Routing Tool + "tools_iso_tooldia": "0.1", + "tools_iso_order": 'rev', + "tools_iso_tool_type": 'C1', + "tools_iso_tool_vtipdia": 0.1, + "tools_iso_tool_vtipangle": 30, + "tools_iso_tool_cutz": -0.05, + "tools_iso_newdia": 0.1, + + "tools_iso_passes": 1, + "tools_iso_overlap": 10, + "tools_iso_milling_type": "cl", + "tools_iso_follow": False, + "tools_iso_isotype": "full", + + "tools_iso_rest": False, + "tools_iso_combine_passes": False, + "tools_iso_isoexcept": False, + "tools_iso_selection": _("All"), + "tools_iso_area_shape": "square", + "tools_iso_plotting": 'normal', + # NCC Tool "tools_ncctools": "1.0, 0.5", "tools_nccorder": 'rev', @@ -400,13 +418,13 @@ class FlatCAMDefaults: "tools_ncc_offset_value": 0.0000, "tools_nccref": _('Itself'), "tools_ncc_area_shape": "square", - "tools_ncc_plotting": 'normal', "tools_nccmilling_type": 'cl', "tools_ncctool_type": 'C1', "tools_ncccutz": -0.05, "tools_ncctipdia": 0.1, "tools_ncctipangle": 30, "tools_nccnewdia": 0.1, + "tools_ncc_plotting": 'normal', # Cutout Tool "tools_cutouttooldia": 2.4, @@ -425,7 +443,7 @@ class FlatCAMDefaults: "tools_paintoverlap": 20, "tools_paintmargin": 0.0, "tools_paintmethod": _("Seed"), - "tools_selectmethod": _("All Polygons"), + "tools_selectmethod": _("All"), "tools_paint_area_shape": "square", "tools_pathconnect": True, "tools_paintcontour": True, @@ -491,7 +509,7 @@ class FlatCAMDefaults: "tools_transform_offset_x": 0.0, "tools_transform_offset_y": 0.0, "tools_transform_mirror_reference": False, - "tools_transform_mirror_point": (0, 0), + "tools_transform_mirror_point": "0.0, 0.0", "tools_transform_buffer_dis": 0.0, "tools_transform_buffer_factor": 100.0, "tools_transform_buffer_corner": True, @@ -520,6 +538,12 @@ class FlatCAMDefaults: # Distance Tool "tools_dist_snap_center": False, + # Corner Markers Tool + + "tools_corners_thickness": 0.1, + "tools_corners_length": 3.0, + "tools_corners_margin": 0.0, + # ######################################################################################################## # ################################ TOOLS 2 ############################################################### # ######################################################################################################## @@ -693,13 +717,19 @@ class FlatCAMDefaults: except Exception as e: log.error("save_factory_defaults() -> %s" % str(e)) - def __init__(self): + def __init__(self, callback=lambda x: None): + """ + + :param callback: A method called each time that one of the values are changed in the self.defaults LouDict + """ self.defaults = LoudDict() self.defaults.update(self.factory_defaults) self.current_defaults = {} # copy used for restoring after cancelled prefs changes self.current_defaults.update(self.factory_defaults) self.old_defaults_found = False + self.defaults.set_change_callback(callback) + # #### Pass-through to the defaults LoudDict ##### def __len__(self): return self.defaults.__len__() diff --git a/flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py b/flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py deleted file mode 100644 index 404f17ba..00000000 --- a/flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py +++ /dev/null @@ -1,187 +0,0 @@ -from PyQt5 import QtWidgets -from PyQt5.QtCore import QSettings - -from flatcamGUI.GUIElements import FCDoubleSpinner, FCSpinner, RadioSet, FCCheckBox -from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI - -import gettext -import FlatCAMTranslation as fcTranslate -import builtins - -fcTranslate.apply_language('strings') -if '_' not in builtins.__dict__: - _ = gettext.gettext - -settings = QSettings("Open Source", "FlatCAM") -if settings.contains("machinist"): - machinist_setting = settings.value('machinist', type=int) -else: - machinist_setting = 0 - - -class GerberOptPrefGroupUI(OptionsGroupUI): - def __init__(self, decimals=4, parent=None): - # OptionsGroupUI.__init__(self, "Gerber Options Preferences", parent=parent) - super(GerberOptPrefGroupUI, self).__init__(self, parent=parent) - - self.decimals = decimals - - self.setTitle(str(_("Gerber Options"))) - - # ## Isolation Routing - self.isolation_routing_label = QtWidgets.QLabel("%s:" % _("Isolation Routing")) - self.isolation_routing_label.setToolTip( - _("Create a Geometry object with\n" - "toolpaths to cut outside polygons.") - ) - self.layout.addWidget(self.isolation_routing_label) - - # Cutting Tool Diameter - grid0 = QtWidgets.QGridLayout() - self.layout.addLayout(grid0) - - tdlabel = QtWidgets.QLabel('%s:' % _('Tool dia')) - tdlabel.setToolTip( - _("Diameter of the cutting tool.") - ) - grid0.addWidget(tdlabel, 0, 0) - self.iso_tool_dia_entry = FCDoubleSpinner() - self.iso_tool_dia_entry.set_precision(self.decimals) - self.iso_tool_dia_entry.setSingleStep(0.1) - self.iso_tool_dia_entry.set_range(-9999, 9999) - - grid0.addWidget(self.iso_tool_dia_entry, 0, 1) - - # Nr of passes - passlabel = QtWidgets.QLabel('%s:' % _('# Passes')) - passlabel.setToolTip( - _("Width of the isolation gap in\n" - "number (integer) of tool widths.") - ) - self.iso_width_entry = FCSpinner() - self.iso_width_entry.set_range(1, 999) - - grid0.addWidget(passlabel, 1, 0) - grid0.addWidget(self.iso_width_entry, 1, 1) - - # Pass overlap - overlabel = QtWidgets.QLabel('%s:' % _('Pass overlap')) - overlabel.setToolTip( - _("How much (percentage) of the tool width to overlap each tool pass.") - ) - self.iso_overlap_entry = FCDoubleSpinner(suffix='%') - self.iso_overlap_entry.set_precision(self.decimals) - self.iso_overlap_entry.setWrapping(True) - self.iso_overlap_entry.setRange(0.0000, 99.9999) - self.iso_overlap_entry.setSingleStep(0.1) - - grid0.addWidget(overlabel, 2, 0) - grid0.addWidget(self.iso_overlap_entry, 2, 1) - - # Isolation Scope - self.iso_scope_label = QtWidgets.QLabel('%s:' % _('Scope')) - self.iso_scope_label.setToolTip( - _("Isolation scope. Choose what to isolate:\n" - "- 'All' -> Isolate all the polygons in the object\n" - "- 'Selection' -> Isolate a selection of polygons.") - ) - self.iso_scope_radio = RadioSet([{'label': _('All'), 'value': 'all'}, - {'label': _('Selection'), 'value': 'single'}]) - - grid0.addWidget(self.iso_scope_label, 3, 0) - grid0.addWidget(self.iso_scope_radio, 3, 1, 1, 2) - - # Milling Type - milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) - milling_type_label.setToolTip( - _("Milling type:\n" - "- climb / best for precision milling and to reduce tool usage\n" - "- conventional / useful when there is no backlash compensation") - ) - grid0.addWidget(milling_type_label, 4, 0) - self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, - {'label': _('Conventional'), 'value': 'cv'}]) - grid0.addWidget(self.milling_type_radio, 4, 1) - - # Combine passes - self.combine_passes_cb = FCCheckBox(label=_('Combine Passes')) - self.combine_passes_cb.setToolTip( - _("Combine all passes into one object") - ) - grid0.addWidget(self.combine_passes_cb, 5, 0, 1, 2) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid0.addWidget(separator_line, 6, 0, 1, 2) - - # ## Clear non-copper regions - self.clearcopper_label = QtWidgets.QLabel("%s:" % _("Non-copper regions")) - self.clearcopper_label.setToolTip( - _("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.") - ) - self.layout.addWidget(self.clearcopper_label) - - grid1 = QtWidgets.QGridLayout() - self.layout.addLayout(grid1) - - # Margin - bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin')) - bmlabel.setToolTip( - _("Specify the edge of the PCB\n" - "by drawing a box around all\n" - "objects with this minimum\n" - "distance.") - ) - grid1.addWidget(bmlabel, 0, 0) - self.noncopper_margin_entry = FCDoubleSpinner() - self.noncopper_margin_entry.set_precision(self.decimals) - self.noncopper_margin_entry.setSingleStep(0.1) - self.noncopper_margin_entry.set_range(-9999, 9999) - grid1.addWidget(self.noncopper_margin_entry, 0, 1) - - # Rounded corners - self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo")) - self.noncopper_rounded_cb.setToolTip( - _("Resulting geometry will have rounded corners.") - ) - grid1.addWidget(self.noncopper_rounded_cb, 1, 0, 1, 2) - - separator_line = QtWidgets.QFrame() - separator_line.setFrameShape(QtWidgets.QFrame.HLine) - separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) - grid1.addWidget(separator_line, 2, 0, 1, 2) - - # ## Bounding box - self.boundingbox_label = QtWidgets.QLabel('%s:' % _('Bounding Box')) - self.layout.addWidget(self.boundingbox_label) - - grid2 = QtWidgets.QGridLayout() - self.layout.addLayout(grid2) - - bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin')) - bbmargin.setToolTip( - _("Distance of the edges of the box\n" - "to the nearest polygon.") - ) - self.bbmargin_entry = FCDoubleSpinner() - self.bbmargin_entry.set_precision(self.decimals) - self.bbmargin_entry.setSingleStep(0.1) - self.bbmargin_entry.set_range(-9999, 9999) - - grid2.addWidget(bbmargin, 0, 0) - grid2.addWidget(self.bbmargin_entry, 0, 1) - - self.bbrounded_cb = FCCheckBox(label='%s' % _("Rounded Geo")) - self.bbrounded_cb.setToolTip( - _("If the bounding box is \n" - "to have rounded corners\n" - "their radius is equal to\n" - "the margin.") - ) - grid2.addWidget(self.bbrounded_cb, 1, 0, 1, 2) - self.layout.addStretch() diff --git a/flatcamTools/__init__.py b/flatcamTools/__init__.py deleted file mode 100644 index bdd7e58b..00000000 --- a/flatcamTools/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -import sys - -from flatcamTools.ToolCalculators import ToolCalculator -from flatcamTools.ToolCalibration import ToolCalibration -from flatcamTools.ToolCutOut import CutOut - -from flatcamTools.ToolDblSided import DblSidedTool -from flatcamTools.ToolExtractDrills import ToolExtractDrills -from flatcamTools.ToolAlignObjects import AlignObjects - -from flatcamTools.ToolFilm import Film - -from flatcamTools.ToolImage import ToolImage - -from flatcamTools.ToolDistance import Distance -from flatcamTools.ToolDistanceMin import DistanceMin - -from flatcamTools.ToolMove import ToolMove - -from flatcamTools.ToolNCC import NonCopperClear -from flatcamTools.ToolPaint import ToolPaint - -from flatcamTools.ToolOptimal import ToolOptimal - -from flatcamTools.ToolPanelize import Panelize -from flatcamTools.ToolPcbWizard import PcbWizard -from flatcamTools.ToolPDF import ToolPDF -from flatcamTools.ToolProperties import Properties - -from flatcamTools.ToolQRCode import QRCode -from flatcamTools.ToolRulesCheck import RulesCheck - -from flatcamTools.ToolCopperThieving import ToolCopperThieving -from flatcamTools.ToolFiducials import ToolFiducials - -from flatcamTools.ToolShell import FCShell -from flatcamTools.ToolSolderPaste import SolderPaste -from flatcamTools.ToolSub import ToolSub - -from flatcamTools.ToolTransform import ToolTransform -from flatcamTools.ToolPunchGerber import ToolPunchGerber - -from flatcamTools.ToolInvertGerber import ToolInvertGerber diff --git a/locale/de/LC_MESSAGES/strings.mo b/locale/de/LC_MESSAGES/strings.mo index 22400a33..6b860729 100644 Binary files a/locale/de/LC_MESSAGES/strings.mo and b/locale/de/LC_MESSAGES/strings.mo differ diff --git a/locale/de/LC_MESSAGES/strings.po b/locale/de/LC_MESSAGES/strings.po index 17d6af72..b8e0d53e 100644 --- a/locale/de/LC_MESSAGES/strings.po +++ b/locale/de/LC_MESSAGES/strings.po @@ -1,15 +1,15 @@ msgid "" msgstr "" "Project-Id-Version: \n" -"POT-Creation-Date: 2020-05-03 15:59+0300\n" -"PO-Revision-Date: 2020-05-03 16:02+0300\n" +"POT-Creation-Date: 2020-05-19 02:25+0300\n" +"PO-Revision-Date: 2020-05-19 02:25+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.4\n" +"X-Generator: Poedit 2.3.1\n" "X-Poedit-Basepath: ../../..\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-SearchPath-0: .\n" @@ -17,17 +17,16911 @@ msgstr "" "X-Poedit-SearchPathExcluded-1: doc\n" "X-Poedit-SearchPathExcluded-2: tests\n" -#: FlatCAMApp.py:491 +#: AppDatabase.py:86 +msgid "Add Geometry Tool in DB" +msgstr "Geometriewerkzeug in DB hinzufügen" + +#: AppDatabase.py:88 AppDatabase.py:1643 +msgid "" +"Add a new tool in the Tools Database.\n" +"It will be used in the Geometry UI.\n" +"You can edit it after it is added." +msgstr "" +"Fügen Sie der Werkzeugdatenbank ein neues Werkzeug hinzu\n" +"Es wird in der Geometrie-Benutzeroberfläche verwendet.\n" +"Danach können Sie es modifizieren." + +#: AppDatabase.py:102 AppDatabase.py:1657 +msgid "Delete Tool from DB" +msgstr "Werkzeug aus DB löschen" + +#: AppDatabase.py:104 AppDatabase.py:1659 +msgid "Remove a selection of tools in the Tools Database." +msgstr "Eine Auswahl von Werkzeugen aus der Werkzeugdatenbank entfernen." + +#: AppDatabase.py:108 AppDatabase.py:1663 +msgid "Export DB" +msgstr "DB exportieren" + +#: AppDatabase.py:110 AppDatabase.py:1665 +msgid "Save the Tools Database to a custom text file." +msgstr "Werkzeugdatenbank als Textdatei speichern." + +#: AppDatabase.py:114 AppDatabase.py:1669 +msgid "Import DB" +msgstr "Importieren Sie DB" + +#: AppDatabase.py:116 AppDatabase.py:1671 +msgid "Load the Tools Database information's from a custom text file." +msgstr "Werkzeugdatenbank aus einer Textdatei importieren." + +#: AppDatabase.py:120 AppDatabase.py:1681 +msgid "Add Tool from Tools DB" +msgstr "Werkzeug aus Werkzeugdatenbank hinzufügen" + +#: AppDatabase.py:122 AppDatabase.py:1683 +msgid "" +"Add a new tool in the Tools Table of the\n" +"active Geometry object after selecting a tool\n" +"in the Tools Database." +msgstr "" +"Fügen Sie ein neues Werkzeug in die Werkzeugtabelle der\n" +"aktiven Geometrie hinzu, nachdem Sie das Werkzeug in\n" +"der Werkzeugdatenbank ausgewählt haben." + +#: AppDatabase.py:128 AppDatabase.py:1689 AppGUI/MainGUI.py:1347 +#: AppGUI/preferences/PreferencesUIManager.py:942 App_Main.py:2203 +#: App_Main.py:3054 App_Main.py:3928 App_Main.py:4279 App_Main.py:6338 +msgid "Cancel" +msgstr "Abbrechen" + +#: AppDatabase.py:158 AppDatabase.py:833 AppDatabase.py:1087 +msgid "Tool Name" +msgstr "Werkzeugname" + +#: AppDatabase.py:159 AppDatabase.py:835 AppDatabase.py:1100 +#: AppEditors/FlatCAMExcEditor.py:1604 AppGUI/ObjectUI.py:1447 +#: AppGUI/ObjectUI.py:1685 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:132 +#: AppTools/ToolNCC.py:278 AppTools/ToolNCC.py:287 AppTools/ToolPaint.py:260 +msgid "Tool Dia" +msgstr "Werkzeugdurchm" + +#: AppDatabase.py:160 AppDatabase.py:837 AppDatabase.py:1281 +#: AppGUI/ObjectUI.py:1660 +msgid "Tool Offset" +msgstr "Werkzeugversatz" + +#: AppDatabase.py:161 AppDatabase.py:839 AppDatabase.py:1298 +msgid "Custom Offset" +msgstr "Selbstdefinierter Werkzeugversatz" + +#: AppDatabase.py:162 AppDatabase.py:841 AppDatabase.py:1265 +#: AppGUI/ObjectUI.py:309 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:67 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:53 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:62 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:72 AppTools/ToolNCC.py:213 +#: AppTools/ToolNCC.py:227 AppTools/ToolPaint.py:195 +msgid "Tool Type" +msgstr "Werkzeugtyp" + +#: AppDatabase.py:163 AppDatabase.py:843 AppDatabase.py:1113 +msgid "Tool Shape" +msgstr "Werkzeugform" + +#: AppDatabase.py:164 AppDatabase.py:846 AppDatabase.py:1129 +#: AppGUI/ObjectUI.py:350 AppGUI/ObjectUI.py:900 AppGUI/ObjectUI.py:1805 +#: AppGUI/ObjectUI.py:2466 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:93 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:48 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:107 +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:78 +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:58 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:98 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:105 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:113 +#: AppTools/ToolCalculators.py:114 AppTools/ToolCutOut.py:138 +#: AppTools/ToolNCC.py:260 AppTools/ToolNCC.py:268 AppTools/ToolPaint.py:242 +msgid "Cut Z" +msgstr "Schnitttiefe Z" + +#: AppDatabase.py:165 AppDatabase.py:848 AppDatabase.py:1143 +msgid "MultiDepth" +msgstr "Mehrfache Durchgänge" + +# Abbrev. unclear: Depth Per Pass? +# Perhaps better not translate +#: AppDatabase.py:166 AppDatabase.py:850 AppDatabase.py:1156 +msgid "DPP" +msgstr "DPP" + +#: AppDatabase.py:167 AppDatabase.py:852 AppDatabase.py:1312 +msgid "V-Dia" +msgstr "V-Durchm." + +#: AppDatabase.py:168 AppDatabase.py:854 AppDatabase.py:1326 +msgid "V-Angle" +msgstr "Winkel der V-Form" + +#: AppDatabase.py:169 AppDatabase.py:856 AppDatabase.py:1170 +#: AppGUI/ObjectUI.py:946 AppGUI/ObjectUI.py:1852 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:134 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:101 +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:61 +#: AppObjects/FlatCAMExcellon.py:1396 AppObjects/FlatCAMGeometry.py:1660 +#: AppTools/ToolCalibration.py:74 +msgid "Travel Z" +msgstr "Bewegungshöhe Z (Travel)" + +# I think this is FeedRate XY +#: AppDatabase.py:170 AppDatabase.py:858 +msgid "FR" +msgstr "Vorschub (XY)" + +#: AppDatabase.py:171 AppDatabase.py:860 +msgid "FR Z" +msgstr "Vorschub (Z)" + +#: AppDatabase.py:172 AppDatabase.py:862 AppDatabase.py:1340 +msgid "FR Rapids" +msgstr "Vorschub ohne Last" + +#: AppDatabase.py:173 AppDatabase.py:864 AppDatabase.py:1213 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:222 +msgid "Spindle Speed" +msgstr "Drehgeschwindigkeit" + +#: AppDatabase.py:174 AppDatabase.py:866 AppDatabase.py:1228 +#: AppGUI/ObjectUI.py:1064 AppGUI/ObjectUI.py:1959 +msgid "Dwell" +msgstr "Warten zum Beschleunigen" + +#: AppDatabase.py:175 AppDatabase.py:868 AppDatabase.py:1241 +msgid "Dwelltime" +msgstr "Wartezeit zum Beschleunigen" + +#: AppDatabase.py:176 AppDatabase.py:870 AppGUI/ObjectUI.py:2116 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:257 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:254 +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:237 +#: AppTools/ToolSolderPaste.py:335 +msgid "Preprocessor" +msgstr "Postprozessor" + +#: AppDatabase.py:177 AppDatabase.py:872 AppDatabase.py:1356 +msgid "ExtraCut" +msgstr "Zusätzlicher Schnitt" + +#: AppDatabase.py:178 AppDatabase.py:874 AppDatabase.py:1371 +msgid "E-Cut Length" +msgstr "Extra Schnittlänge" + +#: AppDatabase.py:179 AppDatabase.py:876 +msgid "Toolchange" +msgstr "Werkzeugwechsel" + +#: AppDatabase.py:180 AppDatabase.py:878 +msgid "Toolchange XY" +msgstr "Werkzeugwechsel XY" + +#: AppDatabase.py:181 AppDatabase.py:880 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:160 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:131 +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:98 +#: AppTools/ToolCalibration.py:111 +msgid "Toolchange Z" +msgstr "Werkzeugwechsel Z" + +#: AppDatabase.py:182 AppDatabase.py:882 AppGUI/ObjectUI.py:1193 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:69 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:54 +msgid "Start Z" +msgstr "Start Z" + +#: AppDatabase.py:183 AppDatabase.py:885 +msgid "End Z" +msgstr "Ende Z" + +#: AppDatabase.py:187 +msgid "Tool Index." +msgstr "Werkzeugverzeichnis." + +#: AppDatabase.py:189 AppDatabase.py:1089 +msgid "" +"Tool name.\n" +"This is not used in the app, it's function\n" +"is to serve as a note for the user." +msgstr "" +"Werkzeugname\n" +"Wird in der App nicht verwendet,\n" +"sondern dient als Kommentar für den Nutzer." + +#: AppDatabase.py:193 AppDatabase.py:1102 +msgid "Tool Diameter." +msgstr "Werkzeugdurchmesser." + +#: AppDatabase.py:195 AppDatabase.py:1283 +msgid "" +"Tool Offset.\n" +"Can be of a few types:\n" +"Path = zero offset\n" +"In = offset inside by half of tool diameter\n" +"Out = offset outside by half of tool diameter\n" +"Custom = custom offset using the Custom Offset value" +msgstr "" +"Werkzeug Offest.\n" +"Folgende Typen sind erlaubt:\n" +"Path: kein Offset\n" +"In: Offset einen halben Werkzeugdurchmesser innerhalb.\n" +"Out: Offset einen halben Werkzeugdurchmesser ausserhalb\n" +"Custom: selbstdefinierter Wert im Feld \"Selbstdefinierter Offset\"" + +#: AppDatabase.py:202 AppDatabase.py:1300 +msgid "" +"Custom Offset.\n" +"A value to be used as offset from the current path." +msgstr "" +"Selbstdefinierter Offset.\n" +"Ein Wert der als Offset zum aktellen Pfad hinzugefügt wird." + +#: AppDatabase.py:205 AppDatabase.py:1267 +msgid "" +"Tool Type.\n" +"Can be:\n" +"Iso = isolation cut\n" +"Rough = rough cut, low feedrate, multiple passes\n" +"Finish = finishing cut, high feedrate" +msgstr "" +"Werkzeugart.\n" +"Erlaubt sind:\n" +"Iso: Isolationsschnitte\n" +"Rough: Roughen, um viel Material abzutragen, geringer Vorschub, viele " +"Durchgänge\n" +"Finish: Finishing, hoher Vorschub" + +#: AppDatabase.py:211 AppDatabase.py:1115 +msgid "" +"Tool Shape. \n" +"Can be:\n" +"C1 ... C4 = circular tool with x flutes\n" +"B = ball tip milling tool\n" +"V = v-shape milling tool" +msgstr "" +"Werkzeugform.\n" +"Erlaubt sind:\n" +"C1 … C4: Runde Form mit x Schneiden\n" +"B: Kugelförmig\n" +"V: V-Förmig" + +#: AppDatabase.py:217 AppDatabase.py:1131 +msgid "" +"Cutting Depth.\n" +"The depth at which to cut into material." +msgstr "" +"Schneidtiefe.\n" +"Eindringtiefe in das Material." + +# MultiDepth is hard to translate, cause it is somewhat artificial. If you need to abbreviate perhaps "MehrfDurchg" could suffice, but stays ugly. +#: AppDatabase.py:220 AppDatabase.py:1145 +msgid "" +"Multi Depth.\n" +"Selecting this will allow cutting in multiple passes,\n" +"each pass adding a DPP parameter depth." +msgstr "" +"Mehrfache Durchgänge.\n" +"Wenn ausgewählt wird der Schnitt in mehreren Stufen\n" +"durchgeführt. Die Schnitttiefe jedes Schnittes ist in DPP angegeben." + +#: AppDatabase.py:224 AppDatabase.py:1158 +msgid "" +"DPP. Depth per Pass.\n" +"The value used to cut into material on each pass." +msgstr "" +"DPP: Tiefe pro Schnitt. Definiert die einzelne Schnitttiefe in mehrfachen " +"Durchgängen." + +#: AppDatabase.py:227 AppDatabase.py:1314 +msgid "" +"V-Dia.\n" +"Diameter of the tip for V-Shape Tools." +msgstr "" +"V-Durchmesser.\n" +"Durchmesser der Spitze eines V-Förmigen Werkzeugs." + +# Typo in english? V-Angle, missing n? +#: AppDatabase.py:230 AppDatabase.py:1328 +msgid "" +"V-Agle.\n" +"Angle at the tip for the V-Shape Tools." +msgstr "" +"V-Winkel.\n" +"Öffnungswinkel an der Spitze eine V-Förmigen Werkzeugs." + +#: AppDatabase.py:233 AppDatabase.py:1172 +msgid "" +"Clearance Height.\n" +"Height at which the milling bit will travel between cuts,\n" +"above the surface of the material, avoiding all fixtures." +msgstr "" +"Freilauf Höhe.\n" +"Die Höhe in der das Fräswerkzeug sich zwischen den Schnitten \n" +"frei bewegen kann ohne auf Hindernisse zu stossen." + +#: AppDatabase.py:237 +msgid "" +"FR. Feedrate\n" +"The speed on XY plane used while cutting into material." +msgstr "" +"FR: Feedrate\n" +"Geschwindkeit beim fräsen. Angegeben in cm pro Minute." + +#: AppDatabase.py:240 +msgid "" +"FR Z. Feedrate Z\n" +"The speed on Z plane." +msgstr "" +"FR Z: Feedrate Z:\n" +"Geschwindigkeit beim Fräsen in Z-Richtung." + +#: AppDatabase.py:243 AppDatabase.py:1342 +msgid "" +"FR Rapids. Feedrate Rapids\n" +"Speed used while moving as fast as possible.\n" +"This is used only by some devices that can't use\n" +"the G0 g-code command. Mostly 3D printers." +msgstr "" +"FR Rapids: Feedrate ohne Last\n" +"Geschwindigkeit die ohne Last gefahren werden kann.\n" +"Wird benutzt bei Geräten die das G0 Kommando nicht \n" +"unterstützen (oft 3D Drucker)." + +#: AppDatabase.py:248 AppDatabase.py:1215 +msgid "" +"Spindle Speed.\n" +"If it's left empty it will not be used.\n" +"The speed of the spindle in RPM." +msgstr "" +"Drehzahl.\n" +"Drehzahl des Fräsmotors in U/min.\n" +"Wird nicht benutzt, wenn leer." + +#: AppDatabase.py:252 AppDatabase.py:1230 +msgid "" +"Dwell.\n" +"Check this if a delay is needed to allow\n" +"the spindle motor to reach it's set speed." +msgstr "" +"Verweilen.\n" +"Überprüfen Sie dies, wenn eine Verzögerung erforderlich ist\n" +"Der Spindelmotor erreicht die eingestellte Drehzahl." + +#: AppDatabase.py:256 AppDatabase.py:1243 +msgid "" +"Dwell Time.\n" +"A delay used to allow the motor spindle reach it's set speed." +msgstr "" +"Verweilzeit.\n" +"Eine Verzögerung, mit der die Motorspindel ihre eingestellte Drehzahl " +"erreicht." + +#: AppDatabase.py:259 +msgid "" +"Preprocessor.\n" +"A selection of files that will alter the generated G-code\n" +"to fit for a number of use cases." +msgstr "" +"Präprozessor.\n" +"Diese Dateien werden den erzeugten G-Code modifizieren\n" +"um eine große Anzahl Anwendungsmöglichkeiten zu unterstützen." + +#: AppDatabase.py:263 AppDatabase.py:1358 +msgid "" +"Extra Cut.\n" +"If checked, after a isolation is finished an extra cut\n" +"will be added where the start and end of isolation meet\n" +"such as that this point is covered by this extra cut to\n" +"ensure a complete isolation." +msgstr "" +"Zusatzschnitt.\n" +"Wenn gewählt, wird nach dem Isolationsschnitt ein Zusatzschnitt\n" +"durchgeführt, um Start und Endpunkt definitiv zu verbinden und \n" +"so eine vollständige Isolation zu gewährleisten." + +#: AppDatabase.py:269 AppDatabase.py:1373 +msgid "" +"Extra Cut length.\n" +"If checked, after a isolation is finished an extra cut\n" +"will be added where the start and end of isolation meet\n" +"such as that this point is covered by this extra cut to\n" +"ensure a complete isolation. This is the length of\n" +"the extra cut." +msgstr "" +"Zusatzschnitt.\n" +"Wenn gewählt, wird nach dem Isolationsschnitt ein Zusatzschnitt\n" +"durchgeführt, um Start und Endpunkt definitiv zu verbinden und \n" +"so eine vollständige Isolation zu gewährleisten." + +#: AppDatabase.py:276 +msgid "" +"Toolchange.\n" +"It will create a toolchange event.\n" +"The kind of toolchange is determined by\n" +"the preprocessor file." +msgstr "" +"Werkzeugwechsel.\n" +"Löst ein Werkzeugwechselereignis aus.\n" +"Die Art wie der Werkzeugwechsel durchgeführt wird\n" +"hängt vom gewählten Präprozessor ab." + +#: AppDatabase.py:281 +msgid "" +"Toolchange XY.\n" +"A set of coordinates in the format (x, y).\n" +"Will determine the cartesian position of the point\n" +"where the tool change event take place." +msgstr "" +"Werkzeugwechsel XY\n" +"Ein Satz von Koordinaten im Format (x,y).\n" +"An der Position dieses Punktes wird ein \n" +"Werkzeugwechselereignis ausgelöst." + +# Is this really the height of where a toolchange event takes place or is it the position of where to go to for being able to change the tool? +#: AppDatabase.py:286 +msgid "" +"Toolchange Z.\n" +"The position on Z plane where the tool change event take place." +msgstr "" +"Werkzeugwechsel Z.\n" +"Die Position in der Z Ebene an der ein Werkzeugwechselereignis ausgelöst " +"wird." + +#: AppDatabase.py:289 +msgid "" +"Start Z.\n" +"If it's left empty it will not be used.\n" +"A position on Z plane to move immediately after job start." +msgstr "" +"Start Z.\n" +"Nicht benutzt wenn leer.\n" +"Die Z-Position die zum Start angefahren wird." + +#: AppDatabase.py:293 +msgid "" +"End Z.\n" +"A position on Z plane to move immediately after job stop." +msgstr "" +"End Z.\n" +"Die Z-Position die bei Beendigung des Jobs angefahren wird." + +#: AppDatabase.py:305 AppDatabase.py:682 AppDatabase.py:716 AppDatabase.py:1898 +#: AppDatabase.py:2144 AppDatabase.py:2178 +msgid "Could not load Tools DB file." +msgstr "Werkzeugdatenbank konnte nicht geladen werden." + +#: AppDatabase.py:313 AppDatabase.py:724 AppDatabase.py:1906 +#: AppDatabase.py:2186 +msgid "Failed to parse Tools DB file." +msgstr "Formatfehler beim Einlesen der Werkzeugdatenbank." + +#: AppDatabase.py:316 AppDatabase.py:727 AppDatabase.py:1909 +#: AppDatabase.py:2189 +msgid "Loaded FlatCAM Tools DB from" +msgstr "Geladene FlatCAM Tools DB von" + +#: AppDatabase.py:322 AppDatabase.py:1823 +msgid "Add to DB" +msgstr "Hinzufügen" + +#: AppDatabase.py:324 AppDatabase.py:1826 +msgid "Copy from DB" +msgstr "Von Datenbank kopieren" + +#: AppDatabase.py:326 AppDatabase.py:1829 +msgid "Delete from DB" +msgstr "Aus Datenbank löschen" + +#: AppDatabase.py:603 AppDatabase.py:2044 +msgid "Tool added to DB." +msgstr "Werkzeug wurde zur Werkzeugdatenbank hinzugefügt." + +#: AppDatabase.py:624 AppDatabase.py:2077 +msgid "Tool copied from Tools DB." +msgstr "Das Werkzeug wurde aus der Werkzeugdatenbank kopiert." + +#: AppDatabase.py:642 AppDatabase.py:2104 +msgid "Tool removed from Tools DB." +msgstr "Werkzeug wurde aus der Werkzeugdatenbank gelöscht." + +#: AppDatabase.py:653 AppDatabase.py:2115 +msgid "Export Tools Database" +msgstr "Werkzeugdatenbank exportieren" + +#: AppDatabase.py:656 AppDatabase.py:2118 +msgid "Tools_Database" +msgstr "Werkzeugdatenbank" + +#: AppDatabase.py:663 AppDatabase.py:709 AppDatabase.py:2125 +#: AppDatabase.py:2171 AppEditors/FlatCAMExcEditor.py:1023 +#: AppEditors/FlatCAMExcEditor.py:1091 AppEditors/FlatCAMTextEditor.py:223 +#: AppGUI/MainGUI.py:2690 AppGUI/MainGUI.py:2906 AppGUI/MainGUI.py:3121 +#: AppObjects/ObjectCollection.py:126 AppTools/ToolFilm.py:739 +#: AppTools/ToolFilm.py:885 AppTools/ToolImage.py:247 AppTools/ToolMove.py:269 +#: AppTools/ToolPcbWizard.py:301 AppTools/ToolPcbWizard.py:324 +#: AppTools/ToolQRCode.py:791 AppTools/ToolQRCode.py:838 App_Main.py:1694 +#: App_Main.py:2430 App_Main.py:2465 App_Main.py:2512 App_Main.py:3991 +#: App_Main.py:6529 App_Main.py:6566 App_Main.py:6608 App_Main.py:6637 +#: App_Main.py:6678 App_Main.py:6703 App_Main.py:6755 App_Main.py:6790 +#: App_Main.py:6835 App_Main.py:6876 App_Main.py:6917 App_Main.py:6958 +#: App_Main.py:6999 App_Main.py:7043 App_Main.py:7099 App_Main.py:7131 +#: App_Main.py:7163 App_Main.py:7394 App_Main.py:7432 App_Main.py:7475 +#: App_Main.py:7552 App_Main.py:7607 Bookmark.py:300 Bookmark.py:342 +msgid "Cancelled." +msgstr "Abgebrochen." + +#: AppDatabase.py:671 AppDatabase.py:2133 AppEditors/FlatCAMTextEditor.py:276 +#: AppObjects/FlatCAMCNCJob.py:959 AppTools/ToolFilm.py:1016 +#: AppTools/ToolFilm.py:1197 AppTools/ToolSolderPaste.py:1534 App_Main.py:2520 +#: App_Main.py:7851 App_Main.py:7899 App_Main.py:8024 App_Main.py:8160 +#: Bookmark.py:308 +msgid "" +"Permission denied, saving not possible.\n" +"Most likely another app is holding the file open and not accessible." +msgstr "" +"Berechtigung verweigert, Speichern nicht möglich.\n" +"Wahrscheinlich hält eine andere App die Datei offen oder ist geschützt." + +#: AppDatabase.py:693 AppDatabase.py:696 AppDatabase.py:748 AppDatabase.py:2155 +#: AppDatabase.py:2158 AppDatabase.py:2211 +msgid "Failed to write Tools DB to file." +msgstr "Fehler beim Schreiben der Werkzeugdatenbank in eine Datei." + +#: AppDatabase.py:699 AppDatabase.py:2161 +msgid "Exported Tools DB to" +msgstr "Werkzeugdatenbank wurde exportiert nach" + +#: AppDatabase.py:706 AppDatabase.py:2168 +msgid "Import FlatCAM Tools DB" +msgstr "Import der FlatCAM-Werkzeugdatenbank" + +#: AppDatabase.py:738 AppDatabase.py:913 AppDatabase.py:2200 +#: AppDatabase.py:2418 AppObjects/FlatCAMGeometry.py:947 +#: AppTools/ToolNCC.py:4015 AppTools/ToolNCC.py:4099 AppTools/ToolPaint.py:3558 +#: AppTools/ToolPaint.py:3643 App_Main.py:5172 App_Main.py:5200 +#: App_Main.py:5227 App_Main.py:5247 +msgid "Tools Database" +msgstr "Werkzeugdatenbank" + +#: AppDatabase.py:752 AppDatabase.py:2215 +msgid "Saved Tools DB." +msgstr "Datenbank der gespeicherten Werkzeuge." + +#: AppDatabase.py:899 AppDatabase.py:2405 +msgid "No Tool/row selected in the Tools Database table" +msgstr "" +"Gescheitert. Kein Werkzeug (keine Spalte) in der Werkzeugtabelle ausgewählt" + +#: AppDatabase.py:917 AppDatabase.py:2422 +msgid "Cancelled adding tool from DB." +msgstr "Hinzufügen aus der Datenbank wurde abgebrochen." + +#: AppDatabase.py:1018 +msgid "Basic Geo Parameters" +msgstr "Grundlegende Geoparameter" + +#: AppDatabase.py:1030 +msgid "Advanced Geo Parameters" +msgstr "Erweiterte Geoparameter" + +#: AppDatabase.py:1042 +msgid "NCC Parameters" +msgstr "NCC-Parameter" + +#: AppDatabase.py:1054 +msgid "Paint Parameters" +msgstr "Lackparameter" + +#: AppDatabase.py:1185 AppGUI/ObjectUI.py:967 AppGUI/ObjectUI.py:1871 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:185 +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:148 +#: AppTools/ToolSolderPaste.py:253 +msgid "Feedrate X-Y" +msgstr "Vorschub X-Y" + +#: AppDatabase.py:1187 +msgid "" +"Feedrate X-Y. Feedrate\n" +"The speed on XY plane used while cutting into material." +msgstr "" +"Vorschub X-Y. Vorschubgeschwindigkeit\n" +"Die Geschwindigkeit in der XY-Ebene, die beim Schneiden in Material " +"verwendet wird." + +#: AppDatabase.py:1199 AppGUI/ObjectUI.py:982 AppGUI/ObjectUI.py:1885 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:207 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:200 +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:161 +#: AppTools/ToolSolderPaste.py:265 +msgid "Feedrate Z" +msgstr "Vorschub Z" + +#: AppDatabase.py:1201 +msgid "" +"Feedrate Z\n" +"The speed on Z plane." +msgstr "" +"Vorschub Z.\n" +"Die Geschwindigkeit in der Z-Ebene." + +#: AppDatabase.py:1399 AppGUI/ObjectUI.py:845 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:46 +#: AppTools/ToolNCC.py:341 +msgid "Operation" +msgstr "Operation" + +#: AppDatabase.py:1401 AppTools/ToolNCC.py:343 +msgid "" +"The 'Operation' can be:\n" +"- Isolation -> will ensure that the non-copper clearing is always complete.\n" +"If it's not successful then the non-copper clearing will fail, too.\n" +"- Clear -> the regular non-copper clearing." +msgstr "" +"Die 'Operation' kann sein:\n" +"- Isolierung-> stellt sicher, dass das Löschen ohne Kupfer immer " +"abgeschlossen ist.\n" +"Wenn dies nicht erfolgreich ist, schlägt auch das Löschen ohne Kupfer fehl.\n" +"- Klären-> das reguläre Nicht-Kupfer-löschen." + +#: AppDatabase.py:1408 AppEditors/FlatCAMGrbEditor.py:2740 +#: AppGUI/GUIElements.py:2604 AppTools/ToolNCC.py:350 +msgid "Clear" +msgstr "Klären" + +#: AppDatabase.py:1409 AppTools/ToolNCC.py:351 AppTools/ToolNCC.py:1624 +msgid "Isolation" +msgstr "Isolation" + +#: AppDatabase.py:1417 AppGUI/ObjectUI.py:409 AppGUI/ObjectUI.py:867 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:62 +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:56 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:95 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:137 AppTools/ToolNCC.py:359 +msgid "Milling Type" +msgstr "Fräsart" + +#: AppDatabase.py:1419 AppDatabase.py:1427 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:139 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:147 AppTools/ToolNCC.py:361 +#: AppTools/ToolNCC.py:369 +msgid "" +"Milling type when the selected tool is of type: 'iso_op':\n" +"- climb / best for precision milling and to reduce tool usage\n" +"- conventional / useful when there is no backlash compensation" +msgstr "" +"Frästyp, wenn das ausgewählte Werkzeug vom Typ 'iso_op' ist:\n" +"- Besteigung / am besten zum Präzisionsfräsen und zur Reduzierung des " +"Werkzeugverbrauchs\n" +"- konventionell / nützlich, wenn kein Spielausgleich vorhanden ist" + +#: AppDatabase.py:1424 AppGUI/ObjectUI.py:415 +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:62 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:102 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:144 AppTools/ToolNCC.py:366 +msgid "Climb" +msgstr "Steigen" + +# Cannot translate without context. +#: AppDatabase.py:1425 AppGUI/ObjectUI.py:416 +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:63 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:103 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:145 AppTools/ToolNCC.py:367 +msgid "Conventional" +msgstr "Konventionell" + +#: AppDatabase.py:1437 AppDatabase.py:1546 AppEditors/FlatCAMGeoEditor.py:450 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:182 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:163 +#: AppTools/ToolNCC.py:382 AppTools/ToolPaint.py:328 +msgid "Overlap" +msgstr "Überlappung" + +# Double +#: AppDatabase.py:1439 AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:184 +#: AppTools/ToolNCC.py:384 +msgid "" +"How much (percentage) of the tool width to overlap each tool pass.\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 CNC.\n" +"Higher values = slow processing and slow execution on CNC\n" +"due of too many paths." +msgstr "" +"Wie viel (Prozent) der Werkzeugbreite, um jeden Werkzeugdurchlauf zu " +"überlappen.\n" +"Passen Sie den Wert beginnend mit niedrigeren Werten an\n" +"und es zu erhöhen, wenn noch Bereiche sind, die geräumt werden sollen\n" +"ungeklärt.\n" +"Niedrigere Werte = schnellere Verarbeitung, schnellere Ausführung auf CNC.\n" +"Höhere Werte = langsame Verarbeitung und langsame Ausführung auf CNC\n" +"wegen zu vieler Wege." + +#: AppDatabase.py:1458 AppDatabase.py:1567 AppEditors/FlatCAMGeoEditor.py:470 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:72 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:229 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:59 +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:45 +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:53 +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:66 +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:115 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:202 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:183 +#: AppTools/ToolCopperThieving.py:111 AppTools/ToolCopperThieving.py:362 +#: AppTools/ToolCorners.py:140 AppTools/ToolCutOut.py:190 +#: AppTools/ToolFiducials.py:172 AppTools/ToolInvertGerber.py:88 +#: AppTools/ToolInvertGerber.py:96 AppTools/ToolNCC.py:403 +#: AppTools/ToolPaint.py:349 +msgid "Margin" +msgstr "Marge" + +#: AppDatabase.py:1460 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:74 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:61 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:125 +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:68 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:204 +#: AppTools/ToolCopperThieving.py:113 AppTools/ToolCorners.py:142 +#: AppTools/ToolFiducials.py:174 AppTools/ToolNCC.py:405 +msgid "Bounding box margin." +msgstr "Begrenzungsrahmenrand." + +#: AppDatabase.py:1471 AppDatabase.py:1582 AppEditors/FlatCAMGeoEditor.py:484 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:105 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:106 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:215 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:198 +#: AppTools/ToolExtractDrills.py:128 AppTools/ToolNCC.py:416 +#: AppTools/ToolPaint.py:364 AppTools/ToolPunchGerber.py:139 +msgid "Method" +msgstr "Methode" + +#: AppDatabase.py:1473 AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:217 +#: AppTools/ToolNCC.py:418 +msgid "" +"Algorithm for copper clearing:\n" +"- Standard: Fixed step inwards.\n" +"- Seed-based: Outwards from seed.\n" +"- Line-based: Parallel lines." +msgstr "" +"Algorithmus zur Kupferreinigung:\n" +"- Standard: Schritt nach innen behoben.\n" +"- Samenbasiert: Aus dem Samen heraus.\n" +"- Linienbasiert: Parallele Linien." + +#: AppDatabase.py:1481 AppDatabase.py:1596 AppEditors/FlatCAMGeoEditor.py:498 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:230 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 +#: AppTools/ToolNCC.py:431 AppTools/ToolNCC.py:2212 AppTools/ToolNCC.py:2739 +#: AppTools/ToolNCC.py:2771 AppTools/ToolPaint.py:389 +#: AppTools/ToolPaint.py:1839 tclCommands/TclCommandCopperClear.py:126 +#: tclCommands/TclCommandCopperClear.py:134 tclCommands/TclCommandPaint.py:125 +msgid "Standard" +msgstr "Standard" + +#: AppDatabase.py:1481 AppDatabase.py:1596 AppEditors/FlatCAMGeoEditor.py:498 +#: AppEditors/FlatCAMGeoEditor.py:568 AppEditors/FlatCAMGeoEditor.py:5146 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:230 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 +#: AppTools/ToolNCC.py:431 AppTools/ToolNCC.py:2223 AppTools/ToolNCC.py:2745 +#: AppTools/ToolNCC.py:2777 AppTools/ToolPaint.py:389 +#: AppTools/ToolPaint.py:1853 defaults.py:400 defaults.py:432 +#: tclCommands/TclCommandCopperClear.py:128 +#: tclCommands/TclCommandCopperClear.py:136 tclCommands/TclCommandPaint.py:127 +msgid "Seed" +msgstr "Keim" + +#: AppDatabase.py:1481 AppDatabase.py:1596 AppEditors/FlatCAMGeoEditor.py:498 +#: AppEditors/FlatCAMGeoEditor.py:5150 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:230 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 +#: AppTools/ToolNCC.py:431 AppTools/ToolNCC.py:2234 AppTools/ToolPaint.py:389 +#: AppTools/ToolPaint.py:698 AppTools/ToolPaint.py:1867 +#: tclCommands/TclCommandCopperClear.py:130 tclCommands/TclCommandPaint.py:129 +msgid "Lines" +msgstr "Linien" + +#: AppDatabase.py:1481 AppDatabase.py:1596 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:230 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 +#: AppTools/ToolNCC.py:431 AppTools/ToolNCC.py:2245 AppTools/ToolPaint.py:389 +#: AppTools/ToolPaint.py:2032 tclCommands/TclCommandPaint.py:133 +msgid "Combo" +msgstr "Combo" + +#: AppDatabase.py:1489 AppDatabase.py:1607 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:237 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:224 +#: AppTools/ToolNCC.py:439 AppTools/ToolPaint.py:400 +msgid "Connect" +msgstr "Verbinden" + +#: AppDatabase.py:1493 AppDatabase.py:1610 AppEditors/FlatCAMGeoEditor.py:507 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:239 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:226 +#: AppTools/ToolNCC.py:443 AppTools/ToolPaint.py:403 +msgid "" +"Draw lines between resulting\n" +"segments to minimize tool lifts." +msgstr "" +"Zeichnen Sie Linien zwischen den Ergebnissen\n" +"Segmente, um Werkzeuglifte zu minimieren." + +#: AppDatabase.py:1499 AppDatabase.py:1614 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:246 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:232 +#: AppTools/ToolNCC.py:449 AppTools/ToolPaint.py:407 +msgid "Contour" +msgstr "Kontur" + +#: AppDatabase.py:1503 AppDatabase.py:1617 AppEditors/FlatCAMGeoEditor.py:517 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:248 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:234 +#: AppTools/ToolNCC.py:453 AppTools/ToolPaint.py:410 +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." + +#: AppDatabase.py:1509 AppEditors/FlatCAMGeoEditor.py:611 +#: AppEditors/FlatCAMGrbEditor.py:5289 AppGUI/ObjectUI.py:143 +#: AppGUI/ObjectUI.py:1599 AppGUI/ObjectUI.py:2456 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:255 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:142 +#: AppTools/ToolNCC.py:459 AppTools/ToolTransform.py:28 +msgid "Offset" +msgstr "Versatz" + +#: AppDatabase.py:1513 AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:257 +#: AppTools/ToolNCC.py:463 +msgid "" +"If used, it will add an offset to the copper features.\n" +"The copper clearing will finish to a distance\n" +"from the copper features.\n" +"The value can be between 0 and 10 FlatCAM units." +msgstr "" +"Bei Verwendung wird den Kupferelementen ein Offset hinzugefügt.\n" +"Die Kupferreinigung wird bis zu einer gewissen Entfernung enden\n" +"von den Kupfermerkmalen.\n" +"Der Wert kann zwischen 0 und 10 FlatCAM-Einheiten liegen." + +# 3rd Time +#: AppDatabase.py:1548 AppEditors/FlatCAMGeoEditor.py:452 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:165 +#: AppTools/ToolPaint.py:330 +msgid "" +"How much (percentage) of the tool width to overlap each tool pass.\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 CNC.\n" +"Higher values = slow processing and slow execution on CNC\n" +"due of too many paths." +msgstr "" +"Wie viel (Prozent) der Werkzeugbreite, um jeden Werkzeugdurchlauf zu " +"überlappen.\n" +"Passen Sie den Wert beginnend mit niedrigeren Werten an\n" +"und erhöhen, wenn Bereiche, die gestrichen werden sollen, noch vorhanden " +"sind\n" +"nicht gemalt.\n" +"Niedrigere Werte = schnellere Verarbeitung, schnellere Ausführung auf CNC.\n" +"Höhere Werte = langsame Verarbeitung und langsame Ausführung auf CNC\n" +"wegen zu vieler Wege." + +#: AppDatabase.py:1569 AppEditors/FlatCAMGeoEditor.py:472 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:185 +#: AppTools/ToolPaint.py:351 +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." + +#: AppDatabase.py:1584 AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:200 +#: AppTools/ToolPaint.py:366 +msgid "" +"Algorithm for painting:\n" +"- Standard: Fixed step inwards.\n" +"- Seed-based: Outwards from seed.\n" +"- Line-based: Parallel lines.\n" +"- Laser-lines: Active only for Gerber objects.\n" +"Will create lines that follow the traces.\n" +"- Combo: In case of failure a new method will be picked from the above\n" +"in the order specified." +msgstr "" +"Algorithmus zum Malen:\n" +"- Standard: Schritt nach innen behoben.\n" +"- Samenbasiert: Aus dem Samen heraus.\n" +"- Linienbasiert: Parallele Linien.\n" +"- Laserlinien: Nur für Gerber-Objekte aktiv.\n" +"Erstellt Linien, die den Spuren folgen.\n" +"- Combo: Im Fehlerfall wird eine neue Methode aus den oben genannten " +"ausgewählt\n" +"in der angegebenen Reihenfolge." + +#: AppDatabase.py:1596 AppDatabase.py:1598 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 +#: AppTools/ToolPaint.py:389 AppTools/ToolPaint.py:391 +#: AppTools/ToolPaint.py:692 AppTools/ToolPaint.py:697 +#: AppTools/ToolPaint.py:1881 tclCommands/TclCommandPaint.py:131 +msgid "Laser_lines" +msgstr "LaserlinienLinien" + +#: AppDatabase.py:1641 +msgid "Add Tool in DB" +msgstr "Werkzeug in DB hinzufügen" + +#: AppDatabase.py:1675 +msgid "Save DB" +msgstr "Speichern DB" + +#: AppDatabase.py:1677 +msgid "Save the Tools Database information's." +msgstr "Speichern Sie die Tools-Datenbankinformationen." + +#: AppEditors/FlatCAMExcEditor.py:50 AppEditors/FlatCAMExcEditor.py:74 +#: AppEditors/FlatCAMExcEditor.py:168 AppEditors/FlatCAMExcEditor.py:385 +#: AppEditors/FlatCAMExcEditor.py:589 AppEditors/FlatCAMGrbEditor.py:241 +#: AppEditors/FlatCAMGrbEditor.py:248 +msgid "Click to place ..." +msgstr "Klicken um zu platzieren ..." + +#: AppEditors/FlatCAMExcEditor.py:58 +msgid "To add a drill first select a tool" +msgstr "Um einen Bohrer hinzuzufügen, wählen Sie zuerst ein Werkzeug aus" + +#: AppEditors/FlatCAMExcEditor.py:122 +msgid "Done. Drill added." +msgstr "Erledigt. Bohrer hinzugefügt." + +#: AppEditors/FlatCAMExcEditor.py:176 +msgid "To add an Drill Array first select a tool in Tool Table" +msgstr "" +"Um ein Bohr-Array hinzuzufügen, wählen Sie zunächst ein Werkzeug in der " +"Werkzeugtabelle aus" + +#: AppEditors/FlatCAMExcEditor.py:192 AppEditors/FlatCAMExcEditor.py:415 +#: AppEditors/FlatCAMExcEditor.py:636 AppEditors/FlatCAMExcEditor.py:1151 +#: AppEditors/FlatCAMExcEditor.py:1178 AppEditors/FlatCAMGrbEditor.py:471 +#: AppEditors/FlatCAMGrbEditor.py:1935 AppEditors/FlatCAMGrbEditor.py:1965 +msgid "Click on target location ..." +msgstr "Klicken Sie auf den Zielort ..." + +#: AppEditors/FlatCAMExcEditor.py:211 +msgid "Click on the Drill Circular Array Start position" +msgstr "Klicken Sie auf die Startposition des Bohrkreis-Arrays" + +#: AppEditors/FlatCAMExcEditor.py:233 AppEditors/FlatCAMExcEditor.py:677 +#: AppEditors/FlatCAMGrbEditor.py:516 +msgid "The value is not Float. Check for comma instead of dot separator." +msgstr "" +"Der Wert ist nicht Real. Überprüfen Sie das Komma anstelle des Trennzeichens." + +#: AppEditors/FlatCAMExcEditor.py:237 +msgid "The value is mistyped. Check the value" +msgstr "Der Wert ist falsch geschrieben. Überprüfen Sie den Wert" + +#: AppEditors/FlatCAMExcEditor.py:336 +msgid "Too many drills for the selected spacing angle." +msgstr "Zu viele Bohrer für den ausgewählten Abstandswinkel." + +#: AppEditors/FlatCAMExcEditor.py:354 +msgid "Done. Drill Array added." +msgstr "Erledigt. Bohrfeld hinzugefügt." + +#: AppEditors/FlatCAMExcEditor.py:394 +msgid "To add a slot first select a tool" +msgstr "Um einen Steckplatz hinzuzufügen, wählen Sie zunächst ein Werkzeug aus" + +#: AppEditors/FlatCAMExcEditor.py:454 AppEditors/FlatCAMExcEditor.py:461 +#: AppEditors/FlatCAMExcEditor.py:742 AppEditors/FlatCAMExcEditor.py:749 +msgid "Value is missing or wrong format. Add it and retry." +msgstr "" +"Wert fehlt oder falsches Format. Fügen Sie es hinzu und versuchen Sie es " +"erneut." + +#: AppEditors/FlatCAMExcEditor.py:559 +msgid "Done. Adding Slot completed." +msgstr "Erledigt. Das Hinzufügen des Slots ist abgeschlossen." + +#: AppEditors/FlatCAMExcEditor.py:597 +msgid "To add an Slot Array first select a tool in Tool Table" +msgstr "" +"Um ein Schlitze-Array hinzuzufügen, wählen Sie zunächst ein Werkzeug in der " +"Werkzeugtabelle aus" + +#: AppEditors/FlatCAMExcEditor.py:655 +msgid "Click on the Slot Circular Array Start position" +msgstr "Klicken Sie auf die kreisförmige Startposition des Arrays" + +#: AppEditors/FlatCAMExcEditor.py:680 AppEditors/FlatCAMGrbEditor.py:519 +msgid "The value is mistyped. Check the value." +msgstr "Der Wert ist falsch geschrieben. Überprüfen Sie den Wert." + +#: AppEditors/FlatCAMExcEditor.py:859 +msgid "Too many Slots for the selected spacing angle." +msgstr "Zu viele Slots für den ausgewählten Abstandswinkel." + +#: AppEditors/FlatCAMExcEditor.py:882 +msgid "Done. Slot Array added." +msgstr "Erledigt. Schlitze Array hinzugefügt." + +#: AppEditors/FlatCAMExcEditor.py:904 +msgid "Click on the Drill(s) to resize ..." +msgstr "Klicken Sie auf die Bohrer, um die Größe zu ändern ..." + +#: AppEditors/FlatCAMExcEditor.py:934 +msgid "Resize drill(s) failed. Please enter a diameter for resize." +msgstr "" +"Die Größe der Bohrer ist fehlgeschlagen. Bitte geben Sie einen Durchmesser " +"für die Größenänderung ein." + +#: AppEditors/FlatCAMExcEditor.py:1112 +msgid "Done. Drill/Slot Resize completed." +msgstr "Getan. Bohrer / Schlitz Größenänderung abgeschlossen." + +#: AppEditors/FlatCAMExcEditor.py:1115 +msgid "Cancelled. No drills/slots selected for resize ..." +msgstr "Abgebrochen. Keine Bohrer / Schlitze für Größenänderung ausgewählt ..." + +#: AppEditors/FlatCAMExcEditor.py:1153 AppEditors/FlatCAMGrbEditor.py:1937 +msgid "Click on reference location ..." +msgstr "Klicken Sie auf die Referenzposition ..." + +#: AppEditors/FlatCAMExcEditor.py:1210 +msgid "Done. Drill(s) Move completed." +msgstr "Erledigt. Bohrer Bewegen abgeschlossen." + +#: AppEditors/FlatCAMExcEditor.py:1318 +msgid "Done. Drill(s) copied." +msgstr "Erledigt. Bohrer kopiert." + +#: AppEditors/FlatCAMExcEditor.py:1557 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:26 +msgid "Excellon Editor" +msgstr "Excellon Editor" + +#: AppEditors/FlatCAMExcEditor.py:1564 AppEditors/FlatCAMGrbEditor.py:2460 +msgid "Name:" +msgstr "Name:" + +#: AppEditors/FlatCAMExcEditor.py:1570 AppGUI/ObjectUI.py:761 +#: AppGUI/ObjectUI.py:1567 AppTools/ToolNCC.py:120 AppTools/ToolPaint.py:114 +#: AppTools/ToolSolderPaste.py:74 +msgid "Tools Table" +msgstr "Werkzeugtabelle" + +#: AppEditors/FlatCAMExcEditor.py:1572 AppGUI/ObjectUI.py:763 +msgid "" +"Tools in this Excellon object\n" +"when are used for drilling." +msgstr "" +"Werkzeuge in diesem Excellon-Objekt\n" +"Wann werden zum Bohren verwendet." + +#: AppEditors/FlatCAMExcEditor.py:1584 AppEditors/FlatCAMExcEditor.py:3041 +#: AppGUI/ObjectUI.py:781 AppObjects/FlatCAMExcellon.py:1177 +#: AppObjects/FlatCAMExcellon.py:1268 AppObjects/FlatCAMExcellon.py:1453 +#: AppTools/ToolNCC.py:132 AppTools/ToolPaint.py:127 +#: AppTools/ToolPcbWizard.py:76 AppTools/ToolProperties.py:416 +#: AppTools/ToolProperties.py:476 AppTools/ToolSolderPaste.py:85 +#: tclCommands/TclCommandDrillcncjob.py:195 +msgid "Diameter" +msgstr "Durchmesser" + +#: AppEditors/FlatCAMExcEditor.py:1592 +msgid "Add/Delete Tool" +msgstr "Werkzeug hinzufügen / löschen" + +#: AppEditors/FlatCAMExcEditor.py:1594 +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." + +#: AppEditors/FlatCAMExcEditor.py:1606 AppGUI/ObjectUI.py:1687 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:57 +msgid "Diameter for the new tool" +msgstr "Durchmesser für das neue Werkzeug" + +#: AppEditors/FlatCAMExcEditor.py:1616 +msgid "Add Tool" +msgstr "Werkzeug hinzufügen" + +#: AppEditors/FlatCAMExcEditor.py:1618 +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." + +#: AppEditors/FlatCAMExcEditor.py:1630 +msgid "Delete Tool" +msgstr "Werkzeug löschen" + +#: AppEditors/FlatCAMExcEditor.py:1632 +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." + +#: AppEditors/FlatCAMExcEditor.py:1650 AppGUI/MainGUI.py:4311 +msgid "Resize Drill(s)" +msgstr "Größe der Bohrer ändern" + +#: AppEditors/FlatCAMExcEditor.py:1652 +msgid "Resize a drill or a selection of drills." +msgstr "Ändern Sie die Größe eines Bohrers oder einer Auswahl von Bohrern." + +#: AppEditors/FlatCAMExcEditor.py:1659 +msgid "Resize Dia" +msgstr "Durchmesser ändern" + +#: AppEditors/FlatCAMExcEditor.py:1661 +msgid "Diameter to resize to." +msgstr "Durchmesser zur Größenänderung." + +#: AppEditors/FlatCAMExcEditor.py:1672 +msgid "Resize" +msgstr "Größe ändern" + +#: AppEditors/FlatCAMExcEditor.py:1674 +msgid "Resize drill(s)" +msgstr "Bohrer verkleinern" + +#: AppEditors/FlatCAMExcEditor.py:1699 AppGUI/MainGUI.py:1473 +#: AppGUI/MainGUI.py:4310 +msgid "Add Drill Array" +msgstr "Bohrer-Array hinzufügen" + +#: AppEditors/FlatCAMExcEditor.py:1701 +msgid "Add an array of drills (linear or circular array)" +msgstr "" +"Hinzufügen eines Arrays von Bohrern (lineares oder kreisförmiges Array)" + +#: AppEditors/FlatCAMExcEditor.py:1707 +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" + +#: AppEditors/FlatCAMExcEditor.py:1710 AppEditors/FlatCAMExcEditor.py:1924 +#: AppEditors/FlatCAMGrbEditor.py:2773 +msgid "Linear" +msgstr "Linear" + +#: AppEditors/FlatCAMExcEditor.py:1711 AppEditors/FlatCAMExcEditor.py:1925 +#: AppEditors/FlatCAMGrbEditor.py:2774 AppGUI/ObjectUI.py:316 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:52 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:149 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:107 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:52 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:151 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:61 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:70 +#: AppTools/ToolExtractDrills.py:78 AppTools/ToolExtractDrills.py:201 +#: AppTools/ToolFiducials.py:220 AppTools/ToolNCC.py:221 +#: AppTools/ToolPaint.py:203 AppTools/ToolPunchGerber.py:89 +#: AppTools/ToolPunchGerber.py:229 +msgid "Circular" +msgstr "Kreisförmig" + +#: AppEditors/FlatCAMExcEditor.py:1719 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:68 +msgid "Nr of drills" +msgstr "Anzahl der Bohrer" + +#: AppEditors/FlatCAMExcEditor.py:1720 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:70 +msgid "Specify how many drills to be in the array." +msgstr "Geben Sie an, wie viele Drills im Array enthalten sein sollen." + +#: AppEditors/FlatCAMExcEditor.py:1738 AppEditors/FlatCAMExcEditor.py:1788 +#: AppEditors/FlatCAMExcEditor.py:1860 AppEditors/FlatCAMExcEditor.py:1953 +#: AppEditors/FlatCAMExcEditor.py:2004 AppEditors/FlatCAMGrbEditor.py:1571 +#: AppEditors/FlatCAMGrbEditor.py:2802 AppEditors/FlatCAMGrbEditor.py:2851 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:178 +msgid "Direction" +msgstr "Richtung" + +#: AppEditors/FlatCAMExcEditor.py:1740 AppEditors/FlatCAMExcEditor.py:1955 +#: AppEditors/FlatCAMGrbEditor.py:2804 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:86 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:234 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:122 +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" + +#: AppEditors/FlatCAMExcEditor.py:1747 AppEditors/FlatCAMExcEditor.py:1869 +#: AppEditors/FlatCAMExcEditor.py:1962 AppEditors/FlatCAMGrbEditor.py:2811 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:92 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:187 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:240 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:128 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:208 +#: AppTools/ToolFilm.py:239 +msgid "X" +msgstr "X" + +#: AppEditors/FlatCAMExcEditor.py:1748 AppEditors/FlatCAMExcEditor.py:1870 +#: AppEditors/FlatCAMExcEditor.py:1963 AppEditors/FlatCAMGrbEditor.py:2812 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:93 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:188 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:241 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:129 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:209 +#: AppTools/ToolFilm.py:240 +msgid "Y" +msgstr "Y" + +#: AppEditors/FlatCAMExcEditor.py:1749 AppEditors/FlatCAMExcEditor.py:1766 +#: AppEditors/FlatCAMExcEditor.py:1800 AppEditors/FlatCAMExcEditor.py:1871 +#: AppEditors/FlatCAMExcEditor.py:1875 AppEditors/FlatCAMExcEditor.py:1964 +#: AppEditors/FlatCAMExcEditor.py:1982 AppEditors/FlatCAMExcEditor.py:2016 +#: AppEditors/FlatCAMGrbEditor.py:2813 AppEditors/FlatCAMGrbEditor.py:2830 +#: AppEditors/FlatCAMGrbEditor.py:2866 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:94 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:113 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:189 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:194 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:242 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:263 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:130 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:148 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:53 +#: AppTools/ToolDistance.py:120 AppTools/ToolDistanceMin.py:68 +#: AppTools/ToolTransform.py:60 +msgid "Angle" +msgstr "Winkel" + +#: AppEditors/FlatCAMExcEditor.py:1753 AppEditors/FlatCAMExcEditor.py:1968 +#: AppEditors/FlatCAMGrbEditor.py:2817 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:100 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:248 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:136 +msgid "Pitch" +msgstr "Abstand" + +#: AppEditors/FlatCAMExcEditor.py:1755 AppEditors/FlatCAMExcEditor.py:1970 +#: AppEditors/FlatCAMGrbEditor.py:2819 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:102 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:250 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:138 +msgid "Pitch = Distance between elements of the array." +msgstr "Abstand = Abstand zwischen Elementen des Arrays." + +#: AppEditors/FlatCAMExcEditor.py:1768 AppEditors/FlatCAMExcEditor.py:1984 +msgid "" +"Angle at which the linear array is placed.\n" +"The precision is of max 2 decimals.\n" +"Min value is: -360 degrees.\n" +"Max value is: 360.00 degrees." +msgstr "" +"Winkel, bei dem das lineare Feld platziert wird.\n" +"Die Genauigkeit beträgt maximal 2 Dezimalstellen.\n" +"Der Mindestwert beträgt -360 Grad.\n" +"Maximalwert ist: 360.00 Grad." + +#: AppEditors/FlatCAMExcEditor.py:1789 AppEditors/FlatCAMExcEditor.py:2005 +#: AppEditors/FlatCAMGrbEditor.py:2853 +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." + +#: AppEditors/FlatCAMExcEditor.py:1796 AppEditors/FlatCAMExcEditor.py:2012 +#: AppEditors/FlatCAMGrbEditor.py:2861 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:129 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:136 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:286 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:142 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:170 +msgid "CW" +msgstr "CW" + +#: AppEditors/FlatCAMExcEditor.py:1797 AppEditors/FlatCAMExcEditor.py:2013 +#: AppEditors/FlatCAMGrbEditor.py:2862 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:130 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:137 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:287 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:143 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:171 +msgid "CCW" +msgstr "CCW" + +#: AppEditors/FlatCAMExcEditor.py:1801 AppEditors/FlatCAMExcEditor.py:2017 +#: AppEditors/FlatCAMGrbEditor.py:2868 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:115 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:145 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:265 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:295 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:150 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:179 +msgid "Angle at which each element in circular array is placed." +msgstr "" +"Winkel, um den jedes Element in einer kreisförmigen Anordnung platziert wird." + +#: AppEditors/FlatCAMExcEditor.py:1835 +msgid "Slot Parameters" +msgstr "Schlitze-Parameter" + +#: AppEditors/FlatCAMExcEditor.py:1837 +msgid "" +"Parameters for adding a slot (hole with oval shape)\n" +"either single or as an part of an array." +msgstr "" +"Parameter zum Hinzufügen eines Schlitzes (Loch mit ovaler Form)\n" +"entweder einzeln oder als Teil eines Arrays." + +#: AppEditors/FlatCAMExcEditor.py:1846 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:162 +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:56 +#: AppTools/ToolCorners.py:127 AppTools/ToolProperties.py:559 +msgid "Length" +msgstr "Länge" + +#: AppEditors/FlatCAMExcEditor.py:1848 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:164 +msgid "Length = The length of the slot." +msgstr "Länge = Die Länge des Schlitzes." + +#: AppEditors/FlatCAMExcEditor.py:1862 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:180 +msgid "" +"Direction on which the slot is oriented:\n" +"- 'X' - horizontal axis \n" +"- 'Y' - vertical axis or \n" +"- 'Angle' - a custom angle for the slot inclination" +msgstr "" +"Richtung, in die der Steckplatz ausgerichtet ist:\n" +"- 'X' - horizontale Achse\n" +"- 'Y' - vertikale Achse oder\n" +"- 'Winkel' - Ein benutzerdefinierter Winkel für die Schlitzneigung" + +#: AppEditors/FlatCAMExcEditor.py:1877 +msgid "" +"Angle at which the slot is placed.\n" +"The precision is of max 2 decimals.\n" +"Min value is: -360 degrees.\n" +"Max value is: 360.00 degrees." +msgstr "" +"Winkel, in dem der Schlitz platziert ist.\n" +"Die Genauigkeit beträgt maximal 2 Dezimalstellen.\n" +"Der Mindestwert beträgt: -360 Grad.\n" +"Maximaler Wert ist: 360.00 Grad." + +#: AppEditors/FlatCAMExcEditor.py:1910 +msgid "Slot Array Parameters" +msgstr "Schlitzes Array-Parameter" + +#: AppEditors/FlatCAMExcEditor.py:1912 +msgid "Parameters for the array of slots (linear or circular array)" +msgstr "" +"Parameter für das Array von Schlitzes (lineares oder kreisförmiges Array)" + +#: AppEditors/FlatCAMExcEditor.py:1921 +msgid "" +"Select the type of slot array to create.\n" +"It can be Linear X(Y) or Circular" +msgstr "" +"Wählen Sie den Typ des zu erstellenden Slot-Arrays.\n" +"Es kann ein lineares X (Y) oder ein kreisförmiges sein" + +#: AppEditors/FlatCAMExcEditor.py:1933 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:219 +msgid "Nr of slots" +msgstr "Anzahl der Slots" + +#: AppEditors/FlatCAMExcEditor.py:1934 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:221 +msgid "Specify how many slots to be in the array." +msgstr "Geben Sie an, wie viele Steckplätze sich im Array befinden sollen." + +#: AppEditors/FlatCAMExcEditor.py:2452 AppObjects/FlatCAMExcellon.py:423 +msgid "Total Drills" +msgstr "Bohrungen insgesamt" + +#: AppEditors/FlatCAMExcEditor.py:2484 AppObjects/FlatCAMExcellon.py:454 +msgid "Total Slots" +msgstr "Schlitz insgesamt" + +#: AppEditors/FlatCAMExcEditor.py:2559 AppEditors/FlatCAMGeoEditor.py:1075 +#: AppEditors/FlatCAMGeoEditor.py:1116 AppEditors/FlatCAMGeoEditor.py:1144 +#: AppEditors/FlatCAMGeoEditor.py:1172 AppEditors/FlatCAMGeoEditor.py:1216 +#: AppEditors/FlatCAMGeoEditor.py:1251 AppEditors/FlatCAMGeoEditor.py:1279 +#: AppObjects/FlatCAMGeometry.py:656 AppObjects/FlatCAMGeometry.py:1090 +#: AppObjects/FlatCAMGeometry.py:1830 AppObjects/FlatCAMGeometry.py:2480 +#: AppTools/ToolNCC.py:1498 AppTools/ToolPaint.py:1248 +#: AppTools/ToolPaint.py:1419 AppTools/ToolSolderPaste.py:883 +#: AppTools/ToolSolderPaste.py:956 +msgid "Wrong value format entered, use a number." +msgstr "Falsches Wertformat eingegeben, eine Zahl verwenden." + +#: AppEditors/FlatCAMExcEditor.py:2570 +msgid "" +"Tool already in the original or actual tool list.\n" +"Save and reedit Excellon if you need to add this tool. " +msgstr "" +"Werkzeug bereits in der ursprünglichen oder tatsächlichen Werkzeugliste.\n" +"Speichern Sie Excellon und bearbeiten Sie es erneut, wenn Sie dieses Tool " +"hinzufügen müssen. " + +#: AppEditors/FlatCAMExcEditor.py:2579 AppGUI/MainGUI.py:3318 +msgid "Added new tool with dia" +msgstr "Neues Werkzeug mit Durchmesser hinzugefügt" + +#: AppEditors/FlatCAMExcEditor.py:2612 +msgid "Select a tool in Tool Table" +msgstr "Wählen Sie ein Werkzeug in der Werkzeugtabelle aus" + +#: AppEditors/FlatCAMExcEditor.py:2642 +msgid "Deleted tool with diameter" +msgstr "Gelöschtes Werkzeug mit Durchmesser" + +#: AppEditors/FlatCAMExcEditor.py:2790 +msgid "Done. Tool edit completed." +msgstr "Erledigt. Werkzeugbearbeitung abgeschlossen." + +#: AppEditors/FlatCAMExcEditor.py:3327 +msgid "There are no Tools definitions in the file. Aborting Excellon creation." +msgstr "" +"Die Datei enthält keine Werkzeugdefinitionen. Abbruch der Excellon-" +"Erstellung." + +#: AppEditors/FlatCAMExcEditor.py:3331 +msgid "An internal error has ocurred. See Shell.\n" +msgstr "" +"Ein interner Fehler ist aufgetreten. Siehe Shell.\n" +"\n" + +#: AppEditors/FlatCAMExcEditor.py:3336 +msgid "Creating Excellon." +msgstr "Excellon erstellen." + +#: AppEditors/FlatCAMExcEditor.py:3348 +msgid "Excellon editing finished." +msgstr "Excellon-Bearbeitung abgeschlossen." + +#: AppEditors/FlatCAMExcEditor.py:3365 +msgid "Cancelled. There is no Tool/Drill selected" +msgstr "Abgebrochen. Es ist kein Werkzeug / Bohrer ausgewählt" + +#: AppEditors/FlatCAMExcEditor.py:3599 AppEditors/FlatCAMExcEditor.py:3607 +#: AppEditors/FlatCAMGeoEditor.py:4343 AppEditors/FlatCAMGeoEditor.py:4357 +#: AppEditors/FlatCAMGrbEditor.py:1085 AppEditors/FlatCAMGrbEditor.py:1202 +#: AppEditors/FlatCAMGrbEditor.py:1488 AppEditors/FlatCAMGrbEditor.py:1757 +#: AppEditors/FlatCAMGrbEditor.py:4595 AppEditors/FlatCAMGrbEditor.py:4610 +#: AppGUI/MainGUI.py:2671 AppGUI/MainGUI.py:2683 +#: AppTools/ToolAlignObjects.py:393 AppTools/ToolAlignObjects.py:415 +#: App_Main.py:4649 App_Main.py:4803 +msgid "Done." +msgstr "Fertig." + +#: AppEditors/FlatCAMExcEditor.py:3982 +msgid "Done. Drill(s) deleted." +msgstr "Erledigt. Bohrer gelöscht." + +#: AppEditors/FlatCAMExcEditor.py:4055 AppEditors/FlatCAMExcEditor.py:4065 +#: AppEditors/FlatCAMGrbEditor.py:5041 +msgid "Click on the circular array Center position" +msgstr "Klicken Sie auf die kreisförmige Anordnung in der Mitte" + +#: AppEditors/FlatCAMGeoEditor.py:84 +msgid "Buffer distance:" +msgstr "Pufferabstand:" + +#: AppEditors/FlatCAMGeoEditor.py:85 +msgid "Buffer corner:" +msgstr "Pufferecke:" + +#: AppEditors/FlatCAMGeoEditor.py:87 +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" + +#: AppEditors/FlatCAMGeoEditor.py:93 AppEditors/FlatCAMGrbEditor.py:2629 +msgid "Round" +msgstr "Runden" + +#: AppEditors/FlatCAMGeoEditor.py:94 AppEditors/FlatCAMGrbEditor.py:2630 +#: AppGUI/ObjectUI.py:1370 AppGUI/ObjectUI.py:2204 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:217 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:68 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:175 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:68 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:177 +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:143 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:327 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:291 +#: AppTools/ToolExtractDrills.py:94 AppTools/ToolExtractDrills.py:227 +#: AppTools/ToolNCC.py:583 AppTools/ToolPaint.py:526 +#: AppTools/ToolPunchGerber.py:105 AppTools/ToolPunchGerber.py:255 +#: AppTools/ToolQRCode.py:198 +msgid "Square" +msgstr "Quadrat" + +#: AppEditors/FlatCAMGeoEditor.py:95 AppEditors/FlatCAMGrbEditor.py:2631 +msgid "Beveled" +msgstr "Abgeschrägt" + +#: AppEditors/FlatCAMGeoEditor.py:102 +msgid "Buffer Interior" +msgstr "Pufferinnenraum" + +#: AppEditors/FlatCAMGeoEditor.py:104 +msgid "Buffer Exterior" +msgstr "Puffer außen" + +#: AppEditors/FlatCAMGeoEditor.py:110 +msgid "Full Buffer" +msgstr "Voller Puffer" + +#: AppEditors/FlatCAMGeoEditor.py:131 AppEditors/FlatCAMGeoEditor.py:3016 +#: AppGUI/MainGUI.py:4220 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:190 +msgid "Buffer Tool" +msgstr "Pufferwerkzeug" + +#: AppEditors/FlatCAMGeoEditor.py:143 AppEditors/FlatCAMGeoEditor.py:160 +#: AppEditors/FlatCAMGeoEditor.py:177 AppEditors/FlatCAMGeoEditor.py:3035 +#: AppEditors/FlatCAMGeoEditor.py:3063 AppEditors/FlatCAMGeoEditor.py:3091 +#: AppEditors/FlatCAMGrbEditor.py:5094 +msgid "Buffer distance value is missing or wrong format. Add it and retry." +msgstr "" +"Pufferabstandswert fehlt oder falsches Format. Fügen Sie es hinzu und " +"versuchen Sie es erneut." + +#: AppEditors/FlatCAMGeoEditor.py:241 +msgid "Font" +msgstr "Schrift" + +#: AppEditors/FlatCAMGeoEditor.py:322 AppGUI/MainGUI.py:1411 +msgid "Text" +msgstr "Text" + +#: AppEditors/FlatCAMGeoEditor.py:348 +msgid "Text Tool" +msgstr "Textwerkzeug" + +#: AppEditors/FlatCAMGeoEditor.py:404 AppGUI/MainGUI.py:513 +#: AppGUI/MainGUI.py:1158 AppGUI/ObjectUI.py:818 AppGUI/ObjectUI.py:1764 +#: AppObjects/FlatCAMExcellon.py:821 AppObjects/FlatCAMExcellon.py:1163 +#: AppObjects/FlatCAMGeometry.py:816 AppTools/ToolNCC.py:331 +#: AppTools/ToolNCC.py:797 AppTools/ToolPaint.py:313 AppTools/ToolPaint.py:766 +msgid "Tool" +msgstr "Werkzeug" + +#: AppEditors/FlatCAMGeoEditor.py:438 AppGUI/ObjectUI.py:364 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:43 +msgid "Tool dia" +msgstr "Werkzeugdurchmesser" + +#: AppEditors/FlatCAMGeoEditor.py:440 +msgid "Diameter of the tool to be used in the operation." +msgstr "Durchmesser des im Betrieb zu verwendenden Werkzeugs." + +#: AppEditors/FlatCAMGeoEditor.py:486 +msgid "" +"Algorithm to paint the polygons:\n" +"- Standard: Fixed step inwards.\n" +"- Seed-based: Outwards from seed.\n" +"- Line-based: Parallel lines." +msgstr "" +"Algorithmus zum Malen der Polygone:\n" +"- Standard: Schritt nach innen behoben.\n" +"- Samenbasiert: Aus dem Samen heraus.\n" +"- Linienbasiert: Parallele Linien." + +#: AppEditors/FlatCAMGeoEditor.py:505 +msgid "Connect:" +msgstr "Verbinden:" + +#: AppEditors/FlatCAMGeoEditor.py:515 +msgid "Contour:" +msgstr "Kontur:" + +#: AppEditors/FlatCAMGeoEditor.py:528 AppGUI/MainGUI.py:1415 +msgid "Paint" +msgstr "Malen" + +#: AppEditors/FlatCAMGeoEditor.py:546 AppGUI/MainGUI.py:917 +#: AppGUI/MainGUI.py:1910 AppGUI/ObjectUI.py:2269 AppTools/ToolPaint.py:42 +#: AppTools/ToolPaint.py:737 +msgid "Paint Tool" +msgstr "Werkzeug Malen" + +#: AppEditors/FlatCAMGeoEditor.py:582 AppEditors/FlatCAMGeoEditor.py:1054 +#: AppEditors/FlatCAMGeoEditor.py:3023 AppEditors/FlatCAMGeoEditor.py:3051 +#: AppEditors/FlatCAMGeoEditor.py:3079 AppEditors/FlatCAMGeoEditor.py:4496 +#: AppEditors/FlatCAMGrbEditor.py:5745 +msgid "Cancelled. No shape selected." +msgstr "Abgebrochen. Keine Form ausgewählt." + +#: AppEditors/FlatCAMGeoEditor.py:595 AppEditors/FlatCAMGeoEditor.py:3041 +#: AppEditors/FlatCAMGeoEditor.py:3069 AppEditors/FlatCAMGeoEditor.py:3097 +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:59 +#: AppTools/ToolProperties.py:117 AppTools/ToolProperties.py:162 +msgid "Tools" +msgstr "Werkzeuge" + +#: AppEditors/FlatCAMGeoEditor.py:606 AppEditors/FlatCAMGeoEditor.py:990 +#: AppEditors/FlatCAMGrbEditor.py:5284 AppEditors/FlatCAMGrbEditor.py:5681 +#: AppGUI/MainGUI.py:938 AppGUI/MainGUI.py:1931 AppTools/ToolTransform.py:460 +msgid "Transform Tool" +msgstr "Werkzeug Umwandeln" + +#: AppEditors/FlatCAMGeoEditor.py:607 AppEditors/FlatCAMGeoEditor.py:672 +#: AppEditors/FlatCAMGrbEditor.py:5285 AppEditors/FlatCAMGrbEditor.py:5350 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:45 +#: AppTools/ToolTransform.py:24 AppTools/ToolTransform.py:466 +msgid "Rotate" +msgstr "Drehen" + +#: AppEditors/FlatCAMGeoEditor.py:608 AppEditors/FlatCAMGrbEditor.py:5286 +#: AppTools/ToolTransform.py:25 +msgid "Skew/Shear" +msgstr "Neigung/Schere" + +#: AppEditors/FlatCAMGeoEditor.py:609 AppEditors/FlatCAMGrbEditor.py:2678 +#: AppEditors/FlatCAMGrbEditor.py:5287 AppGUI/MainGUI.py:1060 +#: AppGUI/MainGUI.py:1458 AppGUI/MainGUI.py:2053 AppGUI/MainGUI.py:4432 +#: AppGUI/ObjectUI.py:125 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:95 +#: AppTools/ToolTransform.py:26 +msgid "Scale" +msgstr "Skalieren" + +#: AppEditors/FlatCAMGeoEditor.py:610 AppEditors/FlatCAMGrbEditor.py:5288 +#: AppTools/ToolTransform.py:27 +msgid "Mirror (Flip)" +msgstr "Spiegeln (Flip)" + +#: AppEditors/FlatCAMGeoEditor.py:624 AppEditors/FlatCAMGrbEditor.py:5302 +#: AppGUI/MainGUI.py:849 AppGUI/MainGUI.py:1844 +msgid "Editor" +msgstr "Editor" + +#: AppEditors/FlatCAMGeoEditor.py:656 AppEditors/FlatCAMGrbEditor.py:5334 +msgid "Angle:" +msgstr "Winkel:" + +#: AppEditors/FlatCAMGeoEditor.py:658 AppEditors/FlatCAMGrbEditor.py:5336 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:55 +#: AppTools/ToolTransform.py:62 +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." + +#: AppEditors/FlatCAMGeoEditor.py:674 AppEditors/FlatCAMGrbEditor.py:5352 +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." + +#: AppEditors/FlatCAMGeoEditor.py:697 AppEditors/FlatCAMGrbEditor.py:5375 +msgid "Angle X:" +msgstr "Winkel X:" + +#: AppEditors/FlatCAMGeoEditor.py:699 AppEditors/FlatCAMGeoEditor.py:719 +#: AppEditors/FlatCAMGrbEditor.py:5377 AppEditors/FlatCAMGrbEditor.py:5397 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:74 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:88 +#: AppTools/ToolCalibration.py:505 AppTools/ToolCalibration.py:518 +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." + +#: AppEditors/FlatCAMGeoEditor.py:710 AppEditors/FlatCAMGrbEditor.py:5388 +#: AppTools/ToolTransform.py:467 +msgid "Skew X" +msgstr "Neigung X" + +#: AppEditors/FlatCAMGeoEditor.py:712 AppEditors/FlatCAMGeoEditor.py:732 +#: AppEditors/FlatCAMGrbEditor.py:5390 AppEditors/FlatCAMGrbEditor.py:5410 +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." + +#: AppEditors/FlatCAMGeoEditor.py:717 AppEditors/FlatCAMGrbEditor.py:5395 +msgid "Angle Y:" +msgstr "Winkel Y:" + +#: AppEditors/FlatCAMGeoEditor.py:730 AppEditors/FlatCAMGrbEditor.py:5408 +#: AppTools/ToolTransform.py:468 +msgid "Skew Y" +msgstr "Neigung Y" + +#: AppEditors/FlatCAMGeoEditor.py:758 AppEditors/FlatCAMGrbEditor.py:5436 +msgid "Factor X:" +msgstr "Faktor X:" + +#: AppEditors/FlatCAMGeoEditor.py:760 AppEditors/FlatCAMGrbEditor.py:5438 +#: AppTools/ToolCalibration.py:469 +msgid "Factor for Scale action over X axis." +msgstr "Faktor für die Skalierungsaktion über der X-Achse." + +#: AppEditors/FlatCAMGeoEditor.py:770 AppEditors/FlatCAMGrbEditor.py:5448 +#: AppTools/ToolTransform.py:469 +msgid "Scale X" +msgstr "Maßstab X" + +#: AppEditors/FlatCAMGeoEditor.py:772 AppEditors/FlatCAMGeoEditor.py:791 +#: AppEditors/FlatCAMGrbEditor.py:5450 AppEditors/FlatCAMGrbEditor.py:5469 +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." + +#: AppEditors/FlatCAMGeoEditor.py:777 AppEditors/FlatCAMGrbEditor.py:5455 +msgid "Factor Y:" +msgstr "Faktor Y:" + +#: AppEditors/FlatCAMGeoEditor.py:779 AppEditors/FlatCAMGrbEditor.py:5457 +#: AppTools/ToolCalibration.py:481 +msgid "Factor for Scale action over Y axis." +msgstr "Faktor für die Skalierungsaktion über der Y-Achse." + +#: AppEditors/FlatCAMGeoEditor.py:789 AppEditors/FlatCAMGrbEditor.py:5467 +#: AppTools/ToolTransform.py:470 +msgid "Scale Y" +msgstr "Maßstab Y" + +#: AppEditors/FlatCAMGeoEditor.py:798 AppEditors/FlatCAMGrbEditor.py:5476 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:124 +#: AppTools/ToolTransform.py:189 +msgid "Link" +msgstr "Verknüpfung" + +#: AppEditors/FlatCAMGeoEditor.py:800 AppEditors/FlatCAMGrbEditor.py:5478 +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." + +#: AppEditors/FlatCAMGeoEditor.py:806 AppEditors/FlatCAMGrbEditor.py:5484 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:132 +#: AppTools/ToolTransform.py:196 +msgid "Scale Reference" +msgstr "Skalenreferenz" + +#: AppEditors/FlatCAMGeoEditor.py:808 AppEditors/FlatCAMGrbEditor.py:5486 +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." + +#: AppEditors/FlatCAMGeoEditor.py:836 AppEditors/FlatCAMGrbEditor.py:5515 +msgid "Value X:" +msgstr "Wert X:" + +#: AppEditors/FlatCAMGeoEditor.py:838 AppEditors/FlatCAMGrbEditor.py:5517 +msgid "Value for Offset action on X axis." +msgstr "Wert für die Offset-Aktion auf der X-Achse." + +#: AppEditors/FlatCAMGeoEditor.py:848 AppEditors/FlatCAMGrbEditor.py:5527 +#: AppTools/ToolTransform.py:473 +msgid "Offset X" +msgstr "Versatz X" + +#: AppEditors/FlatCAMGeoEditor.py:850 AppEditors/FlatCAMGeoEditor.py:870 +#: AppEditors/FlatCAMGrbEditor.py:5529 AppEditors/FlatCAMGrbEditor.py:5549 +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" + +#: AppEditors/FlatCAMGeoEditor.py:856 AppEditors/FlatCAMGrbEditor.py:5535 +msgid "Value Y:" +msgstr "Wert Y:" + +#: AppEditors/FlatCAMGeoEditor.py:858 AppEditors/FlatCAMGrbEditor.py:5537 +msgid "Value for Offset action on Y axis." +msgstr "Wert für die Offset-Aktion auf der Y-Achse." + +#: AppEditors/FlatCAMGeoEditor.py:868 AppEditors/FlatCAMGrbEditor.py:5547 +#: AppTools/ToolTransform.py:474 +msgid "Offset Y" +msgstr "Versatz Y" + +#: AppEditors/FlatCAMGeoEditor.py:899 AppEditors/FlatCAMGrbEditor.py:5578 +#: AppTools/ToolTransform.py:475 +msgid "Flip on X" +msgstr "Flip auf X" + +#: AppEditors/FlatCAMGeoEditor.py:901 AppEditors/FlatCAMGeoEditor.py:908 +#: AppEditors/FlatCAMGrbEditor.py:5580 AppEditors/FlatCAMGrbEditor.py:5587 +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." + +#: AppEditors/FlatCAMGeoEditor.py:906 AppEditors/FlatCAMGrbEditor.py:5585 +#: AppTools/ToolTransform.py:476 +msgid "Flip on Y" +msgstr "Flip auf Y" + +#: AppEditors/FlatCAMGeoEditor.py:914 AppEditors/FlatCAMGrbEditor.py:5593 +msgid "Ref Pt" +msgstr "Ref. Pt" + +#: AppEditors/FlatCAMGeoEditor.py:916 AppEditors/FlatCAMGrbEditor.py:5595 +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" + +#: AppEditors/FlatCAMGeoEditor.py:928 AppEditors/FlatCAMGrbEditor.py:5607 +msgid "Point:" +msgstr "Punkt:" + +#: AppEditors/FlatCAMGeoEditor.py:930 AppEditors/FlatCAMGrbEditor.py:5609 +#: AppTools/ToolTransform.py:299 +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." + +#: AppEditors/FlatCAMGeoEditor.py:938 AppEditors/FlatCAMGrbEditor.py:2581 +#: AppEditors/FlatCAMGrbEditor.py:5619 AppGUI/ObjectUI.py:1697 +#: AppTools/ToolDblSided.py:192 AppTools/ToolDblSided.py:425 +#: AppTools/ToolNCC.py:294 AppTools/ToolNCC.py:631 AppTools/ToolPaint.py:276 +#: AppTools/ToolPaint.py:675 AppTools/ToolSolderPaste.py:122 +#: AppTools/ToolSolderPaste.py:597 AppTools/ToolTransform.py:478 +#: App_Main.py:5593 +msgid "Add" +msgstr "Hinzufügen" + +#: AppEditors/FlatCAMGeoEditor.py:940 AppEditors/FlatCAMGrbEditor.py:5621 +#: AppTools/ToolTransform.py:309 +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." + +#: AppEditors/FlatCAMGeoEditor.py:1303 AppEditors/FlatCAMGrbEditor.py:5929 +msgid "No shape selected. Please Select a shape to rotate!" +msgstr "Keine Form ausgewählt Bitte wählen Sie eine Form zum Drehen aus!" + +#: AppEditors/FlatCAMGeoEditor.py:1306 AppEditors/FlatCAMGrbEditor.py:5932 +#: AppTools/ToolTransform.py:679 +msgid "Appying Rotate" +msgstr "Anwenden Drehen" + +#: AppEditors/FlatCAMGeoEditor.py:1332 AppEditors/FlatCAMGrbEditor.py:5964 +msgid "Done. Rotate completed." +msgstr "Erledigt. Drehen abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:1334 +msgid "Rotation action was not executed" +msgstr "Rotationsaktion wurde nicht ausgeführt" + +#: AppEditors/FlatCAMGeoEditor.py:1353 AppEditors/FlatCAMGrbEditor.py:5983 +msgid "No shape selected. Please Select a shape to flip!" +msgstr "Keine Form ausgewählt. Bitte wählen Sie eine Form zum Kippen!" + +#: AppEditors/FlatCAMGeoEditor.py:1356 AppEditors/FlatCAMGrbEditor.py:5986 +#: AppTools/ToolTransform.py:728 +msgid "Applying Flip" +msgstr "Flip anwenden" + +#: AppEditors/FlatCAMGeoEditor.py:1385 AppEditors/FlatCAMGrbEditor.py:6024 +#: AppTools/ToolTransform.py:769 +msgid "Flip on the Y axis done" +msgstr "Spiegeln Sie die Y-Achse bereit" + +#: AppEditors/FlatCAMGeoEditor.py:1389 AppEditors/FlatCAMGrbEditor.py:6033 +#: AppTools/ToolTransform.py:778 +msgid "Flip on the X axis done" +msgstr "Spiegeln Sie die X-Achse bereit" + +#: AppEditors/FlatCAMGeoEditor.py:1397 +msgid "Flip action was not executed" +msgstr "Spiegeln-Aktion wurde nicht ausgeführt" + +#: AppEditors/FlatCAMGeoEditor.py:1415 AppEditors/FlatCAMGrbEditor.py:6053 +msgid "No shape selected. Please Select a shape to shear/skew!" +msgstr "" +"Keine Form ausgewählt. Bitte wählen Sie eine Form zum Scheren / " +"Schrägstellen!" + +#: AppEditors/FlatCAMGeoEditor.py:1418 AppEditors/FlatCAMGrbEditor.py:6056 +#: AppTools/ToolTransform.py:801 +msgid "Applying Skew" +msgstr "Schräglauf anwenden" + +#: AppEditors/FlatCAMGeoEditor.py:1441 AppEditors/FlatCAMGrbEditor.py:6090 +msgid "Skew on the X axis done" +msgstr "Schrägstellung auf der X-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1443 AppEditors/FlatCAMGrbEditor.py:6092 +msgid "Skew on the Y axis done" +msgstr "Schrägstellung auf der Y-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1446 +msgid "Skew action was not executed" +msgstr "Die Versatzaktion wurde nicht ausgeführt" + +#: AppEditors/FlatCAMGeoEditor.py:1468 AppEditors/FlatCAMGrbEditor.py:6114 +msgid "No shape selected. Please Select a shape to scale!" +msgstr "Keine Form ausgewählt. Bitte wählen Sie eine zu skalierende Form!" + +#: AppEditors/FlatCAMGeoEditor.py:1471 AppEditors/FlatCAMGrbEditor.py:6117 +#: AppTools/ToolTransform.py:847 +msgid "Applying Scale" +msgstr "Maßstab anwenden" + +#: AppEditors/FlatCAMGeoEditor.py:1503 AppEditors/FlatCAMGrbEditor.py:6154 +msgid "Scale on the X axis done" +msgstr "Skalieren auf der X-Achse erledigt" + +#: AppEditors/FlatCAMGeoEditor.py:1505 AppEditors/FlatCAMGrbEditor.py:6156 +msgid "Scale on the Y axis done" +msgstr "Skalieren auf der Y-Achse erledigt" + +#: AppEditors/FlatCAMGeoEditor.py:1507 +msgid "Scale action was not executed" +msgstr "Skalierungsaktion wurde nicht ausgeführt" + +#: AppEditors/FlatCAMGeoEditor.py:1522 AppEditors/FlatCAMGrbEditor.py:6173 +msgid "No shape selected. Please Select a shape to offset!" +msgstr "Keine Form ausgewählt. Bitte wählen Sie eine zu versetzende Form!" + +#: AppEditors/FlatCAMGeoEditor.py:1525 AppEditors/FlatCAMGrbEditor.py:6176 +#: AppTools/ToolTransform.py:897 +msgid "Applying Offset" +msgstr "Offsetdruck anwenden" + +#: AppEditors/FlatCAMGeoEditor.py:1535 AppEditors/FlatCAMGrbEditor.py:6197 +msgid "Offset on the X axis done" +msgstr "Versatz auf der X-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1537 AppEditors/FlatCAMGrbEditor.py:6199 +msgid "Offset on the Y axis done" +msgstr "Versatz auf der Y-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1540 +msgid "Offset action was not executed" +msgstr "Offsetaktion wurde nicht ausgeführt" + +#: AppEditors/FlatCAMGeoEditor.py:1544 AppEditors/FlatCAMGrbEditor.py:6206 +msgid "Rotate ..." +msgstr "Drehen ..." + +#: AppEditors/FlatCAMGeoEditor.py:1545 AppEditors/FlatCAMGeoEditor.py:1600 +#: AppEditors/FlatCAMGeoEditor.py:1617 AppEditors/FlatCAMGrbEditor.py:6207 +#: AppEditors/FlatCAMGrbEditor.py:6256 AppEditors/FlatCAMGrbEditor.py:6271 +msgid "Enter an Angle Value (degrees)" +msgstr "Geben Sie einen Winkelwert (Grad) ein" + +#: AppEditors/FlatCAMGeoEditor.py:1554 AppEditors/FlatCAMGrbEditor.py:6215 +msgid "Geometry shape rotate done" +msgstr "Geometrieform drehen fertig" + +#: AppEditors/FlatCAMGeoEditor.py:1558 AppEditors/FlatCAMGrbEditor.py:6218 +msgid "Geometry shape rotate cancelled" +msgstr "Geometrieform drehen abgebrochen" + +#: AppEditors/FlatCAMGeoEditor.py:1563 AppEditors/FlatCAMGrbEditor.py:6223 +msgid "Offset on X axis ..." +msgstr "Versatz auf der X-Achse ..." + +#: AppEditors/FlatCAMGeoEditor.py:1564 AppEditors/FlatCAMGeoEditor.py:1583 +#: AppEditors/FlatCAMGrbEditor.py:6224 AppEditors/FlatCAMGrbEditor.py:6241 +msgid "Enter a distance Value" +msgstr "Geben Sie einen Abstandswert ein" + +#: AppEditors/FlatCAMGeoEditor.py:1573 AppEditors/FlatCAMGrbEditor.py:6232 +msgid "Geometry shape offset on X axis done" +msgstr "Geometrieformversatz auf der X-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1577 AppEditors/FlatCAMGrbEditor.py:6235 +msgid "Geometry shape offset X cancelled" +msgstr "[WARNING_NOTCL] Geometrieformversatz X abgebrochen" + +#: AppEditors/FlatCAMGeoEditor.py:1582 AppEditors/FlatCAMGrbEditor.py:6240 +msgid "Offset on Y axis ..." +msgstr "Versatz auf der Y-Achse ..." + +#: AppEditors/FlatCAMGeoEditor.py:1592 AppEditors/FlatCAMGrbEditor.py:6249 +msgid "Geometry shape offset on Y axis done" +msgstr "Geometrieformversatz auf Y-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1596 +msgid "Geometry shape offset on Y axis canceled" +msgstr "Geometrieformversatz auf Y-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1599 AppEditors/FlatCAMGrbEditor.py:6255 +msgid "Skew on X axis ..." +msgstr "Neigung auf der X-Achse ..." + +#: AppEditors/FlatCAMGeoEditor.py:1609 AppEditors/FlatCAMGrbEditor.py:6264 +msgid "Geometry shape skew on X axis done" +msgstr "Geometrieformversatz auf X-Achse" + +#: AppEditors/FlatCAMGeoEditor.py:1613 +msgid "Geometry shape skew on X axis canceled" +msgstr "Geometrieformversatz auf X-Achse" + +#: AppEditors/FlatCAMGeoEditor.py:1616 AppEditors/FlatCAMGrbEditor.py:6270 +msgid "Skew on Y axis ..." +msgstr "Neigung auf der Y-Achse ..." + +#: AppEditors/FlatCAMGeoEditor.py:1626 AppEditors/FlatCAMGrbEditor.py:6279 +msgid "Geometry shape skew on Y axis done" +msgstr "Geometrieformversatz auf Y-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:1630 +msgid "Geometry shape skew on Y axis canceled" +msgstr "Geometrieformversatz auf Y-Achse erfolgt" + +#: AppEditors/FlatCAMGeoEditor.py:2007 AppEditors/FlatCAMGeoEditor.py:2078 +#: AppEditors/FlatCAMGrbEditor.py:1435 AppEditors/FlatCAMGrbEditor.py:1513 +msgid "Click on Center point ..." +msgstr "Klicken Sie auf Mittelpunkt." + +#: AppEditors/FlatCAMGeoEditor.py:2020 AppEditors/FlatCAMGrbEditor.py:1445 +msgid "Click on Perimeter point to complete ..." +msgstr "Klicken Sie auf Umfangspunkt, um den Vorgang abzuschließen." + +#: AppEditors/FlatCAMGeoEditor.py:2052 +msgid "Done. Adding Circle completed." +msgstr "Erledigt. Hinzufügen des Kreises abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:2106 AppEditors/FlatCAMGrbEditor.py:1546 +msgid "Click on Start point ..." +msgstr "Klicken Sie auf Startpunkt ..." + +#: AppEditors/FlatCAMGeoEditor.py:2108 AppEditors/FlatCAMGrbEditor.py:1548 +msgid "Click on Point3 ..." +msgstr "Klicken Sie auf Punkt3 ..." + +#: AppEditors/FlatCAMGeoEditor.py:2110 AppEditors/FlatCAMGrbEditor.py:1550 +msgid "Click on Stop point ..." +msgstr "Klicken Sie auf Haltepunkt ..." + +#: AppEditors/FlatCAMGeoEditor.py:2115 AppEditors/FlatCAMGrbEditor.py:1555 +msgid "Click on Stop point to complete ..." +msgstr "Klicken Sie auf Stopp, um den Vorgang abzuschließen." + +#: AppEditors/FlatCAMGeoEditor.py:2117 AppEditors/FlatCAMGrbEditor.py:1557 +msgid "Click on Point2 to complete ..." +msgstr "Klicken Sie auf Punkt2, um den Vorgang abzuschließen." + +#: AppEditors/FlatCAMGeoEditor.py:2119 AppEditors/FlatCAMGrbEditor.py:1559 +msgid "Click on Center point to complete ..." +msgstr "Klicken Sie auf Mittelpunkt, um den Vorgang abzuschließen." + +#: AppEditors/FlatCAMGeoEditor.py:2131 +#, python-format +msgid "Direction: %s" +msgstr "Richtung: %s" + +#: AppEditors/FlatCAMGeoEditor.py:2145 AppEditors/FlatCAMGrbEditor.py:1585 +msgid "Mode: Start -> Stop -> Center. Click on Start point ..." +msgstr "Modus: Start -> Stopp -> Zentrieren. Klicken Sie auf Startpunkt ..." + +#: AppEditors/FlatCAMGeoEditor.py:2148 AppEditors/FlatCAMGrbEditor.py:1588 +msgid "Mode: Point1 -> Point3 -> Point2. Click on Point1 ..." +msgstr "Modus: Punkt 1 -> Punkt 3 -> Punkt 2. Klicken Sie auf Punkt1 ..." + +#: AppEditors/FlatCAMGeoEditor.py:2151 AppEditors/FlatCAMGrbEditor.py:1591 +msgid "Mode: Center -> Start -> Stop. Click on Center point ..." +msgstr "Modus: Mitte -> Start -> Stopp. Klicken Sie auf Mittelpunkt." + +#: AppEditors/FlatCAMGeoEditor.py:2292 +msgid "Done. Arc completed." +msgstr "Erledigt. Arc abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:2323 AppEditors/FlatCAMGeoEditor.py:2396 +msgid "Click on 1st corner ..." +msgstr "Klicken Sie auf die 1. Ecke ..." + +#: AppEditors/FlatCAMGeoEditor.py:2335 +msgid "Click on opposite corner to complete ..." +msgstr "" +"Klicken Sie auf die gegenüberliegende Ecke, um den Vorgang abzuschließen." + +#: AppEditors/FlatCAMGeoEditor.py:2365 +msgid "Done. Rectangle completed." +msgstr "Erledigt. Rechteck fertiggestellt." + +#: AppEditors/FlatCAMGeoEditor.py:2409 AppTools/ToolNCC.py:1734 +#: AppTools/ToolPaint.py:1627 Common.py:303 +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." + +#: AppEditors/FlatCAMGeoEditor.py:2440 +msgid "Done. Polygon completed." +msgstr "Erledigt. Polygon fertiggestellt." + +#: AppEditors/FlatCAMGeoEditor.py:2454 AppEditors/FlatCAMGeoEditor.py:2519 +#: AppEditors/FlatCAMGrbEditor.py:1111 AppEditors/FlatCAMGrbEditor.py:1322 +msgid "Backtracked one point ..." +msgstr "Einen Punkt zurückverfolgt ..." + +#: AppEditors/FlatCAMGeoEditor.py:2497 +msgid "Done. Path completed." +msgstr "Getan. Pfad abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:2656 +msgid "No shape selected. Select a shape to explode" +msgstr "Keine Form ausgewählt. Wählen Sie eine Form zum Auflösen aus" + +#: AppEditors/FlatCAMGeoEditor.py:2689 +msgid "Done. Polygons exploded into lines." +msgstr "Getan. Polygone explodierten in Linien." + +#: AppEditors/FlatCAMGeoEditor.py:2721 +msgid "MOVE: No shape selected. Select a shape to move" +msgstr "Bewegen: Keine Form ausgewählt. Wähle eine Form zum Bewegen aus" + +#: AppEditors/FlatCAMGeoEditor.py:2724 AppEditors/FlatCAMGeoEditor.py:2744 +msgid " MOVE: Click on reference point ..." +msgstr " Bewegen: Referenzpunkt anklicken ..." + +#: AppEditors/FlatCAMGeoEditor.py:2729 +msgid " Click on destination point ..." +msgstr " Klicken Sie auf den Zielpunkt ..." + +#: AppEditors/FlatCAMGeoEditor.py:2769 +msgid "Done. Geometry(s) Move completed." +msgstr "Erledigt. Geometrie(n) Bewegung abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:2902 +msgid "Done. Geometry(s) Copy completed." +msgstr "Erledigt. Geometrie(n) Kopieren abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:2933 AppEditors/FlatCAMGrbEditor.py:897 +msgid "Click on 1st point ..." +msgstr "Klicken Sie auf den 1. Punkt ..." + +#: AppEditors/FlatCAMGeoEditor.py:2957 +msgid "" +"Font not supported. Only Regular, Bold, Italic and BoldItalic are supported. " +"Error" +msgstr "" +"Schrift wird nicht unterstützt. Es werden nur Regular, Bold, Italic und " +"BoldItalic unterstützt. Error" + +#: AppEditors/FlatCAMGeoEditor.py:2965 +msgid "No text to add." +msgstr "Kein Text zum Hinzufügen." + +#: AppEditors/FlatCAMGeoEditor.py:2975 +msgid " Done. Adding Text completed." +msgstr " Erledigt. Hinzufügen von Text abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:3012 +msgid "Create buffer geometry ..." +msgstr "Puffergeometrie erstellen ..." + +#: AppEditors/FlatCAMGeoEditor.py:3047 AppEditors/FlatCAMGrbEditor.py:5138 +msgid "Done. Buffer Tool completed." +msgstr "Erledigt. Pufferwerkzeug abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:3075 +msgid "Done. Buffer Int Tool completed." +msgstr "Erledigt. Innenpufferwerkzeug abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:3103 +msgid "Done. Buffer Ext Tool completed." +msgstr "Erledigt. Außenpufferwerkzeug abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:3152 AppEditors/FlatCAMGrbEditor.py:2151 +msgid "Select a shape to act as deletion area ..." +msgstr "Wählen Sie eine Form als Löschbereich aus ..." + +#: AppEditors/FlatCAMGeoEditor.py:3154 AppEditors/FlatCAMGeoEditor.py:3180 +#: AppEditors/FlatCAMGeoEditor.py:3186 AppEditors/FlatCAMGrbEditor.py:2153 +msgid "Click to pick-up the erase shape..." +msgstr "Klicken Sie, um die Löschform aufzunehmen ..." + +#: AppEditors/FlatCAMGeoEditor.py:3190 AppEditors/FlatCAMGrbEditor.py:2212 +msgid "Click to erase ..." +msgstr "Klicken zum Löschen ..." + +#: AppEditors/FlatCAMGeoEditor.py:3219 AppEditors/FlatCAMGrbEditor.py:2245 +msgid "Done. Eraser tool action completed." +msgstr "Erledigt. Radiergummi-Aktion abgeschlossen." + +#: AppEditors/FlatCAMGeoEditor.py:3269 +msgid "Create Paint geometry ..." +msgstr "Malen geometrie erstellen ..." + +#: AppEditors/FlatCAMGeoEditor.py:3282 AppEditors/FlatCAMGrbEditor.py:2408 +msgid "Shape transformations ..." +msgstr "Formtransformationen ..." + +#: AppEditors/FlatCAMGeoEditor.py:3338 +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:27 +msgid "Geometry Editor" +msgstr "Geo-Editor" + +#: AppEditors/FlatCAMGeoEditor.py:3344 AppEditors/FlatCAMGrbEditor.py:2486 +#: AppEditors/FlatCAMGrbEditor.py:3943 AppGUI/ObjectUI.py:263 +#: AppGUI/ObjectUI.py:1599 AppGUI/ObjectUI.py:2456 AppTools/ToolCutOut.py:95 +msgid "Type" +msgstr "Typ" + +#: AppEditors/FlatCAMGeoEditor.py:3344 AppGUI/ObjectUI.py:218 +#: AppGUI/ObjectUI.py:742 AppGUI/ObjectUI.py:1535 AppGUI/ObjectUI.py:2365 +#: AppGUI/ObjectUI.py:2669 AppGUI/ObjectUI.py:2736 +#: AppTools/ToolCalibration.py:234 AppTools/ToolFiducials.py:73 +msgid "Name" +msgstr "Name" + +#: AppEditors/FlatCAMGeoEditor.py:3596 +msgid "Ring" +msgstr "Ring" + +#: AppEditors/FlatCAMGeoEditor.py:3598 +msgid "Line" +msgstr "Linie" + +#: AppEditors/FlatCAMGeoEditor.py:3600 AppGUI/MainGUI.py:1405 +#: AppGUI/ObjectUI.py:1371 AppGUI/ObjectUI.py:2205 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:218 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:328 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:292 +#: AppTools/ToolNCC.py:584 AppTools/ToolPaint.py:527 +msgid "Polygon" +msgstr "Polygon" + +#: AppEditors/FlatCAMGeoEditor.py:3602 +msgid "Multi-Line" +msgstr "Mehrzeilig" + +#: AppEditors/FlatCAMGeoEditor.py:3604 +msgid "Multi-Polygon" +msgstr "Multi-Polygon" + +#: AppEditors/FlatCAMGeoEditor.py:3611 +msgid "Geo Elem" +msgstr "Geoelement" + +#: AppEditors/FlatCAMGeoEditor.py:4064 +msgid "Editing MultiGeo Geometry, tool" +msgstr "Bearbeiten von MultiGeo Geometry, Werkzeug" + +#: AppEditors/FlatCAMGeoEditor.py:4066 +msgid "with diameter" +msgstr "mit Durchmesser" + +#: AppEditors/FlatCAMGeoEditor.py:4138 +#, fuzzy +#| msgid "Workspace Settings" +msgid "Grid snap enabled." +msgstr "Arbeitsbereichseinstellungen" + +#: AppEditors/FlatCAMGeoEditor.py:4142 +#, fuzzy +#| msgid "Grid X snapping distance" +msgid "Grid snap disabled." +msgstr "Raster X Fangdistanz" + +#: AppEditors/FlatCAMGeoEditor.py:4503 AppGUI/MainGUI.py:3000 +#: AppGUI/MainGUI.py:3046 AppGUI/MainGUI.py:3064 AppGUI/MainGUI.py:3208 +#: AppGUI/MainGUI.py:3247 AppGUI/MainGUI.py:3259 AppGUI/MainGUI.py:3276 +msgid "Click on target point." +msgstr "Klicken Sie auf den Zielpunkt." + +#: AppEditors/FlatCAMGeoEditor.py:4817 AppEditors/FlatCAMGeoEditor.py:4852 +msgid "A selection of at least 2 geo items is required to do Intersection." +msgstr "" +"Eine Auswahl von mindestens 2 Geo-Elementen ist erforderlich, um die " +"Kreuzung durchzuführen." + +#: AppEditors/FlatCAMGeoEditor.py:4938 AppEditors/FlatCAMGeoEditor.py:5042 +msgid "" +"Negative buffer value is not accepted. Use Buffer interior to generate an " +"'inside' shape" +msgstr "" +"Negativer Pufferwert wird nicht akzeptiert. Verwenden Sie den " +"Pufferinnenraum, um eine Innenform zu erzeugen" + +#: AppEditors/FlatCAMGeoEditor.py:4948 AppEditors/FlatCAMGeoEditor.py:5001 +#: AppEditors/FlatCAMGeoEditor.py:5051 +msgid "Nothing selected for buffering." +msgstr "Nichts ist für die Pufferung ausgewählt." + +#: AppEditors/FlatCAMGeoEditor.py:4953 AppEditors/FlatCAMGeoEditor.py:5005 +#: AppEditors/FlatCAMGeoEditor.py:5056 +msgid "Invalid distance for buffering." +msgstr "Ungültige Entfernung zum Puffern." + +#: AppEditors/FlatCAMGeoEditor.py:4977 AppEditors/FlatCAMGeoEditor.py:5076 +msgid "Failed, the result is empty. Choose a different buffer value." +msgstr "" +"Fehlgeschlagen, das Ergebnis ist leer. Wählen Sie einen anderen Pufferwert." + +#: AppEditors/FlatCAMGeoEditor.py:4988 +msgid "Full buffer geometry created." +msgstr "Volle Puffergeometrie erstellt." + +#: AppEditors/FlatCAMGeoEditor.py:4994 +msgid "Negative buffer value is not accepted." +msgstr "Negativer Pufferwert wird nicht akzeptiert." + +#: AppEditors/FlatCAMGeoEditor.py:5025 +msgid "Failed, the result is empty. Choose a smaller buffer value." +msgstr "" +"Fehlgeschlagen, das Ergebnis ist leer. Wählen Sie einen kleineren Pufferwert." + +#: AppEditors/FlatCAMGeoEditor.py:5035 +msgid "Interior buffer geometry created." +msgstr "Innere Puffergeometrie erstellt." + +#: AppEditors/FlatCAMGeoEditor.py:5086 +msgid "Exterior buffer geometry created." +msgstr "Außenpuffergeometrie erstellt." + +#: AppEditors/FlatCAMGeoEditor.py:5092 +#, python-format +msgid "Could not do Paint. Overlap value has to be less than 100%%." +msgstr "Konnte nicht Malen. Der Überlappungswert muss kleiner als 100 %% sein." + +#: AppEditors/FlatCAMGeoEditor.py:5099 +msgid "Nothing selected for painting." +msgstr "Nichts zum Malen ausgewählt." + +#: AppEditors/FlatCAMGeoEditor.py:5105 +msgid "Invalid value for" +msgstr "Ungültiger Wert für" + +#: AppEditors/FlatCAMGeoEditor.py:5164 +msgid "" +"Could not do Paint. Try a different combination of parameters. Or a " +"different method of Paint" +msgstr "" +"Konnte nicht malen. Probieren Sie eine andere Kombination von Parametern " +"aus. Oder eine andere Malmethode" + +#: AppEditors/FlatCAMGeoEditor.py:5175 +msgid "Paint done." +msgstr "Malen fertig." + +#: AppEditors/FlatCAMGrbEditor.py:211 +msgid "To add an Pad first select a aperture in Aperture Table" +msgstr "" +"Um ein Pad hinzuzufügen, wählen Sie zunächst eine Blende in der Aperture " +"Table aus" + +#: AppEditors/FlatCAMGrbEditor.py:218 AppEditors/FlatCAMGrbEditor.py:418 +msgid "Aperture size is zero. It needs to be greater than zero." +msgstr "Die Größe der Blende ist Null. Es muss größer als Null sein." + +#: AppEditors/FlatCAMGrbEditor.py:371 AppEditors/FlatCAMGrbEditor.py:684 +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'." + +#: AppEditors/FlatCAMGrbEditor.py:383 +msgid "Done. Adding Pad completed." +msgstr "Erledigt. Hinzufügen von Pad abgeschlossen." + +#: AppEditors/FlatCAMGrbEditor.py:410 +msgid "To add an Pad Array first select a aperture in Aperture Table" +msgstr "" +"Um ein Pad-Array hinzuzufügen, wählen Sie zunächst eine Blende in der " +"Aperture-Tabelle aus" + +#: AppEditors/FlatCAMGrbEditor.py:490 +msgid "Click on the Pad Circular Array Start position" +msgstr "Klicken Sie auf die Startposition des Pad-Kreis-Arrays" + +#: AppEditors/FlatCAMGrbEditor.py:710 +msgid "Too many Pads for the selected spacing angle." +msgstr "Zu viele Pad für den ausgewählten Abstandswinkel." + +#: AppEditors/FlatCAMGrbEditor.py:733 +msgid "Done. Pad Array added." +msgstr "Erledigt. Pad Array hinzugefügt." + +#: AppEditors/FlatCAMGrbEditor.py:758 +msgid "Select shape(s) and then click ..." +msgstr "Wählen Sie die Form (en) aus und klicken Sie dann auf ..." + +#: AppEditors/FlatCAMGrbEditor.py:770 +msgid "Failed. Nothing selected." +msgstr "Gescheitert. Nichts ausgewählt." + +#: AppEditors/FlatCAMGrbEditor.py:786 +msgid "" +"Failed. Poligonize works only on geometries belonging to the same aperture." +msgstr "" +"Gescheitert. Poligonize funktioniert nur bei Geometrien, die zur selben " +"Apertur gehören." + +#: AppEditors/FlatCAMGrbEditor.py:840 +msgid "Done. Poligonize completed." +msgstr "Erledigt. Poligonize abgeschlossen." + +#: AppEditors/FlatCAMGrbEditor.py:895 AppEditors/FlatCAMGrbEditor.py:1128 +#: AppEditors/FlatCAMGrbEditor.py:1152 +msgid "Corner Mode 1: 45 degrees ..." +msgstr "Eckmodus 1: 45 Grad ..." + +#: AppEditors/FlatCAMGrbEditor.py:907 AppEditors/FlatCAMGrbEditor.py:1237 +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." + +#: AppEditors/FlatCAMGrbEditor.py:1116 AppEditors/FlatCAMGrbEditor.py:1149 +msgid "Corner Mode 2: Reverse 45 degrees ..." +msgstr "Eckmodus 2: 45 Grad umkehren ..." + +#: AppEditors/FlatCAMGrbEditor.py:1119 AppEditors/FlatCAMGrbEditor.py:1146 +msgid "Corner Mode 3: 90 degrees ..." +msgstr "Eckmodus 3: 90 Grad ..." + +#: AppEditors/FlatCAMGrbEditor.py:1122 AppEditors/FlatCAMGrbEditor.py:1143 +msgid "Corner Mode 4: Reverse 90 degrees ..." +msgstr "Eckmodus 4: Um 90 Grad umkehren ..." + +#: AppEditors/FlatCAMGrbEditor.py:1125 AppEditors/FlatCAMGrbEditor.py:1140 +msgid "Corner Mode 5: Free angle ..." +msgstr "Eckmodus 5: Freiwinkel ..." + +#: AppEditors/FlatCAMGrbEditor.py:1182 AppEditors/FlatCAMGrbEditor.py:1358 +#: AppEditors/FlatCAMGrbEditor.py:1397 +msgid "Track Mode 1: 45 degrees ..." +msgstr "Spurmodus 1: 45 Grad ..." + +#: AppEditors/FlatCAMGrbEditor.py:1338 AppEditors/FlatCAMGrbEditor.py:1392 +msgid "Track Mode 2: Reverse 45 degrees ..." +msgstr "Spurmodus 2: 45 Grad umkehren ..." + +#: AppEditors/FlatCAMGrbEditor.py:1343 AppEditors/FlatCAMGrbEditor.py:1387 +msgid "Track Mode 3: 90 degrees ..." +msgstr "Spurmodus 3: 90 Grad ..." + +#: AppEditors/FlatCAMGrbEditor.py:1348 AppEditors/FlatCAMGrbEditor.py:1382 +msgid "Track Mode 4: Reverse 90 degrees ..." +msgstr "Spurmodus 4: Um 90 Grad umkehren ..." + +#: AppEditors/FlatCAMGrbEditor.py:1353 AppEditors/FlatCAMGrbEditor.py:1377 +msgid "Track Mode 5: Free angle ..." +msgstr "Spurmodus 5: Freiwinkel ..." + +#: AppEditors/FlatCAMGrbEditor.py:1778 +msgid "Scale the selected Gerber apertures ..." +msgstr "Skalieren Sie die ausgewählten Gerber-Öffnungen ..." + +#: AppEditors/FlatCAMGrbEditor.py:1820 +msgid "Buffer the selected apertures ..." +msgstr "Die ausgewählten Öffnungen puffern ..." + +#: AppEditors/FlatCAMGrbEditor.py:1862 +msgid "Mark polygon areas in the edited Gerber ..." +msgstr "Markiere Polygonbereiche im bearbeiteten Gerber ..." + +#: AppEditors/FlatCAMGrbEditor.py:1928 +msgid "Nothing selected to move" +msgstr "Nichts zum Bewegen ausgewählt" + +#: AppEditors/FlatCAMGrbEditor.py:2053 +msgid "Done. Apertures Move completed." +msgstr "Erledigt. Öffnungsbewegung abgeschlossen." + +#: AppEditors/FlatCAMGrbEditor.py:2135 +msgid "Done. Apertures copied." +msgstr "Erledigt. Blende kopiert." + +#: AppEditors/FlatCAMGrbEditor.py:2453 AppGUI/MainGUI.py:1436 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:27 +msgid "Gerber Editor" +msgstr "Gerber-Editor" + +#: AppEditors/FlatCAMGrbEditor.py:2473 AppGUI/ObjectUI.py:228 +#: AppTools/ToolProperties.py:159 +msgid "Apertures" +msgstr "Öffnungen" + +#: AppEditors/FlatCAMGrbEditor.py:2475 AppGUI/ObjectUI.py:230 +msgid "Apertures Table for the Gerber Object." +msgstr "Blendentabelle für das Gerberobjekt." + +#: AppEditors/FlatCAMGrbEditor.py:2486 AppEditors/FlatCAMGrbEditor.py:3943 +#: AppGUI/ObjectUI.py:263 +msgid "Code" +msgstr "Code" + +#: AppEditors/FlatCAMGrbEditor.py:2486 AppEditors/FlatCAMGrbEditor.py:3943 +#: AppGUI/ObjectUI.py:263 +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:103 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:167 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:196 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:43 +#: AppTools/ToolCopperThieving.py:261 AppTools/ToolCopperThieving.py:301 +#: AppTools/ToolFiducials.py:156 +msgid "Size" +msgstr "Größe" + +#: AppEditors/FlatCAMGrbEditor.py:2486 AppEditors/FlatCAMGrbEditor.py:3943 +#: AppGUI/ObjectUI.py:263 +msgid "Dim" +msgstr "Maße" + +#: AppEditors/FlatCAMGrbEditor.py:2491 AppGUI/ObjectUI.py:267 +msgid "Index" +msgstr "Index" + +#: AppEditors/FlatCAMGrbEditor.py:2493 AppEditors/FlatCAMGrbEditor.py:2522 +#: AppGUI/ObjectUI.py:269 +msgid "Aperture Code" +msgstr "Öffnungscode" + +#: AppEditors/FlatCAMGrbEditor.py:2495 AppGUI/ObjectUI.py:271 +msgid "Type of aperture: circular, rectangle, macros etc" +msgstr "Öffnungsart: kreisförmig, rechteckig, Makros usw" + +#: AppEditors/FlatCAMGrbEditor.py:2497 AppGUI/ObjectUI.py:273 +msgid "Aperture Size:" +msgstr "Öffnungsgröße:" + +#: AppEditors/FlatCAMGrbEditor.py:2499 AppGUI/ObjectUI.py:275 +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" + +#: AppEditors/FlatCAMGrbEditor.py:2523 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:58 +msgid "Code for the new aperture" +msgstr "Code für die neue Blende" + +#: AppEditors/FlatCAMGrbEditor.py:2532 +msgid "Aperture Size" +msgstr "Öffnungsgröße" + +#: AppEditors/FlatCAMGrbEditor.py:2534 +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)" + +#: AppEditors/FlatCAMGrbEditor.py:2548 +msgid "Aperture Type" +msgstr "Blendentyp" + +#: AppEditors/FlatCAMGrbEditor.py:2550 +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" + +#: AppEditors/FlatCAMGrbEditor.py:2561 +msgid "Aperture Dim" +msgstr "Öffnungsmaße" + +#: AppEditors/FlatCAMGrbEditor.py:2563 +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)" + +#: AppEditors/FlatCAMGrbEditor.py:2572 +msgid "Add/Delete Aperture" +msgstr "Blende hinzufügen / löschen" + +#: AppEditors/FlatCAMGrbEditor.py:2574 +msgid "Add/Delete an aperture in the aperture table" +msgstr "Eine Blende in der Blendentabelle hinzufügen / löschen" + +#: AppEditors/FlatCAMGrbEditor.py:2583 +msgid "Add a new aperture to the aperture list." +msgstr "Fügen Sie der Blendenliste eine neue Blende hinzu." + +#: AppEditors/FlatCAMGrbEditor.py:2586 AppEditors/FlatCAMGrbEditor.py:2734 +#: AppGUI/MainGUI.py:753 AppGUI/MainGUI.py:1071 AppGUI/MainGUI.py:1487 +#: AppGUI/MainGUI.py:2063 AppGUI/MainGUI.py:4433 AppGUI/ObjectUI.py:1725 +#: AppObjects/FlatCAMGeometry.py:556 AppTools/ToolNCC.py:316 +#: AppTools/ToolNCC.py:637 AppTools/ToolPaint.py:298 AppTools/ToolPaint.py:681 +#: AppTools/ToolSolderPaste.py:128 AppTools/ToolSolderPaste.py:600 +#: App_Main.py:5595 +msgid "Delete" +msgstr "Löschen" + +#: AppEditors/FlatCAMGrbEditor.py:2588 +msgid "Delete a aperture in the aperture list" +msgstr "Löschen Sie eine Blende in der Blendenliste" + +#: AppEditors/FlatCAMGrbEditor.py:2605 +msgid "Buffer Aperture" +msgstr "Pufferblende" + +#: AppEditors/FlatCAMGrbEditor.py:2607 +msgid "Buffer a aperture in the aperture list" +msgstr "Puffern Sie eine Blende in der Blendenliste" + +#: AppEditors/FlatCAMGrbEditor.py:2620 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:194 +msgid "Buffer distance" +msgstr "Pufferabstand" + +#: AppEditors/FlatCAMGrbEditor.py:2621 +msgid "Buffer corner" +msgstr "Pufferecke" + +#: AppEditors/FlatCAMGrbEditor.py:2623 +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" + +#: AppEditors/FlatCAMGrbEditor.py:2638 AppGUI/MainGUI.py:1058 +#: AppGUI/MainGUI.py:1413 AppGUI/MainGUI.py:1456 AppGUI/MainGUI.py:2051 +#: AppGUI/MainGUI.py:4430 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:200 +#: AppTools/ToolTransform.py:29 +msgid "Buffer" +msgstr "Puffer" + +#: AppEditors/FlatCAMGrbEditor.py:2653 +msgid "Scale Aperture" +msgstr "Skalenöffnung" + +#: AppEditors/FlatCAMGrbEditor.py:2655 +msgid "Scale a aperture in the aperture list" +msgstr "Skalieren Sie eine Blende in der Blendenliste" + +#: AppEditors/FlatCAMGrbEditor.py:2663 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:209 +msgid "Scale factor" +msgstr "Skalierungsfaktor" + +#: AppEditors/FlatCAMGrbEditor.py:2665 +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" + +#: AppEditors/FlatCAMGrbEditor.py:2693 +msgid "Mark polygons" +msgstr "Polygone markieren" + +#: AppEditors/FlatCAMGrbEditor.py:2695 +msgid "Mark the polygon areas." +msgstr "Markieren Sie die Polygonbereiche." + +#: AppEditors/FlatCAMGrbEditor.py:2703 +msgid "Area UPPER threshold" +msgstr "Flächenobergrenze" + +#: AppEditors/FlatCAMGrbEditor.py:2705 +msgid "" +"The threshold value, all areas less than this are marked.\n" +"Can have a value between 0.0000 and 9999.9999" +msgstr "" +"Der Schwellenwert, alle Bereiche, die darunter liegen, sind markiert.\n" +"Kann einen Wert zwischen 0,0000 und 9999,9999 haben" + +#: AppEditors/FlatCAMGrbEditor.py:2712 +msgid "Area LOWER threshold" +msgstr "Bereichsuntergrenze" + +#: AppEditors/FlatCAMGrbEditor.py:2714 +msgid "" +"The threshold value, all areas more than this are marked.\n" +"Can have a value between 0.0000 and 9999.9999" +msgstr "" +"Mit dem Schwellwert sind alle Bereiche gekennzeichnet, die darüber " +"hinausgehen.\n" +"Kann einen Wert zwischen 0,0000 und 9999,9999 haben" + +#: AppEditors/FlatCAMGrbEditor.py:2728 +msgid "Mark" +msgstr "Kennzeichen" + +#: AppEditors/FlatCAMGrbEditor.py:2730 +msgid "Mark the polygons that fit within limits." +msgstr "Markieren Sie die Polygone, die in Grenzen passen." + +#: AppEditors/FlatCAMGrbEditor.py:2736 +msgid "Delete all the marked polygons." +msgstr "Löschen Sie alle markierten Polygone." + +#: AppEditors/FlatCAMGrbEditor.py:2742 +msgid "Clear all the markings." +msgstr "Alle Markierungen entfernen." + +#: AppEditors/FlatCAMGrbEditor.py:2762 AppGUI/MainGUI.py:1043 +#: AppGUI/MainGUI.py:2036 AppGUI/MainGUI.py:4430 +msgid "Add Pad Array" +msgstr "Pad-Array hinzufügen" + +#: AppEditors/FlatCAMGrbEditor.py:2764 +msgid "Add an array of pads (linear or circular array)" +msgstr "Hinzufügen eines Arrays von Pads (lineares oder kreisförmiges Array)" + +#: AppEditors/FlatCAMGrbEditor.py:2770 +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" + +#: AppEditors/FlatCAMGrbEditor.py:2781 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:95 +msgid "Nr of pads" +msgstr "Anzahl der Pads" + +#: AppEditors/FlatCAMGrbEditor.py:2783 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:97 +msgid "Specify how many pads to be in the array." +msgstr "Geben Sie an, wie viele Pads sich im Array befinden sollen." + +#: AppEditors/FlatCAMGrbEditor.py:2832 +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." + +#: AppEditors/FlatCAMGrbEditor.py:3326 AppEditors/FlatCAMGrbEditor.py:3330 +msgid "Aperture code value is missing or wrong format. Add it and retry." +msgstr "" +"Blendencodewert fehlt oder falsches Format. Fügen Sie es hinzu und versuchen " +"Sie es erneut." + +#: AppEditors/FlatCAMGrbEditor.py:3366 +msgid "" +"Aperture dimensions value is missing or wrong format. Add it in format " +"(width, height) and retry." +msgstr "" +"Wert für Blendenmaße fehlt oder falsches Format. Fügen Sie es im Format " +"(Breite, Höhe) hinzu und versuchen Sie es erneut." + +#: AppEditors/FlatCAMGrbEditor.py:3379 +msgid "Aperture size value is missing or wrong format. Add it and retry." +msgstr "" +"Der Wert für die Blendengröße fehlt oder das Format ist falsch. Fügen Sie es " +"hinzu und versuchen Sie es erneut." + +#: AppEditors/FlatCAMGrbEditor.py:3390 +msgid "Aperture already in the aperture table." +msgstr "Blende bereits in der Blendentabelle." + +#: AppEditors/FlatCAMGrbEditor.py:3397 +msgid "Added new aperture with code" +msgstr "Neue Blende mit Code hinzugefügt" + +#: AppEditors/FlatCAMGrbEditor.py:3429 +msgid " Select an aperture in Aperture Table" +msgstr " Wählen Sie in Blende Table eine Blende aus" + +#: AppEditors/FlatCAMGrbEditor.py:3437 +msgid "Select an aperture in Aperture Table -->" +msgstr "Wählen Sie in Blende Table eine Blende aus -->" + +#: AppEditors/FlatCAMGrbEditor.py:3451 +msgid "Deleted aperture with code" +msgstr "Blende mit Code gelöscht" + +#: AppEditors/FlatCAMGrbEditor.py:3519 +msgid "Dimensions need two float values separated by comma." +msgstr "Bemaßungen benötigen zwei durch Komma getrennte Gleitkommawerte." + +#: AppEditors/FlatCAMGrbEditor.py:3528 +msgid "Dimensions edited." +msgstr "Abmessungen bearbeitet." + +#: AppEditors/FlatCAMGrbEditor.py:4058 +msgid "Loading Gerber into Editor" +msgstr "Gerber File wird in den Editor geladen" + +#: AppEditors/FlatCAMGrbEditor.py:4186 +msgid "Setting up the UI" +msgstr "UI wird initialisiert" + +#: AppEditors/FlatCAMGrbEditor.py:4187 +#, fuzzy +#| msgid "Adding geometry finished. Preparing the GUI" +msgid "Adding geometry finished. Preparing the AppGUI" +msgstr "Geometrie wurde hinzugefügt. User Interface wird vorbereitet" + +#: AppEditors/FlatCAMGrbEditor.py:4196 +msgid "Finished loading the Gerber object into the editor." +msgstr "Gerber-Objekte wurde in den Editor geladen." + +#: AppEditors/FlatCAMGrbEditor.py:4335 +msgid "" +"There are no Aperture definitions in the file. Aborting Gerber creation." +msgstr "" +"Die Datei enthält keine Aperture-Definitionen. Abbruch der Gerber-Erstellung." + +#: AppEditors/FlatCAMGrbEditor.py:4338 AppObjects/AppObject.py:133 +#: AppObjects/FlatCAMGeometry.py:1775 AppParsers/ParseExcellon.py:896 +#: AppTools/ToolPcbWizard.py:432 App_Main.py:8369 App_Main.py:8433 +#: App_Main.py:8564 App_Main.py:8629 App_Main.py:9281 +msgid "An internal error has occurred. See shell.\n" +msgstr "Ein interner Fehler ist aufgetreten. Siehe Shell.\n" + +#: AppEditors/FlatCAMGrbEditor.py:4345 +msgid "Creating Gerber." +msgstr "Gerber erstellen." + +#: AppEditors/FlatCAMGrbEditor.py:4354 +msgid "Done. Gerber editing finished." +msgstr "Erledigt. Gerber-Bearbeitung beendet." + +#: AppEditors/FlatCAMGrbEditor.py:4372 +msgid "Cancelled. No aperture is selected" +msgstr "Abgebrochen. Es ist keine Blende ausgewählt" + +#: AppEditors/FlatCAMGrbEditor.py:4527 App_Main.py:5921 +msgid "Coordinates copied to clipboard." +msgstr "Koordinaten in die Zwischenablage kopiert." + +#: AppEditors/FlatCAMGrbEditor.py:4970 +msgid "Failed. No aperture geometry is selected." +msgstr "Gescheitert. Es ist keine Aperturgeometrie ausgewählt." + +#: AppEditors/FlatCAMGrbEditor.py:4979 AppEditors/FlatCAMGrbEditor.py:5250 +msgid "Done. Apertures geometry deleted." +msgstr "Fertig. Blendengeometrie gelöscht." + +#: AppEditors/FlatCAMGrbEditor.py:5122 +msgid "No aperture to buffer. Select at least one aperture and try again." +msgstr "" +"Keine Blende zum Puffern Wählen Sie mindestens eine Blende und versuchen Sie " +"es erneut." + +#: AppEditors/FlatCAMGrbEditor.py:5134 +msgid "Failed." +msgstr "Gescheitert." + +#: AppEditors/FlatCAMGrbEditor.py:5153 +msgid "Scale factor value is missing or wrong format. Add it and retry." +msgstr "" +"Der Skalierungsfaktor ist nicht vorhanden oder das Format ist falsch. Fügen " +"Sie es hinzu und versuchen Sie es erneut." + +#: AppEditors/FlatCAMGrbEditor.py:5185 +msgid "No aperture to scale. Select at least one aperture and try again." +msgstr "" +"Keine zu skalierende Blende Wählen Sie mindestens eine Blende und versuchen " +"Sie es erneut." + +#: AppEditors/FlatCAMGrbEditor.py:5201 +msgid "Done. Scale Tool completed." +msgstr "Erledigt. Skalierungswerkzeug abgeschlossen." + +#: AppEditors/FlatCAMGrbEditor.py:5239 +msgid "Polygons marked." +msgstr "Polygone markiert." + +#: AppEditors/FlatCAMGrbEditor.py:5242 +msgid "No polygons were marked. None fit within the limits." +msgstr "Es wurden keine Polygone markiert. Keiner passt in die Grenzen." + +#: AppEditors/FlatCAMGrbEditor.py:5966 +msgid "Rotation action was not executed." +msgstr "Rotationsaktion wurde nicht ausgeführt." + +#: AppEditors/FlatCAMGrbEditor.py:6037 App_Main.py:5354 App_Main.py:5402 +msgid "Flip action was not executed." +msgstr "Flip-Aktion wurde nicht ausgeführt." + +#: AppEditors/FlatCAMGrbEditor.py:6094 +msgid "Skew action was not executed." +msgstr "Die Versatzaktion wurde nicht ausgeführt." + +#: AppEditors/FlatCAMGrbEditor.py:6159 +msgid "Scale action was not executed." +msgstr "Skalierungsaktion wurde nicht ausgeführt." + +#: AppEditors/FlatCAMGrbEditor.py:6202 +msgid "Offset action was not executed." +msgstr "Offsetaktion wurde nicht ausgeführt." + +#: AppEditors/FlatCAMGrbEditor.py:6252 +msgid "Geometry shape offset Y cancelled" +msgstr "Geometrieform-Versatz Y abgebrochen" + +#: AppEditors/FlatCAMGrbEditor.py:6267 +msgid "Geometry shape skew X cancelled" +msgstr "Geometrieformverzerren X abgebrochen" + +#: AppEditors/FlatCAMGrbEditor.py:6282 +msgid "Geometry shape skew Y cancelled" +msgstr "Geometrieformverzerren Y abgebrochen" + +#: AppEditors/FlatCAMTextEditor.py:74 +msgid "Print Preview" +msgstr "Druckvorschau" + +#: AppEditors/FlatCAMTextEditor.py:75 +msgid "Open a OS standard Preview Print window." +msgstr "" +"Öffnen Sie ein Standardfenster für die Druckvorschau des Betriebssystems." + +#: AppEditors/FlatCAMTextEditor.py:78 +msgid "Print Code" +msgstr "Code drucken" + +#: AppEditors/FlatCAMTextEditor.py:79 +msgid "Open a OS standard Print window." +msgstr "Öffnen Sie ein Betriebssystem-Standard-Druckfenster." + +#: AppEditors/FlatCAMTextEditor.py:81 +msgid "Find in Code" +msgstr "Im Code suchen" + +#: AppEditors/FlatCAMTextEditor.py:82 +msgid "Will search and highlight in yellow the string in the Find box." +msgstr "Sucht und hebt die Zeichenfolge im Feld Suchen gelb hervor." + +#: AppEditors/FlatCAMTextEditor.py:86 +msgid "Find box. Enter here the strings to be searched in the text." +msgstr "" +"Suchfeld. Geben Sie hier die Zeichenfolgen ein, nach denen im Text gesucht " +"werden soll." + +#: AppEditors/FlatCAMTextEditor.py:88 +msgid "Replace With" +msgstr "Ersetzen mit" + +#: AppEditors/FlatCAMTextEditor.py:89 +msgid "" +"Will replace the string from the Find box with the one in the Replace box." +msgstr "" +"Ersetzt die Zeichenfolge aus dem Feld Suchen durch die Zeichenfolge aus dem " +"Feld Ersetzen." + +#: AppEditors/FlatCAMTextEditor.py:93 +msgid "String to replace the one in the Find box throughout the text." +msgstr "" +"Zeichenfolge, die die Zeichenfolge im Feld Suchen im gesamten Text ersetzt." + +#: AppEditors/FlatCAMTextEditor.py:95 AppGUI/ObjectUI.py:486 +#: AppGUI/ObjectUI.py:2349 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:54 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:88 +msgid "All" +msgstr "Alles" + +#: AppEditors/FlatCAMTextEditor.py:96 +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\" .." + +#: AppEditors/FlatCAMTextEditor.py:99 +msgid "Copy All" +msgstr "Kopiere alles" + +#: AppEditors/FlatCAMTextEditor.py:100 +msgid "Will copy all the text in the Code Editor to the clipboard." +msgstr "Kopiert den gesamten Text im Code-Editor in die Zwischenablage." + +#: AppEditors/FlatCAMTextEditor.py:103 +msgid "Open Code" +msgstr "Code öffnen" + +#: AppEditors/FlatCAMTextEditor.py:104 +msgid "Will open a text file in the editor." +msgstr "Öffnet eine Textdatei im Editor." + +#: AppEditors/FlatCAMTextEditor.py:106 +msgid "Save Code" +msgstr "Code speichern" + +#: AppEditors/FlatCAMTextEditor.py:107 +msgid "Will save the text in the editor into a file." +msgstr "Speichert den Text im Editor in einer Datei." + +#: AppEditors/FlatCAMTextEditor.py:109 +msgid "Run Code" +msgstr "Code ausführen" + +#: AppEditors/FlatCAMTextEditor.py:110 +msgid "Will run the TCL commands found in the text file, one by one." +msgstr "Führt die in der Textdatei enthaltenen TCL-Befehle nacheinander aus." + +#: AppEditors/FlatCAMTextEditor.py:184 +msgid "Open file" +msgstr "Datei öffnen" + +#: AppEditors/FlatCAMTextEditor.py:215 AppEditors/FlatCAMTextEditor.py:220 +msgid "Export Code ..." +msgstr "Code exportieren ..." + +#: AppEditors/FlatCAMTextEditor.py:272 AppObjects/FlatCAMCNCJob.py:955 +#: AppTools/ToolSolderPaste.py:1530 +msgid "No such file or directory" +msgstr "Keine solche Datei oder Ordner" + +#: AppEditors/FlatCAMTextEditor.py:284 AppObjects/FlatCAMCNCJob.py:969 +msgid "Saved to" +msgstr "Gespeichert in" + +#: AppEditors/FlatCAMTextEditor.py:334 +msgid "Code Editor content copied to clipboard ..." +msgstr "Code Editor Inhalt in die Zwischenablage kopiert ..." + +#: AppGUI/GUIElements.py:2540 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:180 +#: AppTools/ToolDblSided.py:173 AppTools/ToolDblSided.py:388 +#: AppTools/ToolFilm.py:202 +msgid "Reference" +msgstr "Referenz" + +#: AppGUI/GUIElements.py:2542 +msgid "" +"The reference can be:\n" +"- Absolute -> the reference point is point (0,0)\n" +"- Relative -> the reference point is the mouse position before Jump" +msgstr "" +"Die Referenz kann sein:\n" +"- Absolut -> Der Bezugspunkt ist Punkt (0,0)\n" +"- Relativ -> Der Referenzpunkt ist die Mausposition vor dem Sprung" + +#: AppGUI/GUIElements.py:2547 +msgid "Abs" +msgstr "Abs" + +#: AppGUI/GUIElements.py:2548 +msgid "Relative" +msgstr "Relativ" + +#: AppGUI/GUIElements.py:2558 +msgid "Location" +msgstr "Ort" + +#: AppGUI/GUIElements.py:2560 +msgid "" +"The Location value is a tuple (x,y).\n" +"If the reference is Absolute then the Jump will be at the position (x,y).\n" +"If the reference is Relative then the Jump will be at the (x,y) distance\n" +"from the current mouse location point." +msgstr "" +"Der Standortwert ist ein Tupel (x, y).\n" +"Wenn die Referenz Absolut ist, befindet sich der Sprung an der Position (x, " +"y).\n" +"Wenn die Referenz relativ ist, befindet sich der Sprung in der Entfernung " +"(x, y)\n" +"vom aktuellen Mausstandort aus." + +#: AppGUI/GUIElements.py:2600 +msgid "Save Log" +msgstr "Protokoll speichern" + +#: AppGUI/GUIElements.py:2610 App_Main.py:2657 App_Main.py:3018 +msgid "Close" +msgstr "Schließen" + +#: AppGUI/GUIElements.py:2619 AppTools/ToolShell.py:278 +msgid "Type >help< to get started" +msgstr "Geben Sie> help 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 ..." + +#: AppGUI/MainGUI.py:275 +msgid "Export &Gerber ..." +msgstr "Gerber exportieren ..." + +#: AppGUI/MainGUI.py:277 +msgid "" +"Will export an Gerber Object as Gerber file,\n" +"the coordinates format, the file units and zeros\n" +"are set in Preferences -> Gerber Export." +msgstr "" +"Exportiert ein Gerber-Objekt als Gerber-Datei.\n" +"das Koordinatenformat, die Dateieinheiten und Nullen\n" +"werden in den Einstellungen -> Gerber Export eingestellt." + +#: AppGUI/MainGUI.py:287 +msgid "Backup" +msgstr "Sicherungskopie" + +#: AppGUI/MainGUI.py:292 +msgid "Import Preferences from file ..." +msgstr "Einstellungen aus Datei importieren ..." + +#: AppGUI/MainGUI.py:298 +msgid "Export Preferences to file ..." +msgstr "Einstellungen in Datei exportieren ..." + +#: AppGUI/MainGUI.py:306 AppGUI/preferences/PreferencesUIManager.py:1176 +msgid "Save Preferences" +msgstr "Einstellungen speichern" + +#: AppGUI/MainGUI.py:312 AppGUI/MainGUI.py:4022 +msgid "Print (PDF)" +msgstr "Drucken (PDF)" + +#: AppGUI/MainGUI.py:320 +msgid "E&xit" +msgstr "Ausgang" + +#: AppGUI/MainGUI.py:328 AppGUI/MainGUI.py:749 AppGUI/MainGUI.py:1489 +msgid "Edit" +msgstr "Bearbeiten" + +#: AppGUI/MainGUI.py:332 +msgid "Edit Object\tE" +msgstr "Objekt bearbeiten\tE" + +#: AppGUI/MainGUI.py:334 +msgid "Close Editor\tCtrl+S" +msgstr "Schließen Sie Editor\tSTRG+S" + +#: AppGUI/MainGUI.py:343 +msgid "Conversion" +msgstr "Umwandlung" + +#: AppGUI/MainGUI.py:345 +msgid "&Join Geo/Gerber/Exc -> Geo" +msgstr "Geo/Gerber/Exc -> Geo zusammenfassen" + +#: AppGUI/MainGUI.py:347 +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." + +#: AppGUI/MainGUI.py:354 +msgid "Join Excellon(s) -> Excellon" +msgstr "Excellon(s) -> Excellon zusammenfassen" + +#: AppGUI/MainGUI.py:356 +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." + +#: AppGUI/MainGUI.py:359 +msgid "Join Gerber(s) -> Gerber" +msgstr "Gerber(s) -> Gerber zusammenfassen" + +#: AppGUI/MainGUI.py:361 +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." + +#: AppGUI/MainGUI.py:366 +msgid "Convert Single to MultiGeo" +msgstr "Konvertieren Sie Single in MultiGeo" + +#: AppGUI/MainGUI.py:368 +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." + +#: AppGUI/MainGUI.py:372 +msgid "Convert Multi to SingleGeo" +msgstr "Konvertieren Sie Multi in SingleGeo" + +#: AppGUI/MainGUI.py:374 +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." + +#: AppGUI/MainGUI.py:381 +msgid "Convert Any to Geo" +msgstr "Konvertieren Sie Any zu Geo" + +#: AppGUI/MainGUI.py:384 +msgid "Convert Any to Gerber" +msgstr "Konvertieren Sie Any zu Gerber" + +#: AppGUI/MainGUI.py:390 +msgid "&Copy\tCtrl+C" +msgstr "Kopieren\tSTRG+C" + +#: AppGUI/MainGUI.py:395 +msgid "&Delete\tDEL" +msgstr "Löschen\tDEL" + +#: AppGUI/MainGUI.py:400 +msgid "Se&t Origin\tO" +msgstr "Ursprung festlegen\tO" + +#: AppGUI/MainGUI.py:402 +msgid "Move to Origin\tShift+O" +msgstr "Zum Ursprung wechseln\tShift+O" + +#: AppGUI/MainGUI.py:405 +msgid "Jump to Location\tJ" +msgstr "Zum Ort springen\tJ" + +#: AppGUI/MainGUI.py:407 +msgid "Locate in Object\tShift+J" +msgstr "Suchen Sie im Objekt\tShift+J" + +#: AppGUI/MainGUI.py:412 +msgid "Toggle Units\tQ" +msgstr "Einheiten umschalten\tQ" + +#: AppGUI/MainGUI.py:414 +msgid "&Select All\tCtrl+A" +msgstr "Alles auswählen\tSTRG+A" + +#: AppGUI/MainGUI.py:419 +msgid "&Preferences\tShift+P" +msgstr "Einstellungen\tShift+P" + +#: AppGUI/MainGUI.py:425 AppTools/ToolProperties.py:155 +msgid "Options" +msgstr "Optionen" + +#: AppGUI/MainGUI.py:427 +msgid "&Rotate Selection\tShift+(R)" +msgstr "Auswahl drehen\tShift+(R)" + +#: AppGUI/MainGUI.py:432 +msgid "&Skew on X axis\tShift+X" +msgstr "Neigung auf der X-Achse\tShift+X" + +#: AppGUI/MainGUI.py:434 +msgid "S&kew on Y axis\tShift+Y" +msgstr "Neigung auf der Y-Achse\tShift+Y" + +#: AppGUI/MainGUI.py:439 +msgid "Flip on &X axis\tX" +msgstr "X-Achse kippen\tX" + +#: AppGUI/MainGUI.py:441 +msgid "Flip on &Y axis\tY" +msgstr "Y-Achse kippen\tY" + +#: AppGUI/MainGUI.py:446 +msgid "View source\tAlt+S" +msgstr "Quelltext anzeigen\tAlt+S" + +#: AppGUI/MainGUI.py:448 +msgid "Tools DataBase\tCtrl+D" +msgstr "Werkzeugdatenbank\tSTRG+D" + +#: AppGUI/MainGUI.py:455 AppGUI/MainGUI.py:1386 +msgid "View" +msgstr "Aussicht" + +#: AppGUI/MainGUI.py:457 +msgid "Enable all plots\tAlt+1" +msgstr "Alle Diagramme aktivieren\tAlt+1" + +#: AppGUI/MainGUI.py:459 +msgid "Disable all plots\tAlt+2" +msgstr "Alle Diagramme deaktivieren\tAlt+2" + +#: AppGUI/MainGUI.py:461 +msgid "Disable non-selected\tAlt+3" +msgstr "Nicht ausgewählte Diagramme deaktivieren\tAlt+3" + +#: AppGUI/MainGUI.py:465 +msgid "&Zoom Fit\tV" +msgstr "Passed zoomen\tV" + +#: AppGUI/MainGUI.py:467 +msgid "&Zoom In\t=" +msgstr "Hineinzoomen\t=" + +#: AppGUI/MainGUI.py:469 +msgid "&Zoom Out\t-" +msgstr "Rauszoomen\t-" + +#: AppGUI/MainGUI.py:474 +msgid "Redraw All\tF5" +msgstr "Alles neu zeichnen\tF5" + +#: AppGUI/MainGUI.py:478 +msgid "Toggle Code Editor\tShift+E" +msgstr "Code-Editor umschalten\tShift+E" + +#: AppGUI/MainGUI.py:481 +msgid "&Toggle FullScreen\tAlt+F10" +msgstr "FullScreen umschalten\tAlt+F10" + +#: AppGUI/MainGUI.py:483 +msgid "&Toggle Plot Area\tCtrl+F10" +msgstr "Plotbereich umschalten\tSTRG+F10" + +#: AppGUI/MainGUI.py:485 +msgid "&Toggle Project/Sel/Tool\t`" +msgstr "Projekt/Auswahl/Werkzeug umschalten\t`" + +#: AppGUI/MainGUI.py:489 +msgid "&Toggle Grid Snap\tG" +msgstr "Schaltet den Rasterfang ein\tG" + +#: AppGUI/MainGUI.py:491 +msgid "&Toggle Grid Lines\tAlt+G" +msgstr "Gitterlinien umschalten\tAlt+G" + +#: AppGUI/MainGUI.py:493 +msgid "&Toggle Axis\tShift+G" +msgstr "Achse umschalten\tShift+G" + +#: AppGUI/MainGUI.py:495 +msgid "Toggle Workspace\tShift+W" +msgstr "Arbeitsbereich umschalten\tShift+W" + +#: AppGUI/MainGUI.py:497 +#, fuzzy +#| msgid "Toggle Units" +msgid "Toggle HUD\tAlt+M" +msgstr "Einheiten wechseln" + +#: AppGUI/MainGUI.py:502 +msgid "Objects" +msgstr "Objekte" + +#: AppGUI/MainGUI.py:505 AppGUI/MainGUI.py:4020 +#: AppObjects/ObjectCollection.py:1120 AppObjects/ObjectCollection.py:1167 +msgid "Select All" +msgstr "Select All" + +#: AppGUI/MainGUI.py:507 AppObjects/ObjectCollection.py:1124 +#: AppObjects/ObjectCollection.py:1171 +msgid "Deselect All" +msgstr "Alle abwählen" + +#: AppGUI/MainGUI.py:516 +msgid "&Command Line\tS" +msgstr "Befehlszeile\tS" + +#: AppGUI/MainGUI.py:521 +msgid "Help" +msgstr "Hilfe" + +#: AppGUI/MainGUI.py:523 +msgid "Online Help\tF1" +msgstr "Onlinehilfe\tF1" + +#: AppGUI/MainGUI.py:526 Bookmark.py:293 +msgid "Bookmarks" +msgstr "Lesezeichen" + +#: AppGUI/MainGUI.py:529 App_Main.py:2989 App_Main.py:2998 +msgid "Bookmarks Manager" +msgstr "Lesezeichen verwalten" + +#: AppGUI/MainGUI.py:533 +msgid "Report a bug" +msgstr "Einen Fehler melden" + +#: AppGUI/MainGUI.py:536 +msgid "Excellon Specification" +msgstr "Excellon-Spezifikation" + +#: AppGUI/MainGUI.py:538 +msgid "Gerber Specification" +msgstr "Gerber-Spezifikation" + +#: AppGUI/MainGUI.py:543 +msgid "Shortcuts List\tF3" +msgstr "Tastenkürzel Liste\tF3" + +#: AppGUI/MainGUI.py:545 +msgid "YouTube Channel\tF4" +msgstr "Youtube Kanal\tF4" + +#: AppGUI/MainGUI.py:547 App_Main.py:2624 +msgid "About FlatCAM" +msgstr "Über FlatCAM" + +#: AppGUI/MainGUI.py:556 +msgid "Add Circle\tO" +msgstr "Kreis hinzufügen\tO" + +#: AppGUI/MainGUI.py:559 +msgid "Add Arc\tA" +msgstr "Bogen hinzufügen\tA" + +#: AppGUI/MainGUI.py:562 +msgid "Add Rectangle\tR" +msgstr "Rechteck hinzufügen\tR" + +#: AppGUI/MainGUI.py:565 +msgid "Add Polygon\tN" +msgstr "Polygon hinzufügen\tN" + +#: AppGUI/MainGUI.py:568 +msgid "Add Path\tP" +msgstr "Pfad hinzufügen\tP" + +#: AppGUI/MainGUI.py:571 +msgid "Add Text\tT" +msgstr "Text hinzufügen\tT" + +#: AppGUI/MainGUI.py:574 +msgid "Polygon Union\tU" +msgstr "Polygon-Vereinigung\tU" + +#: AppGUI/MainGUI.py:576 +msgid "Polygon Intersection\tE" +msgstr "Polygonschnitt\tE" + +#: AppGUI/MainGUI.py:578 +msgid "Polygon Subtraction\tS" +msgstr "Polygon-Subtraktion\tS" + +#: AppGUI/MainGUI.py:582 +msgid "Cut Path\tX" +msgstr "Pfad ausschneiden\tX" + +#: AppGUI/MainGUI.py:586 +msgid "Copy Geom\tC" +msgstr "Geometrie kopieren\tC" + +#: AppGUI/MainGUI.py:588 +msgid "Delete Shape\tDEL" +msgstr "Form löschen\tDEL" + +#: AppGUI/MainGUI.py:592 AppGUI/MainGUI.py:679 +msgid "Move\tM" +msgstr "Bewegung\tM" + +#: AppGUI/MainGUI.py:594 +msgid "Buffer Tool\tB" +msgstr "Pufferwerkzeug\tB" + +#: AppGUI/MainGUI.py:597 +msgid "Paint Tool\tI" +msgstr "Malenwerkzeug\tI" + +#: AppGUI/MainGUI.py:600 +msgid "Transform Tool\tAlt+R" +msgstr "Transformationswerkzeug\tAlt+R" + +#: AppGUI/MainGUI.py:604 +msgid "Toggle Corner Snap\tK" +msgstr "Eckfang umschalten\tK" + +#: AppGUI/MainGUI.py:610 +msgid ">Excellon Editor<" +msgstr ">Excellon Editor<" + +#: AppGUI/MainGUI.py:614 +msgid "Add Drill Array\tA" +msgstr "Bohrfeld hinzufügen\tA" + +#: AppGUI/MainGUI.py:616 +msgid "Add Drill\tD" +msgstr "Bohrer hinzufügen\tD" + +#: AppGUI/MainGUI.py:620 +msgid "Add Slot Array\tQ" +msgstr "Steckplatz-Array hinzufügen\tQ" + +#: AppGUI/MainGUI.py:622 +msgid "Add Slot\tW" +msgstr "Slot hinzufügen\tW" + +#: AppGUI/MainGUI.py:626 +msgid "Resize Drill(S)\tR" +msgstr "Bohrer verkleinern\tR" + +#: AppGUI/MainGUI.py:629 AppGUI/MainGUI.py:673 +msgid "Copy\tC" +msgstr "Kopieren\tC" + +#: AppGUI/MainGUI.py:631 AppGUI/MainGUI.py:675 +msgid "Delete\tDEL" +msgstr "Löschen\tDEL" + +#: AppGUI/MainGUI.py:636 +msgid "Move Drill(s)\tM" +msgstr "Bohrer verschieben\tM" + +#: AppGUI/MainGUI.py:641 +msgid ">Gerber Editor<" +msgstr ">Gerber-Editor<" + +#: AppGUI/MainGUI.py:645 +msgid "Add Pad\tP" +msgstr "Pad hinzufügen\tP" + +#: AppGUI/MainGUI.py:647 +msgid "Add Pad Array\tA" +msgstr "Pad-Array hinzufügen\tA" + +#: AppGUI/MainGUI.py:649 +msgid "Add Track\tT" +msgstr "Track hinzufügen\tA" + +#: AppGUI/MainGUI.py:651 +msgid "Add Region\tN" +msgstr "Region hinzufügen\tN" + +#: AppGUI/MainGUI.py:655 +msgid "Poligonize\tAlt+N" +msgstr "Polygonisieren\tAlt+N" + +#: AppGUI/MainGUI.py:657 +msgid "Add SemiDisc\tE" +msgstr "Halbschibe hinzufügen\tE" + +#: AppGUI/MainGUI.py:659 +msgid "Add Disc\tD" +msgstr "Schibe hinzufügen\tD" + +#: AppGUI/MainGUI.py:661 +msgid "Buffer\tB" +msgstr "Puffer\tB" + +#: AppGUI/MainGUI.py:663 +msgid "Scale\tS" +msgstr "Skalieren\tS" + +#: AppGUI/MainGUI.py:665 +msgid "Mark Area\tAlt+A" +msgstr "Bereich markieren\tAlt+A" + +#: AppGUI/MainGUI.py:667 +msgid "Eraser\tCtrl+E" +msgstr "Radiergummi\tSTRG+E" + +#: AppGUI/MainGUI.py:669 +msgid "Transform\tAlt+R" +msgstr "Transformationswerkzeug\tSTRG+R" + +#: AppGUI/MainGUI.py:696 +msgid "Enable Plot" +msgstr "Diagramm aktivieren" + +#: AppGUI/MainGUI.py:698 +msgid "Disable Plot" +msgstr "Diagramm deaktivieren" + +#: AppGUI/MainGUI.py:702 +msgid "Set Color" +msgstr "Farbsatz" + +#: AppGUI/MainGUI.py:705 App_Main.py:9548 +msgid "Red" +msgstr "Rote" + +#: AppGUI/MainGUI.py:708 App_Main.py:9550 +msgid "Blue" +msgstr "Blau" + +#: AppGUI/MainGUI.py:711 App_Main.py:9553 +msgid "Yellow" +msgstr "Gelb" + +#: AppGUI/MainGUI.py:714 App_Main.py:9555 +msgid "Green" +msgstr "Grün" + +#: AppGUI/MainGUI.py:717 App_Main.py:9557 +msgid "Purple" +msgstr "Lila" + +#: AppGUI/MainGUI.py:720 App_Main.py:9559 +msgid "Brown" +msgstr "Braun" + +#: AppGUI/MainGUI.py:723 App_Main.py:9561 App_Main.py:9617 +msgid "White" +msgstr "Weiß" + +#: AppGUI/MainGUI.py:726 App_Main.py:9563 +msgid "Black" +msgstr "Schwarz" + +#: AppGUI/MainGUI.py:731 AppTools/ToolEtchCompensation.py:110 App_Main.py:9566 +msgid "Custom" +msgstr "Maßgeschn." + +#: AppGUI/MainGUI.py:736 App_Main.py:9600 +msgid "Opacity" +msgstr "Opazität" + +#: AppGUI/MainGUI.py:739 App_Main.py:9576 +msgid "Default" +msgstr "Standard" + +#: AppGUI/MainGUI.py:744 +msgid "Generate CNC" +msgstr "CNC generieren" + +#: AppGUI/MainGUI.py:746 +msgid "View Source" +msgstr "Quelltext anzeigen" + +#: AppGUI/MainGUI.py:751 AppGUI/MainGUI.py:856 AppGUI/MainGUI.py:1069 +#: AppGUI/MainGUI.py:1485 AppGUI/MainGUI.py:1852 AppGUI/MainGUI.py:2061 +#: AppGUI/MainGUI.py:4430 AppGUI/ObjectUI.py:1719 +#: AppObjects/FlatCAMGeometry.py:553 AppTools/ToolPanelize.py:551 +#: AppTools/ToolPanelize.py:578 AppTools/ToolPanelize.py:671 +#: AppTools/ToolPanelize.py:700 AppTools/ToolPanelize.py:762 +msgid "Copy" +msgstr "Kopieren" + +#: AppGUI/MainGUI.py:759 AppGUI/MainGUI.py:1498 AppTools/ToolProperties.py:31 +msgid "Properties" +msgstr "Eigenschaften" + +#: AppGUI/MainGUI.py:788 +msgid "File Toolbar" +msgstr "Dateisymbolleiste" + +#: AppGUI/MainGUI.py:792 +msgid "Edit Toolbar" +msgstr "Symbolleiste bearbeiten" + +#: AppGUI/MainGUI.py:796 +msgid "View Toolbar" +msgstr "Symbolleiste anzeigen" + +#: AppGUI/MainGUI.py:800 +msgid "Shell Toolbar" +msgstr "Shell-Symbolleiste" + +#: AppGUI/MainGUI.py:804 +msgid "Tools Toolbar" +msgstr "Werkzeugleiste" + +#: AppGUI/MainGUI.py:808 +msgid "Excellon Editor Toolbar" +msgstr "Excellon Editor-Symbolleiste" + +#: AppGUI/MainGUI.py:814 +msgid "Geometry Editor Toolbar" +msgstr "Geometrie Editor-Symbolleiste" + +#: AppGUI/MainGUI.py:818 +msgid "Gerber Editor Toolbar" +msgstr "Gerber Editor-Symbolleiste" + +#: AppGUI/MainGUI.py:822 +msgid "Grid Toolbar" +msgstr "Raster-Symbolleiste" + +#: AppGUI/MainGUI.py:836 AppGUI/MainGUI.py:1831 App_Main.py:6513 +#: App_Main.py:6517 +msgid "Open Gerber" +msgstr "Gerber öffnen" + +#: AppGUI/MainGUI.py:838 AppGUI/MainGUI.py:1833 App_Main.py:6551 +#: App_Main.py:6555 +msgid "Open Excellon" +msgstr "Excellon öffnen" + +#: AppGUI/MainGUI.py:841 AppGUI/MainGUI.py:1836 +msgid "Open project" +msgstr "Projekt öffnen" + +#: AppGUI/MainGUI.py:843 AppGUI/MainGUI.py:1838 +msgid "Save project" +msgstr "Projekt speichern" + +#: AppGUI/MainGUI.py:851 AppGUI/MainGUI.py:1847 +msgid "Save Object and close the Editor" +msgstr "Speichern Sie das Objekt und schließen Sie den Editor" + +#: AppGUI/MainGUI.py:858 AppGUI/MainGUI.py:1854 +msgid "&Delete" +msgstr "&Löschen" + +#: AppGUI/MainGUI.py:861 AppGUI/MainGUI.py:1857 AppGUI/MainGUI.py:4021 +#: AppGUI/MainGUI.py:4227 AppTools/ToolDistance.py:35 +#: AppTools/ToolDistance.py:197 +msgid "Distance Tool" +msgstr "Entfernungswerkzeug" + +#: AppGUI/MainGUI.py:863 AppGUI/MainGUI.py:1859 +msgid "Distance Min Tool" +msgstr "Werkzeug für Mindestabstand" + +#: AppGUI/MainGUI.py:865 AppGUI/MainGUI.py:1861 AppGUI/MainGUI.py:4014 +msgid "Set Origin" +msgstr "Nullpunkt festlegen" + +#: AppGUI/MainGUI.py:867 AppGUI/MainGUI.py:1863 +msgid "Move to Origin" +msgstr "Zum Ursprung wechseln" + +#: AppGUI/MainGUI.py:870 AppGUI/MainGUI.py:1865 +msgid "Jump to Location" +msgstr "Zur Position springen\tJ" + +#: AppGUI/MainGUI.py:872 AppGUI/MainGUI.py:1867 AppGUI/MainGUI.py:4026 +msgid "Locate in Object" +msgstr "Suchen Sie im Objekt" + +#: AppGUI/MainGUI.py:878 AppGUI/MainGUI.py:1873 +msgid "&Replot" +msgstr "Neuzeichnen &R" + +#: AppGUI/MainGUI.py:880 AppGUI/MainGUI.py:1875 +msgid "&Clear plot" +msgstr "Darstellung löschen &C" + +#: AppGUI/MainGUI.py:882 AppGUI/MainGUI.py:1877 AppGUI/MainGUI.py:4017 +msgid "Zoom In" +msgstr "Hineinzoomen" + +#: AppGUI/MainGUI.py:884 AppGUI/MainGUI.py:1879 AppGUI/MainGUI.py:4017 +msgid "Zoom Out" +msgstr "Rauszoomen" + +#: AppGUI/MainGUI.py:886 AppGUI/MainGUI.py:1388 AppGUI/MainGUI.py:1881 +#: AppGUI/MainGUI.py:4016 +msgid "Zoom Fit" +msgstr "Passend zoomen" + +#: AppGUI/MainGUI.py:894 AppGUI/MainGUI.py:1887 +msgid "&Command Line" +msgstr "Befehlszeile" + +#: AppGUI/MainGUI.py:906 AppGUI/MainGUI.py:1899 +msgid "2Sided Tool" +msgstr "2Seitiges Werkzeug" + +#: AppGUI/MainGUI.py:908 AppGUI/MainGUI.py:1901 AppGUI/MainGUI.py:4032 +msgid "Align Objects Tool" +msgstr "Werkzeug \"Objekte ausrichten\"" + +#: AppGUI/MainGUI.py:910 AppGUI/MainGUI.py:1903 AppGUI/MainGUI.py:4033 +#: AppTools/ToolExtractDrills.py:393 +msgid "Extract Drills Tool" +msgstr "Bohrer Extrahieren Werkzeug" + +#: AppGUI/MainGUI.py:913 AppGUI/ObjectUI.py:596 AppTools/ToolCutOut.py:440 +msgid "Cutout Tool" +msgstr "Ausschnittwerkzeug" + +#: AppGUI/MainGUI.py:915 AppGUI/MainGUI.py:1908 AppGUI/ObjectUI.py:574 +#: AppGUI/ObjectUI.py:2287 AppTools/ToolNCC.py:974 +msgid "NCC Tool" +msgstr "NCC Werkzeug" + +#: AppGUI/MainGUI.py:921 AppGUI/MainGUI.py:1914 +msgid "Panel Tool" +msgstr "Platte Werkzeug" + +#: AppGUI/MainGUI.py:923 AppGUI/MainGUI.py:1916 AppTools/ToolFilm.py:569 +msgid "Film Tool" +msgstr "Filmwerkzeug" + +#: AppGUI/MainGUI.py:925 AppGUI/MainGUI.py:1918 AppTools/ToolSolderPaste.py:553 +msgid "SolderPaste Tool" +msgstr "Lötpaste-Werkzeug" + +#: AppGUI/MainGUI.py:927 AppGUI/MainGUI.py:1920 AppTools/ToolSub.py:35 +msgid "Subtract Tool" +msgstr "Subtraktionswerkzeug" + +#: AppGUI/MainGUI.py:929 AppGUI/MainGUI.py:1922 AppTools/ToolRulesCheck.py:616 +msgid "Rules Tool" +msgstr "Regelwerkzeug" + +#: AppGUI/MainGUI.py:931 AppGUI/MainGUI.py:1924 AppGUI/MainGUI.py:4035 +#: AppTools/ToolOptimal.py:33 AppTools/ToolOptimal.py:307 +msgid "Optimal Tool" +msgstr "Optimierungswerkzeug" + +#: AppGUI/MainGUI.py:936 AppGUI/MainGUI.py:1929 AppGUI/MainGUI.py:4032 +msgid "Calculators Tool" +msgstr "Rechnerwerkzeug" + +#: AppGUI/MainGUI.py:940 AppGUI/MainGUI.py:1933 AppGUI/MainGUI.py:4036 +#: AppTools/ToolQRCode.py:43 AppTools/ToolQRCode.py:382 +msgid "QRCode Tool" +msgstr "QRCode Werkzeug" + +# Really don't know +#: AppGUI/MainGUI.py:942 AppGUI/MainGUI.py:1935 +#: AppTools/ToolCopperThieving.py:39 AppTools/ToolCopperThieving.py:568 +msgid "Copper Thieving Tool" +msgstr "Copper Thieving Werkzeug" + +# Really don't know +#: AppGUI/MainGUI.py:945 AppGUI/MainGUI.py:1938 AppGUI/MainGUI.py:4033 +#: AppTools/ToolFiducials.py:33 AppTools/ToolFiducials.py:396 +msgid "Fiducials Tool" +msgstr "Passermarken-Tool" + +#: AppGUI/MainGUI.py:947 AppGUI/MainGUI.py:1940 AppTools/ToolCalibration.py:37 +#: AppTools/ToolCalibration.py:759 +msgid "Calibration Tool" +msgstr "Kalibierungswerkzeug" + +#: AppGUI/MainGUI.py:949 AppGUI/MainGUI.py:1942 AppGUI/MainGUI.py:4033 +msgid "Punch Gerber Tool" +msgstr "Stanzen Sie das Gerber-Werkzeug" + +#: AppGUI/MainGUI.py:951 AppGUI/MainGUI.py:1944 AppTools/ToolInvertGerber.py:31 +msgid "Invert Gerber Tool" +msgstr "Invertieren Sie das Gerber-Werkzeug" + +#: AppGUI/MainGUI.py:953 AppGUI/MainGUI.py:1946 AppGUI/MainGUI.py:4035 +#: AppTools/ToolCorners.py:31 +#, fuzzy +#| msgid "Invert Gerber Tool" +msgid "Corner Markers Tool" +msgstr "Invertieren Sie das Gerber-Werkzeug" + +#: AppGUI/MainGUI.py:955 AppGUI/MainGUI.py:1948 +#: AppTools/ToolEtchCompensation.py:31 +#, fuzzy +#| msgid "Editor Transformation Tool" +msgid "Etch Compensation Tool" +msgstr "Editor-Transformationstool" + +#: AppGUI/MainGUI.py:961 AppGUI/MainGUI.py:987 AppGUI/MainGUI.py:1039 +#: AppGUI/MainGUI.py:1954 AppGUI/MainGUI.py:2032 +msgid "Select" +msgstr "Wählen" + +#: AppGUI/MainGUI.py:963 AppGUI/MainGUI.py:1956 +msgid "Add Drill Hole" +msgstr "Bohrloch hinzufügen" + +#: AppGUI/MainGUI.py:965 AppGUI/MainGUI.py:1958 +msgid "Add Drill Hole Array" +msgstr "Bohrlochfeld hinzufügen" + +#: AppGUI/MainGUI.py:967 AppGUI/MainGUI.py:1476 AppGUI/MainGUI.py:1962 +#: AppGUI/MainGUI.py:4312 +msgid "Add Slot" +msgstr "Steckplatz hinzufügen" + +#: AppGUI/MainGUI.py:969 AppGUI/MainGUI.py:1478 AppGUI/MainGUI.py:1964 +#: AppGUI/MainGUI.py:4311 +msgid "Add Slot Array" +msgstr "Steckplatz-Array hinzufügen" + +#: AppGUI/MainGUI.py:971 AppGUI/MainGUI.py:1481 AppGUI/MainGUI.py:1960 +msgid "Resize Drill" +msgstr "Bohrergröße ändern" + +#: AppGUI/MainGUI.py:975 AppGUI/MainGUI.py:1968 +msgid "Copy Drill" +msgstr "Bohrer kopieren" + +#: AppGUI/MainGUI.py:977 AppGUI/MainGUI.py:1970 +msgid "Delete Drill" +msgstr "Bohrer löschen" + +#: AppGUI/MainGUI.py:981 AppGUI/MainGUI.py:1974 +msgid "Move Drill" +msgstr "Bohrer bewegen" + +#: AppGUI/MainGUI.py:989 AppGUI/MainGUI.py:1982 +msgid "Add Circle" +msgstr "Kreis hinzufügen" + +#: AppGUI/MainGUI.py:991 AppGUI/MainGUI.py:1984 +msgid "Add Arc" +msgstr "Bogen hinzufügen" + +#: AppGUI/MainGUI.py:993 AppGUI/MainGUI.py:1986 +msgid "Add Rectangle" +msgstr "Rechteck hinzufügen" + +#: AppGUI/MainGUI.py:997 AppGUI/MainGUI.py:1990 +msgid "Add Path" +msgstr "Pfad hinzufügen" + +#: AppGUI/MainGUI.py:999 AppGUI/MainGUI.py:1992 +msgid "Add Polygon" +msgstr "Polygon hinzufügen" + +#: AppGUI/MainGUI.py:1002 AppGUI/MainGUI.py:1995 +msgid "Add Text" +msgstr "Text hinzufügen" + +#: AppGUI/MainGUI.py:1004 AppGUI/MainGUI.py:1997 +msgid "Add Buffer" +msgstr "Puffer hinzufügen" + +#: AppGUI/MainGUI.py:1006 AppGUI/MainGUI.py:1999 +msgid "Paint Shape" +msgstr "Malen Form" + +#: AppGUI/MainGUI.py:1008 AppGUI/MainGUI.py:1065 AppGUI/MainGUI.py:1417 +#: AppGUI/MainGUI.py:1462 AppGUI/MainGUI.py:2001 AppGUI/MainGUI.py:2057 +msgid "Eraser" +msgstr "Radiergummi" + +#: AppGUI/MainGUI.py:1012 AppGUI/MainGUI.py:2005 +msgid "Polygon Union" +msgstr "Polygon-Vereinigung" + +#: AppGUI/MainGUI.py:1014 AppGUI/MainGUI.py:2007 +msgid "Polygon Explode" +msgstr "Polygon explodieren" + +#: AppGUI/MainGUI.py:1017 AppGUI/MainGUI.py:2010 +msgid "Polygon Intersection" +msgstr "Polygonschnitt" + +#: AppGUI/MainGUI.py:1019 AppGUI/MainGUI.py:2012 +msgid "Polygon Subtraction" +msgstr "Polygon-Subtraktion" + +#: AppGUI/MainGUI.py:1023 AppGUI/MainGUI.py:2016 +msgid "Cut Path" +msgstr "Pfad ausschneiden" + +#: AppGUI/MainGUI.py:1025 +msgid "Copy Shape(s)" +msgstr "Form kopieren" + +#: AppGUI/MainGUI.py:1028 +msgid "Delete Shape '-'" +msgstr "Form löschen" + +#: AppGUI/MainGUI.py:1030 AppGUI/MainGUI.py:1073 AppGUI/MainGUI.py:1429 +#: AppGUI/MainGUI.py:1466 AppGUI/MainGUI.py:2022 AppGUI/MainGUI.py:2065 +#: AppGUI/ObjectUI.py:109 +msgid "Transformations" +msgstr "Transformationen" + +#: AppGUI/MainGUI.py:1033 +msgid "Move Objects " +msgstr "Objekte verschieben " + +#: AppGUI/MainGUI.py:1041 AppGUI/MainGUI.py:2034 AppGUI/MainGUI.py:4431 +msgid "Add Pad" +msgstr "Pad hinzufügen" + +#: AppGUI/MainGUI.py:1045 AppGUI/MainGUI.py:2038 AppGUI/MainGUI.py:4432 +msgid "Add Track" +msgstr "Track hinzufügen" + +#: AppGUI/MainGUI.py:1047 AppGUI/MainGUI.py:2040 AppGUI/MainGUI.py:4431 +msgid "Add Region" +msgstr "Region hinzufügen" + +#: AppGUI/MainGUI.py:1049 AppGUI/MainGUI.py:1448 AppGUI/MainGUI.py:2042 +msgid "Poligonize" +msgstr "Polygonisieren" + +#: AppGUI/MainGUI.py:1052 AppGUI/MainGUI.py:1450 AppGUI/MainGUI.py:2045 +msgid "SemiDisc" +msgstr "Halbscheibe" + +#: AppGUI/MainGUI.py:1054 AppGUI/MainGUI.py:1452 AppGUI/MainGUI.py:2047 +msgid "Disc" +msgstr "Scheibe" + +#: AppGUI/MainGUI.py:1062 AppGUI/MainGUI.py:1460 AppGUI/MainGUI.py:2055 +msgid "Mark Area" +msgstr "Bereich markieren" + +#: AppGUI/MainGUI.py:1076 AppGUI/MainGUI.py:1433 AppGUI/MainGUI.py:1496 +#: AppGUI/MainGUI.py:2068 AppGUI/MainGUI.py:4431 AppTools/ToolMove.py:27 +msgid "Move" +msgstr "Bewegung" + +#: AppGUI/MainGUI.py:1084 +msgid "Snap to grid" +msgstr "Am Raster ausrichten" + +#: AppGUI/MainGUI.py:1087 +msgid "Grid X snapping distance" +msgstr "Raster X Fangdistanz" + +#: AppGUI/MainGUI.py:1092 +msgid "Grid Y snapping distance" +msgstr "Raster Y Fangdistanz" + +#: AppGUI/MainGUI.py:1098 +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." + +#: AppGUI/MainGUI.py:1105 +msgid "Snap to corner" +msgstr "In der Ecke ausrichten" + +#: AppGUI/MainGUI.py:1109 AppGUI/preferences/general/GeneralAPPSetGroupUI.py:78 +msgid "Max. magnet distance" +msgstr "Max. Magnetabstand" + +#: AppGUI/MainGUI.py:1134 AppGUI/MainGUI.py:1379 App_Main.py:7543 +msgid "Project" +msgstr "Projekt" + +#: AppGUI/MainGUI.py:1149 +msgid "Selected" +msgstr "Ausgewählt" + +#: AppGUI/MainGUI.py:1177 AppGUI/MainGUI.py:1185 +msgid "Plot Area" +msgstr "Grundstücksfläche" + +#: AppGUI/MainGUI.py:1212 +msgid "General" +msgstr "Allgemeines" + +#: AppGUI/MainGUI.py:1227 AppTools/ToolCopperThieving.py:74 +#: AppTools/ToolCorners.py:55 AppTools/ToolDblSided.py:64 +#: AppTools/ToolEtchCompensation.py:72 AppTools/ToolExtractDrills.py:61 +#: AppTools/ToolInvertGerber.py:72 AppTools/ToolOptimal.py:71 +#: AppTools/ToolPunchGerber.py:64 +msgid "GERBER" +msgstr "GERBER" + +#: AppGUI/MainGUI.py:1237 AppTools/ToolDblSided.py:92 +msgid "EXCELLON" +msgstr "EXCELLON" + +#: AppGUI/MainGUI.py:1247 AppTools/ToolDblSided.py:120 +msgid "GEOMETRY" +msgstr "GEOMETRY" + +#: AppGUI/MainGUI.py:1257 +msgid "CNC-JOB" +msgstr "CNC-Auftrag" + +#: AppGUI/MainGUI.py:1266 AppGUI/ObjectUI.py:563 AppGUI/ObjectUI.py:2262 +msgid "TOOLS" +msgstr "WERKZEUGE" + +#: AppGUI/MainGUI.py:1275 +msgid "TOOLS 2" +msgstr "WERKZEUGE 2" + +#: AppGUI/MainGUI.py:1285 +msgid "UTILITIES" +msgstr "NUTZEN" + +#: AppGUI/MainGUI.py:1302 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:192 +msgid "Restore Defaults" +msgstr "Standard wiederherstellen" + +#: AppGUI/MainGUI.py:1305 +msgid "" +"Restore the entire set of default values\n" +"to the initial values loaded after first launch." +msgstr "" +"Stellen Sie den gesamten Satz von Standardwerten wieder her\n" +"auf die nach dem ersten Start geladenen Anfangswerte." + +#: AppGUI/MainGUI.py:1310 +msgid "Open Pref Folder" +msgstr "Öffnen Sie den Einstellungsordner" + +#: AppGUI/MainGUI.py:1313 +msgid "Open the folder where FlatCAM save the preferences files." +msgstr "" +"Öffnen Sie den Ordner, in dem FlatCAM die Voreinstellungsdateien speichert." + +#: AppGUI/MainGUI.py:1317 AppGUI/MainGUI.py:1804 +msgid "Clear GUI Settings" +msgstr "Löschen Sie die GUI-Einstellungen" + +#: AppGUI/MainGUI.py:1321 +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." + +#: AppGUI/MainGUI.py:1332 +msgid "Apply" +msgstr "Anwenden" + +#: AppGUI/MainGUI.py:1335 +msgid "Apply the current preferences without saving to a file." +msgstr "Anwenden ohne zu speichern." + +#: AppGUI/MainGUI.py:1342 +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." + +#: AppGUI/MainGUI.py:1350 +msgid "Will not save the changes and will close the preferences window." +msgstr "Einstellungen werden geschlossen ohne die Änderungen zu speichern." + +#: AppGUI/MainGUI.py:1364 +msgid "Toggle Visibility" +msgstr "Sichtbarkeit umschalten" + +#: AppGUI/MainGUI.py:1370 +msgid "New" +msgstr "Neu" + +#: AppGUI/MainGUI.py:1372 AppGUI/ObjectUI.py:450 +#: AppObjects/FlatCAMGerber.py:239 AppObjects/FlatCAMGerber.py:327 +#: AppTools/ToolCalibration.py:631 AppTools/ToolCalibration.py:648 +#: AppTools/ToolCalibration.py:815 AppTools/ToolCopperThieving.py:144 +#: AppTools/ToolCopperThieving.py:158 AppTools/ToolCopperThieving.py:604 +#: AppTools/ToolCutOut.py:92 AppTools/ToolDblSided.py:226 +#: AppTools/ToolFilm.py:69 AppTools/ToolFilm.py:92 AppTools/ToolImage.py:49 +#: AppTools/ToolImage.py:271 AppTools/ToolNCC.py:95 AppTools/ToolNCC.py:558 +#: AppTools/ToolNCC.py:1300 AppTools/ToolPaint.py:501 AppTools/ToolPaint.py:705 +#: AppTools/ToolPanelize.py:116 AppTools/ToolPanelize.py:385 +#: AppTools/ToolPanelize.py:402 +msgid "Geometry" +msgstr "Geometrie" + +#: AppGUI/MainGUI.py:1376 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:99 +#: AppTools/ToolAlignObjects.py:74 AppTools/ToolAlignObjects.py:110 +#: AppTools/ToolCalibration.py:197 AppTools/ToolCalibration.py:631 +#: AppTools/ToolCalibration.py:648 AppTools/ToolCalibration.py:807 +#: AppTools/ToolCalibration.py:815 AppTools/ToolCopperThieving.py:144 +#: AppTools/ToolCopperThieving.py:158 AppTools/ToolCopperThieving.py:604 +#: AppTools/ToolDblSided.py:225 AppTools/ToolFilm.py:342 +#: AppTools/ToolNCC.py:558 AppTools/ToolNCC.py:1300 AppTools/ToolPaint.py:501 +#: AppTools/ToolPaint.py:705 AppTools/ToolPanelize.py:385 +#: AppTools/ToolPunchGerber.py:149 AppTools/ToolPunchGerber.py:164 +msgid "Excellon" +msgstr "Excellon" + +#: AppGUI/MainGUI.py:1383 +msgid "Grids" +msgstr "Raster" + +#: AppGUI/MainGUI.py:1390 +msgid "Clear Plot" +msgstr "Plot klar löschen" + +#: AppGUI/MainGUI.py:1392 +msgid "Replot" +msgstr "Replotieren" + +#: AppGUI/MainGUI.py:1396 +msgid "Geo Editor" +msgstr "Geo-Editor" + +#: AppGUI/MainGUI.py:1398 +msgid "Path" +msgstr "Pfad" + +#: AppGUI/MainGUI.py:1400 +msgid "Rectangle" +msgstr "Rechteck" + +#: AppGUI/MainGUI.py:1403 +msgid "Circle" +msgstr "Kreis" + +#: AppGUI/MainGUI.py:1407 +msgid "Arc" +msgstr "Bogen" + +#: AppGUI/MainGUI.py:1421 +msgid "Union" +msgstr "Vereinigung" + +#: AppGUI/MainGUI.py:1423 +msgid "Intersection" +msgstr "Überschneidung" + +#: AppGUI/MainGUI.py:1425 +msgid "Subtraction" +msgstr "Subtraktion" + +#: AppGUI/MainGUI.py:1427 AppGUI/ObjectUI.py:2351 +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:56 +msgid "Cut" +msgstr "Schnitt" + +#: AppGUI/MainGUI.py:1438 +msgid "Pad" +msgstr "Pad" + +#: AppGUI/MainGUI.py:1440 +msgid "Pad Array" +msgstr "Pad-Array" + +#: AppGUI/MainGUI.py:1444 +msgid "Track" +msgstr "Track" + +#: AppGUI/MainGUI.py:1446 +msgid "Region" +msgstr "Region" + +#: AppGUI/MainGUI.py:1469 +msgid "Exc Editor" +msgstr "Exc-Editor" + +#: AppGUI/MainGUI.py:1471 AppGUI/MainGUI.py:4310 +msgid "Add Drill" +msgstr "Bohrer hinzufügen" + +#: AppGUI/MainGUI.py:1491 App_Main.py:2198 +msgid "Close Editor" +msgstr "Editor schließen" + +#: AppGUI/MainGUI.py:1516 +msgid "" +"Absolute measurement.\n" +"Reference is (X=0, Y= 0) position" +msgstr "" +"Absolute Messung.\n" +"Referenz ist (X = 0, Y = 0)" + +#: AppGUI/MainGUI.py:1523 +msgid "HUD (Heads up display)" +msgstr "" + +#: AppGUI/MainGUI.py:1622 +msgid "Lock Toolbars" +msgstr "Symbolleisten sperren" + +#: AppGUI/MainGUI.py:1792 +msgid "FlatCAM Preferences Folder opened." +msgstr "FlatCAM-Einstellungsordner geöffnet." + +#: AppGUI/MainGUI.py:1803 +msgid "Are you sure you want to delete the GUI Settings? \n" +msgstr "Möchten Sie die GUI-Einstellungen wirklich löschen?\n" + +#: AppGUI/MainGUI.py:1806 AppGUI/preferences/PreferencesUIManager.py:941 +#: AppGUI/preferences/PreferencesUIManager.py:1179 AppTranslation.py:109 +#: AppTranslation.py:206 App_Main.py:2201 App_Main.py:3052 App_Main.py:5276 +#: App_Main.py:6336 +msgid "Yes" +msgstr "Ja" + +#: AppGUI/MainGUI.py:1807 AppGUI/preferences/PreferencesUIManager.py:1180 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:164 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:150 +#: AppTools/ToolNCC.py:182 AppTools/ToolPaint.py:165 AppTranslation.py:110 +#: AppTranslation.py:207 App_Main.py:2202 App_Main.py:3053 App_Main.py:5277 +#: App_Main.py:6337 +msgid "No" +msgstr "Nein" + +#: AppGUI/MainGUI.py:1906 +msgid "&Cutout Tool" +msgstr "Ausschnittwerkzeug" + +#: AppGUI/MainGUI.py:1980 +msgid "Select 'Esc'" +msgstr "Wählen" + +#: AppGUI/MainGUI.py:2018 +msgid "Copy Objects" +msgstr "Objekte kopieren" + +#: AppGUI/MainGUI.py:2020 AppGUI/MainGUI.py:4230 +msgid "Delete Shape" +msgstr "Form löschen" + +#: AppGUI/MainGUI.py:2026 +msgid "Move Objects" +msgstr "Objekte verschieben" + +#: AppGUI/MainGUI.py:2610 +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." + +#: AppGUI/MainGUI.py:2617 AppGUI/MainGUI.py:2779 AppGUI/MainGUI.py:2824 +#: AppGUI/MainGUI.py:2844 +msgid "Warning" +msgstr "Warnung" + +#: AppGUI/MainGUI.py:2774 +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." + +#: AppGUI/MainGUI.py:2819 +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." + +#: AppGUI/MainGUI.py:2839 +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." + +#: AppGUI/MainGUI.py:2922 AppGUI/MainGUI.py:3137 +msgid "Cancelled. Nothing selected to delete." +msgstr "Abgebrochen. Nichts zum Löschen ausgewählt." + +#: AppGUI/MainGUI.py:3006 AppGUI/MainGUI.py:3253 +msgid "Cancelled. Nothing selected to copy." +msgstr "Abgebrochen. Nichts zum Kopieren ausgewählt." + +#: AppGUI/MainGUI.py:3052 AppGUI/MainGUI.py:3282 +msgid "Cancelled. Nothing selected to move." +msgstr "Abgebrochen. Nichts ausgewählt, um sich zu bewegen." + +#: AppGUI/MainGUI.py:3308 +msgid "New Tool ..." +msgstr "Neues Werkzeug ..." + +#: AppGUI/MainGUI.py:3309 AppTools/ToolNCC.py:924 AppTools/ToolPaint.py:849 +#: AppTools/ToolSolderPaste.py:560 +msgid "Enter a Tool Diameter" +msgstr "Geben Sie einen Werkzeugdurchmesser ein" + +#: AppGUI/MainGUI.py:3321 +msgid "Adding Tool cancelled ..." +msgstr "Tool wird hinzugefügt abgebrochen ..." + +#: AppGUI/MainGUI.py:3335 +msgid "Distance Tool exit..." +msgstr "Entfernungstool beenden ..." + +#: AppGUI/MainGUI.py:3514 App_Main.py:3042 +msgid "Application is saving the project. Please wait ..." +msgstr "Anwendung speichert das Projekt. Warten Sie mal ..." + +#: AppGUI/MainGUI.py:3649 App_Main.py:9059 +msgid "Shortcut Key List" +msgstr " Liste der Tastenkombinationen " + +#: AppGUI/MainGUI.py:4010 +#, fuzzy +#| msgid "Key Shortcut List" +msgid "General Shortcut list" +msgstr "Tastenkürzel Liste" + +#: AppGUI/MainGUI.py:4011 +msgid "SHOW SHORTCUT LIST" +msgstr "Verknüpfungsliste anzeigen" + +#: AppGUI/MainGUI.py:4011 +msgid "Switch to Project Tab" +msgstr "Wechseln Sie zur Registerkarte Projekt" + +#: AppGUI/MainGUI.py:4011 +msgid "Switch to Selected Tab" +msgstr "Wechseln Sie zur ausgewählten Registerkarte" + +#: AppGUI/MainGUI.py:4012 +msgid "Switch to Tool Tab" +msgstr "Wechseln Sie zur Werkzeugregisterkarte" + +#: AppGUI/MainGUI.py:4013 +msgid "New Gerber" +msgstr "Neuer Gerber" + +#: AppGUI/MainGUI.py:4013 +msgid "Edit Object (if selected)" +msgstr "Objekt bearbeiten (falls ausgewählt)" + +#: AppGUI/MainGUI.py:4013 App_Main.py:5581 +msgid "Grid On/Off" +msgstr "Raster ein/aus" + +#: AppGUI/MainGUI.py:4013 +msgid "Jump to Coordinates" +msgstr "Springe zu den Koordinaten" + +#: AppGUI/MainGUI.py:4014 +msgid "New Excellon" +msgstr "Neuer Excellon" + +#: AppGUI/MainGUI.py:4014 +msgid "Move Obj" +msgstr "Objekt verschieben" + +#: AppGUI/MainGUI.py:4014 +msgid "New Geometry" +msgstr "Neue Geometrie" + +#: AppGUI/MainGUI.py:4014 +msgid "Change Units" +msgstr "Einheiten ändern" + +#: AppGUI/MainGUI.py:4015 +msgid "Open Properties Tool" +msgstr "Öffnen Sie das Eigenschaften-Tool" + +#: AppGUI/MainGUI.py:4015 +msgid "Rotate by 90 degree CW" +msgstr "Um 90 Grad im Uhrzeigersinn drehen" + +#: AppGUI/MainGUI.py:4015 +msgid "Shell Toggle" +msgstr "Shell umschalten" + +#: AppGUI/MainGUI.py:4016 +msgid "" +"Add a Tool (when in Geometry Selected Tab or in Tools NCC or Tools Paint)" +msgstr "" +"Hinzufügen eines Werkzeugs (auf der Registerkarte \"Geometrie ausgewählt\" " +"oder unter \"Werkzeuge\", \"NCC\" oder \"Werkzeuge\", \"Malen\")" + +#: AppGUI/MainGUI.py:4017 +msgid "Flip on X_axis" +msgstr "Auf X-Achse spiegeln" + +#: AppGUI/MainGUI.py:4017 +msgid "Flip on Y_axis" +msgstr "Auf Y-Achse spiegeln" + +#: AppGUI/MainGUI.py:4020 +msgid "Copy Obj" +msgstr "Objekt kopieren" + +#: AppGUI/MainGUI.py:4020 +msgid "Open Tools Database" +msgstr "Werkzeugdatenbank öffnen" + +#: AppGUI/MainGUI.py:4021 +msgid "Open Excellon File" +msgstr "Öffnen Sie die Excellon-Datei" + +#: AppGUI/MainGUI.py:4021 +msgid "Open Gerber File" +msgstr "Öffnen Sie die Gerber-Datei" + +#: AppGUI/MainGUI.py:4021 +msgid "New Project" +msgstr "Neues Projekt" + +#: AppGUI/MainGUI.py:4022 App_Main.py:6626 App_Main.py:6629 +msgid "Open Project" +msgstr "Projekt öffnen" + +#: AppGUI/MainGUI.py:4022 AppTools/ToolPDF.py:41 +msgid "PDF Import Tool" +msgstr "PDF-Importwerkzeug" + +#: AppGUI/MainGUI.py:4022 +msgid "Save Project" +msgstr "Projekt speichern" + +#: AppGUI/MainGUI.py:4022 +msgid "Toggle Plot Area" +msgstr "Zeichenbereich umschalten0" + +#: AppGUI/MainGUI.py:4025 +msgid "Copy Obj_Name" +msgstr "Kopieren Sie den Namen des Objekts" + +#: AppGUI/MainGUI.py:4026 +msgid "Toggle Code Editor" +msgstr "Code-Editor umschalten" + +#: AppGUI/MainGUI.py:4026 +msgid "Toggle the axis" +msgstr "Achse umschalten" + +#: AppGUI/MainGUI.py:4026 AppGUI/MainGUI.py:4225 AppGUI/MainGUI.py:4312 +#: AppGUI/MainGUI.py:4434 +msgid "Distance Minimum Tool" +msgstr "Mindestabstand Werkzeug" + +#: AppGUI/MainGUI.py:4027 +msgid "Open Preferences Window" +msgstr "Öffnen Sie das Einstellungsfenster" + +#: AppGUI/MainGUI.py:4028 +msgid "Rotate by 90 degree CCW" +msgstr "Um 90 Grad gegen den Uhrzeigersinn drehen" + +#: AppGUI/MainGUI.py:4028 +msgid "Run a Script" +msgstr "Führen Sie ein Skript aus" + +#: AppGUI/MainGUI.py:4028 +msgid "Toggle the workspace" +msgstr "Arbeitsbereich umschalten" + +#: AppGUI/MainGUI.py:4028 +msgid "Skew on X axis" +msgstr "Neigung auf der X-Achse" + +#: AppGUI/MainGUI.py:4029 +msgid "Skew on Y axis" +msgstr "Neigung auf der Y-Achse" + +#: AppGUI/MainGUI.py:4032 +msgid "2-Sided PCB Tool" +msgstr "2-seitiges PCB Werkzeug" + +#: AppGUI/MainGUI.py:4032 +msgid "Transformations Tool" +msgstr "Transformations-Tool" + +#: AppGUI/MainGUI.py:4034 +msgid "Solder Paste Dispensing Tool" +msgstr "Lotpasten-Dosierwerkzeug" + +#: AppGUI/MainGUI.py:4035 +msgid "Film PCB Tool" +msgstr "Film PCB Werkzeug" + +#: AppGUI/MainGUI.py:4035 +msgid "Non-Copper Clearing Tool" +msgstr "Nicht-Kupfer-Räumwerkzeug" + +#: AppGUI/MainGUI.py:4036 +msgid "Paint Area Tool" +msgstr "Malbereichswerkzeug" + +#: AppGUI/MainGUI.py:4036 +msgid "Rules Check Tool" +msgstr "Regelprüfwerkzeug" + +#: AppGUI/MainGUI.py:4037 +msgid "View File Source" +msgstr "Dateiquelle anzeigen" + +#: AppGUI/MainGUI.py:4038 +msgid "Cutout PCB Tool" +msgstr "Ausschnitt PCB Tool" + +#: AppGUI/MainGUI.py:4038 +msgid "Enable all Plots" +msgstr "Alle Zeichnungen aktivieren" + +#: AppGUI/MainGUI.py:4038 +msgid "Disable all Plots" +msgstr "Alle Zeichnungen deaktivieren" + +#: AppGUI/MainGUI.py:4038 +msgid "Disable Non-selected Plots" +msgstr "Nicht ausgewählte Zeichnungen deaktiv" + +#: AppGUI/MainGUI.py:4039 +msgid "Toggle Full Screen" +msgstr "Vollbild umschalten" + +#: AppGUI/MainGUI.py:4042 +msgid "Abort current task (gracefully)" +msgstr "Aktuelle Aufgabe abbrechen (ordnungsgemäß)" + +#: AppGUI/MainGUI.py:4045 +msgid "Save Project As" +msgstr "Projekt speichern als" + +#: AppGUI/MainGUI.py:4046 +msgid "" +"Paste Special. Will convert a Windows path style to the one required in Tcl " +"Shell" +msgstr "" +"Paste Special. Konvertiert einen Windows-Pfadstil in den in Tcl Shell " +"erforderlichen" + +#: AppGUI/MainGUI.py:4049 +msgid "Open Online Manual" +msgstr "Online-Handbuch öffnen" + +#: AppGUI/MainGUI.py:4050 +msgid "Open Online Tutorials" +msgstr "Öffnen Sie Online-Tutorials" + +#: AppGUI/MainGUI.py:4050 +msgid "Refresh Plots" +msgstr "Zeichnungen aktualisieren" + +#: AppGUI/MainGUI.py:4050 AppTools/ToolSolderPaste.py:509 +msgid "Delete Object" +msgstr "Objekt löschen" + +#: AppGUI/MainGUI.py:4050 +msgid "Alternate: Delete Tool" +msgstr "Alternative: Werkzeug löschen" + +#: AppGUI/MainGUI.py:4051 +msgid "(left to Key_1)Toggle Notebook Area (Left Side)" +msgstr "(links neben Taste_1) Notebook-Bereich umschalten (linke Seite)" + +#: AppGUI/MainGUI.py:4051 +msgid "En(Dis)able Obj Plot" +msgstr "Objektzeichnung (de)aktivieren" + +#: AppGUI/MainGUI.py:4052 +msgid "Deselects all objects" +msgstr "Hebt die Auswahl aller Objekte auf" + +#: AppGUI/MainGUI.py:4066 +msgid "Editor Shortcut list" +msgstr "Editor-Verknüpfungsliste" + +#: AppGUI/MainGUI.py:4220 +msgid "GEOMETRY EDITOR" +msgstr "GEOMETRIE-EDITOR" + +#: AppGUI/MainGUI.py:4220 +msgid "Draw an Arc" +msgstr "Zeichnen Sie einen Bogen" + +#: AppGUI/MainGUI.py:4220 +msgid "Copy Geo Item" +msgstr "Geo-Objekt kopieren" + +#: AppGUI/MainGUI.py:4221 +msgid "Within Add Arc will toogle the ARC direction: CW or CCW" +msgstr "" +"Innerhalb von Bogen hinzufügen wird die ARC-Richtung getippt: CW oder CCW" + +#: AppGUI/MainGUI.py:4221 +msgid "Polygon Intersection Tool" +msgstr "Werkzeug Polygonschnitt" + +#: AppGUI/MainGUI.py:4222 +msgid "Geo Paint Tool" +msgstr "Geo-Malwerkzeug" + +#: AppGUI/MainGUI.py:4222 AppGUI/MainGUI.py:4311 AppGUI/MainGUI.py:4431 +msgid "Jump to Location (x, y)" +msgstr "Zum Standort springen (x, y)" + +#: AppGUI/MainGUI.py:4222 +msgid "Toggle Corner Snap" +msgstr "Eckfang umschalten" + +#: AppGUI/MainGUI.py:4222 +msgid "Move Geo Item" +msgstr "Geo-Objekt verschieben" + +#: AppGUI/MainGUI.py:4223 +msgid "Within Add Arc will cycle through the ARC modes" +msgstr "Innerhalb von Bogen hinzufügen werden die ARC-Modi durchlaufen" + +#: AppGUI/MainGUI.py:4223 +msgid "Draw a Polygon" +msgstr "Zeichnen Sie ein Polygon" + +#: AppGUI/MainGUI.py:4223 +msgid "Draw a Circle" +msgstr "Zeichne einen Kreis" + +#: AppGUI/MainGUI.py:4224 +msgid "Draw a Path" +msgstr "Zeichne einen Pfad" + +#: AppGUI/MainGUI.py:4224 +msgid "Draw Rectangle" +msgstr "Rechteck zeichnen" + +#: AppGUI/MainGUI.py:4224 +msgid "Polygon Subtraction Tool" +msgstr "Polygon-Subtraktionswerkzeug" + +#: AppGUI/MainGUI.py:4224 +msgid "Add Text Tool" +msgstr "Textwerkzeug hinzufügen" + +#: AppGUI/MainGUI.py:4225 +msgid "Polygon Union Tool" +msgstr "Polygonverbindungswerkzeug" + +#: AppGUI/MainGUI.py:4225 +msgid "Flip shape on X axis" +msgstr "Form auf der X-Achse spiegeln" + +#: AppGUI/MainGUI.py:4225 +msgid "Flip shape on Y axis" +msgstr "Form auf der Y-Achse spiegeln" + +#: AppGUI/MainGUI.py:4226 +msgid "Skew shape on X axis" +msgstr "Neigung auf der X-Achse" + +#: AppGUI/MainGUI.py:4226 +msgid "Skew shape on Y axis" +msgstr "Neigung auf der Y-Achse" + +#: AppGUI/MainGUI.py:4226 +msgid "Editor Transformation Tool" +msgstr "Editor-Transformationstool" + +#: AppGUI/MainGUI.py:4227 +msgid "Offset shape on X axis" +msgstr "Versetzte Form auf der X-Achse" + +#: AppGUI/MainGUI.py:4227 +msgid "Offset shape on Y axis" +msgstr "Versetzte Form auf der Y-Achse" + +#: AppGUI/MainGUI.py:4228 AppGUI/MainGUI.py:4314 AppGUI/MainGUI.py:4436 +msgid "Save Object and Exit Editor" +msgstr "Objekt speichern und Editor beenden" + +#: AppGUI/MainGUI.py:4228 +msgid "Polygon Cut Tool" +msgstr "Polygon-Schneidewerkzeug" + +#: AppGUI/MainGUI.py:4229 +msgid "Rotate Geometry" +msgstr "Geometrie drehen" + +#: AppGUI/MainGUI.py:4229 +msgid "Finish drawing for certain tools" +msgstr "Beenden Sie das Zeichnen für bestimmte Werkzeuge" + +#: AppGUI/MainGUI.py:4229 AppGUI/MainGUI.py:4314 AppGUI/MainGUI.py:4434 +msgid "Abort and return to Select" +msgstr "Abbrechen und zurück zu Auswählen" + +#: AppGUI/MainGUI.py:4310 +msgid "EXCELLON EDITOR" +msgstr "EXCELLON EDITOR" + +#: AppGUI/MainGUI.py:4310 +msgid "Copy Drill(s)" +msgstr "Bohrer kopieren" + +#: AppGUI/MainGUI.py:4311 +msgid "Move Drill(s)" +msgstr "Bohrer verschieben" + +#: AppGUI/MainGUI.py:4312 +msgid "Add a new Tool" +msgstr "Fügen Sie ein neues Werkzeug hinzu" + +#: AppGUI/MainGUI.py:4313 +msgid "Delete Drill(s)" +msgstr "Bohrer löschen" + +#: AppGUI/MainGUI.py:4313 +msgid "Alternate: Delete Tool(s)" +msgstr "Alternative: Werkzeug (e) löschen" + +#: AppGUI/MainGUI.py:4430 +msgid "GERBER EDITOR" +msgstr "GERBER EDITOR" + +#: AppGUI/MainGUI.py:4430 +msgid "Add Disc" +msgstr "Fügen Sie eine Scheiben hinzu" + +#: AppGUI/MainGUI.py:4430 +msgid "Add SemiDisc" +msgstr "Halbschibe hinzufügen" + +#: AppGUI/MainGUI.py:4432 +msgid "Within Track & Region Tools will cycle in REVERSE the bend modes" +msgstr "" +"Innerhalb von Track- und Region-Werkzeugen werden die Biegemodi umgekehrt" + +#: AppGUI/MainGUI.py:4433 +msgid "Within Track & Region Tools will cycle FORWARD the bend modes" +msgstr "" +"Innerhalb von Track und Region werden mit Tools die Biegemodi vorwärts " +"durchlaufen" + +#: AppGUI/MainGUI.py:4434 +msgid "Alternate: Delete Apertures" +msgstr "Alternative: Löschen Sie die Blenden" + +#: AppGUI/MainGUI.py:4435 +msgid "Eraser Tool" +msgstr "Radiergummi" + +#: AppGUI/MainGUI.py:4436 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:220 +msgid "Mark Area Tool" +msgstr "Bereich markieren Werkzeug" + +#: AppGUI/MainGUI.py:4436 +msgid "Poligonize Tool" +msgstr "Werkzeug Polygonisieren" + +#: AppGUI/MainGUI.py:4436 +msgid "Transformation Tool" +msgstr "Transformationswerkzeug" + +#: AppGUI/ObjectUI.py:38 +msgid "FlatCAM Object" +msgstr "FlatCAM-Objekt" + +#: AppGUI/ObjectUI.py:78 +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\"." + +#: AppGUI/ObjectUI.py:111 +msgid "Geometrical transformations of the current object." +msgstr "Geometrische Transformationen des aktuellen Objekts." + +#: AppGUI/ObjectUI.py:120 +msgid "" +"Factor by which to multiply\n" +"geometric features of this object.\n" +"Expressions are allowed. E.g: 1/25.4" +msgstr "" +"Faktor, mit dem sich multiplizieren soll\n" +"geometrische Merkmale dieses Objekts.\n" +"Ausdrücke sind erlaubt. Zum Beispiel: 1 / 25.4" + +#: AppGUI/ObjectUI.py:127 +msgid "Perform scaling operation." +msgstr "Führen Sie die Skalierung durch." + +#: AppGUI/ObjectUI.py:138 +msgid "" +"Amount by which to move the object\n" +"in the x and y axes in (x, y) format.\n" +"Expressions are allowed. E.g: (1/3.2, 0.5*3)" +msgstr "" +"Betrag, um den das Objekt verschoben werden soll\n" +"in der x- und y-Achse im (x, y) -Format.\n" +"Ausdrücke sind erlaubt. Zum Beispiel: (1/3.2, 0.5*3)" + +#: AppGUI/ObjectUI.py:145 +msgid "Perform the offset operation." +msgstr "Führen Sie den Versatzvorgang aus." + +#: AppGUI/ObjectUI.py:157 AppGUI/ObjectUI.py:164 AppTool.py:281 AppTool.py:287 +msgid "Edited value is out of range" +msgstr "Der bearbeitete Wert liegt außerhalb des Bereichs" + +#: AppGUI/ObjectUI.py:159 AppGUI/ObjectUI.py:166 AppTool.py:283 AppTool.py:289 +msgid "Edited value is within limits." +msgstr "Der bearbeitete Wert liegt innerhalb der Grenzen." + +#: AppGUI/ObjectUI.py:178 +msgid "Gerber Object" +msgstr "Gerber-Objekt" + +#: AppGUI/ObjectUI.py:187 AppGUI/ObjectUI.py:730 AppGUI/ObjectUI.py:1528 +#: AppGUI/ObjectUI.py:2335 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:30 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:33 +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:31 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:31 +msgid "Plot Options" +msgstr "Diagrammoptionen" + +#: AppGUI/ObjectUI.py:193 AppGUI/ObjectUI.py:731 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:45 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:38 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:119 +#: AppTools/ToolCopperThieving.py:191 +msgid "Solid" +msgstr "Solide" + +#: AppGUI/ObjectUI.py:195 AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:40 +msgid "Solid color polygons." +msgstr "Einfarbige Polygone." + +#: AppGUI/ObjectUI.py:201 +msgid "Multi-Color" +msgstr "M-farbig" + +#: AppGUI/ObjectUI.py:203 AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:47 +msgid "Draw polygons in different colors." +msgstr "Zeichnen Sie Polygone in verschiedenen Farben." + +#: AppGUI/ObjectUI.py:209 AppGUI/ObjectUI.py:769 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:39 +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:35 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:52 +msgid "Plot" +msgstr "Zeichn" + +#: AppGUI/ObjectUI.py:211 AppGUI/ObjectUI.py:771 AppGUI/ObjectUI.py:1588 +#: AppGUI/ObjectUI.py:2445 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:41 +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:37 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:54 +msgid "Plot (show) this object." +msgstr "Plotten (zeigen) dieses Objekt." + +#: AppGUI/ObjectUI.py:239 +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." + +#: AppGUI/ObjectUI.py:249 +msgid "Mark All" +msgstr "Alles mark" + +#: AppGUI/ObjectUI.py:251 +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." + +#: AppGUI/ObjectUI.py:279 +msgid "Mark the aperture instances on canvas." +msgstr "Markieren Sie die Blendeninstanzen auf der Leinwand." + +#: AppGUI/ObjectUI.py:291 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:32 +msgid "Isolation Routing" +msgstr "Isolierungsrouting" + +#: AppGUI/ObjectUI.py:293 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:34 +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." + +#: AppGUI/ObjectUI.py:311 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:69 +msgid "" +"Choose which tool to use for Gerber isolation:\n" +"'Circular' or 'V-shape'.\n" +"When the 'V-shape' is selected then the tool\n" +"diameter will depend on the chosen cut depth." +msgstr "" +"Wählen Sie das Werkzeug für die Gerber-Isolierung aus:\n" +"\"Rund\" oder \"V-Form\".\n" +"Wenn die 'V-Form' ausgewählt ist, dann das Werkzeug\n" +"Der Durchmesser hängt von der gewählten Schnitttiefe ab." + +#: AppGUI/ObjectUI.py:317 +msgid "V-Shape" +msgstr "V-Form" + +#: AppGUI/ObjectUI.py:323 AppGUI/ObjectUI.py:1774 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:81 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:72 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:78 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:85 AppTools/ToolNCC.py:233 +#: AppTools/ToolNCC.py:240 AppTools/ToolPaint.py:215 +msgid "V-Tip Dia" +msgstr "Stichelspitzen-Durchm" + +#: AppGUI/ObjectUI.py:325 AppGUI/ObjectUI.py:1777 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:83 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:74 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:80 AppTools/ToolNCC.py:235 +#: AppTools/ToolPaint.py:217 +msgid "The tip diameter for V-Shape Tool" +msgstr "Der Spitzendurchmesser für das V-Shape-Werkzeug" + +#: AppGUI/ObjectUI.py:336 AppGUI/ObjectUI.py:1789 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:94 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:84 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:91 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:99 AppTools/ToolNCC.py:246 +#: AppTools/ToolNCC.py:254 AppTools/ToolPaint.py:228 +msgid "V-Tip Angle" +msgstr "Stichel-Winkel" + +#: AppGUI/ObjectUI.py:338 AppGUI/ObjectUI.py:1792 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:96 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:86 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:93 AppTools/ToolNCC.py:248 +#: AppTools/ToolPaint.py:230 +msgid "" +"The tip angle for V-Shape Tool.\n" +"In degree." +msgstr "" +"Der Spitzenwinkel für das Stichel-Werkzeug.\n" +"In grad." + +#: AppGUI/ObjectUI.py:352 AppGUI/ObjectUI.py:1808 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:50 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:109 +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:61 +#: AppObjects/FlatCAMGeometry.py:1229 AppTools/ToolCutOut.py:141 +msgid "" +"Cutting depth (negative)\n" +"below the copper surface." +msgstr "" +"Schnitttiefe (negativ)\n" +"unter der Kupferoberfläche." + +#: AppGUI/ObjectUI.py:366 +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." + +#: AppGUI/ObjectUI.py:382 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:56 +msgid "# Passes" +msgstr "Durchgang" + +#: AppGUI/ObjectUI.py:384 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:58 +msgid "" +"Width of the isolation gap in\n" +"number (integer) of tool widths." +msgstr "" +"Breite der Isolationslücke in\n" +"Anzahl (Ganzzahl) der Werkzeugbreiten." + +#: AppGUI/ObjectUI.py:395 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:68 +msgid "Pass overlap" +msgstr "Passüberlappung" + +#: AppGUI/ObjectUI.py:397 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:70 +msgid "How much (percentage) of the tool width to overlap each tool pass." +msgstr "" +"Wie viel (Prozent) der Werkzeugbreite, um jeden Werkzeugdurchlauf zu " +"überlappen." + +#: AppGUI/ObjectUI.py:411 +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:58 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:97 +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" + +#: AppGUI/ObjectUI.py:421 +msgid "Combine" +msgstr "Kombinieren" + +#: AppGUI/ObjectUI.py:423 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:109 +msgid "Combine all passes into one object" +msgstr "Kombinieren Sie alle Durchgänge in einem Objekt" + +#: AppGUI/ObjectUI.py:427 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:43 +msgid "\"Follow\"" +msgstr "\"Folgen\"" + +#: AppGUI/ObjectUI.py:428 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:45 +msgid "" +"Generate a 'Follow' geometry.\n" +"This means that it will cut through\n" +"the middle of the trace." +msgstr "" +"Erzeugen Sie eine 'Follow'-Geometrie.\n" +"Dies bedeutet, dass es durchschneiden wird\n" +"die Mitte der Spur." + +#: AppGUI/ObjectUI.py:434 +msgid "Except" +msgstr "Außer" + +#: AppGUI/ObjectUI.py:437 +msgid "" +"When the isolation geometry is generated,\n" +"by checking this, the area of the object below\n" +"will be subtracted from the isolation geometry." +msgstr "" +"Wenn die Isolationsgeometrie generiert wird,\n" +"indem Sie dies überprüfen, wird der Bereich des Objekts unten\n" +"wird von der Isolationsgeometrie abgezogen." + +#: AppGUI/ObjectUI.py:450 +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:97 +#: AppObjects/FlatCAMGerber.py:239 AppObjects/FlatCAMGerber.py:327 +#: AppTools/ToolAlignObjects.py:73 AppTools/ToolAlignObjects.py:109 +#: AppTools/ToolCalibration.py:196 AppTools/ToolCalibration.py:631 +#: AppTools/ToolCalibration.py:648 AppTools/ToolCalibration.py:807 +#: AppTools/ToolCalibration.py:815 AppTools/ToolCopperThieving.py:144 +#: AppTools/ToolCopperThieving.py:158 AppTools/ToolCopperThieving.py:604 +#: AppTools/ToolCutOut.py:91 AppTools/ToolDblSided.py:224 +#: AppTools/ToolFilm.py:68 AppTools/ToolFilm.py:91 AppTools/ToolImage.py:49 +#: AppTools/ToolImage.py:252 AppTools/ToolImage.py:273 AppTools/ToolNCC.py:96 +#: AppTools/ToolNCC.py:558 AppTools/ToolNCC.py:1300 AppTools/ToolPaint.py:501 +#: AppTools/ToolPaint.py:705 AppTools/ToolPanelize.py:116 +#: AppTools/ToolPanelize.py:210 AppTools/ToolPanelize.py:385 +#: AppTools/ToolPanelize.py:402 +msgid "Gerber" +msgstr "Gerber" + +#: AppGUI/ObjectUI.py:457 AppTools/ToolNCC.py:86 AppTools/ToolPaint.py:79 +msgid "Obj Type" +msgstr "Obj-Typ" + +#: AppGUI/ObjectUI.py:459 +msgid "" +"Specify the type of object to be excepted from isolation.\n" +"It can be of type: Gerber or Geometry.\n" +"What is selected here will dictate the kind\n" +"of objects that will populate the 'Object' combobox." +msgstr "" +"Geben Sie den Objekttyp an, der von der Isolation ausgenommen werden soll.\n" +"Es kann vom Typ Gerber oder Geometrie sein.\n" +"Was hier ausgewählt wird, bestimmt die Art\n" +"von Objekten, die das Kombinationsfeld \"Objekt\" füllen." + +#: AppGUI/ObjectUI.py:472 AppGUI/ObjectUI.py:1324 AppGUI/ObjectUI.py:2158 +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:48 +#: AppTools/ToolCalibration.py:186 AppTools/ToolNCC.py:109 +#: AppTools/ToolPaint.py:102 AppTools/ToolPanelize.py:98 +#: AppTools/ToolQRCode.py:78 +msgid "Object" +msgstr "Objekt" + +#: AppGUI/ObjectUI.py:473 +msgid "Object whose area will be removed from isolation geometry." +msgstr "Objekt, dessen Bereich aus der Isolationsgeometrie entfernt wird." + +#: AppGUI/ObjectUI.py:480 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:82 +msgid "Scope" +msgstr "Wertebereich" + +#: AppGUI/ObjectUI.py:482 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:84 +msgid "" +"Isolation scope. Choose what to isolate:\n" +"- 'All' -> Isolate all the polygons in the object\n" +"- 'Selection' -> Isolate a selection of polygons." +msgstr "" +"Isolationsmenge. Was soll isoliert werden:\n" +"- 'Alles' -> Alle Polygone im Objekt isolieren\n" +"- ' Auswahl' -> Eine Auswahl der polygone isolieren." + +#: AppGUI/ObjectUI.py:487 +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:307 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:89 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:311 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:258 +#: AppTools/ToolNCC.py:539 AppTools/ToolPaint.py:455 +msgid "Selection" +msgstr "Auswahl" + +#: AppGUI/ObjectUI.py:495 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:122 +msgid "Isolation Type" +msgstr "Isolierungsart" + +#: AppGUI/ObjectUI.py:497 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:124 +msgid "" +"Choose how the isolation will be executed:\n" +"- 'Full' -> complete isolation of polygons\n" +"- 'Ext' -> will isolate only on the outside\n" +"- 'Int' -> will isolate only on the inside\n" +"'Exterior' isolation is almost always possible\n" +"(with the right tool) but 'Interior'\n" +"isolation can be done only when there is an opening\n" +"inside of the polygon (e.g polygon is a 'doughnut' shape)." +msgstr "" +"Wählen Sie, wie die Isolation ausgeführt wird:\n" +"- Vollständig: Es werden alle Polygone isoliert\n" +"- Ext: Die ausserhalb liegenden Polygone werden isoliert\n" +"- Int: Die innerhalb liegenden Polygone werden isoliert\n" +"Achtung Ext ist fast immer möglich (mit dem richtigen Werkzeug)\n" +"wohingegen \"Int\" Isolation nur möglich ist, wenn es ein Loch \n" +"innerhalb des Polygons gibt (also z.B. ein Torus)" + +#: AppGUI/ObjectUI.py:506 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:133 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:154 +msgid "Full" +msgstr "Voll" + +#: AppGUI/ObjectUI.py:507 +msgid "Ext" +msgstr "Ausserhalb" + +#: AppGUI/ObjectUI.py:508 +msgid "Int" +msgstr "Innerhalb" + +#: AppGUI/ObjectUI.py:513 +msgid "Generate Isolation Geometry" +msgstr "Isolationsgeometrie erzeugen" + +#: AppGUI/ObjectUI.py:521 +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." + +#: AppGUI/ObjectUI.py:533 +msgid "Buffer Solid Geometry" +msgstr "Festkörpergeometrie puffern" + +#: AppGUI/ObjectUI.py:535 +msgid "" +"This button is shown only when the Gerber file\n" +"is loaded without buffering.\n" +"Clicking this will create the buffered geometry\n" +"required for isolation." +msgstr "" +"Diese Schaltfläche wird nur bei der Gerber-Datei angezeigt\n" +"wird ohne Pufferung geladen.\n" +"Durch Klicken auf diese Schaltfläche wird die gepufferte Geometrie erstellt\n" +"für die Isolierung erforderlich." + +#: AppGUI/ObjectUI.py:567 +msgid "Clear N-copper" +msgstr "N-Kupfer löschen" + +#: AppGUI/ObjectUI.py:569 AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:33 +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." + +#: AppGUI/ObjectUI.py:576 AppGUI/ObjectUI.py:2289 AppTools/ToolNCC.py:599 +msgid "" +"Create the Geometry Object\n" +"for non-copper routing." +msgstr "" +"Erstellen Sie das Geometrieobjekt\n" +"für kupferfreies Routing." + +#: AppGUI/ObjectUI.py:589 +msgid "Board cutout" +msgstr "Kartenausschnitt" + +#: AppGUI/ObjectUI.py:591 AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:34 +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." + +#: AppGUI/ObjectUI.py:598 +msgid "" +"Generate the geometry for\n" +"the board cutout." +msgstr "" +"Generieren Sie die Geometrie für\n" +"der Brettausschnitt." + +#: AppGUI/ObjectUI.py:616 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:119 +msgid "Non-copper regions" +msgstr "Regionen ohne Kupfer" + +#: AppGUI/ObjectUI.py:618 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:121 +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." + +#: AppGUI/ObjectUI.py:628 AppGUI/ObjectUI.py:669 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:133 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:166 +msgid "Boundary Margin" +msgstr "Grenzmarge" + +#: AppGUI/ObjectUI.py:630 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:135 +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." + +#: AppGUI/ObjectUI.py:645 AppGUI/ObjectUI.py:683 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:148 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:179 +msgid "Rounded Geo" +msgstr "Abgerundete Geo" + +#: AppGUI/ObjectUI.py:647 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:150 +msgid "Resulting geometry will have rounded corners." +msgstr "Die resultierende Geometrie hat abgerundete Ecken." + +#: AppGUI/ObjectUI.py:651 AppGUI/ObjectUI.py:692 +#: AppTools/ToolSolderPaste.py:134 +msgid "Generate Geo" +msgstr "Geo erzeugen" + +#: AppGUI/ObjectUI.py:661 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:160 +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:137 +#: AppTools/ToolPanelize.py:99 AppTools/ToolQRCode.py:192 +msgid "Bounding Box" +msgstr "Begrenzungsrahmen" + +#: AppGUI/ObjectUI.py:663 +msgid "" +"Create a geometry surrounding the Gerber object.\n" +"Square shape." +msgstr "" +"Erstellen Sie eine Geometrie, die das Gerber-Objekt umgibt.\n" +"Quadratische Form." + +#: AppGUI/ObjectUI.py:671 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:168 +msgid "" +"Distance of the edges of the box\n" +"to the nearest polygon." +msgstr "" +"Abstand der Kanten der Box\n" +"zum nächsten Polygon." + +#: AppGUI/ObjectUI.py:685 AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:181 +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." + +#: AppGUI/ObjectUI.py:694 +msgid "Generate the Geometry object." +msgstr "Generieren Sie das Geometrieobjekt." + +#: AppGUI/ObjectUI.py:721 +msgid "Excellon Object" +msgstr "Excellon-Objekt" + +#: AppGUI/ObjectUI.py:733 +msgid "Solid circles." +msgstr "Feste Kreise." + +#: AppGUI/ObjectUI.py:781 AppGUI/ObjectUI.py:876 AppGUI/ObjectUI.py:2466 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:71 +#: AppTools/ToolProperties.py:166 +msgid "Drills" +msgstr "Bohrer" + +#: AppGUI/ObjectUI.py:781 AppGUI/ObjectUI.py:877 AppGUI/ObjectUI.py:2466 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:158 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:72 +#: AppTools/ToolProperties.py:168 +msgid "Slots" +msgstr "Schlüssel" + +#: AppGUI/ObjectUI.py:786 +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.\n" +"\n" +"Here the tools are selected for G-code generation." +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.\n" +"\n" +"Hier werden die Werkzeuge zur G-Code-Generierung ausgewählt." + +#: AppGUI/ObjectUI.py:791 AppGUI/ObjectUI.py:1612 AppTools/ToolPaint.py:141 +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." + +#: AppGUI/ObjectUI.py:794 +msgid "" +"The number of Drill holes. Holes that are drilled with\n" +"a drill bit." +msgstr "" +"Die Anzahl der Bohrlöcher. Löcher, mit denen gebohrt wird\n" +"ein Bohrer." + +#: AppGUI/ObjectUI.py:797 +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." + +#: AppGUI/ObjectUI.py:800 +msgid "" +"Toggle display of the drills for the current tool.\n" +"This does not select the tools for G-code generation." +msgstr "" +"Anzeige der Bohrer für das aktuelle Werkzeug umschalten.\n" +"Hiermit werden die Tools für die G-Code-Generierung nicht ausgewählt." + +#: AppGUI/ObjectUI.py:818 AppGUI/ObjectUI.py:1764 +#: AppObjects/FlatCAMExcellon.py:527 AppObjects/FlatCAMExcellon.py:805 +#: AppObjects/FlatCAMExcellon.py:821 AppObjects/FlatCAMExcellon.py:825 +#: AppObjects/FlatCAMGeometry.py:322 AppObjects/FlatCAMGeometry.py:816 +#: AppObjects/FlatCAMGeometry.py:852 AppTools/ToolNCC.py:331 +#: AppTools/ToolNCC.py:797 AppTools/ToolNCC.py:811 AppTools/ToolNCC.py:1196 +#: AppTools/ToolPaint.py:313 AppTools/ToolPaint.py:766 +#: AppTools/ToolPaint.py:778 AppTools/ToolPaint.py:1170 +msgid "Parameters for" +msgstr "Parameter für" + +#: AppGUI/ObjectUI.py:821 AppGUI/ObjectUI.py:1767 AppTools/ToolNCC.py:334 +#: AppTools/ToolPaint.py:316 +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." + +#: AppGUI/ObjectUI.py:847 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:48 +msgid "" +"Operation type:\n" +"- Drilling -> will drill the drills/slots associated with this tool\n" +"- Milling -> will mill the drills/slots" +msgstr "" +"Betriebsart:\n" +"- Bohren -> bohrt die mit diesem Werkzeug verbundenen Bohrer / Schlitze\n" +"- Fräsen -> fräst die Bohrer / Schlitze" + +#: AppGUI/ObjectUI.py:853 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:54 +msgid "Drilling" +msgstr "Bohren" + +#: AppGUI/ObjectUI.py:854 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:55 +msgid "Milling" +msgstr "Fräsprozess" + +#: AppGUI/ObjectUI.py:869 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:64 +msgid "" +"Milling type:\n" +"- Drills -> will mill the drills associated with this tool\n" +"- Slots -> will mill the slots associated with this tool\n" +"- Both -> will mill both drills and mills or whatever is available" +msgstr "" +"Frästyp:\n" +"- Bohrer -> fräst die mit diesem Werkzeug verbundenen Bohrer\n" +"- Schlüssel-> fräst die diesem Tool zugeordneten Slots\n" +"- Beide -> fräsen sowohl Bohrer als auch Fräser oder was auch immer " +"verfügbar ist" + +#: AppGUI/ObjectUI.py:878 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:73 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:210 +#: AppTools/ToolFilm.py:241 +msgid "Both" +msgstr "Both" + +#: AppGUI/ObjectUI.py:886 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:80 +msgid "Milling Diameter" +msgstr "Fräsdurchmesser" + +#: AppGUI/ObjectUI.py:888 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:82 +msgid "The diameter of the tool who will do the milling" +msgstr "Der Durchmesser des Werkzeugs, das das Fräsen übernimmt" + +#: AppGUI/ObjectUI.py:902 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:95 +msgid "" +"Drill depth (negative)\n" +"below the copper surface." +msgstr "" +"Bohrtiefe (negativ)\n" +"unter der Kupferoberfläche." + +#: AppGUI/ObjectUI.py:921 AppGUI/ObjectUI.py:1826 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:113 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:68 +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:79 +#: AppTools/ToolCutOut.py:159 +msgid "Multi-Depth" +msgstr "Mehrfache Tiefe" + +#: AppGUI/ObjectUI.py:924 AppGUI/ObjectUI.py:1829 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:116 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:71 +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:82 +#: AppTools/ToolCutOut.py:162 +msgid "" +"Use multiple passes to limit\n" +"the cut depth in each pass. Will\n" +"cut multiple times until Cut Z is\n" +"reached." +msgstr "" +"Verwenden Sie zum Begrenzen mehrere Durchgänge\n" +"die Schnitttiefe in jedem Durchgang. Wille\n" +"mehrmals schneiden, bis Schnitttiefe Z\n" +"erreicht ist." + +#: AppGUI/ObjectUI.py:937 AppGUI/ObjectUI.py:1843 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:128 +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:94 +#: AppTools/ToolCutOut.py:176 +msgid "Depth of each pass (positive)." +msgstr "Tiefe jedes Durchgangs (positiv)." + +#: AppGUI/ObjectUI.py:948 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:136 +msgid "" +"Tool height when travelling\n" +"across the XY plane." +msgstr "" +"Werkzeughöhe auf Reisen\n" +"über die XY-Ebene." + +#: AppGUI/ObjectUI.py:969 AppGUI/ObjectUI.py:1873 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:187 +msgid "" +"Cutting speed in the XY\n" +"plane in units per minute" +msgstr "" +"Schnittgeschwindigkeit im XY\n" +"Flugzeug in Einheiten pro Minute" + +#: AppGUI/ObjectUI.py:984 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:209 +msgid "" +"Tool speed while drilling\n" +"(in units per minute).\n" +"So called 'Plunge' feedrate.\n" +"This is for linear move G01." +msgstr "" +"Werkzeuggeschwindigkeit beim Bohren\n" +"(in Einheiten pro Minute).\n" +"Sogenannter Eintauchvorschub.\n" +"Dies ist für die lineare Bewegung G01." + +#: AppGUI/ObjectUI.py:999 AppGUI/ObjectUI.py:1900 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:80 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:64 +msgid "Feedrate Rapids" +msgstr "Vorschubgeschwindigkeit" + +#: AppGUI/ObjectUI.py:1001 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:82 +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." + +#: AppGUI/ObjectUI.py:1021 AppGUI/ObjectUI.py:1920 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:82 +msgid "Re-cut" +msgstr "Nachschneiden" + +#: AppGUI/ObjectUI.py:1023 AppGUI/ObjectUI.py:1036 AppGUI/ObjectUI.py:1922 +#: AppGUI/ObjectUI.py:1934 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:84 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:96 +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." + +#: AppGUI/ObjectUI.py:1049 AppGUI/ObjectUI.py:1943 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:216 +#: AppObjects/FlatCAMExcellon.py:1412 AppObjects/FlatCAMGeometry.py:1676 +msgid "Spindle speed" +msgstr "Spulengeschwindigkeit" + +#: AppGUI/ObjectUI.py:1051 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:224 +msgid "" +"Speed of the spindle\n" +"in RPM (optional)" +msgstr "" +"Geschwindigkeit der Spindel\n" +"in RPM (optional)" + +#: AppGUI/ObjectUI.py:1066 AppGUI/ObjectUI.py:1962 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:238 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:234 +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." + +#: AppGUI/ObjectUI.py:1077 AppGUI/ObjectUI.py:1972 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:246 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:239 +msgid "Number of time units for spindle to dwell." +msgstr "Anzahl der Zeiteinheiten, in denen die Spindel verweilen soll." + +#: AppGUI/ObjectUI.py:1087 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:46 +msgid "Offset Z" +msgstr "Versatz Z" + +#: AppGUI/ObjectUI.py:1089 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:48 +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." + +#: AppGUI/ObjectUI.py:1149 AppGUI/ObjectUI.py:2026 AppTools/ToolNCC.py:492 +#: AppTools/ToolPaint.py:422 +msgid "Apply parameters to all tools" +msgstr "Parameter auf alle Werkzeuge anwenden" + +#: AppGUI/ObjectUI.py:1151 AppGUI/ObjectUI.py:2028 AppTools/ToolNCC.py:494 +#: AppTools/ToolPaint.py:424 +msgid "" +"The parameters in the current form will be applied\n" +"on all the tools from the Tool Table." +msgstr "" +"Die aktuell angegebenen Parameter werden allen Werkzeugen der " +"Werkzeugtabelle zugeordnet." + +#: AppGUI/ObjectUI.py:1162 AppGUI/ObjectUI.py:2039 AppTools/ToolNCC.py:505 +#: AppTools/ToolPaint.py:435 +msgid "Common Parameters" +msgstr "Allgemeine Parameter" + +#: AppGUI/ObjectUI.py:1164 AppGUI/ObjectUI.py:2041 AppTools/ToolNCC.py:507 +#: AppTools/ToolPaint.py:437 +msgid "Parameters that are common for all tools." +msgstr "Parameter, die allen Werkzeugen gemeinsam sind." + +#: AppGUI/ObjectUI.py:1169 AppGUI/ObjectUI.py:2046 +msgid "Tool change Z" +msgstr "Werkzeugwechsel Z" + +#: AppGUI/ObjectUI.py:1171 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:154 +msgid "" +"Include tool-change sequence\n" +"in G-Code (Pause for tool change)." +msgstr "" +"Werkzeugwechselfolge einbeziehen\n" +"im G-Code (Pause für Werkzeugwechsel)." + +#: AppGUI/ObjectUI.py:1178 AppGUI/ObjectUI.py:2057 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:162 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:134 +msgid "" +"Z-axis position (height) for\n" +"tool change." +msgstr "" +"Z-Achsenposition (Höhe) für\n" +"Werkzeugwechsel." + +#: AppGUI/ObjectUI.py:1195 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:71 +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." + +#: AppGUI/ObjectUI.py:1204 AppGUI/ObjectUI.py:2085 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:178 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:153 +msgid "End move Z" +msgstr "Bewegung beenden Z" + +#: AppGUI/ObjectUI.py:1206 AppGUI/ObjectUI.py:2087 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:180 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:155 +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." + +#: AppGUI/ObjectUI.py:1223 AppGUI/ObjectUI.py:2104 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:195 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:173 +msgid "End move X,Y" +msgstr "Bewegung beenden X, Y" + +#: AppGUI/ObjectUI.py:1225 AppGUI/ObjectUI.py:2106 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:197 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:175 +msgid "" +"End move X,Y position. In format (x,y).\n" +"If no value is entered then there is no move\n" +"on X,Y plane at the end of the job." +msgstr "" +"Beenden Sie die X-, Y-Position. Im Format (x, y).\n" +"Wenn kein Wert eingegeben wird, erfolgt keine Bewegung\n" +"auf der X, Y-Ebene am Ende des Jobs." + +#: AppGUI/ObjectUI.py:1235 AppGUI/ObjectUI.py:1980 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:96 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:105 +msgid "Probe Z depth" +msgstr "Sonde Z Tiefe" + +#: AppGUI/ObjectUI.py:1237 AppGUI/ObjectUI.py:1982 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:98 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:107 +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." + +#: AppGUI/ObjectUI.py:1254 AppGUI/ObjectUI.py:1997 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:109 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:120 +msgid "Feedrate Probe" +msgstr "Vorschubsonde" + +#: AppGUI/ObjectUI.py:1256 AppGUI/ObjectUI.py:1999 +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:111 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:122 +msgid "The feedrate used while the probe is probing." +msgstr "Der Vorschub während der Sondenmessung." + +#: AppGUI/ObjectUI.py:1272 +msgid "Preprocessor E" +msgstr "Postprozessor E" + +#: AppGUI/ObjectUI.py:1274 +msgid "" +"The preprocessor JSON file that dictates\n" +"Gcode output for Excellon Objects." +msgstr "" +"Die diktierende Präprozessor-JSON-Datei\n" +"Gcode-Ausgabe für Excellon-Objekte." + +#: AppGUI/ObjectUI.py:1284 +msgid "Preprocessor G" +msgstr "Postprozessor G" + +#: AppGUI/ObjectUI.py:1286 +msgid "" +"The preprocessor JSON file that dictates\n" +"Gcode output for Geometry (Milling) Objects." +msgstr "" +"Die diktierende Präprozessor-JSON-Datei\n" +"Gcode-Ausgabe für Geometrieobjekte (Fräsen)." + +#: AppGUI/ObjectUI.py:1300 AppGUI/ObjectUI.py:2134 +#, fuzzy +#| msgid "Exclusion areas" +msgid "Add exclusion areas" +msgstr "Ausschlussbereiche" + +#: AppGUI/ObjectUI.py:1303 AppGUI/ObjectUI.py:2137 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:204 +msgid "" +"Include exclusion areas.\n" +"In those areas the travel of the tools\n" +"is forbidden." +msgstr "" +"Ausschlussbereiche einschließen.\n" +"In diesen Bereichen die Reise der Werkzeuge\n" +"ist verboten." + +#: AppGUI/ObjectUI.py:1324 AppGUI/ObjectUI.py:1343 AppGUI/ObjectUI.py:2158 +#: AppGUI/ObjectUI.py:2177 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:224 +msgid "Strategy" +msgstr "Strategie" + +#: AppGUI/ObjectUI.py:1324 AppGUI/ObjectUI.py:1355 AppGUI/ObjectUI.py:2158 +#: AppGUI/ObjectUI.py:2189 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:236 +msgid "Over Z" +msgstr "Über Z." + +#: AppGUI/ObjectUI.py:1326 AppGUI/ObjectUI.py:2160 +msgid "This is the Area ID." +msgstr "" + +#: AppGUI/ObjectUI.py:1328 AppGUI/ObjectUI.py:2162 +msgid "Type of the object where the exclusion area was added." +msgstr "" + +#: AppGUI/ObjectUI.py:1330 AppGUI/ObjectUI.py:2164 +msgid "" +"The strategy used for exclusion area. Go around the exclusion areas or over " +"it." +msgstr "" + +#: AppGUI/ObjectUI.py:1332 AppGUI/ObjectUI.py:2166 +msgid "" +"If the strategy is to go over the area then this is the height at which the " +"tool will go to avoid the exclusion area." +msgstr "" + +#: AppGUI/ObjectUI.py:1344 AppGUI/ObjectUI.py:2178 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:225 +msgid "" +"The strategy followed when encountering an exclusion area.\n" +"Can be:\n" +"- Over -> when encountering the area, the tool will go to a set height\n" +"- Around -> will avoid the exclusion area by going around the area" +msgstr "" +"Die Strategie wurde verfolgt, wenn auf einen Ausschlussbereich gestoßen " +"wurde.\n" +"Kann sein:\n" +"- Über -> Wenn Sie auf den Bereich stoßen, erreicht das Werkzeug eine " +"festgelegte Höhe\n" +"- Vermeiden -> vermeidet den Ausschlussbereich, indem Sie den Bereich umgehen" + +#: AppGUI/ObjectUI.py:1348 AppGUI/ObjectUI.py:2182 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:229 +msgid "Over" +msgstr "Über" + +#: AppGUI/ObjectUI.py:1349 AppGUI/ObjectUI.py:2183 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:230 +msgid "Around" +msgstr "Vermeiden" + +#: AppGUI/ObjectUI.py:1356 AppGUI/ObjectUI.py:2190 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:237 +msgid "" +"The height Z to which the tool will rise in order to avoid\n" +"an interdiction area." +msgstr "" +"Die Höhe Z, auf die das Werkzeug ansteigt, um dies zu vermeiden\n" +"ein Verbotsbereich." + +#: AppGUI/ObjectUI.py:1366 AppGUI/ObjectUI.py:2200 +#, fuzzy +#| msgid "Add area" +msgid "Add area:" +msgstr "Bereich hinzufügen" + +#: AppGUI/ObjectUI.py:1367 AppGUI/ObjectUI.py:2201 +msgid "Add an Exclusion Area." +msgstr "Fügen Sie einen Ausschlussbereich hinzu." + +#: AppGUI/ObjectUI.py:1373 AppGUI/ObjectUI.py:2207 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:214 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:324 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:288 +#: AppTools/ToolNCC.py:580 AppTools/ToolPaint.py:523 +msgid "The kind of selection shape used for area selection." +msgstr "Die Art der Auswahlform, die für die Bereichsauswahl verwendet wird." + +#: AppGUI/ObjectUI.py:1383 AppGUI/ObjectUI.py:2217 +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:32 +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:42 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:32 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:32 +msgid "Delete All" +msgstr "Alles löschen" + +#: AppGUI/ObjectUI.py:1384 AppGUI/ObjectUI.py:2218 +msgid "Delete all exclusion areas." +msgstr "Löschen Sie alle Ausschlussbereiche." + +#: AppGUI/ObjectUI.py:1387 AppGUI/ObjectUI.py:2221 +#, fuzzy +#| msgid "Delete Object" +msgid "Delete Selected" +msgstr "Objekt löschen" + +#: AppGUI/ObjectUI.py:1388 AppGUI/ObjectUI.py:2222 +#, fuzzy +#| msgid "Delete all exclusion areas." +msgid "Delete all exclusion areas that are selected in the table." +msgstr "Löschen Sie alle Ausschlussbereiche." + +#: AppGUI/ObjectUI.py:1412 AppGUI/ObjectUI.py:2238 +msgid "" +"Add / Select 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 "" +"Hinzufügen / Auswählen mindestens eines Werkzeugs in der Werkzeugtabelle.\n" +"Klicken Sie auf die Überschrift #, um alle auszuwählen, oder auf Strg + LMB\n" +"zur benutzerdefinierten Auswahl von Werkzeugen." + +#: AppGUI/ObjectUI.py:1420 AppGUI/ObjectUI.py:2245 +msgid "Generate CNCJob object" +msgstr "Generieren des CNC-Job-Objekts" + +#: AppGUI/ObjectUI.py:1422 +msgid "" +"Generate the CNC Job.\n" +"If milling then an additional Geometry object will be created" +msgstr "" +"Generieren Sie den CNC-Auftrag.\n" +"Beim Fräsen wird ein zusätzliches Geometrieobjekt erstellt" + +#: AppGUI/ObjectUI.py:1439 +msgid "Milling Geometry" +msgstr "Fräsgeometrie" + +#: AppGUI/ObjectUI.py:1441 +msgid "" +"Create Geometry for milling holes.\n" +"Select from the Tools Table above the hole dias to be\n" +"milled. Use the # column to make the selection." +msgstr "" +"Erzeugen Sie eine Geometrie um Löcher zu bohren.\n" +"Wählen Sie aus der Werkzeugtabelle oben die Durchmesser\n" +"die gefräst werden sollen. Verwenden Sie die Spalte #, um die Auswahl zu " +"treffen." + +#: AppGUI/ObjectUI.py:1449 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:296 +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:45 +msgid "Diameter of the cutting tool." +msgstr "Durchmesser des Schneidewerkzeugs." + +#: AppGUI/ObjectUI.py:1459 +msgid "Mill Drills" +msgstr "Mühlenbohrer" + +#: AppGUI/ObjectUI.py:1461 +msgid "" +"Create the Geometry Object\n" +"for milling DRILLS toolpaths." +msgstr "" +"Erstellen Sie das Geometrieobjekt\n" +"zum Fräsen von BOHRER-Werkzeugwegen." + +#: AppGUI/ObjectUI.py:1479 +msgid "Mill Slots" +msgstr "Mühlenschlitze" + +#: AppGUI/ObjectUI.py:1481 +msgid "" +"Create the Geometry Object\n" +"for milling SLOTS toolpaths." +msgstr "" +"Erstellen Sie das Geometrieobjekt\n" +"zum Fräsen von Werkzeugwegen." + +#: AppGUI/ObjectUI.py:1523 AppTools/ToolCutOut.py:319 +msgid "Geometry Object" +msgstr "Geometrieobjekt" + +#: AppGUI/ObjectUI.py:1569 +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." + +#: AppGUI/ObjectUI.py:1586 AppGUI/ObjectUI.py:2443 +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:40 +msgid "Plot Object" +msgstr "Plotobjekt" + +#: AppGUI/ObjectUI.py:1599 AppGUI/ObjectUI.py:2456 AppGUI/ObjectUI.py:2466 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:138 +#: AppTools/ToolCopperThieving.py:221 +msgid "Dia" +msgstr "Durchm" + +#: AppGUI/ObjectUI.py:1599 AppGUI/ObjectUI.py:2456 AppTools/ToolNCC.py:132 +#: AppTools/ToolPaint.py:127 +msgid "TT" +msgstr "TT" + +#: AppGUI/ObjectUI.py:1606 +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" + +#: AppGUI/ObjectUI.py:1617 +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." + +#: AppGUI/ObjectUI.py:1624 +msgid "" +"The (Operation) Type has only informative value. Usually the UI form " +"values \n" +"are choose based on the operation type and this will serve as a reminder.\n" +"Can be 'Roughing', 'Finishing' or 'Isolation'.\n" +"For Roughing we may choose a lower Feedrate and multiDepth cut.\n" +"For Finishing we may choose a higher Feedrate, without multiDepth.\n" +"For Isolation we need a lower Feedrate as it use a milling bit with a fine " +"tip." +msgstr "" +"Der Typ (Operation) hat nur informativen Wert. Normalerweise bilden die " +"Benutzeroberflächen Werte\n" +"Die Auswahl richtet sich nach der Art des Vorgangs und dient als " +"Erinnerung.\n" +"Kann \"Schruppen\", \"Fertigstellen\" oder \"Isolieren\" sein.\n" +"Für das Schruppen können wir einen niedrigeren Vorschub und einen Schnitt " +"mit mehreren Tiefen wählen.\n" +"Für das Finishing können wir eine höhere Vorschubgeschwindigkeit ohne " +"Mehrfachtiefe wählen.\n" +"Für die Isolierung benötigen wir einen niedrigeren Vorschub, da ein Fräser " +"mit einer feinen Spitze verwendet wird." + +#: AppGUI/ObjectUI.py:1633 +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 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 -> nur informativ. Kreisförmig ist die " +"Schnittbreite im Material\n" +"ist genau der Werkzeugdurchmesser.\n" +"- Ball -> nur informativ und auf den Ball-Schaftfräser verweisen.\n" +"- V-Form -> Deaktiviert den Z-Cut-Parameter im UI-Formular und aktiviert " +"zwei zusätzliche UI-Formulare\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" +"Wenn Sie den V-Form-Werkzeugtyp automatisch auswählen, wird der " +"Operationstyp als Isolation ausgewählt." + +#: AppGUI/ObjectUI.py:1645 +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." + +#: AppGUI/ObjectUI.py:1663 +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." + +#: AppGUI/ObjectUI.py:1682 AppTools/ToolNCC.py:209 AppTools/ToolNCC.py:923 +#: AppTools/ToolPaint.py:191 AppTools/ToolPaint.py:848 +#: AppTools/ToolSolderPaste.py:559 +msgid "New Tool" +msgstr "Neues Werkzeug" + +#: AppGUI/ObjectUI.py:1699 +msgid "" +"Add a new tool to the Tool Table\n" +"with the specified diameter." +msgstr "" +"Fügen Sie der Werkzeugtabelle ein neues Werkzeug\n" +"mit dem oben angegebenen Durchmesser hinzu." + +#: AppGUI/ObjectUI.py:1704 AppTools/ToolNCC.py:300 AppTools/ToolNCC.py:634 +#: AppTools/ToolPaint.py:282 AppTools/ToolPaint.py:678 +msgid "Add from DB" +msgstr "Aus DB hinzufügen" + +#: AppGUI/ObjectUI.py:1706 AppTools/ToolNCC.py:302 AppTools/ToolPaint.py:284 +msgid "" +"Add a new tool to the Tool Table\n" +"from the Tool DataBase." +msgstr "" +"Fügen Sie der Werkzeugtabelle ein neues Werkzeug aus der\n" +"aus der Werkzeugdatenbank hinzu." + +#: AppGUI/ObjectUI.py:1721 +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." + +#: AppGUI/ObjectUI.py:1727 +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." + +#: AppGUI/ObjectUI.py:1854 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:103 +msgid "" +"Height of the tool when\n" +"moving without cutting." +msgstr "" +"Höhe des Werkzeugs bei\n" +"Bewegen ohne zu schneiden." + +#: AppGUI/ObjectUI.py:1887 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:202 +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." + +#: AppGUI/ObjectUI.py:1902 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:66 +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." + +#: AppGUI/ObjectUI.py:1946 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:219 +msgid "" +"Speed of the spindle in RPM (optional).\n" +"If LASER preprocessor 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." + +#: AppGUI/ObjectUI.py:2049 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:124 +msgid "" +"Include tool-change sequence\n" +"in the Machine Code (Pause for tool change)." +msgstr "" +"Werkzeugwechselfolge einbeziehen\n" +"im Maschinencode (Pause für Werkzeugwechsel)." + +#: AppGUI/ObjectUI.py:2118 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:256 +msgid "" +"The Preprocessor 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)." + +#: AppGUI/ObjectUI.py:2247 Common.py:405 Common.py:520 Common.py:573 +msgid "Generate the CNC Job object." +msgstr "Generieren Sie das CNC-Job-Objekt." + +#: AppGUI/ObjectUI.py:2264 +msgid "Launch Paint Tool in Tools Tab." +msgstr "Starten Sie das Paint Werkzeug in der Registerkarte \"Tools\"." + +#: AppGUI/ObjectUI.py:2272 AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:35 +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." + +#: AppGUI/ObjectUI.py:2327 +msgid "CNC Job Object" +msgstr "CNC-Auftragsobjekt" + +#: AppGUI/ObjectUI.py:2338 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:45 +msgid "Plot kind" +msgstr "Darstellungsart" + +#: AppGUI/ObjectUI.py:2341 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:47 +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." + +#: AppGUI/ObjectUI.py:2350 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:55 +msgid "Travel" +msgstr "Reise" + +#: AppGUI/ObjectUI.py:2354 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:64 +msgid "Display Annotation" +msgstr "Anmerkung anzeigen" + +#: AppGUI/ObjectUI.py:2356 AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:66 +msgid "" +"This selects if to display text annotation on the plot.\n" +"When checked it will display numbers in order for each end\n" +"of a travel line." +msgstr "" +"Hiermit wird ausgewählt, ob Textanmerkungen auf dem Plot angezeigt werden " +"sollen.\n" +"Wenn diese Option aktiviert ist, werden die Nummern für jedes Ende in der " +"richtigen Reihenfolge angezeigt\n" +"einer Reiseleitung." + +#: AppGUI/ObjectUI.py:2371 +msgid "Travelled dist." +msgstr "Zurückgelegte Strecke." + +#: AppGUI/ObjectUI.py:2373 AppGUI/ObjectUI.py:2378 +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." + +#: AppGUI/ObjectUI.py:2383 +msgid "Estimated time" +msgstr "Geschätzte Zeit" + +#: AppGUI/ObjectUI.py:2385 AppGUI/ObjectUI.py:2390 +msgid "" +"This is the estimated time to do the routing/drilling,\n" +"without the time spent in ToolChange events." +msgstr "" +"Dies ist die geschätzte Zeit für das Fräsen / Bohren.\n" +"ohne die Zeit, die in Werkzeugwechselereignissen verbracht wird." + +#: AppGUI/ObjectUI.py:2425 +msgid "CNC Tools Table" +msgstr "CNC Werkzeugtabelle" + +#: AppGUI/ObjectUI.py:2428 +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)." + +#: AppGUI/ObjectUI.py:2456 AppGUI/ObjectUI.py:2467 +msgid "P" +msgstr "P" + +#: AppGUI/ObjectUI.py:2477 +msgid "Update Plot" +msgstr "Plot aktualisieren" + +#: AppGUI/ObjectUI.py:2479 +msgid "Update the plot." +msgstr "Aktualisieren Sie die Darstellung." + +#: AppGUI/ObjectUI.py:2486 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:30 +msgid "Export CNC Code" +msgstr "CNC-Code exportieren" + +#: AppGUI/ObjectUI.py:2488 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:32 +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:33 +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." + +#: AppGUI/ObjectUI.py:2494 +msgid "Prepend to CNC Code" +msgstr "CNC-Code voranstellen" + +#: AppGUI/ObjectUI.py:2496 AppGUI/ObjectUI.py:2503 +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:49 +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" +"die Sie am Anfang der G-Code-Datei hinzufügen möchten." + +#: AppGUI/ObjectUI.py:2509 +msgid "Append to CNC Code" +msgstr "An CNC Code anhängen" + +#: AppGUI/ObjectUI.py:2511 AppGUI/ObjectUI.py:2519 +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:65 +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" +"die Sie an die generierte Datei anhängen möchten.\n" +"z.B.: M2 (Programmende)" + +#: AppGUI/ObjectUI.py:2533 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:38 +msgid "Toolchange G-Code" +msgstr "Werkzeugwechsel G-Code" + +#: AppGUI/ObjectUI.py:2536 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:41 +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 preprocessor 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." + +#: AppGUI/ObjectUI.py:2551 +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" +"WARNING: it can be used only with a preprocessor file\n" +"that has 'toolchange_custom' in it's name." +msgstr "" +"Geben Sie hier G-Code-Befehle ein\n" +"die ausgeführt werden, wenn ein Werkzeugwechselereignis auftritt.\n" +"So kann ein benutzerdefinierter Werkzeugwechsel in GCode definiert werden.\n" +"Die FlatCAM-Variablen sind vom '%'-Symbol umgeben.\n" +"\n" +"WARNUNG: Ein Werkzeugwechselereignis kann nur mit einer Präprozessor-Datei " +"verwendet werden\n" +"die das Präfix \"toolchange_custom\" im Namen hat und nach Vorlage der \n" +" \n" +"\"Toolchange Custom\" -Prozessordatei erzeugt wurde." + +#: AppGUI/ObjectUI.py:2566 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:80 +msgid "Use Toolchange Macro" +msgstr "Benutze das Werkzeugwechselmakro" + +#: AppGUI/ObjectUI.py:2568 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:82 +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)." + +#: AppGUI/ObjectUI.py:2576 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:94 +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" + +#: AppGUI/ObjectUI.py:2583 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:101 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:30 +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:31 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:31 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:37 +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:36 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:36 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:36 +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:31 +#: AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py:31 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:36 +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:31 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:30 +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:31 +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:35 +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:32 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:31 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:31 +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:31 +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:31 +#: AppGUI/preferences/tools/ToolsSubPrefGroupUI.py:31 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:31 +#: AppTools/ToolCopperThieving.py:89 AppTools/ToolCorners.py:106 +#: AppTools/ToolEtchCompensation.py:82 AppTools/ToolFiducials.py:149 +#: AppTools/ToolInvertGerber.py:82 +msgid "Parameters" +msgstr "Parameters" + +#: AppGUI/ObjectUI.py:2586 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:106 +msgid "FlatCAM CNC parameters" +msgstr "FlatCAM CNC-Parameter" + +#: AppGUI/ObjectUI.py:2587 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:111 +msgid "tool number" +msgstr "Werkzeugnummer" + +#: AppGUI/ObjectUI.py:2588 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:112 +msgid "tool diameter" +msgstr "Werkzeugdurchmesser" + +#: AppGUI/ObjectUI.py:2589 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:113 +msgid "for Excellon, total number of drills" +msgstr "für Excellon die Gesamtzahl der Bohrer" + +#: AppGUI/ObjectUI.py:2591 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:115 +msgid "X coord for Toolchange" +msgstr "X-Koordinate für Werkzeugwechsel" + +#: AppGUI/ObjectUI.py:2592 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:116 +msgid "Y coord for Toolchange" +msgstr "Y-Koordinate für Werkzeugwechsel" + +#: AppGUI/ObjectUI.py:2593 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:118 +msgid "Z coord for Toolchange" +msgstr "Z-Koordinate für Werkzeugwechsel" + +#: AppGUI/ObjectUI.py:2594 +msgid "depth where to cut" +msgstr "tiefe wo zu schneiden" + +#: AppGUI/ObjectUI.py:2595 +msgid "height where to travel" +msgstr "Höhe, wohin man reist" + +#: AppGUI/ObjectUI.py:2596 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:121 +msgid "the step value for multidepth cut" +msgstr "der Schrittwert für den mehrstufigen Schnitt" + +#: AppGUI/ObjectUI.py:2598 +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:123 +msgid "the value for the spindle speed" +msgstr "der Wert für die Spindeldrehzahl" + +#: AppGUI/ObjectUI.py:2600 +msgid "time to dwell to allow the spindle to reach it's set RPM" +msgstr "" +"Zeit zum Verweilen, damit die Spindel die eingestellte Drehzahl erreicht" + +#: AppGUI/ObjectUI.py:2616 +msgid "View CNC Code" +msgstr "CNC-Code anzeigen" + +#: AppGUI/ObjectUI.py:2618 +msgid "" +"Opens TAB to view/modify/print G-Code\n" +"file." +msgstr "" +"Öffnet die Registerkarte zum Anzeigen / Ändern / Drucken von G-Code\n" +"Datei." + +#: AppGUI/ObjectUI.py:2623 +msgid "Save CNC Code" +msgstr "CNC-Code speichern" + +#: AppGUI/ObjectUI.py:2625 +msgid "" +"Opens dialog to save G-Code\n" +"file." +msgstr "" +"Öffnet den Dialog zum Speichern des G-Codes\n" +"Datei." + +#: AppGUI/ObjectUI.py:2659 +msgid "Script Object" +msgstr "Skriptobjekt" + +#: AppGUI/ObjectUI.py:2679 AppGUI/ObjectUI.py:2753 +msgid "Auto Completer" +msgstr "Auto-Vervollständiger" + +#: AppGUI/ObjectUI.py:2681 +msgid "This selects if the auto completer is enabled in the Script Editor." +msgstr "" +"Hiermit wird ausgewählt, ob der automatische Vervollständiger im Skript-" +"Editor aktiviert ist." + +#: AppGUI/ObjectUI.py:2726 +msgid "Document Object" +msgstr "Dokumentobjekt" + +#: AppGUI/ObjectUI.py:2755 +msgid "This selects if the auto completer is enabled in the Document Editor." +msgstr "" +"Hiermit wird ausgewählt, ob der automatische Vervollständiger im " +"Dokumenteditor aktiviert ist." + +#: AppGUI/ObjectUI.py:2773 +msgid "Font Type" +msgstr "Schriftart" + +#: AppGUI/ObjectUI.py:2790 +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:197 +msgid "Font Size" +msgstr "Schriftgröße" + +#: AppGUI/ObjectUI.py:2826 +msgid "Alignment" +msgstr "Ausrichtung" + +#: AppGUI/ObjectUI.py:2831 +msgid "Align Left" +msgstr "Linksbündig" + +#: AppGUI/ObjectUI.py:2836 App_Main.py:4687 +msgid "Center" +msgstr "Center" + +#: AppGUI/ObjectUI.py:2841 +msgid "Align Right" +msgstr "Rechts ausrichten" + +#: AppGUI/ObjectUI.py:2846 +msgid "Justify" +msgstr "Rechtfertigen" + +#: AppGUI/ObjectUI.py:2853 +msgid "Font Color" +msgstr "Schriftfarbe" + +#: AppGUI/ObjectUI.py:2855 +msgid "Set the font color for the selected text" +msgstr "Stellen Sie die Schriftfarbe für den ausgewählten Text ein" + +#: AppGUI/ObjectUI.py:2869 +msgid "Selection Color" +msgstr "Auswahlfarbe" + +#: AppGUI/ObjectUI.py:2871 +msgid "Set the selection color when doing text selection." +msgstr "Stellen Sie die Auswahlfarbe bei der Textauswahl ein." + +#: AppGUI/ObjectUI.py:2885 +msgid "Tab Size" +msgstr "Tab-Größe" + +#: AppGUI/ObjectUI.py:2887 +msgid "Set the tab size. In pixels. Default value is 80 pixels." +msgstr "" +"Stellen Sie die Größe der Registerkarte ein. In Pixeln. Der Standardwert " +"beträgt 80 Pixel." + +#: AppGUI/PlotCanvasLegacy.py:1464 +msgid "" +"Could not annotate due of a difference between the number of text elements " +"and the number of text positions." +msgstr "" +"Aufgrund eines Unterschieds zwischen der Anzahl der Textelemente und der " +"Anzahl der Textpositionen konnten keine Anmerkungen erstellt werden." + +#: AppGUI/preferences/PreferencesUIManager.py:911 +#: AppGUI/preferences/PreferencesUIManager.py:1002 +#: AppGUI/preferences/PreferencesUIManager.py:1026 +#: AppGUI/preferences/PreferencesUIManager.py:1132 App_Main.py:5107 +msgid "Preferences" +msgstr "Einstellungen" + +#: AppGUI/preferences/PreferencesUIManager.py:917 +msgid "Preferences applied." +msgstr "Einstellungen werden angewendet." + +#: AppGUI/preferences/PreferencesUIManager.py:937 +#, fuzzy +#| msgid "Are you sure you want to delete the GUI Settings? \n" +msgid "Are you sure you want to continue?" +msgstr "Möchten Sie die GUI-Einstellungen wirklich löschen?\n" + +#: AppGUI/preferences/PreferencesUIManager.py:938 +#, fuzzy +#| msgid "Application started ..." +msgid "Application restart" +msgstr "Bewerbung gestartet ..." + +#: AppGUI/preferences/PreferencesUIManager.py:1031 +msgid "Preferences closed without saving." +msgstr "Einstellungen geschlossen ohne zu speichern." + +#: AppGUI/preferences/PreferencesUIManager.py:1043 +msgid "Preferences default values are restored." +msgstr "Die Standardeinstellungen werden wiederhergestellt." + +#: AppGUI/preferences/PreferencesUIManager.py:1075 App_Main.py:2476 +#: App_Main.py:2544 +msgid "Failed to write defaults to file." +msgstr "Fehler beim Schreiben der Voreinstellungen in die Datei." + +#: AppGUI/preferences/PreferencesUIManager.py:1079 +#: AppGUI/preferences/PreferencesUIManager.py:1188 +msgid "Preferences saved." +msgstr "Einstellungen gespeichert." + +#: AppGUI/preferences/PreferencesUIManager.py:1129 +msgid "Preferences edited but not saved." +msgstr "Einstellungen bearbeitet, aber nicht gespeichert." + +#: AppGUI/preferences/PreferencesUIManager.py:1174 +msgid "" +"One or more values are changed.\n" +"Do you want to save the Preferences?" +msgstr "" +"Ein oder mehrere Werte werden geändert.\n" +"Möchten Sie die Einstellungen speichern?" + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:27 +msgid "CNC Job Adv. Options" +msgstr "Erw. CNC-Joboptionen" + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:64 +msgid "" +"Type here any G-Code commands you would like to be executed when Toolchange " +"event is encountered.\n" +"This will constitute a Custom Toolchange GCode, or a Toolchange Macro.\n" +"The FlatCAM variables are surrounded by '%' symbol.\n" +"WARNING: it can be used only with a preprocessor file that has " +"'toolchange_custom' in it's name." +msgstr "" +"Geben Sie hier G-Code-Befehle ein, die ausgeführt werden, wenn ein " +"Werkzeugwechselereignis auftritt.\n" +"So kann ein benutzerdefinierter Werkzeugwechsel in GCode definiert werden.\n" +"Die FlatCAM-Variablen sind vom '%'-Symbol umgeben.\n" +"\n" +"WARNUNG: Ein Werkzeugwechselereignis kann nur mit einer Präprozessor-Datei " +"verwendet warden, die das Präfix \"toolchange_custom\" im Namen hat und nach " +"Vorlage der \"Toolchange Custom\" -Prozessordatei erzeugt wurde." + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:119 +msgid "Z depth for the cut" +msgstr "Z Tiefe für den Schnitt" + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:120 +msgid "Z height for travel" +msgstr "Z Höhe für die Reise" + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:126 +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" + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:145 +msgid "Annotation Size" +msgstr "Anmerkungsgröße" + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:147 +msgid "The font size of the annotation text. In pixels." +msgstr "Die Schriftgröße des Anmerkungstextes. In Pixeln." + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:157 +msgid "Annotation Color" +msgstr "Anmerkungsfarbe" + +#: AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py:159 +msgid "Set the font color for the annotation texts." +msgstr "Legen Sie die Schriftfarbe für die Anmerkungstexte fest." + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:26 +msgid "CNC Job General" +msgstr "CNC-Job Allgemein" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:77 +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:47 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:59 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:45 +msgid "Circle Steps" +msgstr "Kreisschritte" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:79 +msgid "" +"The number of circle steps for GCode \n" +"circle and arc shapes linear approximation." +msgstr "" +"Die Anzahl der Kreisschritte für GCode\n" +"Kreis- und Bogenformen lineare Annäherung." + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:88 +msgid "Travel dia" +msgstr "Verfahrdurchm" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:90 +msgid "" +"The width of the travel lines to be\n" +"rendered in the plot." +msgstr "" +"Die Breite der Fahrlinien soll sein\n" +"in der Handlung gerendert." + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:103 +msgid "G-code Decimals" +msgstr "G-Code-Dezimalstellen" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:106 +#: AppTools/ToolFiducials.py:74 +msgid "Coordinates" +msgstr "Koordinaten" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:108 +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.)" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:119 +#: AppTools/ToolProperties.py:519 +msgid "Feedrate" +msgstr "Vorschubgeschwindigkeit" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:121 +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.)" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:132 +msgid "Coordinates type" +msgstr "Koordinaten eingeben" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:134 +msgid "" +"The type of coordinates to be used in Gcode.\n" +"Can be:\n" +"- Absolute G90 -> the reference is the origin x=0, y=0\n" +"- Incremental G91 -> the reference is the previous position" +msgstr "" +"Der in Gcode zu verwendende Koordinatentyp.\n" +"Kann sein:\n" +"- Absolut G90 -> die Referenz ist der Ursprung x = 0, y = 0\n" +"- Inkrementell G91 -> Die Referenz ist die vorherige Position" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:140 +msgid "Absolute G90" +msgstr "Absolut G90" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:141 +msgid "Incremental G91" +msgstr "Inkrementelles G91" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:151 +msgid "Force Windows style line-ending" +msgstr "Windows Zeilenendemarkierung erzwingen" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:153 +msgid "" +"When checked will force a Windows style line-ending\n" +"(\\r\\n) on non-Windows OS's." +msgstr "" +"Wenn ausgewählt werden Zeilenendungsmarkierungen von Windows (CRLF) auch auf " +"anderen Betriebssystemen geschrieben." + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:165 +msgid "Travel Line Color" +msgstr "Reiselinienfarbe" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:169 +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:235 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:262 +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:154 +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:220 +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:84 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:153 +#: AppTools/ToolRulesCheck.py:186 +msgid "Outline" +msgstr "Gliederung" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:171 +msgid "Set the travel line color for plotted objects." +msgstr "Legen Sie die Reiselinienfarbe für geplottete Objekte fest." + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:186 +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:252 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:279 +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:170 +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:237 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:170 +msgid "Fill" +msgstr "Füll" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:188 +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:254 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:281 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:172 +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)." + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:205 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:298 +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:190 +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:257 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:189 +msgid "Alpha" +msgstr "Alpha" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:207 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:300 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:191 +msgid "Set the fill transparency for plotted objects." +msgstr "Legen Sie die Füllungstransparenz für geplottete Objekte fest." + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:231 +msgid "CNCJob Object Color" +msgstr "CNCJob-Objektfarbe" + +#: AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:237 +msgid "Set the color for plotted objects." +msgstr "Legen Sie die Farbe für geplottete Objekte fest." + +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:27 +msgid "CNC Job Options" +msgstr "CNC-Auftragsoptionen" + +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:31 +msgid "Export G-Code" +msgstr "G-Code exportieren" + +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:47 +msgid "Prepend to G-Code" +msgstr "Voranstellen an G-Code" + +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:56 +msgid "" +"Type here any G-Code commands you would like to add at the beginning of the " +"G-Code file." +msgstr "" +"Geben Sie hier alle G-Code-Befehle ein\n" +"die Sie zum Anfang der G-Code-Datei hinzufügen möchten." + +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:63 +msgid "Append to G-Code" +msgstr "An G-Code anhängen" + +#: AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py:73 +msgid "" +"Type here any G-Code commands you would like to append to the generated " +"file.\n" +"I.e.: M2 (End of program)" +msgstr "" +"Geben Sie hier alle G-Code-Befehle ein, die Sie an die generierte Datei " +"anhängen möchten.\n" +"Zum Beispiel: M2 (Programmende)" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:27 +msgid "Excellon Adv. Options" +msgstr "Excellon erweiterte Optionen" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:34 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:33 +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:31 +msgid "Advanced Options" +msgstr "Erweiterte Optionen" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:36 +msgid "" +"A list of Excellon advanced parameters.\n" +"Those parameters are available only for\n" +"Advanced App. Level." +msgstr "" +"Eine Liste der erweiterten Excellon-Parameter.\n" +"Diese Parameter sind nur für verfügbar\n" +"Erweiterte App. Niveau." + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:59 +msgid "Toolchange X,Y" +msgstr "Werkzeugwechsel X, Y" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:61 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:47 +msgid "Toolchange X,Y position." +msgstr "Werkzeugwechsel X, Y Position." + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:121 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:134 +msgid "Spindle direction" +msgstr "Drehrichtung" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:123 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:136 +msgid "" +"This sets the direction that the spindle is rotating.\n" +"It can be either:\n" +"- CW = clockwise or\n" +"- CCW = counter clockwise" +msgstr "" +"Hiermit wird die Drehrichtung der Spindel eingestellt.\n" +"Es kann entweder sein:\n" +"- CW = im Uhrzeigersinn oder\n" +"- CCW = gegen den Uhrzeigersinn" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:134 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:148 +msgid "Fast Plunge" +msgstr "Schneller Sprung" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:136 +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:150 +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." + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:143 +msgid "Fast Retract" +msgstr "Schneller Rückzug" + +#: AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:145 +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." + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:32 +msgid "A list of Excellon Editor parameters." +msgstr "Eine Liste der Excellon Editor-Parameter." + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:40 +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:41 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:41 +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:194 +msgid "Selection limit" +msgstr "Auswahllimit" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:42 +msgid "" +"Set the number of selected Excellon geometry\n" +"items above which the utility geometry\n" +"becomes just a selection rectangle.\n" +"Increases the performance when moving a\n" +"large number of geometric elements." +msgstr "" +"Stellen Sie die Anzahl der ausgewählten Excellon-Geometrien ein\n" +"Elemente, über denen die Nutzgeometrie\n" +"wird nur ein Auswahlrechteck.\n" +"Erhöht die Leistung beim Bewegen von a\n" +"große Anzahl von geometrischen Elementen." + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:55 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:117 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:123 +msgid "New Dia" +msgstr "Neuer Durchmesser" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:80 +msgid "Linear Drill Array" +msgstr "Linearbohrer-Array" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:84 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:232 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:120 +msgid "Linear Direction" +msgstr "Lineare Richtung" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:126 +msgid "Circular Drill Array" +msgstr "Rundbohrer-Array" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:130 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:280 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:164 +msgid "Circular Direction" +msgstr "Kreisrichtung" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:132 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:282 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:166 +msgid "" +"Direction for circular array.\n" +"Can be CW = clockwise or CCW = counter clockwise." +msgstr "" +"Richtung für kreisförmige Anordnung. \n" +"Kann CW = Uhrzeigersinn oder CCW = Gegenuhrzeigersinn sein." + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:143 +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:293 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:177 +msgid "Circular Angle" +msgstr "Kreiswinkel" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:196 +msgid "" +"Angle at which the slot 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, in dem der Schlitz platziert ist.\n" +"Die Genauigkeit beträgt maximal 2 Dezimalstellen.\n" +"Der Mindestwert beträgt: -359,99 Grad.\n" +"Maximaler Wert ist: 360.00 Grad." + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:215 +msgid "Linear Slot Array" +msgstr "Lineare Schlitzanordnung" + +#: AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:276 +msgid "Circular Slot Array" +msgstr "Kreisschlitz-Array" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:26 +msgid "Excellon Export" +msgstr "Excellon Export" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:30 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:31 +msgid "Export Options" +msgstr "Exportoptionen" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:32 +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." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:41 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:163 +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:39 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:42 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:82 +#: AppTools/ToolDistance.py:56 AppTools/ToolDistanceMin.py:49 +#: AppTools/ToolPcbWizard.py:127 AppTools/ToolProperties.py:154 +msgid "Units" +msgstr "Einheiten" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:43 +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:49 +msgid "The units used in the Excellon file." +msgstr "Die in der Excellon-Datei verwendeten Einheiten." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:46 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:87 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:173 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:47 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:87 +#: AppTools/ToolCalculators.py:61 AppTools/ToolPcbWizard.py:125 +msgid "INCH" +msgstr "ZOLL" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:47 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:174 +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:43 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:48 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:88 +#: AppTools/ToolCalculators.py:62 AppTools/ToolPcbWizard.py:126 +msgid "MM" +msgstr "MM" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:55 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:56 +msgid "Int/Decimals" +msgstr "Ganzzahl / Dezimalzahl" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:57 +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." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:69 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:95 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:124 +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." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:82 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:108 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:137 +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." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:91 +msgid "Format" +msgstr "Format" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:93 +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:103 +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." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:100 +msgid "Decimal" +msgstr "Dezimal" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:101 +msgid "No-Decimal" +msgstr "Keine Dezimalzahl" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:114 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:145 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:96 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:97 +msgid "Zeros" +msgstr "Nullen" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:117 +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." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:124 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:158 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:106 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:107 +#: AppTools/ToolPcbWizard.py:111 +msgid "LZ" +msgstr "LZ" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:125 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:159 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:107 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:108 +#: AppTools/ToolPcbWizard.py:112 +msgid "TZ" +msgstr "TZ" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:127 +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." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:137 +msgid "Slot type" +msgstr "Schlitze-Typ" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:140 +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:150 +msgid "" +"This sets how the slots will be exported.\n" +"If ROUTED then the slots will be routed\n" +"using M15/M16 commands.\n" +"If DRILLED(G85) the slots will be exported\n" +"using the Drilled slot command (G85)." +msgstr "" +"Hier legen Sie fest, wie die Slots exportiert werden.\n" +"Wenn geroutet, werden die Slots geroutet\n" +"mit M15 / M16 Befehlen.\n" +"Beim Bohren (G85) werden die Steckplätze exportiert\n" +"Verwenden Sie den Befehl Bohrschlitz (G85)." + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:147 +msgid "Routed" +msgstr "Geroutet" + +#: AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py:148 +msgid "Drilled(G85)" +msgstr "Gebohrt (G85)" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:29 +msgid "Excellon General" +msgstr "Excellon Allgemeines" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:62 +msgid "Excellon Format" +msgstr "Excellon Format" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:64 +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" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:88 +msgid "Default values for INCH are 2:4" +msgstr "Die Standardwerte für ZOLL sind 2: 4" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:116 +msgid "METRIC" +msgstr "METRISCH" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:117 +msgid "Default values for METRIC are 3:3" +msgstr "Die Standardwerte für METRISCH sind 3: 3" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:148 +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.\n" +"\n" +"This is used when there is no information\n" +"stored in the Excellon file." +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.\n" +"\n" +"Dies wird verwendet, wenn keine Informationen vorliegen\n" +"in der Excellon-Datei gespeichert." + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:166 +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." + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:176 +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." + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:184 +msgid "Update Export settings" +msgstr "Exporteinstellungen aktual" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:201 +msgid "Excellon Optimization" +msgstr "Optimierung der Excellons" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:204 +msgid "Algorithm:" +msgstr "Algorithmus:" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:206 +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:222 +msgid "" +"This sets the optimization type for the Excellon drill path.\n" +"If <> is checked then Google OR-Tools algorithm with\n" +"MetaHeuristic Guided Local Path is used. Default search time is 3sec.\n" +"If <> is checked then Google OR-Tools Basic algorithm is used.\n" +"If <> is checked then Travelling Salesman algorithm is used for\n" +"drill path optimization.\n" +"\n" +"If this control is disabled, then FlatCAM works in 32bit mode and it uses\n" +"Travelling Salesman algorithm for path optimization." +msgstr "" +"Hiermit wird der Optimierungstyp für den Excellon-Bohrpfad festgelegt.\n" +"Wenn << MetaHeuristic >> aktiviert ist, verwenden Sie den Google OR-Tools-" +"Algorithmus\n" +"Es wird ein metaHeuristisch geführter lokaler Pfad verwendet. Die " +"Standardsuchzeit beträgt 3 Sekunden.\n" +"Wenn << Basic >> aktiviert ist, wird der Google OR-Tools Basic-Algorithmus " +"verwendet.\n" +"Wenn << TSA >> markiert ist, wird der Traveling Salesman-Algorithmus für " +"verwendet\n" +"Bohrpfadoptimierung.\n" +"\n" +"Wenn dieses Steuerelement deaktiviert ist, arbeitet FlatCAM im 32-Bit-Modus " +"und verwendet\n" +"Travelling Salesman-Algorithmus zur Pfadoptimierung." + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:217 +msgid "MetaHeuristic" +msgstr "MetaHeuristic" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:218 +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:104 +#: AppObjects/FlatCAMExcellon.py:683 AppObjects/FlatCAMGeometry.py:561 +#: AppObjects/FlatCAMGerber.py:251 +msgid "Basic" +msgstr "Basis" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:219 +msgid "TSA" +msgstr "TSA" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:236 +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:245 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:237 +msgid "Duration" +msgstr "Dauer" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:239 +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." + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:258 +msgid "Excellon Object Color" +msgstr "Excellon-Objektfarbe" + +#: AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:264 +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:86 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:155 +msgid "Set the line color for plotted objects." +msgstr "Legen Sie die Linienfarbe für geplottete Objekte fest." + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:29 +msgid "Excellon Options" +msgstr "Excellon-Optionen" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:33 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:34 +msgid "Create CNC Job" +msgstr "CNC-Job erstellen" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:35 +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." + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:152 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:121 +msgid "Tool change" +msgstr "Werkzeugwechsel" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:236 +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:232 +msgid "Enable Dwell" +msgstr "Verweilzeit aktivieren" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:259 +msgid "" +"The preprocessor JSON file that dictates\n" +"Gcode output." +msgstr "" +"Die Postprozessor-JSON-Datei, die diktiert\n" +"Gcode-Ausgabe." + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:270 +msgid "Gcode" +msgstr "Gcode" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:272 +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." + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:288 +msgid "Mill Holes" +msgstr "Löcher bohren" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:290 +msgid "Create Geometry for milling holes." +msgstr "Erstellen Sie Geometrie zum Fräsen von Löchern." + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:294 +msgid "Drill Tool dia" +msgstr "Bohrwerkzeugs Durchm" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:305 +msgid "Slot Tool dia" +msgstr "Schlitzwerkzeug Durchmesser" + +#: AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:307 +msgid "" +"Diameter of the cutting tool\n" +"when milling slots." +msgstr "" +"Durchmesser des Schneidewerkzeugs\n" +"beim Fräsen von Schlitzen." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:28 +msgid "App Settings" +msgstr "App Einstellungen" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:49 +msgid "Grid Settings" +msgstr "Rastereinstellungen" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:53 +msgid "X value" +msgstr "X-Wert" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:55 +msgid "This is the Grid snap value on X axis." +msgstr "Dies ist der Rasterfangwert auf der X-Achse." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:65 +msgid "Y value" +msgstr "Y-Wert" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:67 +msgid "This is the Grid snap value on Y axis." +msgstr "Dies ist der Rasterfangwert auf der Y-Achse." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:77 +msgid "Snap Max" +msgstr "Fang Max" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:92 +msgid "Workspace Settings" +msgstr "Arbeitsbereichseinstellungen" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:95 +msgid "Active" +msgstr "Aktiv" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:97 +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." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:105 +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." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:171 +msgid "Orientation" +msgstr "Orientierung" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:172 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:239 +#: AppTools/ToolFilm.py:405 +msgid "" +"Can be:\n" +"- Portrait\n" +"- Landscape" +msgstr "" +"Eines von\n" +"- Hochformat\n" +"- Querformat" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:176 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:243 +#: AppTools/ToolFilm.py:409 +msgid "Portrait" +msgstr "Hochformat" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:177 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:244 +#: AppTools/ToolFilm.py:410 +msgid "Landscape" +msgstr "Querformat" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:201 +msgid "Notebook" +msgstr "Notizbuch" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:203 +#, fuzzy +#| msgid "" +#| "This sets the font size for the elements found in the Notebook.\n" +#| "The notebook is the collapsible area in the left side of the GUI,\n" +#| "and include the Project, Selected and Tool tabs." +msgid "" +"This sets the font size for the elements found in the Notebook.\n" +"The notebook is the collapsible area in the left side of the AppGUI,\n" +"and include the Project, Selected and Tool tabs." +msgstr "" +"Hiermit wird die Schriftgröße für die im Notizbuch enthaltenen Elemente " +"festgelegt.\n" +"Das Notizbuch ist der ausblendbare Bereich auf der linken Seite der " +"Benutzeroberfläche.\n" +"und schließen Sie die Registerkarten Projekt, Ausgewählt und Werkzeug ein." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:222 +msgid "Axis" +msgstr "Achse" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:224 +msgid "This sets the font size for canvas axis." +msgstr "Hiermit wird die Schriftgröße für die Zeichenbereichsachse festgelegt." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:241 +msgid "Textbox" +msgstr "Textfeld" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:243 +#, fuzzy +#| msgid "" +#| "This sets the font size for the Textbox GUI\n" +#| "elements that are used in FlatCAM." +msgid "" +"This sets the font size for the Textbox AppGUI\n" +"elements that are used in FlatCAM." +msgstr "" +"Hiermit wird die Schriftgröße für die Textbox-GUI festgelegt\n" +"Elemente, die in FlatCAM verwendet werden." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:261 +msgid "HUD" +msgstr "" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:263 +#, fuzzy +#| msgid "This sets the font size for canvas axis." +msgid "This sets the font size for the Heads Up Display." +msgstr "Hiermit wird die Schriftgröße für die Zeichenbereichsachse festgelegt." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:288 +msgid "Mouse Settings" +msgstr "Mauseinstellungen" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:292 +msgid "Cursor Shape" +msgstr "Mauszeiger Form" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:294 +msgid "" +"Choose a mouse cursor shape.\n" +"- Small -> with a customizable size.\n" +"- Big -> Infinite lines" +msgstr "" +"Wählen Sie eine Mauszeigerform.\n" +"- Klein -> mit einer anpassbaren Größe.\n" +"- Groß -> Unendliche Linien" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:300 +msgid "Small" +msgstr "Klein" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:301 +msgid "Big" +msgstr "Groß" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:308 +msgid "Cursor Size" +msgstr "Mauszeigergröße" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:310 +msgid "Set the size of the mouse cursor, in pixels." +msgstr "Stellen Sie die Größe des Mauszeigers in Pixel ein." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:321 +msgid "Cursor Width" +msgstr "Mauszeiger Breite" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:323 +msgid "Set the line width of the mouse cursor, in pixels." +msgstr "Legen Sie die Linienbreite des Mauszeigers in Pixel fest." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:334 +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:341 +msgid "Cursor Color" +msgstr "Mauszeigerfarbe" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:336 +msgid "Check this box to color mouse cursor." +msgstr "Aktivieren Sie dieses Kontrollkästchen, um den Mauszeiger einzufärben." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:343 +msgid "Set the color of the mouse cursor." +msgstr "Stellen Sie die Farbe des Mauszeigers ein." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:366 +msgid "Pan Button" +msgstr "Pan-Taste" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:368 +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" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:372 +msgid "MMB" +msgstr "MMB" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:373 +msgid "RMB" +msgstr "RMB" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:379 +msgid "Multiple Selection" +msgstr "Mehrfachauswahl" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:381 +msgid "Select the key used for multiple selection." +msgstr "Wählen Sie den Schlüssel für die Mehrfachauswahl aus." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:383 +msgid "CTRL" +msgstr "STRG" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:384 +msgid "SHIFT" +msgstr "SHIFT" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:395 +msgid "Delete object confirmation" +msgstr "Objektbestätigung löschen" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:397 +msgid "" +"When checked the application will ask for user confirmation\n" +"whenever the Delete object(s) event is triggered, either by\n" +"menu shortcut or key shortcut." +msgstr "" +"Wenn diese Option aktiviert ist, werden Sie von der Anwendung um eine\n" +"Bestätigung des Benutzers gebeten Jedes Mal, wenn das Ereignis Objekt (e)\n" +"löschen ausgelöst wird, entweder durch\n" +"Menüverknüpfung oder Tastenkombination." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:404 +msgid "\"Open\" behavior" +msgstr "\"Offen\" -Verhalten" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:406 +msgid "" +"When checked the path for the last saved file is used when saving files,\n" +"and the path for the last opened file is used when opening files.\n" +"\n" +"When unchecked the path for opening files is the one used last: either the\n" +"path for saving files or the path for opening files." +msgstr "" +"Wenn diese Option aktiviert ist, wird beim Speichern der Dateien der Pfad " +"für die zuletzt gespeicherte Datei verwendet.\n" +"und der Pfad für die zuletzt geöffnete Datei wird beim Öffnen von Dateien " +"verwendet.\n" +"\n" +"Wenn das Kontrollkästchen deaktiviert ist, wird der Pfad zum Öffnen der " +"Dateien zuletzt verwendet: entweder der Pfad\n" +"Pfad zum Speichern von Dateien oder Pfad zum Öffnen von Dateien." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:415 +msgid "Enable ToolTips" +msgstr "QuickInfos aktivieren" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:417 +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." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:424 +msgid "Allow Machinist Unsafe Settings" +msgstr "Unsichere Maschineneinstellungen erlauben" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:426 +msgid "" +"If checked, some of the application settings will be allowed\n" +"to have values that are usually unsafe to use.\n" +"Like Z travel negative values or Z Cut positive values.\n" +"It will applied at the next application start.\n" +"<>: Don't change this unless you know what you are doing !!!" +msgstr "" +"Wenn gewählt werden Applikationseinstellungen erlaubt,\n" +"die im normalen Betrieb als unsicher gelten.\n" +"Zum Beispiel negative Werte für schnelle Z Bewegungen, oder \n" +"Positive Z-Werte bei schneidvorgängen.\n" +"Wird beim Nächsten Programmstart wirksam\n" +" << ACHTUNG>>: Ändern Sie das nicht, wenn Sie nicht wissen was Sie tun!" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:438 +msgid "Bookmarks limit" +msgstr "Lesezeichenlimit" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:440 +msgid "" +"The maximum number of bookmarks that may be installed in the menu.\n" +"The number of bookmarks in the bookmark manager may be greater\n" +"but the menu will hold only so much." +msgstr "" +"Die maximale Anzahl von Lesezeichen, die im Menü installiert werden dürfen.\n" +"Die Anzahl der Lesezeichen im Lesezeichen-Manager ist möglicherweise größer\n" +"Aber das Menü wird nur so viel enthalten." + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:449 +msgid "Activity Icon" +msgstr "Aktivitätssymbol" + +#: AppGUI/preferences/general/GeneralAPPSetGroupUI.py:451 +msgid "Select the GIF that show activity when FlatCAM is active." +msgstr "" +"Wählen Sie das GIF aus, das die Aktivität anzeigt, wenn FlatCAM aktiv ist." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:29 +msgid "App Preferences" +msgstr "App-Einstellungen" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:40 +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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:44 +msgid "IN" +msgstr "ZOLL" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:50 +msgid "Precision MM" +msgstr "Präzision in mm" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:52 +msgid "" +"The number of decimals used throughout the application\n" +"when the set units are in METRIC system.\n" +"Any change here require an application restart." +msgstr "" +"Die Anzahl der Nachkommastellen die in der gesamten Applikation verwendet " +"werden\n" +"wenn das Metrische Einheitensystem verwendet wird.\n" +"Jede Änderung erfordert einen Neustart der Applikation." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:64 +msgid "Precision INCH" +msgstr "Präzision (Zoll)" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:66 +msgid "" +"The number of decimals used throughout the application\n" +"when the set units are in INCH system.\n" +"Any change here require an application restart." +msgstr "" +"Die Anzahl der Nachkommastellen die in der gesamten Applikation verwendet " +"werden\n" +"wenn das Imperiale (Inches) Einheitensystem verwendet wird.\n" +"Jede Änderung erfordert einen Neustart der Applikation." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:78 +msgid "Graphic Engine" +msgstr "Grafik-Engine" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:79 +msgid "" +"Choose what graphic engine to use in FlatCAM.\n" +"Legacy(2D) -> reduced functionality, slow performance but enhanced " +"compatibility.\n" +"OpenGL(3D) -> full functionality, high performance\n" +"Some graphic cards are too old and do not work in OpenGL(3D) mode, like:\n" +"Intel HD3000 or older. In this case the plot area will be black therefore\n" +"use the Legacy(2D) mode." +msgstr "" +"Wählen Sie aus, welche Grafik-Engine in FlatCAM verwendet werden soll.\n" +"Legacy (2D) -> reduzierte Funktionalität, langsame Leistung, aber " +"verbesserte Kompatibilität.\n" +"OpenGL (3D) -> volle Funktionalität, hohe Leistung\n" +"Einige Grafikkarten sind zu alt und funktionieren nicht im OpenGL (3D) -" +"Modus. Beispiel:\n" +"Intel HD3000 oder älter. In diesem Fall ist der Plotbereich daher schwarz\n" +"Verwenden Sie den Legacy (2D) -Modus." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:85 +msgid "Legacy(2D)" +msgstr "Legacy (2D)" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:86 +msgid "OpenGL(3D)" +msgstr "OpenGL (3D)" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:98 +msgid "APP. LEVEL" +msgstr "Darstellung" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:99 +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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:105 +#: AppObjects/FlatCAMExcellon.py:696 AppObjects/FlatCAMGeometry.py:582 +#: AppObjects/FlatCAMGerber.py:278 +msgid "Advanced" +msgstr "Erweitert" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:111 +msgid "Portable app" +msgstr "Portable Anwendung" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:112 +msgid "" +"Choose if the application should run as portable.\n" +"\n" +"If Checked the application will run portable,\n" +"which means that the preferences files will be saved\n" +"in the application folder, in the lib\\config subfolder." +msgstr "" +"Wählen Sie aus, ob die Anwendung als portabel ausgeführt werden soll.\n" +"\n" +"Wenn diese Option aktiviert ist, wird die Anwendung portabel ausgeführt.\n" +"Dies bedeutet, dass die Voreinstellungsdateien gespeichert werden\n" +"Im Anwendungsordner, im Unterordner lib \\ config." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:125 +msgid "Languages" +msgstr "Sprachen" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:126 +msgid "Set the language used throughout FlatCAM." +msgstr "Stellen Sie die Sprache ein, die in FlatCAM verwendet wird." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:132 +msgid "Apply Language" +msgstr "Sprache anwend" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:133 +msgid "" +"Set the language used throughout FlatCAM.\n" +"The app will restart after click." +msgstr "" +"Stellen Sie die in FlatCAM verwendete Sprache ein.\n" +"Die App wird nach dem Klicken neu gestartet." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:147 +msgid "Startup Settings" +msgstr "Starteinstellungen" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:151 +msgid "Splash Screen" +msgstr "Begrüßungsbildschirm" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:153 +msgid "Enable display of the splash screen at application startup." +msgstr "" +"Aktivieren Sie die Anzeige des Begrüßungsbildschirms beim Start der " +"Anwendung." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:165 +msgid "Sys Tray Icon" +msgstr "Systray-Symbol" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:167 +msgid "Enable display of FlatCAM icon in Sys Tray." +msgstr "Anzeige des FlatCAM-Symbols in Systray aktivieren." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:172 +msgid "Show Shell" +msgstr "Shell anzeigen" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:174 +msgid "" +"Check this box if you want the shell to\n" +"start automatically at startup." +msgstr "" +"Aktivieren Sie dieses Kontrollkästchen, wenn Sie die Shell verwenden " +"möchten\n" +"Beim Start automatisch starten." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:181 +msgid "Show Project" +msgstr "Projekt anzeigen" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:183 +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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:189 +msgid "Version Check" +msgstr "Versionsprüfung" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:191 +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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:198 +msgid "Send Statistics" +msgstr "Statistiken senden" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:200 +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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:214 +msgid "Workers number" +msgstr "Thread Anzahl" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:216 +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, \n" +"anhängig von der Geschwindigkeit Ihres Computers, schneller ausführen.\n" +"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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:230 +msgid "Geo Tolerance" +msgstr "Geo-Toleranz" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:232 +msgid "" +"This value can counter the effect of the Circle Steps\n" +"parameter. Default value is 0.005.\n" +"A lower value will increase the detail both in image\n" +"and in Gcode for the circles, with a higher cost in\n" +"performance. Higher value will provide more\n" +"performance at the expense of level of detail." +msgstr "" +"This value can counter the effect of the Circle Steps\n" +"parameter. Default value is 0.005.\n" +"A lower value will increase the detail both in image\n" +"and in Gcode for the circles, with a higher cost in\n" +"performance. Higher value will provide more\n" +"performance at the expense of level of detail." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:252 +msgid "Save Settings" +msgstr "Einstellungen speichern" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:256 +msgid "Save Compressed Project" +msgstr "Speichern Sie das komprimierte Projekt" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:258 +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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:267 +msgid "Compression" +msgstr "Kompression" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:269 +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." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:280 +msgid "Enable Auto Save" +msgstr "Aktiv. Sie die auto Speicherung" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:282 +msgid "" +"Check to enable the autosave feature.\n" +"When enabled, the application will try to save a project\n" +"at the set interval." +msgstr "" +"Aktivieren Sie diese Option, um die automatische Speicherfunktion zu " +"aktivieren.\n" +"Wenn diese Option aktiviert ist, versucht die Anwendung, ein Projekt zu " +"speichern\n" +"im eingestellten Intervall." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:292 +msgid "Interval" +msgstr "Intervall" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:294 +msgid "" +"Time interval for autosaving. In milliseconds.\n" +"The application will try to save periodically but only\n" +"if the project was saved manually at least once.\n" +"While active, some operations may block this feature." +msgstr "" +"Zeitintervall für die automatische Speicherung. In Millisekunden.\n" +"Die Anwendung versucht regelmäßig, aber nur zu speichern\n" +"wenn das Projekt mindestens einmal manuell gespeichert wurde.\n" +"Während der Aktivierung können einige Vorgänge diese Funktion blockieren." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:310 +msgid "Text to PDF parameters" +msgstr "Text zu PDF-Parametern" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:312 +msgid "Used when saving text in Code Editor or in FlatCAM Document objects." +msgstr "" +"Wird beim Speichern von Text im Code-Editor oder in FlatCAM-Dokumentobjekten " +"verwendet." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:321 +msgid "Top Margin" +msgstr "Oberer Rand" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:323 +msgid "Distance between text body and the top of the PDF file." +msgstr "Abstand zwischen Textkörper und dem oberen Rand der PDF-Datei." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:334 +msgid "Bottom Margin" +msgstr "Unterer Rand" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:336 +msgid "Distance between text body and the bottom of the PDF file." +msgstr "Abstand zwischen Textkörper und dem unteren Rand der PDF-Datei." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:347 +msgid "Left Margin" +msgstr "Linker Rand" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:349 +msgid "Distance between text body and the left of the PDF file." +msgstr "Abstand zwischen Textkörper und der linken Seite der PDF-Datei." + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:360 +msgid "Right Margin" +msgstr "Rechter Rand" + +#: AppGUI/preferences/general/GeneralAppPrefGroupUI.py:362 +msgid "Distance between text body and the right of the PDF file." +msgstr "Abstand zwischen Textkörper und der rechten Seite der PDF-Datei." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:26 +msgid "GUI Preferences" +msgstr "GUI-Einstellungen" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:36 +msgid "Theme" +msgstr "Thema" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:38 +msgid "" +"Select a theme for FlatCAM.\n" +"It will theme the plot area." +msgstr "" +"Wählen Sie ein Thema für FlatCAM.\n" +"Es wird den Handlungsbereich thematisieren." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:43 +msgid "Light" +msgstr "Licht" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:44 +msgid "Dark" +msgstr "Dunkel" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:51 +msgid "Use Gray Icons" +msgstr "Verwenden Sie graue Symbole" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:53 +msgid "" +"Check this box to use a set of icons with\n" +"a lighter (gray) color. To be used when a\n" +"full dark theme is applied." +msgstr "" +"Aktivieren Sie dieses Kontrollkästchen, um eine Reihe von Symbolen mit zu " +"verwenden\n" +"eine hellere (graue) Farbe. Zu verwenden, wenn a\n" +"Volldunkles Thema wird angewendet." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:73 +msgid "Layout" +msgstr "Layout" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:75 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:95 +msgid "Style" +msgstr "Stil" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:97 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:111 +msgid "Activate HDPI Support" +msgstr "Aktivieren Sie die HDPI-Unterstützung" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:113 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:127 +msgid "Display Hover Shape" +msgstr "Schwebeflugform anzeigen" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:129 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:136 +msgid "Display Selection Shape" +msgstr "Auswahlform anzeigen" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:138 +msgid "" +"Enable the display of a selection shape for FlatCAM objects.\n" +"It is displayed whenever the mouse selects an object\n" +"either by clicking or dragging mouse from left to right or\n" +"right to left." +msgstr "" +"Aktivieren Sie die Anzeige einer Auswahlform für FlatCAM-Objekte.\n" +"Es wird angezeigt, wenn die Maus ein Objekt auswählt\n" +"entweder durch Klicken oder Ziehen der Maus von links nach rechts oder\n" +"rechts nach links." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:151 +msgid "Left-Right Selection Color" +msgstr "Links-Rechts-Auswahlfarbe" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:156 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:172 +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)." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:192 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:216 +msgid "Right-Left Selection Color" +msgstr "Rechts-Links-Auswahlfarbe" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:222 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:239 +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)." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:259 +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." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:286 +msgid "Editor Color" +msgstr "Editorfarbe" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:290 +msgid "Drawing" +msgstr "Zeichnung" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:292 +msgid "Set the color for the shape." +msgstr "Legen Sie die Farbe für die Form fest." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:309 +msgid "Set the color of the shape when selected." +msgstr "Legt die Farbe der Form fest, wenn sie ausgewählt wird." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:332 +msgid "Project Items Color" +msgstr "Projektelemente Farbe" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:336 +msgid "Enabled" +msgstr "Aktiviert" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:338 +msgid "Set the color of the items in Project Tab Tree." +msgstr "Legen Sie die Farbe der Elemente im Projektregisterbaum fest." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:352 +msgid "Disabled" +msgstr "Deaktiviert" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:354 +msgid "" +"Set the color of the items in Project Tab Tree,\n" +"for the case when the items are disabled." +msgstr "" +"Legen Sie die Farbe der Elemente in der Projektregisterkarte fest.\n" +"für den Fall, wenn die Elemente deaktiviert sind." + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:370 +msgid "Project AutoHide" +msgstr "Projekt autoausblenden" + +#: AppGUI/preferences/general/GeneralGUIPrefGroupUI.py:372 +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." + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:27 +msgid "Geometry Adv. Options" +msgstr "Geometrie Erw. Optionen" + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:35 +msgid "" +"A list of Geometry advanced parameters.\n" +"Those parameters are available only for\n" +"Advanced App. Level." +msgstr "" +"Eine Liste der erweiterten Geometrieparameter.\n" +"Diese Parameter sind nur für verfügbar\n" +"Erweiterte App. Niveau." + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:45 +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:112 +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:134 +#: AppTools/ToolCalibration.py:125 AppTools/ToolSolderPaste.py:240 +msgid "Toolchange X-Y" +msgstr "Werkzeugwechsel X, Y" + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:56 +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." + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:158 +msgid "Segment X size" +msgstr "Segment X Größe" + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:160 +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." + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:174 +msgid "Segment Y size" +msgstr "Segment Y Größe" + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:176 +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." + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:192 +msgid "Area Exclusion" +msgstr "Gebietsausschluss" + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:194 +msgid "" +"Area exclusion parameters.\n" +"Those parameters are available only for\n" +"Advanced App. Level." +msgstr "" +"Bereichsausschlussparameter.\n" +"Diese Parameter sind nur für verfügbar\n" +"Erweiterte App. Niveau." + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:201 +msgid "Exclusion areas" +msgstr "Ausschlussbereiche" + +#: AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:212 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:322 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:286 +#: AppTools/ToolNCC.py:578 AppTools/ToolPaint.py:521 +msgid "Shape" +msgstr "Form" + +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:33 +msgid "A list of Geometry Editor parameters." +msgstr "Eine Liste der Geometry Editor-Parameter." + +#: AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:43 +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:196 +msgid "" +"Set the number of selected geometry\n" +"items above which the utility geometry\n" +"becomes just a selection rectangle.\n" +"Increases the performance when moving a\n" +"large number of geometric elements." +msgstr "" +"Legen Sie die Anzahl der ausgewählten Geometrien fest\n" +"Elemente, über denen die Nutzgeometrie\n" +"wird nur ein Auswahlrechteck.\n" +"Erhöht die Leistung beim Bewegen von a\n" +"große Anzahl von geometrischen Elementen." + +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:27 +msgid "Geometry General" +msgstr "Geometrie Allgemein" + +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:49 +msgid "" +"The number of circle steps for Geometry \n" +"circle and arc shapes linear approximation." +msgstr "" +"Die Anzahl der Kreisschritte für die Geometrie\n" +"Kreis- und Bogenformen lineare Annäherung." + +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:63 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:41 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:48 +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:42 +msgid "Tools Dia" +msgstr "Werkzeugdurchmesser" + +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:65 +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:108 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:43 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:50 +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:44 +msgid "" +"Diameters of the tools, separated by comma.\n" +"The value of the diameter has to use the dot decimals separator.\n" +"Valid values: 0.3, 1.0" +msgstr "" +"Durchmesser der Werkzeuge, durch Komma getrennt.\n" +"Der Wert des Durchmessers muss das Punkt-Dezimal-Trennzeichen verwenden.\n" +"Gültige Werte: 0.3, 1.0" + +#: AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py:80 +msgid "Geometry Object Color" +msgstr "Geometrieobjekt Farbe" + +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:28 +msgid "Geometry Options" +msgstr "Geometrieoptionen" + +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:36 +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." + +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:80 +msgid "Depth/Pass" +msgstr "Tiefe / Pass" + +#: AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py:82 +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." + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:27 +msgid "Gerber Adv. Options" +msgstr "Erweiterte Optionen von Gerber" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:33 +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." + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:52 +msgid "Table Show/Hide" +msgstr "Tabelle anzeigen / ausblenden" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:54 +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." + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:134 +msgid "Exterior" +msgstr "Äußeres" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:135 +msgid "Interior" +msgstr "Inneres" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:146 +#: AppObjects/FlatCAMGerber.py:497 AppTools/ToolCopperThieving.py:1022 +#: AppTools/ToolCopperThieving.py:1211 AppTools/ToolCopperThieving.py:1223 +#: AppTools/ToolNCC.py:2059 AppTools/ToolNCC.py:2170 AppTools/ToolNCC.py:2185 +#: AppTools/ToolNCC.py:3149 AppTools/ToolNCC.py:3254 AppTools/ToolNCC.py:3269 +#: AppTools/ToolNCC.py:3535 AppTools/ToolNCC.py:3636 AppTools/ToolNCC.py:3651 +#: camlib.py:982 +msgid "Buffering" +msgstr "Pufferung" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:148 +msgid "" +"Buffering type:\n" +"- None --> best performance, fast file loading but no so good display\n" +"- Full --> slow file loading but good visuals. This is the default.\n" +"<>: Don't change this unless you know what you are doing !!!" +msgstr "" +"Puffertyp:\n" +"- Keine -> beste Leistung, schnelles Laden von Dateien, aber keine so gute " +"Anzeige\n" +"- Voll -> langsames Laden von Dateien, aber gute Grafik. Dies ist die " +"Standardeinstellung.\n" +"<< WARNUNG >>: Ändern Sie dies nur, wenn Sie wissen, was Sie tun !!!" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:153 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:88 +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:207 +#: AppTools/ToolFiducials.py:201 AppTools/ToolFilm.py:238 +#: AppTools/ToolProperties.py:452 AppTools/ToolProperties.py:455 +#: AppTools/ToolProperties.py:458 AppTools/ToolProperties.py:483 +msgid "None" +msgstr "Keiner" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:159 +msgid "Simplify" +msgstr "Vereinfachen" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:161 +msgid "" +"When checked all the Gerber polygons will be\n" +"loaded with simplification having a set tolerance.\n" +"<>: Don't change this unless you know what you are doing !!!" +msgstr "" +"Wenn diese Option aktiviert ist, werden alle Gerber-Polygone angezeigt\n" +"geladen mit Vereinfachung mit einer festgelegten Toleranz.\n" +"<< WARNUNG >>: Ändern Sie dies nur, wenn Sie wissen, was Sie tun !!!" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:168 +msgid "Tolerance" +msgstr "Toleranz" + +#: AppGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:169 +msgid "Tolerance for polygon simplification." +msgstr "Toleranz für Polygonvereinfachung." + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:33 +msgid "A list of Gerber Editor parameters." +msgstr "Eine Liste der Gerber-Editor-Parameter." + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:43 +msgid "" +"Set the number of selected Gerber geometry\n" +"items above which the utility geometry\n" +"becomes just a selection rectangle.\n" +"Increases the performance when moving a\n" +"large number of geometric elements." +msgstr "" +"Stellen Sie die Anzahl der ausgewählten Gerber-Geometrie ein\n" +"Elemente, über denen die Nutzgeometrie\n" +"wird nur ein Auswahlrechteck.\n" +"Erhöht die Leistung beim Bewegen von a\n" +"große Anzahl von geometrischen Elementen." + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:56 +msgid "New Aperture code" +msgstr "Neuer Blendencode" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:69 +msgid "New Aperture size" +msgstr "Standard Blendenöffnung" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:71 +msgid "Size for the new aperture" +msgstr "Wert für die neue Blende" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:82 +msgid "New Aperture type" +msgstr "Neuer Blendentyp" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:84 +msgid "" +"Type for the new aperture.\n" +"Can be 'C', 'R' or 'O'." +msgstr "" +"Geben Sie für die neue Blende ein.\n" +"Kann \"C\", \"R\" oder \"O\" sein." + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:106 +msgid "Aperture Dimensions" +msgstr "Öffnungsmaße" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:116 +msgid "Linear Pad Array" +msgstr "Lineares Pad-Array" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:160 +msgid "Circular Pad Array" +msgstr "Kreisschlitz-Array" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:196 +msgid "Distance at which to buffer the Gerber element." +msgstr "Abstand, in dem das Gerber-Element gepuffert werden soll." + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:205 +msgid "Scale Tool" +msgstr "Skalierungswerk" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:211 +msgid "Factor to scale the Gerber element." +msgstr "Faktor zum Skalieren des Gerber-Elements." + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:224 +msgid "Threshold low" +msgstr "Schwelle niedrig" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:226 +msgid "Threshold value under which the apertures are not marked." +msgstr "Schwellenwert, unter dem die Blenden nicht markiert sind." + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:236 +msgid "Threshold high" +msgstr "Schwelle hoch" + +#: AppGUI/preferences/gerber/GerberEditorPrefGroupUI.py:238 +msgid "Threshold value over which the apertures are not marked." +msgstr "Schwellenwert, über dem die Blenden nicht markiert sind." + +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:27 +msgid "Gerber Export" +msgstr "Gerber Export" + +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:33 +msgid "" +"The parameters set here are used in the file exported\n" +"when using the File -> Export -> Export Gerber menu entry." +msgstr "" +"Die hier eingestellten Parameter werden in der exportierten Datei verwendet\n" +"bei Verwendung des Menüeintrags Datei -> Exportieren -> Gerber exportieren." + +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:44 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:50 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:84 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:90 +msgid "The units used in the Gerber file." +msgstr "Die in der Gerber-Datei verwendeten Einheiten." + +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:58 +msgid "" +"The number of digits in the whole part of the number\n" +"and in the fractional part of the number." +msgstr "" +"Die Anzahl der Ziffern im gesamten Teil der Nummer\n" +"und im Bruchteil der Zahl." + +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:71 +msgid "" +"This numbers signify the number of digits in\n" +"the whole part of Gerber coordinates." +msgstr "" +"Diese Zahlen geben die Anzahl der Ziffern in an\n" +"der ganze Teil von Gerber koordiniert." + +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:87 +msgid "" +"This numbers signify the number of digits in\n" +"the decimal part of Gerber coordinates." +msgstr "" +"Diese Zahlen geben die Anzahl der Ziffern in an\n" +"Der Dezimalteil der Gerber-Koordinaten." + +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:99 +#: AppGUI/preferences/gerber/GerberExpPrefGroupUI.py:109 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:100 +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:110 +msgid "" +"This sets the type of Gerber zeros.\n" +"If LZ then Leading Zeros are removed and\n" +"Trailing Zeros are kept.\n" +"If TZ is checked then Trailing Zeros are removed\n" +"and Leading Zeros are kept." +msgstr "" +"Dies legt den Typ der Gerber-Nullen fest.\n" +"Wenn LZ, werden Leading Zeros und entfernt\n" +"Nachgestellte Nullen werden beibehalten.\n" +"Wenn TZ aktiviert ist, werden nachfolgende Nullen entfernt\n" +"und führende Nullen werden beibehalten." + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:27 +msgid "Gerber General" +msgstr "Geometrie Allgemein" + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:45 +msgid "M-Color" +msgstr "M-farbig" + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:61 +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." + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:73 +msgid "Default Values" +msgstr "Standardwerte" + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:75 +msgid "" +"Those values will be used as fallback values\n" +"in case that they are not found in the Gerber file." +msgstr "" +"Diese Werte werden als Ersatzwerte verwendet\n" +"für den Fall, dass sie nicht in der Gerber-Datei gefunden werden." + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:126 +msgid "Clean Apertures" +msgstr "Reinigen Sie die Öffnungen" + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:128 +msgid "" +"Will remove apertures that do not have geometry\n" +"thus lowering the number of apertures in the Gerber object." +msgstr "" +"Entfernt Öffnungen ohne Geometrie\n" +"Dadurch wird die Anzahl der Öffnungen im Gerber-Objekt verringert." + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:134 +msgid "Polarity change buffer" +msgstr "Polaritätswechselpuffer" + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:136 +msgid "" +"Will apply extra buffering for the\n" +"solid geometry when we have polarity changes.\n" +"May help loading Gerber files that otherwise\n" +"do not load correctly." +msgstr "" +"Wendet zusätzliche Pufferung für das an\n" +"Festkörpergeometrie, wenn sich die Polarität ändert.\n" +"Kann helfen, Gerber-Dateien zu laden, die sonst\n" +"nicht richtig laden." + +#: AppGUI/preferences/gerber/GerberGenPrefGroupUI.py:149 +msgid "Gerber Object Color" +msgstr "Gerber-Objektfarbe" + +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:29 +msgid "Gerber Options" +msgstr "Gerber-Optionen" + +#: AppGUI/preferences/gerber/GerberOptPrefGroupUI.py:107 +msgid "Combine Passes" +msgstr "Kombinieren Sie Pässe" + +# Don´t know Copper Thieving +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:27 +msgid "Copper Thieving Tool Options" +msgstr "Copper Thieving Tool Optionen" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:39 +msgid "" +"A tool to generate a Copper Thieving that can be added\n" +"to a selected Gerber file." +msgstr "" +"Ein Werkzeug um Copper Thieving durchzuführen, das auf\n" +"ein ausgewähltes Gerber File angewendet werden kann." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:47 +msgid "Number of steps (lines) used to interpolate circles." +msgstr "Anzahl der Schritte (Linien) um Kreise zu interpolieren." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:57 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:261 +#: AppTools/ToolCopperThieving.py:96 AppTools/ToolCopperThieving.py:431 +msgid "Clearance" +msgstr "Freistellung" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:59 +msgid "" +"This set the distance between the copper Thieving components\n" +"(the polygon fill may be split in multiple polygons)\n" +"and the copper traces in the Gerber file." +msgstr "" +"Diese Auswahl definiert den Abstand zwischen den \"Copper Thieving\" " +"Komponenten.\n" +"und den Kupferverbindungen im Gerber File (möglicherweise wird hierbei ein " +"Polygon\n" +"in mehrere aufgeteilt." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:86 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:309 +#: AppTools/ToolCopperThieving.py:125 AppTools/ToolNCC.py:535 +#: AppTools/ToolNCC.py:1306 AppTools/ToolNCC.py:1635 AppTools/ToolNCC.py:1928 +#: AppTools/ToolNCC.py:1992 AppTools/ToolNCC.py:3013 AppTools/ToolNCC.py:3022 +#: defaults.py:406 tclCommands/TclCommandCopperClear.py:190 +msgid "Itself" +msgstr "Selbst" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:87 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:309 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:280 +#: AppTools/ToolCopperThieving.py:126 AppTools/ToolNCC.py:535 +#: AppTools/ToolNCC.py:1316 AppTools/ToolNCC.py:1648 AppTools/ToolNCC.py:1944 +#: AppTools/ToolNCC.py:1999 AppTools/ToolPaint.py:485 AppTools/ToolPaint.py:945 +#: AppTools/ToolPaint.py:1451 +msgid "Area Selection" +msgstr "Bereichsauswahl" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:88 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:309 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:280 +#: AppTools/ToolCopperThieving.py:127 AppTools/ToolDblSided.py:216 +#: AppTools/ToolNCC.py:535 AppTools/ToolNCC.py:1664 AppTools/ToolNCC.py:1950 +#: AppTools/ToolNCC.py:2007 AppTools/ToolNCC.py:2383 AppTools/ToolNCC.py:2631 +#: AppTools/ToolNCC.py:3058 AppTools/ToolPaint.py:485 AppTools/ToolPaint.py:930 +#: AppTools/ToolPaint.py:1467 tclCommands/TclCommandCopperClear.py:192 +#: tclCommands/TclCommandPaint.py:166 +msgid "Reference Object" +msgstr "Ref. Objekt" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:90 +#: AppTools/ToolCopperThieving.py:129 +msgid "Reference:" +msgstr "Referenz:" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:92 +msgid "" +"- 'Itself' - the copper Thieving extent is based on the object extent.\n" +"- 'Area Selection' - left mouse click to start selection of the area to be " +"filled.\n" +"- 'Reference Object' - will do copper thieving within the area specified by " +"another object." +msgstr "" +"- 'Selbst' - die 'Copper Thieving' basiert auf der Objektausdehnung.\n" +"- 'Bereichsauswahl' - Klicken Sie mit der linken Maustaste, um den zu " +"füllenden Bereich auszuwählen.\n" +"- 'Referenzobjekt' - 'Copper Thieving' innerhalb des von einem anderen " +"Objekt angegebenen Bereichs." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:101 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:76 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:188 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:76 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:190 +#: AppTools/ToolCopperThieving.py:171 AppTools/ToolExtractDrills.py:102 +#: AppTools/ToolExtractDrills.py:240 AppTools/ToolPunchGerber.py:113 +#: AppTools/ToolPunchGerber.py:268 +msgid "Rectangular" +msgstr "Rechteckig" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:102 +#: AppTools/ToolCopperThieving.py:172 +msgid "Minimal" +msgstr "Minimal" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:104 +#: AppTools/ToolCopperThieving.py:174 AppTools/ToolFilm.py:94 +msgid "Box Type:" +msgstr "Box-Typ:" + +# Double +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:106 +#: AppTools/ToolCopperThieving.py:176 +msgid "" +"- 'Rectangular' - the bounding box will be of rectangular shape.\n" +"- 'Minimal' - the bounding box will be the convex hull shape." +msgstr "" +"- 'Rechteckig' - Der Begrenzungsrahmen hat eine rechteckige Form.\n" +"- 'Minimal' - Der Begrenzungsrahmen ist die konvexe Rumpfform." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:120 +#: AppTools/ToolCopperThieving.py:192 +msgid "Dots Grid" +msgstr "Punktmuster" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:121 +#: AppTools/ToolCopperThieving.py:193 +msgid "Squares Grid" +msgstr "Quadratraster" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:122 +#: AppTools/ToolCopperThieving.py:194 +msgid "Lines Grid" +msgstr "Linienraster" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:124 +#: AppTools/ToolCopperThieving.py:196 +msgid "Fill Type:" +msgstr "Füllart:" + +# Double +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:126 +#: AppTools/ToolCopperThieving.py:198 +msgid "" +"- 'Solid' - copper thieving will be a solid polygon.\n" +"- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n" +"- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n" +"- 'Lines Grid' - the empty area will be filled with a pattern of lines." +msgstr "" +"- 'Solide' - 'Copper Thieving' wird ein solides Polygon sein.\n" +"- 'Punktraster' - Der leere Bereich wird mit einem Punktmuster gefüllt.\n" +"- 'Quadratraster ' - Der leere Bereich wird mit einem Quadratmuster " +"gefüllt.\n" +"- 'Linienraster' - Der leere Bereich wird mit einem Linienmuster gefüllt." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:134 +#: AppTools/ToolCopperThieving.py:217 +msgid "Dots Grid Parameters" +msgstr "Punktmuster Parameter" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:140 +#: AppTools/ToolCopperThieving.py:223 +msgid "Dot diameter in Dots Grid." +msgstr "Punktdurchmesser im Punktmuster." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:151 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:180 +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:209 +#: AppTools/ToolCopperThieving.py:234 AppTools/ToolCopperThieving.py:274 +#: AppTools/ToolCopperThieving.py:314 +msgid "Spacing" +msgstr "Abstand" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:153 +#: AppTools/ToolCopperThieving.py:236 +msgid "Distance between each two dots in Dots Grid." +msgstr "Abstand zwischen zwei Punkten im Punktmuster." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:163 +#: AppTools/ToolCopperThieving.py:257 +msgid "Squares Grid Parameters" +msgstr "Quadratraster Parameter" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:169 +#: AppTools/ToolCopperThieving.py:263 +msgid "Square side size in Squares Grid." +msgstr "Quadratlängen im Quadratraster." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:182 +#: AppTools/ToolCopperThieving.py:276 +msgid "Distance between each two squares in Squares Grid." +msgstr "Abstand zwischen zwei Quadraten im Quadratraster." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:192 +#: AppTools/ToolCopperThieving.py:297 +msgid "Lines Grid Parameters" +msgstr "Schraffurparameter" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:198 +#: AppTools/ToolCopperThieving.py:303 +msgid "Line thickness size in Lines Grid." +msgstr "Liniendicke." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:211 +#: AppTools/ToolCopperThieving.py:316 +msgid "Distance between each two lines in Lines Grid." +msgstr "Linienabstand." + +# What is a Robber Bar? +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:221 +#: AppTools/ToolCopperThieving.py:354 +msgid "Robber Bar Parameters" +msgstr "Robber Bar-Parameter" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:223 +#: AppTools/ToolCopperThieving.py:356 +msgid "" +"Parameters used for the robber bar.\n" +"Robber bar = copper border to help in pattern hole plating." +msgstr "" +"Parameter für die Robber Bar\n" +"Eine Robber Bar ist ein Kupferrand bei Lochmustern." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:231 +#: AppTools/ToolCopperThieving.py:364 +msgid "Bounding box margin for robber bar." +msgstr "Begrenzungsrahmenrand der Robber Bar." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:242 +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:42 +#: AppTools/ToolCopperThieving.py:375 AppTools/ToolCorners.py:113 +#: AppTools/ToolEtchCompensation.py:96 +msgid "Thickness" +msgstr "Dicke" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:244 +#: AppTools/ToolCopperThieving.py:377 +msgid "The robber bar thickness." +msgstr "Dicke der Robber Bar." + +# What is pattern plating? +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:254 +#: AppTools/ToolCopperThieving.py:408 +msgid "Pattern Plating Mask" +msgstr "Musterbeschichtungsmaske" + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:256 +#: AppTools/ToolCopperThieving.py:410 +msgid "Generate a mask for pattern plating." +msgstr "Erzeugen Sie eine Maske für die Musterbeschichtung." + +#: AppGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:263 +#: AppTools/ToolCopperThieving.py:433 +msgid "" +"The distance between the possible copper thieving elements\n" +"and/or robber bar and the actual openings in the mask." +msgstr "" +"Der Abstand zwischen den Copper Thieving Elementen \n" +"und/oder der Robber Bar und den tatsächlichen Öffnungen in der Maske." + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:27 +msgid "Calibration Tool Options" +msgstr "Kalibirierungs-Tool-Optionen" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:38 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:38 +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:38 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:38 +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:37 +#: AppTools/ToolCopperThieving.py:91 AppTools/ToolCorners.py:108 +#: AppTools/ToolFiducials.py:151 +msgid "Parameters used for this tool." +msgstr "Parameter für dieses Werkzeug." + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:43 +#: AppTools/ToolCalibration.py:181 +msgid "Source Type" +msgstr "Quellenart" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:44 +#: AppTools/ToolCalibration.py:182 +msgid "" +"The source of calibration points.\n" +"It can be:\n" +"- Object -> click a hole geo for Excellon or a pad for Gerber\n" +"- Free -> click freely on canvas to acquire the calibration points" +msgstr "" +"Die Quelle für Kalibrierungspunkte\n" +"Das können sein:\n" +"- \"Objekt\" klicken Sie auf eine Lochgeometrie oder ein Pad im Gerber\n" +"- \"Frei\" klicken Sie frei auf der Leinwand um einen Kalibierungspunkt zu " +"setzen" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:49 +#: AppTools/ToolCalibration.py:187 +msgid "Free" +msgstr "Frei" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:63 +#: AppTools/ToolCalibration.py:76 +msgid "Height (Z) for travelling between the points." +msgstr "Die Höhe (Z) für den Weg zwischen Pads." + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:75 +#: AppTools/ToolCalibration.py:88 +msgid "Verification Z" +msgstr "Z Überprüfung" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:77 +#: AppTools/ToolCalibration.py:90 +msgid "Height (Z) for checking the point." +msgstr "Höhe (Z) um den Punkt zu prüfen." + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:89 +#: AppTools/ToolCalibration.py:102 +msgid "Zero Z tool" +msgstr "Z Höhen Werkzeug" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:91 +#: AppTools/ToolCalibration.py:104 +msgid "" +"Include a sequence to zero the height (Z)\n" +"of the verification tool." +msgstr "" +"Fügen sie eine Sequenz ein um die Höhe (Z)\n" +"des Überprüfungswerkzeugs zu nullen." + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:100 +#: AppTools/ToolCalibration.py:113 +msgid "Height (Z) for mounting the verification probe." +msgstr "Höhe (Z) zur Installation der Überprüfungssonde." + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:114 +#: AppTools/ToolCalibration.py:127 +msgid "" +"Toolchange X,Y position.\n" +"If no value is entered then the current\n" +"(x, y) point will be used," +msgstr "" +"Werkzeugwechsel X, Y Position.\n" +"Wenn kein Wert eingegeben wird, wird der Strom angezeigt\n" +"(x, y) Punkt wird verwendet," + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:125 +#: AppTools/ToolCalibration.py:153 +msgid "Second point" +msgstr "Zweiter Punkt" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:127 +#: AppTools/ToolCalibration.py:155 +msgid "" +"Second point in the Gcode verification can be:\n" +"- top-left -> the user will align the PCB vertically\n" +"- bottom-right -> the user will align the PCB horizontally" +msgstr "" +"Der zweite Punkt bei der Gcode-Überprüfung kann sein:\n" +"- oben links -> Der Benutzer richtet die Leiterplatte vertikal aus\n" +"- rechts unten -> Der Benutzer richtet die Leiterplatte horizontal aus" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:131 +#: AppTools/ToolCalibration.py:159 App_Main.py:4684 +msgid "Top-Left" +msgstr "Oben links" + +#: AppGUI/preferences/tools/Tools2CalPrefGroupUI.py:132 +#: AppTools/ToolCalibration.py:160 App_Main.py:4685 +msgid "Bottom-Right" +msgstr "Unten rechts" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:27 +msgid "Extract Drills Options" +msgstr "Optionen für Bohrer extrahieren" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:42 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:42 +#: AppTools/ToolExtractDrills.py:68 AppTools/ToolPunchGerber.py:75 +msgid "Processed Pads Type" +msgstr "Verarbeitete Pads Typ" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:44 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:44 +#: AppTools/ToolExtractDrills.py:70 AppTools/ToolPunchGerber.py:77 +msgid "" +"The type of pads shape to be processed.\n" +"If the PCB has many SMD pads with rectangular pads,\n" +"disable the Rectangular aperture." +msgstr "" +"Die Art der zu verarbeitenden Pads.\n" +"Wenn die Leiterplatte viele SMD-Pads mit rechteckigen Pads hat,\n" +"Deaktivieren Sie die rechteckige Öffnung." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:54 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:54 +#: AppTools/ToolExtractDrills.py:80 AppTools/ToolPunchGerber.py:91 +msgid "Process Circular Pads." +msgstr "Prozessrunde Pads." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:60 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:162 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:60 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:164 +#: AppTools/ToolExtractDrills.py:86 AppTools/ToolExtractDrills.py:214 +#: AppTools/ToolPunchGerber.py:97 AppTools/ToolPunchGerber.py:242 +msgid "Oblong" +msgstr "Länglich" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:62 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:62 +#: AppTools/ToolExtractDrills.py:88 AppTools/ToolPunchGerber.py:99 +msgid "Process Oblong Pads." +msgstr "Längliche Pads verarbeiten." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:70 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:70 +#: AppTools/ToolExtractDrills.py:96 AppTools/ToolPunchGerber.py:107 +msgid "Process Square Pads." +msgstr "Quadratische Pads verarbeiten." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:78 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:78 +#: AppTools/ToolExtractDrills.py:104 AppTools/ToolPunchGerber.py:115 +msgid "Process Rectangular Pads." +msgstr "Rechteckige Pads verarbeiten." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:84 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:201 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:84 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:203 +#: AppTools/ToolExtractDrills.py:110 AppTools/ToolExtractDrills.py:253 +#: AppTools/ToolProperties.py:172 AppTools/ToolPunchGerber.py:121 +#: AppTools/ToolPunchGerber.py:281 +msgid "Others" +msgstr "Andere" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:86 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:86 +#: AppTools/ToolExtractDrills.py:112 AppTools/ToolPunchGerber.py:123 +msgid "Process pads not in the categories above." +msgstr "Prozess-Pads nicht in den oben genannten Kategorien." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:99 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:123 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:100 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:125 +#: AppTools/ToolExtractDrills.py:139 AppTools/ToolExtractDrills.py:156 +#: AppTools/ToolPunchGerber.py:150 AppTools/ToolPunchGerber.py:184 +msgid "Fixed Diameter" +msgstr "Fester Durchmesser" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:100 +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:140 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:101 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:142 +#: AppTools/ToolExtractDrills.py:140 AppTools/ToolExtractDrills.py:192 +#: AppTools/ToolPunchGerber.py:151 AppTools/ToolPunchGerber.py:214 +msgid "Fixed Annular Ring" +msgstr "Fester Ring" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:101 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:102 +#: AppTools/ToolExtractDrills.py:141 AppTools/ToolPunchGerber.py:152 +msgid "Proportional" +msgstr "Proportional" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:107 +#: AppTools/ToolExtractDrills.py:130 +msgid "" +"The method for processing pads. Can be:\n" +"- Fixed Diameter -> all holes will have a set size\n" +"- Fixed Annular Ring -> all holes will have a set annular ring\n" +"- Proportional -> each hole size will be a fraction of the pad size" +msgstr "" +"Die Methode zur Verarbeitung von Pads. Kann sein:\n" +"- Fester Durchmesser -> Alle Löcher haben eine festgelegte Größe\n" +"- Fester Ring -> Alle Löcher haben einen festen Ring\n" +"- Proportional -> Jede Lochgröße ist ein Bruchteil der Padgröße" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:131 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:133 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:220 +#: AppTools/ToolExtractDrills.py:164 AppTools/ToolExtractDrills.py:285 +#: AppTools/ToolPunchGerber.py:192 AppTools/ToolPunchGerber.py:308 +#: AppTools/ToolTransform.py:357 App_Main.py:9602 +msgid "Value" +msgstr "Wert" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:133 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:135 +#: AppTools/ToolExtractDrills.py:166 AppTools/ToolPunchGerber.py:194 +msgid "Fixed hole diameter." +msgstr "Fester Lochdurchmesser." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:142 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:144 +#: AppTools/ToolExtractDrills.py:194 AppTools/ToolPunchGerber.py:216 +msgid "" +"The size of annular ring.\n" +"The copper sliver between the hole exterior\n" +"and the margin of the copper pad." +msgstr "" +"Die Größe des Ringes.\n" +"Das Kupfersplitter zwischen dem Loch außen\n" +"und der Rand des Kupferkissens." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:151 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:153 +#: AppTools/ToolExtractDrills.py:203 AppTools/ToolPunchGerber.py:231 +msgid "The size of annular ring for circular pads." +msgstr "Die Größe des Ringes für kreisförmige Pads." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:164 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:166 +#: AppTools/ToolExtractDrills.py:216 AppTools/ToolPunchGerber.py:244 +msgid "The size of annular ring for oblong pads." +msgstr "Die Größe des Ringes für längliche Pads." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:177 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:179 +#: AppTools/ToolExtractDrills.py:229 AppTools/ToolPunchGerber.py:257 +msgid "The size of annular ring for square pads." +msgstr "Die Größe des Ringes für quadratische Pads." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:190 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:192 +#: AppTools/ToolExtractDrills.py:242 AppTools/ToolPunchGerber.py:270 +msgid "The size of annular ring for rectangular pads." +msgstr "Die Größe des Ringes für rechteckige Pads." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:203 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:205 +#: AppTools/ToolExtractDrills.py:255 AppTools/ToolPunchGerber.py:283 +msgid "The size of annular ring for other pads." +msgstr "Die Größe des Ringes für andere Pads." + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:213 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:215 +#: AppTools/ToolExtractDrills.py:276 AppTools/ToolPunchGerber.py:299 +msgid "Proportional Diameter" +msgstr "Proportionaler Durchmesser" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:222 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:224 +msgid "Factor" +msgstr "Faktor" + +#: AppGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:224 +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:226 +#: AppTools/ToolExtractDrills.py:287 AppTools/ToolPunchGerber.py:310 +msgid "" +"Proportional Diameter.\n" +"The hole diameter will be a fraction of the pad size." +msgstr "" +"Proportionaler Durchmesser.\n" +"Der Lochdurchmesser beträgt einen Bruchteil der Padgröße." + +# I have no clue +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:27 +msgid "Fiducials Tool Options" +msgstr "Passermarken-Werkzeugoptionen" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:45 +#: AppTools/ToolFiducials.py:158 +msgid "" +"This set the fiducial diameter if fiducial type is circular,\n" +"otherwise is the size of the fiducial.\n" +"The soldermask opening is double than that." +msgstr "" +"Dies setzt den Durchmesser der Bezugsmarke wenn die Art \n" +"des Bezugspunktes kreisförmig ist, ansonsten die Größe des Bezugspunktes.\n" +"Der Ausschnitt der Lötmaske ist doppelt so groß." + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:73 +#: AppTools/ToolFiducials.py:186 +msgid "Auto" +msgstr "Auto" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:74 +#: AppTools/ToolFiducials.py:187 +msgid "Manual" +msgstr "Manuell" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:76 +#: AppTools/ToolFiducials.py:189 +msgid "Mode:" +msgstr "Modus:" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:78 +msgid "" +"- 'Auto' - automatic placement of fiducials in the corners of the bounding " +"box.\n" +"- 'Manual' - manual placement of fiducials." +msgstr "" +"- \"Auto\" Die Bezugspunkte werden automatisch in den Ecken des Umrisses " +"platziert.\n" +"- \"Manuell\" Die Bezugspunkte werden manuell platziert." + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:86 +#: AppTools/ToolFiducials.py:199 +msgid "Up" +msgstr "Hoch" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:87 +#: AppTools/ToolFiducials.py:200 +msgid "Down" +msgstr "Runter" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:90 +#: AppTools/ToolFiducials.py:203 +msgid "Second fiducial" +msgstr "Zweiter Bezugspunkt" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:92 +#: AppTools/ToolFiducials.py:205 +msgid "" +"The position for the second fiducial.\n" +"- 'Up' - the order is: bottom-left, top-left, top-right.\n" +"- 'Down' - the order is: bottom-left, bottom-right, top-right.\n" +"- 'None' - there is no second fiducial. The order is: bottom-left, top-right." +msgstr "" +"Die Position des zweiten Bezugspunktes\n" +"- \"Hoch\" Die Reihenfolge ist Unten-Links, Oben-Links, Oben-Rechts\n" +"- \"Runter\" Die Reihenfolge ist Untern-Links, Unten-Rechts, Oben-Rechts\n" +"- \"Keine\" Es gibt keinen zweiten Bezugspunkt, die Reihenfolge ist: Unten-" +"Links, Oben-Rechts." + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:108 +#: AppTools/ToolFiducials.py:221 +msgid "Cross" +msgstr "Kreuzförmig" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:109 +#: AppTools/ToolFiducials.py:222 +msgid "Chess" +msgstr "Schachbrett" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:112 +#: AppTools/ToolFiducials.py:224 +msgid "Fiducial Type" +msgstr "Bezugspunktart" + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:114 +#: AppTools/ToolFiducials.py:226 +msgid "" +"The type of fiducial.\n" +"- 'Circular' - this is the regular fiducial.\n" +"- 'Cross' - cross lines fiducial.\n" +"- 'Chess' - chess pattern fiducial." +msgstr "" +"Die Art der Bezugspunkte\n" +"\"Kreisförmig\" Der normale Bezugspunkt\n" +"\"Kreuzförmig\" Kreuzlinienbezugspunkte\n" +"\"Schachbrett\" Schachbrettförmige Bezugspunkte." + +#: AppGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:123 +#: AppTools/ToolFiducials.py:235 +msgid "Line thickness" +msgstr "Liniendicke" + +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:27 +msgid "Invert Gerber Tool Options" +msgstr "Invert. Sie die Gerber-Werkzeugoptionen" + +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:33 +msgid "" +"A tool to invert Gerber geometry from positive to negative\n" +"and in revers." +msgstr "" +"Ein Werkzeug zum Konvertieren der Gerber-Geometrie von positiv nach negativ\n" +"und umgekehrt." + +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:47 +#: AppTools/ToolInvertGerber.py:90 +msgid "" +"Distance by which to avoid\n" +"the edges of the Gerber object." +msgstr "" +"Entfernung, um die vermieden werden muss\n" +"die Kanten des Gerber-Objekts." + +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:58 +#: AppTools/ToolInvertGerber.py:101 +msgid "Lines Join Style" +msgstr "Linien verbinden Stil" + +#: AppGUI/preferences/tools/Tools2InvertPrefGroupUI.py:60 +#: AppTools/ToolInvertGerber.py:103 +msgid "" +"The way that the lines in the object outline will be joined.\n" +"Can be:\n" +"- rounded -> an arc is added between two joining lines\n" +"- square -> the lines meet in 90 degrees angle\n" +"- bevel -> the lines are joined by a third line" +msgstr "" +"Die Art und Weise, wie die Linien in der Objektkontur verbunden werden.\n" +"Kann sein:\n" +"- gerundet -> zwischen zwei Verbindungslinien wird ein Bogen eingefügt\n" +"- Quadrat -> Die Linien treffen sich in einem Winkel von 90 Grad\n" +"- Abschrägung -> Die Linien werden durch eine dritte Linie verbunden" + +#: AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py:27 +msgid "Optimal Tool Options" +msgstr "\"Optimale\" Werkzeugoptionen" + +#: AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py:33 +msgid "" +"A tool to find the minimum distance between\n" +"every two Gerber geometric elements" +msgstr "" +"Ein Werkzeug, um den Mindestabstand zwischen zu finden\n" +"jeweils zwei Gerber geometrische Elemente" + +#: AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py:48 +#: AppTools/ToolOptimal.py:78 +msgid "Precision" +msgstr "Präzision" + +#: AppGUI/preferences/tools/Tools2OptimalPrefGroupUI.py:50 +msgid "Number of decimals for the distances and coordinates in this tool." +msgstr "" +"Anzahl der Dezimalstellen für die Abstände und Koordinaten in diesem " +"Werkzeug." + +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:27 +msgid "Punch Gerber Options" +msgstr "Stanzen Sie die Gerber-Optionen" + +#: AppGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:108 +#: AppTools/ToolPunchGerber.py:141 +msgid "" +"The punch hole source can be:\n" +"- Excellon Object-> the Excellon object drills center will serve as " +"reference.\n" +"- Fixed Diameter -> will try to use the pads center as reference adding " +"fixed diameter holes.\n" +"- Fixed Annular Ring -> will try to keep a set annular ring.\n" +"- Proportional -> will make a Gerber punch hole having the diameter a " +"percentage of the pad diameter." +msgstr "" +"Die Stanzlochquelle kann sein:\n" +"- Excellon-Objekt-> Das Excellon-Objektbohrzentrum dient als Referenz.\n" +"- Fester Durchmesser -> versucht, die Mitte der Pads als Referenz für Löcher " +"mit festem Durchmesser zu verwenden.\n" +"- Fixed Annular Ring -> versucht, einen festgelegten Ring zu behalten.\n" +"- Proportional -> macht ein Gerber-Stanzloch mit dem Durchmesser zu einem " +"Prozentsatz des Pad-Durchmessers." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:27 +msgid "QRCode Tool Options" +msgstr "QR Code-Tooloptionen" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:33 +msgid "" +"A tool to create a QRCode that can be inserted\n" +"into a selected Gerber file, or it can be exported as a file." +msgstr "" +"Ein Werkzeug um QR Codes zu erzeugen, um diese\n" +"in Gerber Dateien einzufügen oder als Datei zu exportieren." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:45 +#: AppTools/ToolQRCode.py:100 +msgid "Version" +msgstr "Version" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:47 +#: AppTools/ToolQRCode.py:102 +msgid "" +"QRCode version can have values from 1 (21x21 boxes)\n" +"to 40 (177x177 boxes)." +msgstr "" +"Je nach QRCode Version kann 1 (21x21 Quadrate)\n" +" bis 40 (177x177 Quadrate) angegeben werden." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:58 +#: AppTools/ToolQRCode.py:113 +msgid "Error correction" +msgstr "Fehlerausgleich" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:60 +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:71 +#: AppTools/ToolQRCode.py:115 AppTools/ToolQRCode.py:126 +#, python-format +msgid "" +"Parameter that controls the error correction used for the QR Code.\n" +"L = maximum 7%% errors can be corrected\n" +"M = maximum 15%% errors can be corrected\n" +"Q = maximum 25%% errors can be corrected\n" +"H = maximum 30%% errors can be corrected." +msgstr "" +"Dieser Parameter kontrolliert die Fehlertoleranz:\n" +"L : max. 7%% Fehler können ausgeglichen werden\n" +"M : max. 15%% Fehler können ausgeglichen werden\n" +"Q: max. 25%% Fehler können ausgeglichen werden\n" +"H : max. 30%% Fehler können ausgeglichen warden." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:81 +#: AppTools/ToolQRCode.py:136 +msgid "Box Size" +msgstr "Quadratgröße" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:83 +#: AppTools/ToolQRCode.py:138 +msgid "" +"Box size control the overall size of the QRcode\n" +"by adjusting the size of each box in the code." +msgstr "" +"Über die Quadratgröße wird die Geamtgröße\n" +"des QRCodes beeinflusst, da sie die Größe jedes einzelnen Quadrats " +"spezifiziert." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:94 +#: AppTools/ToolQRCode.py:149 +msgid "Border Size" +msgstr "Randdicke" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:96 +#: AppTools/ToolQRCode.py:151 +msgid "" +"Size of the QRCode border. How many boxes thick is the border.\n" +"Default value is 4. The width of the clearance around the QRCode." +msgstr "" +"Dicke des Rands des QRCodes in Anzahl von Boxen.\n" +"Standardwert is 4. Die Breite gibt den Raum der Freistellung um den QRCode " +"an." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:107 +#: AppTools/ToolQRCode.py:162 +msgid "QRCode Data" +msgstr "QRCode Daten" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:109 +#: AppTools/ToolQRCode.py:164 +msgid "QRCode Data. Alphanumeric text to be encoded in the QRCode." +msgstr "Beliebiger Text der in den QRCode umgerechnet werden soll." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:113 +#: AppTools/ToolQRCode.py:168 +msgid "Add here the text to be included in the QRCode..." +msgstr "Geben Sie hier den Text in Ihrem QRCode an." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:119 +#: AppTools/ToolQRCode.py:174 +msgid "Polarity" +msgstr "Polarität" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:121 +#: AppTools/ToolQRCode.py:176 +msgid "" +"Choose the polarity of the QRCode.\n" +"It can be drawn in a negative way (squares are clear)\n" +"or in a positive way (squares are opaque)." +msgstr "" +"Geben Sie die Polarität des QRCodes an\n" +"Es kann entweder Negativ sein (die Boxen sind durchsichtig)\n" +"oder Positiv (die Boxen sind undurchsichtig)." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:125 +#: AppTools/ToolFilm.py:279 AppTools/ToolQRCode.py:180 +msgid "Negative" +msgstr "Negativ" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:126 +#: AppTools/ToolFilm.py:278 AppTools/ToolQRCode.py:181 +msgid "Positive" +msgstr "Positiv" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:128 +#: AppTools/ToolQRCode.py:183 +msgid "" +"Choose the type of QRCode to be created.\n" +"If added on a Silkscreen Gerber file the QRCode may\n" +"be added as positive. If it is added to a Copper Gerber\n" +"file then perhaps the QRCode can be added as negative." +msgstr "" +"Wählen Sie die Art des zu erzeugenden QRCodes.\n" +"Wenn er zu ein Silkscreen Gerber file hinzugefügt werden soll\n" +"sollte er \"Positiv\" sein, wenn sie ihn zum Copper File hinzufügen\n" +"wollen sollte er möglichst \"Negativ\" sein." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:139 +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:145 +#: AppTools/ToolQRCode.py:194 AppTools/ToolQRCode.py:200 +msgid "" +"The bounding box, meaning the empty space that surrounds\n" +"the QRCode geometry, can have a rounded or a square shape." +msgstr "" +"Der Umriss um den QRCode, der die Freistellungsfläche definiert\n" +"kann abgerundete oder scharfe Ecken haben." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:142 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:239 +#: AppTools/ToolQRCode.py:197 AppTools/ToolTransform.py:383 +msgid "Rounded" +msgstr "Agberundet" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:152 +#: AppTools/ToolQRCode.py:228 +msgid "Fill Color" +msgstr "Boxfarbe" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:154 +#: AppTools/ToolQRCode.py:230 +msgid "Set the QRCode fill color (squares color)." +msgstr "Wählen Sie die Farbe der Boxen." + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:173 +#: AppTools/ToolQRCode.py:252 +msgid "Back Color" +msgstr "Hintergrundfarbe" + +#: AppGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:175 +#: AppTools/ToolQRCode.py:254 +msgid "Set the QRCode background color." +msgstr "Wählen Sie die Farbe im QRCode, die nicht von einer Box bedeckt ist." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:27 +msgid "Check Rules Tool Options" +msgstr "Optionen des Werkzeugs 'Regeln prüfen'" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:32 +msgid "" +"A tool to check if Gerber files are within a set\n" +"of Manufacturing Rules." +msgstr "" +"Ein Tool zum Überprüfen, ob sich Gerber-Dateien innerhalb eines Satzes " +"befinden\n" +"von Herstellungsregeln." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:42 +#: AppTools/ToolRulesCheck.py:265 AppTools/ToolRulesCheck.py:929 +msgid "Trace Size" +msgstr "Spurengröße" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:44 +#: AppTools/ToolRulesCheck.py:267 +msgid "This checks if the minimum size for traces is met." +msgstr "Hiermit wird überprüft, ob die Mindestgröße für Traces erfüllt ist." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:54 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:74 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:94 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:114 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:134 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:154 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:174 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:194 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:216 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:236 +#: AppTools/ToolRulesCheck.py:277 AppTools/ToolRulesCheck.py:299 +#: AppTools/ToolRulesCheck.py:322 AppTools/ToolRulesCheck.py:345 +#: AppTools/ToolRulesCheck.py:368 AppTools/ToolRulesCheck.py:391 +#: AppTools/ToolRulesCheck.py:414 AppTools/ToolRulesCheck.py:437 +#: AppTools/ToolRulesCheck.py:462 AppTools/ToolRulesCheck.py:485 +msgid "Min value" +msgstr "Min. Wert" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:56 +#: AppTools/ToolRulesCheck.py:279 +msgid "Minimum acceptable trace size." +msgstr "Minimale akzeptable Trace-Größe." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:61 +#: AppTools/ToolRulesCheck.py:286 AppTools/ToolRulesCheck.py:1157 +#: AppTools/ToolRulesCheck.py:1187 +msgid "Copper to Copper clearance" +msgstr "Mininalabstand Kupfer zu Kupfer" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:63 +#: AppTools/ToolRulesCheck.py:288 +msgid "" +"This checks if the minimum clearance between copper\n" +"features is met." +msgstr "" +"Dies überprüft, ob der Mindestabstand zwischen Kupfer\n" +"Spuren ist erfüllt." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:76 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:96 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:116 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:136 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:156 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:176 +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:238 +#: AppTools/ToolRulesCheck.py:301 AppTools/ToolRulesCheck.py:324 +#: AppTools/ToolRulesCheck.py:347 AppTools/ToolRulesCheck.py:370 +#: AppTools/ToolRulesCheck.py:393 AppTools/ToolRulesCheck.py:416 +#: AppTools/ToolRulesCheck.py:464 +msgid "Minimum acceptable clearance value." +msgstr "Minimaler akzeptabler Abstandswert." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:81 +#: AppTools/ToolRulesCheck.py:309 AppTools/ToolRulesCheck.py:1217 +#: AppTools/ToolRulesCheck.py:1223 AppTools/ToolRulesCheck.py:1236 +#: AppTools/ToolRulesCheck.py:1243 +msgid "Copper to Outline clearance" +msgstr "Mininalabstand Kupfer zum Rahmen" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:83 +#: AppTools/ToolRulesCheck.py:311 +msgid "" +"This checks if the minimum clearance between copper\n" +"features and the outline is met." +msgstr "Überprüft den Minimalabstand zwischen Kupfer und Rand." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:101 +#: AppTools/ToolRulesCheck.py:332 +msgid "Silk to Silk Clearance" +msgstr "Siebdruck zu siebdruck Abstand" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:103 +#: AppTools/ToolRulesCheck.py:334 +msgid "" +"This checks if the minimum clearance between silkscreen\n" +"features and silkscreen features is met." +msgstr "" +"Dies prüft, ob der Mindestabstand zwischen Siebdruck\n" +"Objekte und Silkscreen-Objekte erfüllt ist." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:121 +#: AppTools/ToolRulesCheck.py:355 AppTools/ToolRulesCheck.py:1326 +#: AppTools/ToolRulesCheck.py:1332 AppTools/ToolRulesCheck.py:1350 +msgid "Silk to Solder Mask Clearance" +msgstr "Siebdruck auf Lötmaske Clearance" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:123 +#: AppTools/ToolRulesCheck.py:357 +msgid "" +"This checks if the minimum clearance between silkscreen\n" +"features and soldermask features is met." +msgstr "" +"Dies prüft, ob der Mindestabstand zwischen Siebdruck\n" +"Spuren und Lötmaskenspuren werden eingehalten." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:141 +#: AppTools/ToolRulesCheck.py:378 AppTools/ToolRulesCheck.py:1380 +#: AppTools/ToolRulesCheck.py:1386 AppTools/ToolRulesCheck.py:1400 +#: AppTools/ToolRulesCheck.py:1407 +msgid "Silk to Outline Clearance" +msgstr "Siebdruck zur Gliederung Clearance" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:143 +#: AppTools/ToolRulesCheck.py:380 +msgid "" +"This checks if the minimum clearance between silk\n" +"features and the outline is met." +msgstr "" +"Dies prüft, ob der Mindestabstand zwischen Siebdruck\n" +"Spuren und der Umriss ist erfüllt." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:161 +#: AppTools/ToolRulesCheck.py:401 AppTools/ToolRulesCheck.py:1418 +#: AppTools/ToolRulesCheck.py:1445 +msgid "Minimum Solder Mask Sliver" +msgstr "Minimum Lötmaskenband" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:163 +#: AppTools/ToolRulesCheck.py:403 +msgid "" +"This checks if the minimum clearance between soldermask\n" +"features and soldermask features is met." +msgstr "" +"Hiermit wird geprüft, ob der Mindestabstand zwischen den Lötmasken " +"eingehalten wird\n" +"Spuren und Soldermask-Merkmale sind erfüllt." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:181 +#: AppTools/ToolRulesCheck.py:424 AppTools/ToolRulesCheck.py:1483 +#: AppTools/ToolRulesCheck.py:1489 AppTools/ToolRulesCheck.py:1505 +#: AppTools/ToolRulesCheck.py:1512 +msgid "Minimum Annular Ring" +msgstr "Minimaler Ring" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:183 +#: AppTools/ToolRulesCheck.py:426 +msgid "" +"This checks if the minimum copper ring left by drilling\n" +"a hole into a pad is met." +msgstr "" +"Dies überprüft, ob der minimale Kupferring durch Bohren übrig bleibt\n" +"Ein Loch in einem Pad ist getroffen." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:196 +#: AppTools/ToolRulesCheck.py:439 +msgid "Minimum acceptable ring value." +msgstr "Minimaler akzeptabler Ringwert." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:203 +#: AppTools/ToolRulesCheck.py:449 AppTools/ToolRulesCheck.py:873 +msgid "Hole to Hole Clearance" +msgstr "Loch zu Loch Abstand" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:205 +#: AppTools/ToolRulesCheck.py:451 +msgid "" +"This checks if the minimum clearance between a drill hole\n" +"and another drill hole is met." +msgstr "" +"Dies überprüft, ob der Mindestabstand zwischen einem Bohrloch ist\n" +"und ein weiteres Bohrloch ist getroffen." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:218 +#: AppTools/ToolRulesCheck.py:487 +msgid "Minimum acceptable drill size." +msgstr "Minimale zulässige Bohrergröße." + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:223 +#: AppTools/ToolRulesCheck.py:472 AppTools/ToolRulesCheck.py:847 +msgid "Hole Size" +msgstr "Lochgröße" + +#: AppGUI/preferences/tools/Tools2RulesCheckPrefGroupUI.py:225 +#: AppTools/ToolRulesCheck.py:474 +msgid "" +"This checks if the drill holes\n" +"sizes are above the threshold." +msgstr "" +"Dadurch wird geprüft, ob die Bohrlöcher vorhanden sind\n" +"Größen liegen über der Schwelle." + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:27 +msgid "2Sided Tool Options" +msgstr "2Seitige Werkzeugoptionen" + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:33 +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." + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:47 +msgid "Drill dia" +msgstr "Bohrdurchmesser" + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:49 +#: AppTools/ToolDblSided.py:363 AppTools/ToolDblSided.py:368 +msgid "Diameter of the drill for the alignment holes." +msgstr "Durchmesser des Bohrers für die Ausrichtungslöcher." + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:56 +#: AppTools/ToolDblSided.py:377 +msgid "Align Axis" +msgstr "Achse ausrichten" + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:58 +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:71 +#: AppTools/ToolDblSided.py:165 AppTools/ToolDblSided.py:379 +msgid "Mirror vertically (X) or horizontally (Y)." +msgstr "Vertikal spiegeln (X) oder horizontal (Y)." + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:69 +msgid "Mirror Axis:" +msgstr "Spiegelachse:" + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:80 +#: AppTools/ToolDblSided.py:181 +msgid "Point" +msgstr "Punkt" + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:81 +#: AppTools/ToolDblSided.py:182 +msgid "Box" +msgstr "Box" + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:82 +msgid "Axis Ref" +msgstr "Achsenreferenz" + +#: AppGUI/preferences/tools/Tools2sidedPrefGroupUI.py:84 +msgid "" +"The axis should pass through a point or cut\n" +" a specified box (in a FlatCAM object) through \n" +"the center." +msgstr "" +"Die Achse sollte einen Punkt durchlaufen oder schneiden\n" +"eine angegebene Box (in einem FlatCAM-Objekt) durch\n" +"das Zentrum." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:27 +msgid "Calculators Tool Options" +msgstr "Rechner-Tool-Optionen" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:31 +#: AppTools/ToolCalculators.py:25 +msgid "V-Shape Tool Calculator" +msgstr "V-Shape-Werkzeugrechner" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:33 +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." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:50 +#: AppTools/ToolCalculators.py:94 +msgid "Tip Diameter" +msgstr "Spitzendurchmesser" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:52 +#: AppTools/ToolCalculators.py:102 +msgid "" +"This is the tool tip diameter.\n" +"It is specified by manufacturer." +msgstr "" +"Dies ist der Werkzeugspitzendurchmesser.\n" +"Es wird vom Hersteller angegeben." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:64 +#: AppTools/ToolCalculators.py:105 +msgid "Tip Angle" +msgstr "Spitzenwinkel" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:66 +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." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:80 +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." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:87 +#: AppTools/ToolCalculators.py:27 +msgid "ElectroPlating Calculator" +msgstr "Galvanikrechner" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:89 +#: AppTools/ToolCalculators.py:158 +msgid "" +"This calculator is useful for those who plate the via/pad/drill holes,\n" +"using a method like graphite ink or calcium hypophosphite ink or palladium " +"chloride." +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." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:100 +#: AppTools/ToolCalculators.py:167 +msgid "Board Length" +msgstr "PCB Länge" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:102 +#: AppTools/ToolCalculators.py:173 +msgid "This is the board length. In centimeters." +msgstr "Dies ist die Boardlänge. In Zentimeter." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:112 +#: AppTools/ToolCalculators.py:175 +msgid "Board Width" +msgstr "PCB Breite" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:114 +#: AppTools/ToolCalculators.py:181 +msgid "This is the board width.In centimeters." +msgstr "Dies ist die Breite der Platte in Zentimetern." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:119 +#: AppTools/ToolCalculators.py:183 +msgid "Current Density" +msgstr "Stromdichte" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:125 +#: AppTools/ToolCalculators.py:190 +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." + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:131 +#: AppTools/ToolCalculators.py:193 +msgid "Copper Growth" +msgstr "Kupferwachstum" + +#: AppGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:137 +#: AppTools/ToolCalculators.py:200 +msgid "" +"How thick the copper growth is intended to be.\n" +"In microns." +msgstr "" +"Wie dick soll das Kupferwachstum sein.\n" +"In Mikrometern." + +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:27 +#, fuzzy +#| msgid "Gerber Options" +msgid "Corner Markers Options" +msgstr "Gerber-Optionen" + +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:44 +#: AppTools/ToolCorners.py:115 +msgid "The thickness of the line that makes the corner marker." +msgstr "" + +#: AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py:58 +#: AppTools/ToolCorners.py:129 +msgid "The length of the line that makes the corner marker." +msgstr "" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:28 +msgid "Cutout Tool Options" +msgstr "Ausschnittwerkzeug-Optionen" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:43 +#: AppTools/ToolCalculators.py:123 AppTools/ToolCutOut.py:129 +msgid "Tool Diameter" +msgstr "Werkzeugdurchm" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:45 +#: AppTools/ToolCutOut.py:131 +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." + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:100 +msgid "Object kind" +msgstr "Objektart" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:102 +#: AppTools/ToolCutOut.py:77 +msgid "" +"Choice of what kind the object we want to cutout is.
    - Single: " +"contain a single PCB Gerber outline object.
    - Panel: a panel PCB " +"Gerber object, which is made\n" +"out of many individual PCB outlines." +msgstr "" +"Auswahl, welche Art von Objekt ausgeschnitten werden soll.
    - Einfach " +": Enthält ein einzelnes PCB-Gerber-Umrissobjekt.
    - Panel : " +"Ein Panel-PCB-Gerber Objekt, dass\n" +"aus vielen einzelnen PCB-Konturen zusammengesetzt ist." + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:109 +#: AppTools/ToolCutOut.py:83 +msgid "Single" +msgstr "Einzeln" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:110 +#: AppTools/ToolCutOut.py:84 +msgid "Panel" +msgstr "Platte" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:117 +#: AppTools/ToolCutOut.py:192 +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" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:130 +#: AppTools/ToolCutOut.py:203 +msgid "Gap size" +msgstr "Spaltgröße" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:132 +#: AppTools/ToolCutOut.py:205 +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)." + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:146 +#: AppTools/ToolCutOut.py:245 +msgid "Gaps" +msgstr "Spalt" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:148 +msgid "" +"Number of gaps used for the cutout.\n" +"There can be maximum 8 bridges/gaps.\n" +"The choices are:\n" +"- None - no gaps\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" +"- Keine - keine Lücken\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" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:170 +#: AppTools/ToolCutOut.py:222 +msgid "Convex Shape" +msgstr "Konvexe Form" + +#: AppGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:172 +#: AppTools/ToolCutOut.py:225 +msgid "" +"Create a convex shape surrounding the entire PCB.\n" +"Used only if the source object type is Gerber." +msgstr "" +"Erstellen Sie eine konvexe Form, die die gesamte Leiterplatte umgibt.\n" +"Wird nur verwendet, wenn der Quellobjekttyp Gerber ist." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:27 +msgid "Film Tool Options" +msgstr "Filmwerkzeugoptionen" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:33 +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." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:44 +msgid "Film Type" +msgstr "Filmtyp" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:46 AppTools/ToolFilm.py:283 +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." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:57 +msgid "Film Color" +msgstr "Filmfarbe" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:59 +msgid "Set the film color when positive film is selected." +msgstr "Stellen Sie die Filmfarbe ein, wenn Positivfilm ausgewählt ist." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:82 AppTools/ToolFilm.py:299 +msgid "Border" +msgstr "Rand" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:84 AppTools/ToolFilm.py:301 +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." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:101 +#: AppTools/ToolFilm.py:266 +msgid "Scale Stroke" +msgstr "Skalierungshub" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:103 +#: AppTools/ToolFilm.py:268 +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." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:110 +#: AppTools/ToolFilm.py:124 +msgid "Film Adjustments" +msgstr "Filmeinstellungen" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:112 +#: AppTools/ToolFilm.py:126 +msgid "" +"Sometime the printers will distort the print shape, especially the Laser " +"types.\n" +"This section provide the tools to compensate for the print distortions." +msgstr "" +"Manchmal verzerren die Drucker die Druckform, insbesondere die Lasertypen.\n" +"In diesem Abschnitt finden Sie die Tools zum Ausgleichen der " +"Druckverzerrungen." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:119 +#: AppTools/ToolFilm.py:133 +msgid "Scale Film geometry" +msgstr "Filmgeometrie skalieren" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:121 +#: AppTools/ToolFilm.py:135 +msgid "" +"A value greater than 1 will stretch the film\n" +"while a value less than 1 will jolt it." +msgstr "" +"Ein Wert größer als 1 streckt den Film\n" +"Ein Wert unter 1 ruckelt." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:131 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:103 +#: AppTools/ToolFilm.py:145 AppTools/ToolTransform.py:148 +msgid "X factor" +msgstr "X Faktor" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:140 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:116 +#: AppTools/ToolFilm.py:154 AppTools/ToolTransform.py:168 +msgid "Y factor" +msgstr "Y Faktor" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:150 +#: AppTools/ToolFilm.py:172 +msgid "Skew Film geometry" +msgstr "Verzerren Sie die Filmgeometrie" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:152 +#: AppTools/ToolFilm.py:174 +msgid "" +"Positive values will skew to the right\n" +"while negative values will skew to the left." +msgstr "" +"Positive Werte werden nach rechts verschoben\n" +"negative Werte werden nach links verschoben." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:162 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:72 +#: AppTools/ToolFilm.py:184 AppTools/ToolTransform.py:97 +msgid "X angle" +msgstr "X Winkel" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:171 +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:86 +#: AppTools/ToolFilm.py:193 AppTools/ToolTransform.py:118 +msgid "Y angle" +msgstr "Y Winkel" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:182 +#: AppTools/ToolFilm.py:204 +msgid "" +"The reference point to be used as origin for the skew.\n" +"It can be one of the four points of the geometry bounding box." +msgstr "" +"Der Referenzpunkt, der als Ursprung für den Versatz verwendet werden soll.\n" +"Dies kann einer der vier Punkte des Geometrie-Begrenzungsrahmens sein." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:185 +#: AppTools/ToolCorners.py:80 AppTools/ToolFiducials.py:87 +#: AppTools/ToolFilm.py:207 +msgid "Bottom Left" +msgstr "Unten links" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:186 +#: AppTools/ToolCorners.py:88 AppTools/ToolFilm.py:208 +msgid "Top Left" +msgstr "Oben links" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:187 +#: AppTools/ToolCorners.py:84 AppTools/ToolFilm.py:209 +msgid "Bottom Right" +msgstr "Unten rechts" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:188 +#: AppTools/ToolFilm.py:210 +msgid "Top right" +msgstr "Oben rechts" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:196 +#: AppTools/ToolFilm.py:227 +msgid "Mirror Film geometry" +msgstr "Spiegeln Sie die Filmgeometrie" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:198 +#: AppTools/ToolFilm.py:229 +msgid "Mirror the film geometry on the selected axis or on both." +msgstr "" +"Spiegeln Sie die Filmgeometrie auf der ausgewählten Achse oder auf beiden." + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:212 +#: AppTools/ToolFilm.py:243 +msgid "Mirror axis" +msgstr "Achse spiegeln" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:222 +#: AppTools/ToolFilm.py:388 +msgid "SVG" +msgstr "SVG" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:223 +#: AppTools/ToolFilm.py:389 +msgid "PNG" +msgstr "PNG" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:224 +#: AppTools/ToolFilm.py:390 +msgid "PDF" +msgstr "PDF" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:227 +#: AppTools/ToolFilm.py:281 AppTools/ToolFilm.py:393 +msgid "Film Type:" +msgstr "Filmtyp:" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:229 +#: AppTools/ToolFilm.py:395 +msgid "" +"The file type of the saved film. Can be:\n" +"- 'SVG' -> open-source vectorial format\n" +"- 'PNG' -> raster image\n" +"- 'PDF' -> portable document format" +msgstr "" +"Der Dateityp in dem gespeichert werden soll:\n" +"- 'SVG' -> open-source vectorial format\n" +"- 'PNG' -> raster image\n" +"- 'PDF' -> portable document format" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:238 +#: AppTools/ToolFilm.py:404 +msgid "Page Orientation" +msgstr "Seitenausrichtung" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:251 +#: AppTools/ToolFilm.py:417 +msgid "Page Size" +msgstr "Seitengröße" + +#: AppGUI/preferences/tools/ToolsFilmPrefGroupUI.py:252 +#: AppTools/ToolFilm.py:418 +msgid "A selection of standard ISO 216 page sizes." +msgstr "Eine Auswahl von Standard ISO 216 Seitengrößen." + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:27 +msgid "NCC Tool Options" +msgstr "NCC-Tooloptionen" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:49 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:57 +msgid "Comma separated values" +msgstr "Komma-getrennte Werte" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:55 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:63 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:64 AppTools/ToolNCC.py:215 +#: AppTools/ToolNCC.py:223 AppTools/ToolPaint.py:197 AppTools/ToolPaint.py:205 +msgid "" +"Default tool type:\n" +"- 'V-shape'\n" +"- Circular" +msgstr "" +"Standardwerkzeugtyp:\n" +"- \"V-Form\"\n" +"- Rundschreiben" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:60 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:69 AppTools/ToolNCC.py:220 +#: AppTools/ToolPaint.py:202 +msgid "V-shape" +msgstr "V-Form" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:100 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:109 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:107 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:116 +#: AppTools/ToolNCC.py:262 AppTools/ToolNCC.py:271 AppTools/ToolPaint.py:244 +#: AppTools/ToolPaint.py:253 +msgid "" +"Depth of cut into material. Negative value.\n" +"In FlatCAM units." +msgstr "" +"Schnitttiefe in Material. Negativer Wert.\n" +"In FlatCAM-Einheiten." + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:119 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:125 +#: AppTools/ToolNCC.py:280 AppTools/ToolPaint.py:262 +msgid "" +"Diameter for the new tool to add in the Tool Table.\n" +"If the tool is V-shape type then this value is automatically\n" +"calculated from the other parameters." +msgstr "" +"Durchmesser des neuen Werkzeugs das in die Werkzeugtabelle\n" +"aufgenommen werden soll. Wenn das Tool V-Förmig ist, wird dieser\n" +"Wert aus den anderen Parametern berechnet." + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:156 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:142 +#: AppTools/ToolNCC.py:174 AppTools/ToolPaint.py:157 +msgid "Tool order" +msgstr "Werkzeugbestellung" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:157 +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:167 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:143 +#: AppTools/ToolNCC.py:175 AppTools/ToolNCC.py:185 AppTools/ToolPaint.py:158 +#: AppTools/ToolPaint.py:168 +msgid "" +"This set the way that the tools in the tools table are used.\n" +"'No' --> means that the used order is the one in the tool table\n" +"'Forward' --> means that the tools will be ordered from small to big\n" +"'Reverse' --> means that the tools will ordered from big to small\n" +"\n" +"WARNING: using rest machining will automatically set the order\n" +"in reverse and disable this control." +msgstr "" +"Hiermit wird festgelegt, wie die Werkzeuge in der Werkzeugtabelle verwendet " +"werden.\n" +"'Nein' -> bedeutet, dass die verwendete Reihenfolge die in der " +"Werkzeugtabelle ist\n" +"'Weiter' -> bedeutet, dass die Werkzeuge von klein nach groß sortiert " +"werden\n" +"'Rückwärts' -> Menus, die die Werkzeuge von groß nach klein ordnen\n" +"\n" +"WARNUNG: Bei Verwendung der Restbearbeitung wird die Reihenfolge automatisch " +"festgelegt\n" +"in umgekehrter Richtung und deaktivieren Sie diese Steuerung." + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:165 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:151 +#: AppTools/ToolNCC.py:183 AppTools/ToolPaint.py:166 +msgid "Forward" +msgstr "Vorwärts" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:166 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:152 +#: AppTools/ToolNCC.py:184 AppTools/ToolPaint.py:167 +msgid "Reverse" +msgstr "Rückwärts" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:266 +msgid "Offset value" +msgstr "Offsetwert" + +# What the hack is a FlatCAM unit?? +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:268 +msgid "" +"If used, it will add an offset to the copper features.\n" +"The copper clearing will finish to a distance\n" +"from the copper features.\n" +"The value can be between 0.0 and 9999.9 FlatCAM units." +msgstr "" +"Bei Verwendung wird den Kupferelementen ein Offset hinzugefügt.\n" +"Die Kupferreinigung wird bei einer gewissen Entfernung\n" +"zu den Kupferflächen enden.\n" +"Der Wert kann zwischen 0 und 10 FlatCAM-Einheiten liegen." + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:288 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:244 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:245 +#: AppTools/ToolNCC.py:512 AppTools/ToolPaint.py:441 +msgid "Rest Machining" +msgstr "Restbearbeitung" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:290 AppTools/ToolNCC.py:516 +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." + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:313 AppTools/ToolNCC.py:541 +msgid "" +"Selection of area to be processed.\n" +"- 'Itself' - the processing extent is based on the object that is " +"processed.\n" +" - 'Area Selection' - left mouse click to start selection of the area to be " +"processed.\n" +"- 'Reference Object' - will process the area specified by another object." +msgstr "" +"Auswahl des zu verarbeitenden Bereichs.\n" +"- 'Selbst' - Der Verarbeitungsumfang basiert auf dem Objekt, das verarbeitet " +"wird.\n" +"- 'Bereichsauswahl' - Klicken Sie mit der linken Maustaste, um die Auswahl " +"des zu verarbeitenden Bereichs zu starten.\n" +"- 'Referenzobjekt' - verarbeitet den von einem anderen Objekt angegebenen " +"Bereich." + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:339 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:303 +msgid "Normal" +msgstr "NormalFormat" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:340 +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:304 +msgid "Progressive" +msgstr "Progressiv" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:341 +msgid "NCC Plotting" +msgstr "NCC-Plotten" + +#: AppGUI/preferences/tools/ToolsNCCPrefGroupUI.py:343 +msgid "" +"- 'Normal' - normal plotting, done at the end of the NCC job\n" +"- 'Progressive' - after each shape is generated it will be plotted." +msgstr "" +"- 'Normal' - normales Plotten am Ende des NCC-Jobs\n" +"- 'Progressiv' - Nachdem jede Form generiert wurde, wird sie geplottet." + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:27 +msgid "Paint Tool Options" +msgstr "Paint werkzeug-Optionen" + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:33 +msgid "Parameters:" +msgstr "Parameter:" + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:247 +#: AppTools/ToolPaint.py:444 +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." + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:260 +#: AppTools/ToolPaint.py:457 +msgid "" +"Selection of area to be processed.\n" +"- 'Polygon Selection' - left mouse click to add/remove polygons to be " +"processed.\n" +"- 'Area Selection' - left mouse click to start selection of the area to be " +"processed.\n" +"Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple " +"areas.\n" +"- 'All Polygons' - the process will start after click.\n" +"- 'Reference Object' - will process the area specified by another object." +msgstr "" +"Auswahl des zu verarbeitenden Bereichs.\n" +"- 'Polygonauswahl' - Klicken Sie mit der linken Maustaste, um zu " +"verarbeitende Polygone hinzuzufügen / zu entfernen.\n" +"- 'Bereichsauswahl' - Klicken Sie mit der linken Maustaste, um die Auswahl " +"des zu verarbeitenden Bereichs zu starten.\n" +"Wenn Sie eine Modifizierertaste gedrückt halten (STRG oder SHIFT), können " +"Sie mehrere Bereiche hinzufügen.\n" +"- 'Alle Polygone' - Der Vorgang wird nach dem Klicken gestartet.\n" +"- 'Referenzobjekt' - verarbeitet den von einem anderen Objekt angegebenen " +"Bereich." + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:280 +#: AppTools/ToolPaint.py:485 AppTools/ToolPaint.py:941 +#: AppTools/ToolPaint.py:1431 tclCommands/TclCommandPaint.py:164 +msgid "Polygon Selection" +msgstr "Polygon auswahl" + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:280 +#: AppTools/ToolPaint.py:485 AppTools/ToolPaint.py:1426 defaults.py:433 +#: tclCommands/TclCommandPaint.py:162 +msgid "All Polygons" +msgstr "Alle Polygone" + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:305 +msgid "Paint Plotting" +msgstr "Malen Sie Plotten" + +#: AppGUI/preferences/tools/ToolsPaintPrefGroupUI.py:307 +msgid "" +"- 'Normal' - normal plotting, done at the end of the Paint job\n" +"- 'Progressive' - after each shape is generated it will be plotted." +msgstr "" +"- 'Normal' - normales Plotten am Ende des Malvorgangs\n" +"- 'Progressiv' - Nachdem jede Form generiert wurde, wird sie geplottet." + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:27 +msgid "Panelize Tool Options" +msgstr "Panelize Werkzeugoptionen" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:33 +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." + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:50 +#: AppTools/ToolPanelize.py:165 +msgid "Spacing cols" +msgstr "Abstandspalten" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:52 +#: AppTools/ToolPanelize.py:167 +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." + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:64 +#: AppTools/ToolPanelize.py:177 +msgid "Spacing rows" +msgstr "Abstand Reihen" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:66 +#: AppTools/ToolPanelize.py:179 +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." + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:77 +#: AppTools/ToolPanelize.py:188 +msgid "Columns" +msgstr "Säulen" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:79 +#: AppTools/ToolPanelize.py:190 +msgid "Number of columns of the desired panel" +msgstr "Anzahl der Spalten des gewünschten Bereichs" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:89 +#: AppTools/ToolPanelize.py:198 +msgid "Rows" +msgstr "Reihen" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:91 +#: AppTools/ToolPanelize.py:200 +msgid "Number of rows of the desired panel" +msgstr "Anzahl der Zeilen des gewünschten Panels" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:98 +#: AppTools/ToolPanelize.py:211 +msgid "Geo" +msgstr "Geo" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:99 +#: AppTools/ToolPanelize.py:212 +msgid "Panel Type" +msgstr "Panel-Typ" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:101 +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" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:110 +msgid "Constrain within" +msgstr "Beschränkung innerhalb" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:112 +#: AppTools/ToolPanelize.py:224 +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." + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:125 +#: AppTools/ToolPanelize.py:236 +msgid "Width (DX)" +msgstr "Breite (DX)" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:127 +#: AppTools/ToolPanelize.py:238 +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." + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:138 +#: AppTools/ToolPanelize.py:247 +msgid "Height (DY)" +msgstr "Höhe (DY)" + +#: AppGUI/preferences/tools/ToolsPanelizePrefGroupUI.py:140 +#: AppTools/ToolPanelize.py:249 +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." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:27 +msgid "SolderPaste Tool Options" +msgstr "Lötpaste-Werkzeug-Optionen" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:33 +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." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:54 +msgid "New Nozzle Dia" +msgstr "Neuer Düsendurchmesser" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:56 +#: AppTools/ToolSolderPaste.py:107 +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" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:72 +#: AppTools/ToolSolderPaste.py:183 +msgid "Z Dispense Start" +msgstr "Z Dosierbeginn" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:74 +#: AppTools/ToolSolderPaste.py:185 +msgid "The height (Z) when solder paste dispensing starts." +msgstr "Die Höhe (Z) bei der Lotpastendosierung." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:85 +#: AppTools/ToolSolderPaste.py:195 +msgid "Z Dispense" +msgstr "Z-Abgabe" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:87 +#: AppTools/ToolSolderPaste.py:197 +msgid "The height (Z) when doing solder paste dispensing." +msgstr "Die Höhe (Z) bei der Lotpastendosierung." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:98 +#: AppTools/ToolSolderPaste.py:207 +msgid "Z Dispense Stop" +msgstr "Z Abgabestopp" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:100 +#: AppTools/ToolSolderPaste.py:209 +msgid "The height (Z) when solder paste dispensing stops." +msgstr "Die Höhe (Z) bei der Lotpastendosierung stoppt." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:111 +#: AppTools/ToolSolderPaste.py:219 +msgid "Z Travel" +msgstr "Z Reise" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:113 +#: AppTools/ToolSolderPaste.py:221 +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)." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:125 +#: AppTools/ToolSolderPaste.py:232 +msgid "Z Toolchange" +msgstr "Z Werkzeugwechsel" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:127 +#: AppTools/ToolSolderPaste.py:234 +msgid "The height (Z) for tool (nozzle) change." +msgstr "Die Höhe (Z) für Werkzeug (Düse) ändert sich." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:136 +#: AppTools/ToolSolderPaste.py:242 +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." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:150 +#: AppTools/ToolSolderPaste.py:255 +msgid "Feedrate (speed) while moving on the X-Y plane." +msgstr "Vorschub (Geschwindigkeit) während der Bewegung auf der X-Y-Ebene." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:163 +#: AppTools/ToolSolderPaste.py:267 +msgid "" +"Feedrate (speed) while moving vertically\n" +"(on Z plane)." +msgstr "" +"Vorschub (Geschwindigkeit) bei vertikaler Bewegung\n" +"(auf der Z-Ebene)." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:175 +#: AppTools/ToolSolderPaste.py:278 +msgid "Feedrate Z Dispense" +msgstr "Vorschub Z Dosierung" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:177 +msgid "" +"Feedrate (speed) while moving up vertically\n" +"to Dispense position (on Z plane)." +msgstr "" +"Vorschub (Geschwindigkeit) bei vertikaler Aufwärtsbewegung\n" +"in Ausgabeposition (in der Z-Ebene)." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:188 +#: AppTools/ToolSolderPaste.py:290 +msgid "Spindle Speed FWD" +msgstr "Spindeldrehzahl FWD" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:190 +#: AppTools/ToolSolderPaste.py:292 +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." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:202 +#: AppTools/ToolSolderPaste.py:303 +msgid "Dwell FWD" +msgstr "Verweilzeit FWD" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:204 +#: AppTools/ToolSolderPaste.py:305 +msgid "Pause after solder dispensing." +msgstr "Pause nach dem Löten." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:214 +#: AppTools/ToolSolderPaste.py:314 +msgid "Spindle Speed REV" +msgstr "Spindeldrehzahl REV" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:216 +#: AppTools/ToolSolderPaste.py:316 +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." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:228 +#: AppTools/ToolSolderPaste.py:327 +msgid "Dwell REV" +msgstr "Verweilen REV" + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:230 +#: AppTools/ToolSolderPaste.py:329 +msgid "" +"Pause after solder paste dispenser retracted,\n" +"to allow pressure equilibrium." +msgstr "" +"Pause nachdem Lotpastendispenser eingefahren wurde,\n" +"das Druckgleichgewicht zu ermöglichen." + +#: AppGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:239 +#: AppTools/ToolSolderPaste.py:337 +msgid "Files that control the GCode generation." +msgstr "Dateien, die die GCode-Generierung steuern." + +#: AppGUI/preferences/tools/ToolsSubPrefGroupUI.py:27 +msgid "Substractor Tool Options" +msgstr "Substractor-Werkzeug-Optionen" + +#: AppGUI/preferences/tools/ToolsSubPrefGroupUI.py:33 +msgid "" +"A tool to substract one Gerber or Geometry object\n" +"from another of the same type." +msgstr "" +"Ein Werkzeug zum Subtrahieren eines Gerber- oder Geometrieobjekts\n" +"von einem anderen des gleichen Typs." + +#: AppGUI/preferences/tools/ToolsSubPrefGroupUI.py:38 AppTools/ToolSub.py:155 +msgid "Close paths" +msgstr "Wege schließen" + +#: AppGUI/preferences/tools/ToolsSubPrefGroupUI.py:39 +msgid "" +"Checking this will close the paths cut by the Geometry substractor object." +msgstr "" +"Wenn Sie dies aktivieren, werden die vom Geometry-Substractor-Objekt " +"geschnittenen Pfade geschlossen." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:27 +msgid "Transform Tool Options" +msgstr "Umwandlungswerkzeug-Optionen" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:33 +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." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:64 +msgid "Skew" +msgstr "Neigung" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:105 +#: AppTools/ToolTransform.py:150 +msgid "Factor for scaling on X axis." +msgstr "Faktor für die Skalierung auf der X-Achse." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:118 +#: AppTools/ToolTransform.py:170 +msgid "Factor for scaling on Y axis." +msgstr "Faktor für die Skalierung auf der Y-Achse." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:126 +#: AppTools/ToolTransform.py:191 +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." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:134 +#: AppTools/ToolTransform.py:198 +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." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:150 +#: AppTools/ToolTransform.py:217 +msgid "X val" +msgstr "X-Wert" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:152 +#: AppTools/ToolTransform.py:219 +msgid "Distance to offset on X axis. In current units." +msgstr "Abstand zum Offset auf der X-Achse. In aktuellen Einheiten." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:163 +#: AppTools/ToolTransform.py:237 +msgid "Y val" +msgstr "Y-Wert" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:165 +#: AppTools/ToolTransform.py:239 +msgid "Distance to offset on Y axis. In current units." +msgstr "Abstand zum Offset auf der Y-Achse. In aktuellen Einheiten." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:171 +#: AppTools/ToolDblSided.py:67 AppTools/ToolDblSided.py:95 +#: AppTools/ToolDblSided.py:125 +msgid "Mirror" +msgstr "Spiegeln" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:175 +#: AppTools/ToolTransform.py:283 +msgid "Mirror Reference" +msgstr "Spiegelreferenz" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:177 +#: AppTools/ToolTransform.py:285 +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" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:188 +msgid "Mirror Reference point" +msgstr "Referenzpunkt spiegeln" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:190 +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" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:203 +#: AppTools/ToolDistance.py:505 AppTools/ToolDistanceMin.py:286 +#: AppTools/ToolTransform.py:332 +msgid "Distance" +msgstr "Entfernung" + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:205 +#: AppTools/ToolTransform.py:334 +msgid "" +"A positive value will create the effect of dilation,\n" +"while a negative value will create the effect of erosion.\n" +"Each geometry element of the object will be increased\n" +"or decreased with the 'distance'." +msgstr "" +"Ein positiver Wert führt zu einem Dilatationseffekt.\n" +"während ein negativer Wert den Effekt der Abnutzung verursacht.\n" +"Jedes Geometrieelement des Objekts wird vergrößert\n" +"oder mit der \"Entfernung\" verringert." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:222 +#: AppTools/ToolTransform.py:359 +msgid "" +"A positive value will create the effect of dilation,\n" +"while a negative value will create the effect of erosion.\n" +"Each geometry element of the object will be increased\n" +"or decreased to fit the 'Value'. Value is a percentage\n" +"of the initial dimension." +msgstr "" +"Ein positiver Wert erzeugt den Effekt der Dilatation.\n" +"während ein negativer Wert den Effekt der Erosion erzeugt.\n" +"Jedes Geometrieelement des Objekts wird vergrößert\n" +"oder verringert, um dem 'Wert' zu entsprechen. Wert ist ein Prozentsatz\n" +"der ursprünglichen Dimension." + +#: AppGUI/preferences/tools/ToolsTransformPrefGroupUI.py:241 +#: AppTools/ToolTransform.py:385 +msgid "" +"If checked then the buffer will surround the buffered shape,\n" +"every corner will be rounded.\n" +"If not checked then the buffer will follow the exact geometry\n" +"of the buffered shape." +msgstr "" +"Wenn diese Option aktiviert ist, umgibt der Puffer die gepufferte Form.\n" +"Jede Ecke wird abgerundet.\n" +"Wenn nicht markiert, folgt der Puffer der exakten Geometrie\n" +"der gepufferten Form." + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:27 +msgid "Autocompleter Keywords" +msgstr "Autocompleter-Schlüsselwörter" + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:30 +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:40 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:30 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:30 +msgid "Restore" +msgstr "Wiederherstellen" + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:31 +msgid "Restore the autocompleter keywords list to the default state." +msgstr "" +"Stellen Sie den Standardzustand der Autocompleter-Schlüsselwortliste wieder " +"her." + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:33 +msgid "Delete all autocompleter keywords from the list." +msgstr "Löschen Sie alle Autocompleter-Schlüsselwörter aus der Liste." + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:41 +msgid "Keywords list" +msgstr "Liste der Stichwörter" + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:43 +msgid "" +"List of keywords used by\n" +"the autocompleter in FlatCAM.\n" +"The autocompleter is installed\n" +"in the Code Editor and for the Tcl Shell." +msgstr "" +"Liste der von verwendeten Schlüsselwörter\n" +"der Autocompleter in FlatCAM.\n" +"Der Autocompleter ist installiert\n" +"im Code-Editor und für die Tcl-Shell." + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:64 +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:73 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:63 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:62 +msgid "Extension" +msgstr "Erweiterung" + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:65 +msgid "A keyword to be added or deleted to the list." +msgstr "" +"Ein Schlüsselwort, das der Liste hinzugefügt oder gelöscht werden soll." + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:73 +msgid "Add keyword" +msgstr "Keyword hinzufügen" + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:74 +msgid "Add a keyword to the list" +msgstr "Fügen Sie der Liste ein Schlüsselwort hinzu" + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:75 +msgid "Delete keyword" +msgstr "Stichwort löschen" + +#: AppGUI/preferences/utilities/AutoCompletePrefGroupUI.py:76 +msgid "Delete a keyword from the list" +msgstr "Löschen Sie ein Schlüsselwort aus der Liste" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:27 +msgid "Excellon File associations" +msgstr "Excellon-Dateizuordnungen" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:41 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:31 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:31 +msgid "Restore the extension list to the default state." +msgstr "Stellen Sie den Standardzustand der Erweiterungsliste wieder her." + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:43 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:33 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:33 +msgid "Delete all extensions from the list." +msgstr "Löschen Sie alle Erweiterungen aus der Liste." + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:51 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:41 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:41 +msgid "Extensions list" +msgstr "Erweiterungsliste" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:53 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:43 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:43 +msgid "" +"List of file extensions to be\n" +"associated with FlatCAM." +msgstr "" +"Liste der zu verwendenden Dateierweiterungen\n" +"im Zusammenhang mit FlatCAM." + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:74 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:64 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:63 +msgid "A file extension to be added or deleted to the list." +msgstr "A file extension to be added or deleted to the list." + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:82 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:72 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:71 +msgid "Add Extension" +msgstr "Erweiterung hinzufügen" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:83 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:73 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:72 +msgid "Add a file extension to the list" +msgstr "Fügen Sie der Liste eine Dateierweiterung hinzu" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:84 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:74 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:73 +msgid "Delete Extension" +msgstr "Erweiterung löschen" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:85 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:75 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:74 +msgid "Delete a file extension from the list" +msgstr "Löschen Sie eine Dateierweiterung aus der Liste" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:92 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:82 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:81 +msgid "Apply Association" +msgstr "Assoziation anwenden" + +#: AppGUI/preferences/utilities/FAExcPrefGroupUI.py:93 +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:83 +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:82 +msgid "" +"Apply the file associations between\n" +"FlatCAM and the files with above extensions.\n" +"They will be active after next logon.\n" +"This work only in Windows." +msgstr "" +"Wenden Sie die Dateizuordnungen zwischen an\n" +"FlatCAM und die Dateien mit den oben genannten Erweiterungen.\n" +"Sie sind nach der nächsten Anmeldung aktiv.\n" +"Dies funktioniert nur unter Windows." + +#: AppGUI/preferences/utilities/FAGcoPrefGroupUI.py:27 +msgid "GCode File associations" +msgstr "GCode-Dateizuordnungen" + +#: AppGUI/preferences/utilities/FAGrbPrefGroupUI.py:27 +msgid "Gerber File associations" +msgstr "Gerber Dateizuordnungen" + +#: AppObjects/AppObject.py:134 +#, python-brace-format +msgid "" +"Object ({kind}) failed because: {error} \n" +"\n" +msgstr "" +"Objekt ({kind}) gescheitert weil: {error} \n" +"\n" + +#: AppObjects/AppObject.py:149 +msgid "Converting units to " +msgstr "Einheiten umrechnen in " + +#: AppObjects/AppObject.py:254 +msgid "CREATE A NEW FLATCAM TCL SCRIPT" +msgstr "NEUES FLATCAL TCL SCRIPT ERZEUGEN" + +#: AppObjects/AppObject.py:255 +msgid "TCL Tutorial is here" +msgstr "Das TCL Tutorial ist hier" + +#: AppObjects/AppObject.py:257 +msgid "FlatCAM commands list" +msgstr "FlatCAM Befehlsliste" + +#: AppObjects/AppObject.py:258 +msgid "" +"Type >help< followed by Run Code for a list of FlatCAM Tcl Commands " +"(displayed in Tcl Shell)." +msgstr "" +"Geben Sie >help< gefolgt von Run Code ein, um eine Liste der FlatCAM Tcl-" +"Befehle anzuzeigen (angezeigt in der Tcl-Shell)." + +#: AppObjects/AppObject.py:304 AppObjects/AppObject.py:310 +#: AppObjects/AppObject.py:316 AppObjects/AppObject.py:322 +#: AppObjects/AppObject.py:328 AppObjects/AppObject.py:334 +msgid "created/selected" +msgstr "erstellt / ausgewählt" + +#: AppObjects/AppObject.py:349 AppObjects/FlatCAMObj.py:246 +#: AppObjects/FlatCAMObj.py:277 AppObjects/FlatCAMObj.py:293 +#: AppObjects/FlatCAMObj.py:373 AppTools/ToolCopperThieving.py:1487 +#: AppTools/ToolCorners.py:394 AppTools/ToolFiducials.py:810 +#: AppTools/ToolMove.py:229 AppTools/ToolQRCode.py:728 App_Main.py:4369 +msgid "Plotting" +msgstr "Plotten" + +#: AppObjects/FlatCAMCNCJob.py:429 AppObjects/FlatCAMDocument.py:71 +#: AppObjects/FlatCAMScript.py:82 +msgid "Basic" +msgstr "Basic" + +#: AppObjects/FlatCAMCNCJob.py:435 AppObjects/FlatCAMDocument.py:75 +#: AppObjects/FlatCAMScript.py:86 +msgid "Advanced" +msgstr "Erweitert" + +#: AppObjects/FlatCAMCNCJob.py:478 +msgid "Plotting..." +msgstr "Zeichnung..." + +#: AppObjects/FlatCAMCNCJob.py:507 AppObjects/FlatCAMCNCJob.py:512 +#: AppTools/ToolSolderPaste.py:1499 +msgid "Export Machine Code ..." +msgstr "Maschinencode exportieren ..." + +#: AppObjects/FlatCAMCNCJob.py:517 AppTools/ToolSolderPaste.py:1503 +msgid "Export Machine Code cancelled ..." +msgstr "Maschinencode exportieren abgebrochen ..." + +#: AppObjects/FlatCAMCNCJob.py:538 +msgid "Machine Code file saved to" +msgstr "Maschinencode-Datei gespeichert in" + +#: AppObjects/FlatCAMCNCJob.py:548 AppObjects/FlatCAMScript.py:134 +#: App_Main.py:7205 +msgid "Loading..." +msgstr "Wird geladen..." + +#: AppObjects/FlatCAMCNCJob.py:562 App_Main.py:7302 +msgid "Code Editor" +msgstr "Code-Editor" + +#: AppObjects/FlatCAMCNCJob.py:599 AppTools/ToolCalibration.py:1097 +msgid "Loaded Machine Code into Code Editor" +msgstr "Maschinencode in den Code-Editor geladen" + +#: AppObjects/FlatCAMCNCJob.py:740 +msgid "This CNCJob object can't be processed because it is a" +msgstr "Dieses CNCJob-Objekt kann nicht verarbeitet werden, da es sich um ein" + +#: AppObjects/FlatCAMCNCJob.py:742 +msgid "CNCJob object" +msgstr "CNCJob-Objekt" + +#: AppObjects/FlatCAMCNCJob.py:922 +msgid "" +"G-code does not have a G94 code and we will not include the code in the " +"'Prepend to GCode' text box" +msgstr "" +"G-Code hat keinen G94-Code und wir werden den Code nicht in das Textfeld " +"\"Vor dem GCode\" aufnehmen" + +#: AppObjects/FlatCAMCNCJob.py:933 +msgid "Cancelled. The Toolchange Custom code is enabled but it's empty." +msgstr "" +"Abgebrochen. Der benutzerdefinierte Code zum Ändern des Werkzeugs ist " +"aktiviert, aber er ist leer." + +#: AppObjects/FlatCAMCNCJob.py:938 +msgid "Toolchange G-code was replaced by a custom code." +msgstr "" +"Der Werkzeugwechsel-G-Code wurde durch einen benutzerdefinierten Code " +"ersetzt." + +#: AppObjects/FlatCAMCNCJob.py:986 AppObjects/FlatCAMCNCJob.py:996 +msgid "" +"The used preprocessor file has to have in it's name: 'toolchange_custom'" +msgstr "" +"Die verwendete Postprozessor-Datei muss im Namen enthalten sein: " +"'toolchange_custom'" + +#: AppObjects/FlatCAMCNCJob.py:999 +msgid "There is no preprocessor file." +msgstr "Es gibt keine Postprozessor-Datei." + +#: AppObjects/FlatCAMDocument.py:175 +msgid "Document Editor" +msgstr "Dokumenteditor" + +#: AppObjects/FlatCAMExcellon.py:527 AppObjects/FlatCAMExcellon.py:825 +#: AppObjects/FlatCAMGeometry.py:322 AppObjects/FlatCAMGeometry.py:852 +#: AppTools/ToolNCC.py:811 AppTools/ToolNCC.py:1196 AppTools/ToolPaint.py:778 +#: AppTools/ToolPaint.py:1170 +msgid "Multiple Tools" +msgstr "Mehrere Werkzeuge" + +#: AppObjects/FlatCAMExcellon.py:805 +msgid "No Tool Selected" +msgstr "Kein Werkzeug ausgewählt" + +#: AppObjects/FlatCAMExcellon.py:1155 AppObjects/FlatCAMExcellon.py:1248 +#: AppObjects/FlatCAMExcellon.py:1435 +msgid "Please select one or more tools from the list and try again." +msgstr "" +"Bitte wählen Sie ein oder mehrere Werkzeuge aus der Liste aus und versuchen " +"Sie es erneut." + +#: AppObjects/FlatCAMExcellon.py:1162 +msgid "Milling tool for DRILLS is larger than hole size. Cancelled." +msgstr "Das Fräswerkzeug für BOHRER ist größer als die Lochgröße. Abgebrochen." + +#: AppObjects/FlatCAMExcellon.py:1177 AppObjects/FlatCAMExcellon.py:1268 +#: AppObjects/FlatCAMExcellon.py:1453 tclCommands/TclCommandDrillcncjob.py:195 +msgid "Tool_nr" +msgstr "Werkzeugnummer" + +#: AppObjects/FlatCAMExcellon.py:1177 AppObjects/FlatCAMExcellon.py:1268 +#: AppObjects/FlatCAMExcellon.py:1453 tclCommands/TclCommandDrillcncjob.py:195 +msgid "Drills_Nr" +msgstr "Bohrnummer" + +#: AppObjects/FlatCAMExcellon.py:1177 AppObjects/FlatCAMExcellon.py:1268 +#: AppObjects/FlatCAMExcellon.py:1453 tclCommands/TclCommandDrillcncjob.py:195 +msgid "Slots_Nr" +msgstr "Schlitznummer" + +#: AppObjects/FlatCAMExcellon.py:1257 +msgid "Milling tool for SLOTS is larger than hole size. Cancelled." +msgstr "" +"Das Fräswerkzeug für SCHLITZ ist größer als die Lochgröße. Abgebrochen." + +#: AppObjects/FlatCAMExcellon.py:1361 AppObjects/FlatCAMGeometry.py:1625 +msgid "Focus Z" +msgstr "Fokus Z" + +#: AppObjects/FlatCAMExcellon.py:1380 AppObjects/FlatCAMGeometry.py:1644 +msgid "Laser Power" +msgstr "Laserleistung" + +#: AppObjects/FlatCAMExcellon.py:1510 AppObjects/FlatCAMGeometry.py:2077 +#: AppObjects/FlatCAMGeometry.py:2081 AppObjects/FlatCAMGeometry.py:2232 +msgid "Generating CNC Code" +msgstr "CNC-Code generieren" + +#: AppObjects/FlatCAMExcellon.py:1563 AppObjects/FlatCAMGeometry.py:2542 +#, fuzzy +#| msgid "Delete failed. Select a tool to delete." +msgid "Delete failed. There are no exclusion areas to delete." +msgstr "Löschen fehlgeschlagen. Wählen Sie ein Werkzeug zum Löschen aus." + +#: AppObjects/FlatCAMExcellon.py:1580 AppObjects/FlatCAMGeometry.py:2559 +#, fuzzy +#| msgid "Failed. Nothing selected." +msgid "Delete failed. Nothing is selected." +msgstr "Gescheitert. Nichts ausgewählt." + +#: AppObjects/FlatCAMExcellon.py:1804 AppTools/ToolNCC.py:918 +#: AppTools/ToolPaint.py:843 +msgid "Current Tool parameters were applied to all tools." +msgstr "Aktuelle Werkzeugparameter wurden auf alle Werkzeuge angewendet." + +#: AppObjects/FlatCAMGeometry.py:123 AppObjects/FlatCAMGeometry.py:1289 +#: AppObjects/FlatCAMGeometry.py:1290 AppObjects/FlatCAMGeometry.py:1299 +msgid "Iso" +msgstr "Iso" + +#: AppObjects/FlatCAMGeometry.py:123 AppObjects/FlatCAMGeometry.py:515 +#: AppObjects/FlatCAMGeometry.py:911 AppObjects/FlatCAMGerber.py:891 +#: AppObjects/FlatCAMGerber.py:1039 AppTools/ToolCutOut.py:690 +#: AppTools/ToolCutOut.py:886 AppTools/ToolCutOut.py:1046 +msgid "Rough" +msgstr "Rau" + +#: AppObjects/FlatCAMGeometry.py:123 +msgid "Finish" +msgstr "Oberfläche" + +#: AppObjects/FlatCAMGeometry.py:550 +msgid "Add from Tool DB" +msgstr "Werkzeug aus Werkzeugdatenbank hinzufügen" + +#: AppObjects/FlatCAMGeometry.py:930 +msgid "Tool added in Tool Table." +msgstr "Werkzeug in der Werkzeugtabelle hinzugefügt." + +#: AppObjects/FlatCAMGeometry.py:1039 AppObjects/FlatCAMGeometry.py:1048 +msgid "Failed. Select a tool to copy." +msgstr "Fehlgeschlagen. Wählen Sie ein Werkzeug zum Kopieren aus." + +#: AppObjects/FlatCAMGeometry.py:1077 +msgid "Tool was copied in Tool Table." +msgstr "Das Werkzeug wurde in die Werkzeugtabelle kopiert." + +#: AppObjects/FlatCAMGeometry.py:1104 +msgid "Tool was edited in Tool Table." +msgstr "Das Werkzeug wurde in der Werkzeugtabelle bearbeitet." + +#: AppObjects/FlatCAMGeometry.py:1133 AppObjects/FlatCAMGeometry.py:1142 +msgid "Failed. Select a tool to delete." +msgstr "Gescheitert. Wählen Sie ein Werkzeug zum Löschen aus." + +#: AppObjects/FlatCAMGeometry.py:1166 +msgid "Tool was deleted in Tool Table." +msgstr "Werkzeug wurde in der Werkzeugtabelle gelöscht." + +#: AppObjects/FlatCAMGeometry.py:1203 AppObjects/FlatCAMGeometry.py:1212 +msgid "" +"Disabled because the tool is V-shape.\n" +"For V-shape tools the depth of cut is\n" +"calculated from other parameters like:\n" +"- 'V-tip Angle' -> angle at the tip of the tool\n" +"- 'V-tip Dia' -> diameter at the tip of the tool \n" +"- Tool Dia -> 'Dia' column found in the Tool Table\n" +"NB: a value of zero means that Tool Dia = 'V-tip Dia'" +msgstr "" +"Deaktiviert, da das Werkzeug V-förmig ist.\n" +"Bei V-förmigen Werkzeugen beträgt die Schnitttiefe\n" +"berechnet aus anderen Parametern wie:\n" +"- 'V-Spitzenwinkel' -> Winkel an der Spitze des Werkzeugs\n" +"- 'V-Spitze Durchmesser' -> Durchmesser an der Spitze des Werkzeugs\n" +"- Werkzeugdurchmesser -> Spalte 'Durchmesser' in der Werkzeugtabelle\n" +"NB: Ein Wert von Null bedeutet, dass Werkzeugdurchmesser = 'V-Spitze " +"Durchmesser'" + +#: AppObjects/FlatCAMGeometry.py:1697 +msgid "This Geometry can't be processed because it is" +msgstr "Diese Geometrie kann nicht verarbeitet werden, da dies der Fall ist" + +#: AppObjects/FlatCAMGeometry.py:1697 +msgid "geometry" +msgstr "geometrie" + +#: AppObjects/FlatCAMGeometry.py:1738 +msgid "Failed. No tool selected in the tool table ..." +msgstr "Gescheitert. Kein Werkzeug in der Werkzeugtabelle ausgewählt ..." + +#: AppObjects/FlatCAMGeometry.py:1836 AppObjects/FlatCAMGeometry.py:1986 +msgid "" +"Tool Offset is selected in Tool Table but no value is provided.\n" +"Add a Tool Offset or change the Offset Type." +msgstr "" +"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." + +#: AppObjects/FlatCAMGeometry.py:1902 AppObjects/FlatCAMGeometry.py:2048 +msgid "G-Code parsing in progress..." +msgstr "G-Code-Analyse läuft ..." + +#: AppObjects/FlatCAMGeometry.py:1904 AppObjects/FlatCAMGeometry.py:2050 +msgid "G-Code parsing finished..." +msgstr "G-Code-Analyse beendet ..." + +#: AppObjects/FlatCAMGeometry.py:1912 +msgid "Finished G-Code processing" +msgstr "G-Code-Verarbeitung abgeschlossen" + +#: AppObjects/FlatCAMGeometry.py:1914 AppObjects/FlatCAMGeometry.py:2062 +msgid "G-Code processing failed with error" +msgstr "G-Code-Verarbeitung fehlgeschlagen mit Fehler" + +#: AppObjects/FlatCAMGeometry.py:1956 AppTools/ToolSolderPaste.py:1301 +msgid "Cancelled. Empty file, it has no geometry" +msgstr "Abgebrochen. Leere Datei hat keine Geometrie" + +#: AppObjects/FlatCAMGeometry.py:2060 AppObjects/FlatCAMGeometry.py:2227 +msgid "Finished G-Code processing..." +msgstr "Fertige G-Code Verarbeitung ..." + +#: AppObjects/FlatCAMGeometry.py:2079 AppObjects/FlatCAMGeometry.py:2083 +#: AppObjects/FlatCAMGeometry.py:2234 +msgid "CNCjob created" +msgstr "CNCjob erstellt" + +#: AppObjects/FlatCAMGeometry.py:2265 AppObjects/FlatCAMGeometry.py:2274 +#: AppParsers/ParseGerber.py:1866 AppParsers/ParseGerber.py:1876 +msgid "Scale factor has to be a number: integer or float." +msgstr "" +"Der Skalierungsfaktor muss eine Zahl sein: Ganzzahl oder Fließkommazahl." + +#: AppObjects/FlatCAMGeometry.py:2337 +msgid "Geometry Scale done." +msgstr "Geometrie Skalierung fertig." + +#: AppObjects/FlatCAMGeometry.py:2354 AppParsers/ParseGerber.py:1992 +msgid "" +"An (x,y) pair of values are needed. Probable you entered only one value in " +"the Offset field." +msgstr "" +"Ein (x, y) Wertepaar wird benötigt. Wahrscheinlich haben Sie im Feld Offset " +"nur einen Wert eingegeben." + +#: AppObjects/FlatCAMGeometry.py:2410 +msgid "Geometry Offset done." +msgstr "Geometrie Offset fertig." + +#: AppObjects/FlatCAMGeometry.py:2439 +msgid "" +"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 "" +"Das Werkzeugwechsel X, Y Feld in Bearbeiten -> Einstellungen muss im Format " +"(x, y) sein\n" +"Aber jetzt gibt es nur einen Wert, nicht zwei." + +#: AppObjects/FlatCAMGerber.py:494 +msgid "Buffering solid geometry" +msgstr "Festkörpergeometrie puffern" + +#: AppObjects/FlatCAMGerber.py:503 +msgid "Done" +msgstr "Fertig" + +#: AppObjects/FlatCAMGerber.py:529 AppObjects/FlatCAMGerber.py:555 +msgid "Operation could not be done." +msgstr "Operation konnte nicht durchgeführt werden." + +#: AppObjects/FlatCAMGerber.py:572 +msgid "Isolating..." +msgstr "Isolieren ..." + +#: AppObjects/FlatCAMGerber.py:631 +msgid "Click on a polygon to isolate it." +msgstr "Klicken Sie auf ein Plozgon um es zu isolieren." + +#: AppObjects/FlatCAMGerber.py:670 AppObjects/FlatCAMGerber.py:774 +#: AppTools/ToolPaint.py:1515 +msgid "Added polygon" +msgstr "Polygon hinzugefügt" + +#: AppObjects/FlatCAMGerber.py:671 AppObjects/FlatCAMGerber.py:776 +msgid "Click to add next polygon or right click to start isolation." +msgstr "" +"Klicken Sie, um das nächste Polygon hinzuzufügen, oder klicken Sie mit der " +"rechten Maustaste, um den Isolationsvorgang zu beginnen." + +#: AppObjects/FlatCAMGerber.py:683 AppTools/ToolPaint.py:1529 +msgid "Removed polygon" +msgstr "Polygon entfernt" + +# nearly the same as before? What good is this? +#: AppObjects/FlatCAMGerber.py:684 +msgid "Click to add/remove next polygon or right click to start isolation." +msgstr "" +"Klicken Sie, um das nächste Polygon hinzuzufügen oder zu entfernen, oder " +"klicken Sie mit der rechten Maustaste, um den Isolationsvorgang zu beginnen." + +#: AppObjects/FlatCAMGerber.py:689 AppTools/ToolPaint.py:1535 +msgid "No polygon detected under click position." +msgstr "Kein Polygon an der Stelle an die geklickt wurde." + +#: AppObjects/FlatCAMGerber.py:710 AppTools/ToolPaint.py:1564 +msgid "List of single polygons is empty. Aborting." +msgstr "Liste der Einzelpolygone ist leer. Vorgang wird abgebrochen." + +#: AppObjects/FlatCAMGerber.py:779 +msgid "No polygon in selection." +msgstr "Kein Polygon in der Auswahl." + +#: AppObjects/FlatCAMGerber.py:907 AppObjects/FlatCAMGerber.py:986 +#: AppTools/ToolNCC.py:2097 AppTools/ToolNCC.py:3183 AppTools/ToolNCC.py:3562 +msgid "Isolation geometry could not be generated." +msgstr "Isolationsgeometrie konnte nicht generiert werden." + +#: AppObjects/FlatCAMGerber.py:932 AppObjects/FlatCAMGerber.py:1064 +msgid "Isolation geometry created" +msgstr "Isolationsgeometrie erstellt" + +#: AppObjects/FlatCAMGerber.py:941 AppObjects/FlatCAMGerber.py:1071 +msgid "Subtracting Geo" +msgstr "Geo subtrahieren" + +#: AppObjects/FlatCAMGerber.py:1396 +msgid "Plotting Apertures" +msgstr "Plotten Apertures" + +#: AppObjects/FlatCAMObj.py:232 +msgid "Name changed from" +msgstr "Name geändert von" + +#: AppObjects/FlatCAMObj.py:232 +msgid "to" +msgstr "zu" + +#: AppObjects/FlatCAMObj.py:243 +msgid "Offsetting..." +msgstr "Offset hinzufügen ..." + +#: AppObjects/FlatCAMObj.py:257 AppObjects/FlatCAMObj.py:262 +msgid "Scaling could not be executed." +msgstr "Skalierungsaktion wurde nicht ausgeführt." + +#: AppObjects/FlatCAMObj.py:266 AppObjects/FlatCAMObj.py:274 +msgid "Scale done." +msgstr "Skalieren Sie fertig." + +#: AppObjects/FlatCAMObj.py:272 +msgid "Scaling..." +msgstr "Skalierung ..." + +#: AppObjects/FlatCAMObj.py:290 +msgid "Skewing..." +msgstr "Verziehen..." + +#: AppObjects/FlatCAMScript.py:163 +msgid "Script Editor" +msgstr "Script Editor" + +#: AppObjects/ObjectCollection.py:513 +#, python-brace-format +msgid "Object renamed from {old} to {new}" +msgstr "Objekt umbenannt von {old} zu {new}" + +#: AppObjects/ObjectCollection.py:925 AppObjects/ObjectCollection.py:931 +#: AppObjects/ObjectCollection.py:937 AppObjects/ObjectCollection.py:943 +#: AppObjects/ObjectCollection.py:949 AppObjects/ObjectCollection.py:955 +#: App_Main.py:6158 App_Main.py:6164 App_Main.py:6170 App_Main.py:6176 +msgid "selected" +msgstr "ausgewählt" + +#: AppObjects/ObjectCollection.py:986 +msgid "Cause of error" +msgstr "Fehlerursache" + +#: AppObjects/ObjectCollection.py:1187 +msgid "All objects are selected." +msgstr "Alle Objekte werden ausgewählt." + +#: AppObjects/ObjectCollection.py:1197 +msgid "Objects selection is cleared." +msgstr "Die Objektauswahl wird gelöscht." + +#: AppParsers/ParseExcellon.py:315 +msgid "This is GCODE mark" +msgstr "Dies ist die GCODE-Marke" + +#: AppParsers/ParseExcellon.py:432 +msgid "" +"No tool diameter info's. See shell.\n" +"A tool change event: T" +msgstr "" +"Keine Angaben zum Werkzeugdurchmesser. Siehe Shell.\n" +"Ein Werkzeugwechselereignis: T" + +#: AppParsers/ParseExcellon.py:435 +msgid "" +"was found but the Excellon file have no informations regarding the tool " +"diameters therefore the application will try to load it by using some 'fake' " +"diameters.\n" +"The user needs to edit the resulting Excellon object and change the " +"diameters to reflect the real diameters." +msgstr "" +"wurde gefunden, aber die Excellon-Datei enthält keine Informationen zu den " +"Werkzeugdurchmessern. Daher wird die Anwendung versuchen, diese mit Hilfe " +"einiger gefälschter Durchmesser zu laden.\n" +"Der Benutzer muss das resultierende Excellon-Objekt bearbeiten und die " +"Durchmesser so ändern, dass sie den tatsächlichen Durchmessern entsprechen." + +#: AppParsers/ParseExcellon.py:899 +msgid "" +"Excellon Parser error.\n" +"Parsing Failed. Line" +msgstr "" +"Excellon-Parser-Fehler.\n" +"Analyse fehlgeschlagen. Linie" + +#: AppParsers/ParseExcellon.py:981 +msgid "" +"Excellon.create_geometry() -> a drill location was skipped due of not having " +"a tool associated.\n" +"Check the resulting GCode." +msgstr "" +"Excellon.create_geometry () -> Eine Bohrposition wurde übersprungen, da kein " +"Werkzeug zugeordnet ist.\n" +"Überprüfen Sie den resultierenden GCode." + +#: AppParsers/ParseFont.py:303 +msgid "Font not supported, try another one." +msgstr "Schriftart wird nicht unterstützt, versuchen Sie es mit einer anderen." + +#: AppParsers/ParseGerber.py:425 +msgid "Gerber processing. Parsing" +msgstr "Gerber-Verarbeitung. Parsing" + +#: AppParsers/ParseGerber.py:425 AppParsers/ParseHPGL2.py:181 +msgid "lines" +msgstr "Linien" + +#: AppParsers/ParseGerber.py:1001 AppParsers/ParseGerber.py:1101 +#: AppParsers/ParseHPGL2.py:274 AppParsers/ParseHPGL2.py:288 +#: AppParsers/ParseHPGL2.py:307 AppParsers/ParseHPGL2.py:331 +#: AppParsers/ParseHPGL2.py:366 +msgid "Coordinates missing, line ignored" +msgstr "Koordinaten fehlen, Zeile wird ignoriert" + +#: AppParsers/ParseGerber.py:1003 AppParsers/ParseGerber.py:1103 +msgid "GERBER file might be CORRUPT. Check the file !!!" +msgstr "Die GERBER-Datei könnte CORRUPT sein. Überprüfen Sie die Datei !!!" + +#: AppParsers/ParseGerber.py:1057 +msgid "" +"Region does not have enough points. File will be processed but there are " +"parser errors. Line number" +msgstr "" +"Region hat nicht genug Punkte. Die Datei wird verarbeitet, es treten jedoch " +"Parserfehler auf. Linien Nummer" + +#: AppParsers/ParseGerber.py:1487 AppParsers/ParseHPGL2.py:401 +msgid "Gerber processing. Joining polygons" +msgstr "Gerber-Verarbeitung. Polygone verbinden" + +#: AppParsers/ParseGerber.py:1504 +msgid "Gerber processing. Applying Gerber polarity." +msgstr "Gerber-Verarbeitung. Anwenden der Gerber-Polarität." + +#: AppParsers/ParseGerber.py:1564 +msgid "Gerber Line" +msgstr "Gerber Linie" + +#: AppParsers/ParseGerber.py:1564 +msgid "Gerber Line Content" +msgstr "Gerber-Zeileninhalt" + +#: AppParsers/ParseGerber.py:1566 +msgid "Gerber Parser ERROR" +msgstr "Gerber-Parser FEHLER" + +#: AppParsers/ParseGerber.py:1956 +msgid "Gerber Scale done." +msgstr "Gerber-Skalierung erfolgt." + +#: AppParsers/ParseGerber.py:2048 +msgid "Gerber Offset done." +msgstr "Gerber Offset fertig." + +#: AppParsers/ParseGerber.py:2124 +msgid "Gerber Mirror done." +msgstr "Gerber Spiegel fertig." + +#: AppParsers/ParseGerber.py:2198 +msgid "Gerber Skew done." +msgstr "Gerber-Versatz fertig." + +#: AppParsers/ParseGerber.py:2260 +msgid "Gerber Rotate done." +msgstr "Gerber drehen fertig." + +#: AppParsers/ParseGerber.py:2417 +msgid "Gerber Buffer done." +msgstr "Gerber Buffer fertig." + +#: AppParsers/ParseHPGL2.py:181 +msgid "HPGL2 processing. Parsing" +msgstr "HPGL2 -Verarbeitung. Parsing" + +#: AppParsers/ParseHPGL2.py:413 +msgid "HPGL2 Line" +msgstr "HPGL2-Linie" + +#: AppParsers/ParseHPGL2.py:413 +msgid "HPGL2 Line Content" +msgstr "HPGL2-Zeileninhalt" + +#: AppParsers/ParseHPGL2.py:414 +msgid "HPGL2 Parser ERROR" +msgstr "HPGL2 -Parser FEHLER" + +#: AppProcess.py:172 +msgid "processes running." +msgstr "laufende Prozesse." + +#: AppTools/ToolAlignObjects.py:32 +msgid "Align Objects" +msgstr "Objekte ausrichten" + +#: AppTools/ToolAlignObjects.py:61 +msgid "MOVING object" +msgstr "BEWEGLICHES Objekt" + +#: AppTools/ToolAlignObjects.py:65 +msgid "" +"Specify the type of object to be aligned.\n" +"It can be of type: Gerber or Excellon.\n" +"The selection here decide the type of objects that will be\n" +"in the Object combobox." +msgstr "" +"Geben Sie den Objekttyp an, der ausgerichtet werden soll.\n" +"Es kann vom Typ sein: Gerber oder Excellon.\n" +"Die Auswahl hier entscheidet über die Art der Objekte, die sein werden\n" +"in der Objekt-Combobox." + +#: AppTools/ToolAlignObjects.py:86 +msgid "Object to be aligned." +msgstr "Zu ausrichtendes Objekt." + +#: AppTools/ToolAlignObjects.py:98 +msgid "TARGET object" +msgstr "ZIEL-Objekt" + +#: AppTools/ToolAlignObjects.py:100 +msgid "" +"Specify the type of object to be aligned to.\n" +"It can be of type: Gerber or Excellon.\n" +"The selection here decide the type of objects that will be\n" +"in the Object combobox." +msgstr "" +"Geben Sie den Objekttyp an, an dem ausgerichtet werden soll.\n" +"Es kann vom Typ sein: Gerber oder Excellon.\n" +"Die Auswahl hier entscheidet über die Art der Objekte, die sein werden\n" +"in der Objekt-Combobox." + +#: AppTools/ToolAlignObjects.py:122 +msgid "Object to be aligned to. Aligner." +msgstr "Objekt, an dem ausgerichtet werden soll. Aligner." + +#: AppTools/ToolAlignObjects.py:135 +msgid "Alignment Type" +msgstr "AusrichtungstypAusrichtung" + +#: AppTools/ToolAlignObjects.py:137 +msgid "" +"The type of alignment can be:\n" +"- Single Point -> it require a single point of sync, the action will be a " +"translation\n" +"- Dual Point -> it require two points of sync, the action will be " +"translation followed by rotation" +msgstr "" +"Die Art der Ausrichtung kann sein:\n" +"- Einzelpunkt -> Es ist ein einzelner Synchronisierungspunkt erforderlich. " +"Die Aktion ist eine Übersetzung\n" +"- Doppelpunkt -> Es sind zwei Synchronisierungspunkte erforderlich. Die " +"Aktion wird verschoben und anschließend gedreht" + +#: AppTools/ToolAlignObjects.py:143 +msgid "Single Point" +msgstr "Einziger Punkt" + +#: AppTools/ToolAlignObjects.py:144 +msgid "Dual Point" +msgstr "Doppelpunkt" + +#: AppTools/ToolAlignObjects.py:159 +msgid "Align Object" +msgstr "Objekt ausrichten" + +#: AppTools/ToolAlignObjects.py:161 +msgid "" +"Align the specified object to the aligner object.\n" +"If only one point is used then it assumes translation.\n" +"If tho points are used it assume translation and rotation." +msgstr "" +"Richten Sie das angegebene Objekt am Aligner-Objekt aus.\n" +"Wenn nur ein Punkt verwendet wird, wird eine Übersetzung vorausgesetzt.\n" +"Wenn diese Punkte verwendet werden, wird eine Translation und Rotation " +"angenommen." + +#: AppTools/ToolAlignObjects.py:176 AppTools/ToolCalculators.py:246 +#: AppTools/ToolCalibration.py:683 AppTools/ToolCopperThieving.py:484 +#: AppTools/ToolCorners.py:173 AppTools/ToolCutOut.py:362 +#: AppTools/ToolDblSided.py:471 AppTools/ToolEtchCompensation.py:136 +#: AppTools/ToolExtractDrills.py:310 AppTools/ToolFiducials.py:318 +#: AppTools/ToolFilm.py:503 AppTools/ToolInvertGerber.py:140 +#: AppTools/ToolNCC.py:612 AppTools/ToolOptimal.py:237 +#: AppTools/ToolPaint.py:555 AppTools/ToolPanelize.py:280 +#: AppTools/ToolPunchGerber.py:339 AppTools/ToolQRCode.py:314 +#: AppTools/ToolRulesCheck.py:516 AppTools/ToolSolderPaste.py:473 +#: AppTools/ToolSub.py:176 AppTools/ToolTransform.py:398 +msgid "Reset Tool" +msgstr "Reset Werkzeug" + +#: AppTools/ToolAlignObjects.py:178 AppTools/ToolCalculators.py:248 +#: AppTools/ToolCalibration.py:685 AppTools/ToolCopperThieving.py:486 +#: AppTools/ToolCorners.py:175 AppTools/ToolCutOut.py:364 +#: AppTools/ToolDblSided.py:473 AppTools/ToolEtchCompensation.py:138 +#: AppTools/ToolExtractDrills.py:312 AppTools/ToolFiducials.py:320 +#: AppTools/ToolFilm.py:505 AppTools/ToolInvertGerber.py:142 +#: AppTools/ToolNCC.py:614 AppTools/ToolOptimal.py:239 +#: AppTools/ToolPaint.py:557 AppTools/ToolPanelize.py:282 +#: AppTools/ToolPunchGerber.py:341 AppTools/ToolQRCode.py:316 +#: AppTools/ToolRulesCheck.py:518 AppTools/ToolSolderPaste.py:475 +#: AppTools/ToolSub.py:178 AppTools/ToolTransform.py:400 +msgid "Will reset the tool parameters." +msgstr "Wird die Werkzeugeinstellungen zurücksetzen." + +#: AppTools/ToolAlignObjects.py:244 +msgid "Align Tool" +msgstr "Ausrichten Werkzeug" + +#: AppTools/ToolAlignObjects.py:289 +msgid "There is no aligned FlatCAM object selected..." +msgstr "Es ist kein ausgerichtetes FlatCAM-Objekt ausgewählt ..." + +#: AppTools/ToolAlignObjects.py:299 +msgid "There is no aligner FlatCAM object selected..." +msgstr "Es ist kein Aligner FlatCAM-Objekt ausgewählt ..." + +#: AppTools/ToolAlignObjects.py:325 AppTools/ToolAlignObjects.py:385 +msgid "First Point" +msgstr "Erster Punkt" + +#: AppTools/ToolAlignObjects.py:325 AppTools/ToolAlignObjects.py:400 +msgid "Click on the START point." +msgstr "Klicken Sie auf den START-Punkt." + +#: AppTools/ToolAlignObjects.py:380 AppTools/ToolCalibration.py:920 +msgid "Cancelled by user request." +msgstr "Auf Benutzerwunsch storniert." + +#: AppTools/ToolAlignObjects.py:385 AppTools/ToolAlignObjects.py:407 +msgid "Click on the DESTINATION point." +msgstr "Klicken Sie auf den Punkt ZIEL." + +#: AppTools/ToolAlignObjects.py:385 AppTools/ToolAlignObjects.py:400 +#: AppTools/ToolAlignObjects.py:407 +msgid "Or right click to cancel." +msgstr "Oder klicken Sie mit der rechten Maustaste, um abzubrechen." + +#: AppTools/ToolAlignObjects.py:400 AppTools/ToolAlignObjects.py:407 +#: AppTools/ToolFiducials.py:111 +msgid "Second Point" +msgstr "Zweiter Punkt" + +#: AppTools/ToolCalculators.py:24 +msgid "Calculators" +msgstr "Rechner" + +#: AppTools/ToolCalculators.py:26 +msgid "Units Calculator" +msgstr "Einheitenrechner" + +#: AppTools/ToolCalculators.py:70 +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" + +#: AppTools/ToolCalculators.py:75 +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" + +#: AppTools/ToolCalculators.py:111 +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." + +#: AppTools/ToolCalculators.py:120 +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." + +#: AppTools/ToolCalculators.py:128 +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<." + +#: AppTools/ToolCalculators.py:139 AppTools/ToolCalculators.py:235 +msgid "Calculate" +msgstr "Berechnung" + +#: AppTools/ToolCalculators.py:142 +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 die Schnitttiefe Z oder den effektiven " +"Werkzeugdurchmesser.\n" +" je nachdem was gewünscht wird und was bekannt ist. " + +#: AppTools/ToolCalculators.py:205 +msgid "Current Value" +msgstr "Aktueller Wert" + +#: AppTools/ToolCalculators.py:212 +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 einstellen. In Ampere." + +#: AppTools/ToolCalculators.py:216 +msgid "Time" +msgstr "Zeit" + +#: AppTools/ToolCalculators.py:223 +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." + +#: AppTools/ToolCalculators.py:238 +msgid "" +"Calculate the current intensity value and the procedure time,\n" +"depending on the parameters above" +msgstr "" +"Berechnen Sie den aktuellen Intensitätswert und die Eingriffszeit,\n" +"abhängig von den obigen Parametern" + +#: AppTools/ToolCalculators.py:299 +msgid "Calc. Tool" +msgstr "Rechner-Tool" + +#: AppTools/ToolCalibration.py:67 +msgid "GCode Parameters" +msgstr "GCode-Parameter" + +#: AppTools/ToolCalibration.py:69 +msgid "Parameters used when creating the GCode in this tool." +msgstr "Verwendete Parameter zum Erzeugen des GCodes mit diesem Wwerkzeug." + +#: AppTools/ToolCalibration.py:173 +msgid "STEP 1: Acquire Calibration Points" +msgstr "Schritt 1: Kalibrierungspunkte erzeugen" + +#: AppTools/ToolCalibration.py:175 +msgid "" +"Pick four points by clicking on canvas.\n" +"Those four points should be in the four\n" +"(as much as possible) corners of the object." +msgstr "" +"Wählen Sie vier Punkte aus, indem Sie auf die Leinwand klicken.\n" +"Diese vier Punkte sollten in den vier sein\n" +"(so viel wie möglich) Ecken des Objekts." + +#: AppTools/ToolCalibration.py:193 AppTools/ToolFilm.py:71 +#: AppTools/ToolImage.py:54 AppTools/ToolPanelize.py:77 +#: AppTools/ToolProperties.py:177 +msgid "Object Type" +msgstr "Objekttyp" + +#: AppTools/ToolCalibration.py:210 +msgid "Source object selection" +msgstr "Auswahl des Quellobjekts" + +#: AppTools/ToolCalibration.py:212 +msgid "FlatCAM Object to be used as a source for reference points." +msgstr "Das FlatCAM-Objekt, das als Referenzpunkt verwendet werden soll." + +#: AppTools/ToolCalibration.py:218 +msgid "Calibration Points" +msgstr "Kalibrierungspunkte" + +#: AppTools/ToolCalibration.py:220 +msgid "" +"Contain the expected calibration points and the\n" +"ones measured." +msgstr "" +"Enthalten die erwarteten Kalibrierungspunkte sowie\n" +"die gemessenen." + +#: AppTools/ToolCalibration.py:235 AppTools/ToolSub.py:76 +#: AppTools/ToolSub.py:131 +msgid "Target" +msgstr "Ziel" + +#: AppTools/ToolCalibration.py:236 +msgid "Found Delta" +msgstr "Gefundener Unterschied" + +#: AppTools/ToolCalibration.py:248 +msgid "Bot Left X" +msgstr "Unten links X" + +#: AppTools/ToolCalibration.py:257 +msgid "Bot Left Y" +msgstr "Unten links Y" + +#: AppTools/ToolCalibration.py:275 +msgid "Bot Right X" +msgstr "Unten rechts X" + +#: AppTools/ToolCalibration.py:285 +msgid "Bot Right Y" +msgstr "Unten rechts Y" + +#: AppTools/ToolCalibration.py:300 +msgid "Top Left X" +msgstr "Oben links X" + +#: AppTools/ToolCalibration.py:309 +msgid "Top Left Y" +msgstr "Oben links Y" + +#: AppTools/ToolCalibration.py:324 +msgid "Top Right X" +msgstr "Oben rechts X" + +#: AppTools/ToolCalibration.py:334 +msgid "Top Right Y" +msgstr "Oben rechts Y" + +#: AppTools/ToolCalibration.py:367 +msgid "Get Points" +msgstr "Punkte einholen" + +#: AppTools/ToolCalibration.py:369 +msgid "" +"Pick four points by clicking on canvas if the source choice\n" +"is 'free' or inside the object geometry if the source is 'object'.\n" +"Those four points should be in the four squares of\n" +"the object." +msgstr "" +"Wählen Sie vier Punkte indem Sie auf die Leinwand klicken (Freier Modus).\n" +"Oder wählen Sie ein Objekt (Objekt Modus)\n" +"Diese vier Punkte sollten in vier unterschiedlichen Quadranten des Objektes " +"sein." + +#: AppTools/ToolCalibration.py:390 +msgid "STEP 2: Verification GCode" +msgstr "Schritt 2: Überprüfung des GCodes" + +#: AppTools/ToolCalibration.py:392 AppTools/ToolCalibration.py:405 +msgid "" +"Generate GCode file to locate and align the PCB by using\n" +"the four points acquired above.\n" +"The points sequence is:\n" +"- first point -> set the origin\n" +"- second point -> alignment point. Can be: top-left or bottom-right.\n" +"- third point -> check point. Can be: top-left or bottom-right.\n" +"- forth point -> final verification point. Just for evaluation." +msgstr "" +"Erstellen Sie eine GCode-Datei, um die Leiterplatte mithilfe von zu " +"lokalisieren und auszurichten\n" +"die vier oben erworbenen Punkte.\n" +"Die Punktesequenz ist:\n" +"- erster Punkt -> Ursprung einstellen\n" +"- zweiter Punkt -> Ausrichtungspunkt. Kann sein: oben links oder unten " +"rechts.\n" +"- dritter Punkt -> Kontrollpunkt. Kann sein: oben links oder unten rechts.\n" +"- vierter Punkt -> letzter Verifizierungspunkt. Nur zur Bewertung." + +#: AppTools/ToolCalibration.py:403 AppTools/ToolSolderPaste.py:348 +msgid "Generate GCode" +msgstr "GCode generieren" + +#: AppTools/ToolCalibration.py:429 +msgid "STEP 3: Adjustments" +msgstr "Schritt 3: Anpassungen" + +#: AppTools/ToolCalibration.py:431 AppTools/ToolCalibration.py:440 +msgid "" +"Calculate Scale and Skew factors based on the differences (delta)\n" +"found when checking the PCB pattern. The differences must be filled\n" +"in the fields Found (Delta)." +msgstr "" +"Berechne die Skalierungs und Verzerrungsfaktoren basierend auf dem Delta\n" +"das bei der Platinenüberprüfung gefunden wurde. Dieses Delta muss den " +"Feldern\n" +"eingetragen warden." + +#: AppTools/ToolCalibration.py:438 +msgid "Calculate Factors" +msgstr "Berechne Faktoren" + +#: AppTools/ToolCalibration.py:460 +msgid "STEP 4: Adjusted GCode" +msgstr "Schritt 4 Angepasster GCode" + +#: AppTools/ToolCalibration.py:462 +msgid "" +"Generate verification GCode file adjusted with\n" +"the factors above." +msgstr "" +"Erzeuge den GCode mit den zuvor gefundenen\n" +"Faktoren." + +#: AppTools/ToolCalibration.py:467 +msgid "Scale Factor X:" +msgstr "Skalierungsfaktor X:" + +#: AppTools/ToolCalibration.py:479 +msgid "Scale Factor Y:" +msgstr "Skalierungsfaktor Y:" + +#: AppTools/ToolCalibration.py:491 +msgid "Apply Scale Factors" +msgstr "Skalierungen anwenden" + +#: AppTools/ToolCalibration.py:493 +msgid "Apply Scale factors on the calibration points." +msgstr "Anwenden der Skalierungsfaktoren auf die Kalibrierungspunkte." + +#: AppTools/ToolCalibration.py:503 +msgid "Skew Angle X:" +msgstr "Verzerrungs-Winkel X:" + +#: AppTools/ToolCalibration.py:516 +msgid "Skew Angle Y:" +msgstr "Verzerrungs-Winkel Y:" + +#: AppTools/ToolCalibration.py:529 +msgid "Apply Skew Factors" +msgstr "Schrägstellung anwenden" + +#: AppTools/ToolCalibration.py:531 +msgid "Apply Skew factors on the calibration points." +msgstr "Anwenden der Verzerrungswinkel auf die Bezugspunkte." + +#: AppTools/ToolCalibration.py:600 +msgid "Generate Adjusted GCode" +msgstr "Angepassten Überprüfungs-GCode generieren" + +#: AppTools/ToolCalibration.py:602 +msgid "" +"Generate verification GCode file adjusted with\n" +"the factors set above.\n" +"The GCode parameters can be readjusted\n" +"before clicking this button." +msgstr "" +"Bestätigungs-GCode-Datei erstellen angepasst mit\n" +"die oben genannten Faktoren.\n" +"Die GCode-Parameter können neu eingestellt werden\n" +"bevor Sie auf diese Schaltfläche klicken." + +#: AppTools/ToolCalibration.py:623 +msgid "STEP 5: Calibrate FlatCAM Objects" +msgstr "Schritt 5: Kalibrieren der FlatCAM Objekte" + +#: AppTools/ToolCalibration.py:625 +msgid "" +"Adjust the FlatCAM objects\n" +"with the factors determined and verified above." +msgstr "" +"Anpassen der FlatCAM Objekte\n" +"mit den zuvor bestimmten und überprüften Faktoren." + +#: AppTools/ToolCalibration.py:637 +msgid "Adjusted object type" +msgstr "Angepasster Objekttyp" + +#: AppTools/ToolCalibration.py:638 +msgid "Type of the FlatCAM Object to be adjusted." +msgstr "Art des FlatCAM Objektes das angepasst wird." + +#: AppTools/ToolCalibration.py:651 +msgid "Adjusted object selection" +msgstr "Objektauswahl angepasst" + +#: AppTools/ToolCalibration.py:653 +msgid "The FlatCAM Object to be adjusted." +msgstr "Das FlatCAM Objekt das angepasst werden muss." + +#: AppTools/ToolCalibration.py:660 +msgid "Calibrate" +msgstr "Kalibrieren" + +#: AppTools/ToolCalibration.py:662 +msgid "" +"Adjust (scale and/or skew) the objects\n" +"with the factors determined above." +msgstr "" +"Anpassen (Skalieren und/oder Verzerren) der Objekte\n" +"anhand der zuvor gefundenen Faktoren." + +#: AppTools/ToolCalibration.py:770 AppTools/ToolCalibration.py:771 +msgid "Origin" +msgstr "Ursprung" + +#: AppTools/ToolCalibration.py:800 +msgid "Tool initialized" +msgstr "Werkzeug eingerichtet" + +#: AppTools/ToolCalibration.py:838 +msgid "There is no source FlatCAM object selected..." +msgstr "Es is kein FlatCAM Objekt ausgewählt." + +#: AppTools/ToolCalibration.py:859 +msgid "Get First calibration point. Bottom Left..." +msgstr "Lese ersten Kalibrierungspunkt (Unten Links)" + +#: AppTools/ToolCalibration.py:926 +msgid "Get Second calibration point. Bottom Right (Top Left)..." +msgstr "Zweiter Kalibrierungspunkt abrufen. Unten rechts (oben links) ..." + +#: AppTools/ToolCalibration.py:930 +msgid "Get Third calibration point. Top Left (Bottom Right)..." +msgstr "" +"Holen Sie sich den dritten Kalibrierungspunkt. Oben links unten rechts)..." + +#: AppTools/ToolCalibration.py:934 +msgid "Get Forth calibration point. Top Right..." +msgstr "Lese vierten Kalibrierungspunkt (Oben Rechts)" + +#: AppTools/ToolCalibration.py:938 +msgid "Done. All four points have been acquired." +msgstr "Erledigt, alle vier Punkte wurden gelesen." + +#: AppTools/ToolCalibration.py:969 +msgid "Verification GCode for FlatCAM Calibration Tool" +msgstr "Überprüfungs GCode des FlatCAM Kalibrierungstools" + +#: AppTools/ToolCalibration.py:981 AppTools/ToolCalibration.py:1067 +msgid "Gcode Viewer" +msgstr "GCode Anzeige" + +#: AppTools/ToolCalibration.py:997 +msgid "Cancelled. Four points are needed for GCode generation." +msgstr "Abgebrochen. Es werden vier Punkte zur GCode Erzeugung benötigt." + +#: AppTools/ToolCalibration.py:1253 AppTools/ToolCalibration.py:1349 +msgid "There is no FlatCAM object selected..." +msgstr "Es ist kein FlatCAM Objekt ausgewählt." + +#: AppTools/ToolCopperThieving.py:76 AppTools/ToolFiducials.py:261 +msgid "Gerber Object to which will be added a copper thieving." +msgstr "Dem Gerber Objekt wird ein Copper Thieving hinzugefügt." + +# Double +#: AppTools/ToolCopperThieving.py:98 +msgid "" +"This set the distance between the copper thieving components\n" +"(the polygon fill may be split in multiple polygons)\n" +"and the copper traces in the Gerber file." +msgstr "" +"Diese Auswahl definiert den Abstand zwischen den \"Copper Thieving\" " +"Komponenten.\n" +"und den Kupferverbindungen im Gerber File (möglicherweise wird hierbei ein " +"Polygon\n" +"in mehrere aufgeteilt." + +# Double +#: AppTools/ToolCopperThieving.py:131 +msgid "" +"- 'Itself' - the copper thieving extent is based on the object extent.\n" +"- 'Area Selection' - left mouse click to start selection of the area to be " +"filled.\n" +"- 'Reference Object' - will do copper thieving within the area specified by " +"another object." +msgstr "" +"- 'Selbst' - die 'Copper Thieving' Ausdehnung basiert auf der " +"Objektausdehnung.\n" +"- 'Bereichsauswahl' - Klicken Sie mit der linken Maustaste, um den zu " +"füllenden Bereich auszuwählen.\n" +"- 'Referenzobjekt' - 'Copper Thieving' innerhalb des von einem anderen " +"Objekt angegebenen Bereichs." + +#: AppTools/ToolCopperThieving.py:138 AppTools/ToolNCC.py:552 +#: AppTools/ToolPaint.py:495 +msgid "Ref. Type" +msgstr "Ref. Typ" + +#: AppTools/ToolCopperThieving.py:140 +msgid "" +"The type of FlatCAM object to be used as copper thieving reference.\n" +"It can be Gerber, Excellon or Geometry." +msgstr "" +"Der Typ des FlatCAM-Objekts, das Copper Thieving-Referenz verwendet werden " +"soll.\n" +"Es kann Gerber, Excellon oder Geometry sein." + +#: AppTools/ToolCopperThieving.py:149 AppTools/ToolNCC.py:562 +#: AppTools/ToolPaint.py:505 +msgid "Ref. Object" +msgstr "Ref. Objekt" + +#: AppTools/ToolCopperThieving.py:151 AppTools/ToolNCC.py:564 +#: AppTools/ToolPaint.py:507 +msgid "The FlatCAM object to be used as non copper clearing reference." +msgstr "" +"Das FlatCAM-Objekt, das als Nicht-Kupfer-Clearing-Referenz verwendet werden " +"soll." + +# Double +#: AppTools/ToolCopperThieving.py:327 +msgid "Insert Copper thieving" +msgstr "'Coper Thieving' einsetzen" + +# Double +#: AppTools/ToolCopperThieving.py:329 +msgid "" +"Will add a polygon (may be split in multiple parts)\n" +"that will surround the actual Gerber traces at a certain distance." +msgstr "" +"Fügt ein Polygon hinzu (kann in mehrere Teile geteilt werden)\n" +"das wird die eigentlichen Gerber-Spuren in einem gewissen Abstand umgeben." + +# Double +#: AppTools/ToolCopperThieving.py:388 +msgid "Insert Robber Bar" +msgstr "'Robber Bar' einsetzen" + +# Double +#: AppTools/ToolCopperThieving.py:390 +msgid "" +"Will add a polygon with a defined thickness\n" +"that will surround the actual Gerber object\n" +"at a certain distance.\n" +"Required when doing holes pattern plating." +msgstr "" +"Fügt ein Polygon mit einer definierten Dicke hinzu\n" +"das wird das eigentliche Gerber-Objekt umgeben\n" +"in einem bestimmten Abstand.\n" +"Erforderlich für die Lochmusterbeschichtung." + +#: AppTools/ToolCopperThieving.py:414 +msgid "Select Soldermask object" +msgstr "Lötmaskenobjekt auswählen" + +#: AppTools/ToolCopperThieving.py:416 +msgid "" +"Gerber Object with the soldermask.\n" +"It will be used as a base for\n" +"the pattern plating mask." +msgstr "" +"Das Gerber Objekt mit der Lötmaske\n" +"Wird als Basis verwendet." + +#: AppTools/ToolCopperThieving.py:445 +msgid "Plated area" +msgstr "Beschichtetes Areal" + +#: AppTools/ToolCopperThieving.py:447 +msgid "" +"The area to be plated by pattern plating.\n" +"Basically is made from the openings in the plating mask.\n" +"\n" +"<> - the calculated area is actually a bit larger\n" +"due of the fact that the soldermask openings are by design\n" +"a bit larger than the copper pads, and this area is\n" +"calculated from the soldermask openings." +msgstr "" +"Das zu beschichtende Areal.\n" +"Generell wird es durch die Öffnungen in der Beschichtungsmaske erzeugt.\n" +"\n" +"ACHTUNG: das berechnete Areal ist etwas größer da die Lötmaskenöffnungen\n" +"etwas größer als die Pads sind, und dieses Areal aus der Lötmaske berechnet " +"wird." + +#: AppTools/ToolCopperThieving.py:458 +msgid "mm" +msgstr "mm" + +#: AppTools/ToolCopperThieving.py:460 +msgid "in" +msgstr "in" + +#: AppTools/ToolCopperThieving.py:467 +msgid "Generate pattern plating mask" +msgstr "Generieren der Beschichtungsmaske" + +#: AppTools/ToolCopperThieving.py:469 +msgid "" +"Will add to the soldermask gerber geometry\n" +"the geometries of the copper thieving and/or\n" +"the robber bar if those were generated." +msgstr "" +"Wird die Lötmaske des Copper Thivings und/oder der \n" +"Robber Bar zu der Gerber Geometrie hinzufügen, sofern\n" +"diese erzeugt worden sind." + +#: AppTools/ToolCopperThieving.py:625 AppTools/ToolCopperThieving.py:650 +msgid "Lines Grid works only for 'itself' reference ..." +msgstr "Schraffur geht nur bei \"Selbst\" Referenz ..." + +#: AppTools/ToolCopperThieving.py:636 +msgid "Solid fill selected." +msgstr "Vollständige Füllung gewählt." + +#: AppTools/ToolCopperThieving.py:641 +msgid "Dots grid fill selected." +msgstr "Punktmusterfüllung gewählt." + +#: AppTools/ToolCopperThieving.py:646 +msgid "Squares grid fill selected." +msgstr "Quadratfüllung gewählt." + +#: AppTools/ToolCopperThieving.py:667 AppTools/ToolCopperThieving.py:749 +#: AppTools/ToolCopperThieving.py:1351 AppTools/ToolCorners.py:251 +#: AppTools/ToolDblSided.py:657 AppTools/ToolExtractDrills.py:436 +#: AppTools/ToolFiducials.py:467 AppTools/ToolFiducials.py:744 +#: AppTools/ToolOptimal.py:342 AppTools/ToolPunchGerber.py:512 +#: AppTools/ToolQRCode.py:426 +msgid "There is no Gerber object loaded ..." +msgstr "Es ist kein Gerber-Objekt geladen ..." + +#: AppTools/ToolCopperThieving.py:680 AppTools/ToolCopperThieving.py:1279 +msgid "Append geometry" +msgstr "Geometrie angehängt" + +#: AppTools/ToolCopperThieving.py:724 AppTools/ToolCopperThieving.py:1312 +#: AppTools/ToolCopperThieving.py:1465 +msgid "Append source file" +msgstr "Fügen Sie die Quelldatei an" + +# Don`t know what a Copper Thieving Tool would do hence hard to translate +#: AppTools/ToolCopperThieving.py:732 AppTools/ToolCopperThieving.py:1320 +msgid "Copper Thieving Tool done." +msgstr "'Copper Thieving' Werkzeug fertig." + +#: AppTools/ToolCopperThieving.py:759 AppTools/ToolCopperThieving.py:792 +#: AppTools/ToolCutOut.py:519 AppTools/ToolCutOut.py:724 +#: AppTools/ToolEtchCompensation.py:208 AppTools/ToolInvertGerber.py:208 +#: AppTools/ToolNCC.py:1599 AppTools/ToolNCC.py:1641 AppTools/ToolNCC.py:1670 +#: AppTools/ToolPaint.py:1473 AppTools/ToolPanelize.py:423 +#: AppTools/ToolPanelize.py:437 AppTools/ToolSub.py:294 AppTools/ToolSub.py:307 +#: AppTools/ToolSub.py:498 AppTools/ToolSub.py:513 +#: tclCommands/TclCommandCopperClear.py:97 tclCommands/TclCommandPaint.py:99 +msgid "Could not retrieve object" +msgstr "Objekt konnte nicht abgerufen werden" + +#: AppTools/ToolCopperThieving.py:769 AppTools/ToolNCC.py:1649 Common.py:200 +msgid "Click the start point of the area." +msgstr "Klicken Sie auf den Startpunkt des Bereichs." + +#: AppTools/ToolCopperThieving.py:820 +msgid "Click the end point of the filling area." +msgstr "Klicken Sie auf den Endpunkt des Ausfüllbereichs." + +#: AppTools/ToolCopperThieving.py:826 AppTools/ToolNCC.py:1711 +#: AppTools/ToolNCC.py:1763 AppTools/ToolPaint.py:1605 +#: AppTools/ToolPaint.py:1656 Common.py:256 Common.py:356 +msgid "Zone added. Click to start adding next zone or right click to finish." +msgstr "" +"Zone hinzugefügt. Klicken Sie, um die nächste Zone hinzuzufügen, oder " +"klicken Sie mit der rechten Maustaste, um den Vorgang abzuschließen." + +#: AppTools/ToolCopperThieving.py:948 AppTools/ToolCopperThieving.py:952 +#: AppTools/ToolCopperThieving.py:1013 +msgid "Thieving" +msgstr "Diebstahl" + +#: AppTools/ToolCopperThieving.py:959 +msgid "Copper Thieving Tool started. Reading parameters." +msgstr "Copper Thieving Tool gestartet. Parameter lesen." + +#: AppTools/ToolCopperThieving.py:984 +msgid "Copper Thieving Tool. Preparing isolation polygons." +msgstr "Copper Thieving-Tool. Vorbereitung von isolierenden Polygonen." + +#: AppTools/ToolCopperThieving.py:1029 +msgid "Copper Thieving Tool. Preparing areas to fill with copper." +msgstr "Copper Thieving Tool: Areale zur Kupferfüllung vorbereiten." + +#: AppTools/ToolCopperThieving.py:1040 AppTools/ToolOptimal.py:349 +#: AppTools/ToolPanelize.py:810 AppTools/ToolRulesCheck.py:1127 +msgid "Working..." +msgstr "Arbeiten..." + +#: AppTools/ToolCopperThieving.py:1067 +msgid "Geometry not supported for bounding box" +msgstr "Geometrie für Umriss nicht unterstützt" + +#: AppTools/ToolCopperThieving.py:1073 AppTools/ToolNCC.py:1942 +#: AppTools/ToolNCC.py:1997 AppTools/ToolNCC.py:3038 AppTools/ToolPaint.py:3385 +msgid "No object available." +msgstr "Kein Objekt vorhanden." + +#: AppTools/ToolCopperThieving.py:1110 AppTools/ToolNCC.py:1967 +#: AppTools/ToolNCC.py:2020 AppTools/ToolNCC.py:3080 +msgid "The reference object type is not supported." +msgstr "Der Referenzobjekttyp wird nicht unterstützt." + +#: AppTools/ToolCopperThieving.py:1115 +msgid "Copper Thieving Tool. Appending new geometry and buffering." +msgstr "Copper Thieving Tool. Füge neue Geometrie an und puffere sie." + +#: AppTools/ToolCopperThieving.py:1131 +msgid "Create geometry" +msgstr "Geometrie erstellen" + +#: AppTools/ToolCopperThieving.py:1331 AppTools/ToolCopperThieving.py:1335 +msgid "P-Plating Mask" +msgstr "P-Beschichtungsmaske" + +#: AppTools/ToolCopperThieving.py:1357 +msgid "Append PP-M geometry" +msgstr "PPM Geometrie hinzufügen" + +#: AppTools/ToolCopperThieving.py:1483 +msgid "Generating Pattern Plating Mask done." +msgstr "Erzeugen der PPM abgeschlossen." + +#: AppTools/ToolCopperThieving.py:1555 +msgid "Copper Thieving Tool exit." +msgstr "Copper Thieving Tool verlassen." + +#: AppTools/ToolCorners.py:57 +#, fuzzy +#| msgid "Gerber Object to which will be added a copper thieving." +msgid "The Gerber object that to which will be added corner markers." +msgstr "Dem Gerber Objekt wird ein Copper Thieving hinzugefügt." + +#: AppTools/ToolCorners.py:73 +#, fuzzy +#| msgid "Location" +msgid "Locations" +msgstr "Ort" + +#: AppTools/ToolCorners.py:75 +msgid "Locations where to place corner markers." +msgstr "" + +#: AppTools/ToolCorners.py:92 AppTools/ToolFiducials.py:99 +msgid "Top Right" +msgstr "Oben rechts" + +#: AppTools/ToolCorners.py:158 +#, fuzzy +#| msgid "Add area" +msgid "Add Marker" +msgstr "Bereich hinzufügen" + +#: AppTools/ToolCorners.py:160 +msgid "Will add corner markers to the selected Gerber file." +msgstr "" + +#: AppTools/ToolCorners.py:225 +#, fuzzy +#| msgid "QRCode Tool" +msgid "Corners Tool" +msgstr "QRCode Werkzeug" + +#: AppTools/ToolCorners.py:288 +msgid "Please select at least a location" +msgstr "" + +#: AppTools/ToolCorners.py:423 +#, fuzzy +#| msgid "Copper Thieving Tool exit." +msgid "Corners Tool exit." +msgstr "Copper Thieving Tool verlassen." + +#: AppTools/ToolCutOut.py:41 +msgid "Cutout PCB" +msgstr "Ausschnitt PCB" + +#: AppTools/ToolCutOut.py:69 AppTools/ToolPanelize.py:53 +msgid "Source Object" +msgstr "Quellobjekt" + +#: AppTools/ToolCutOut.py:70 +msgid "Object to be cutout" +msgstr "Auszuschneidendes Objekt" + +#: AppTools/ToolCutOut.py:75 +msgid "Kind" +msgstr "Typ" + +#: AppTools/ToolCutOut.py:97 +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." + +#: AppTools/ToolCutOut.py:121 +msgid "Tool Parameters" +msgstr "Werkzeugparameter" + +#: AppTools/ToolCutOut.py:238 +msgid "A. Automatic Bridge Gaps" +msgstr "A. Automatische Brückenlücken" + +#: AppTools/ToolCutOut.py:240 +msgid "This section handle creation of automatic bridge gaps." +msgstr "Dieser Abschnitt behandelt die Erstellung automatischer Brückenlücken." + +#: AppTools/ToolCutOut.py:247 +msgid "" +"Number of gaps used for the Automatic cutout.\n" +"There can be maximum 8 bridges/gaps.\n" +"The choices are:\n" +"- None - no gaps\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" +"- Keine - keine Lücken\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" + +#: AppTools/ToolCutOut.py:269 +msgid "Generate Freeform Geometry" +msgstr "Freiform Geometrie erzeugen" + +#: AppTools/ToolCutOut.py:271 +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." + +#: AppTools/ToolCutOut.py:283 +msgid "Generate Rectangular Geometry" +msgstr "Rechteck Geometrie erzeugen" + +#: AppTools/ToolCutOut.py:285 +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." + +#: AppTools/ToolCutOut.py:304 +msgid "B. Manual Bridge Gaps" +msgstr "B. Manuelle Brückenlücken" + +#: AppTools/ToolCutOut.py:306 +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 wird die Erstellung manueller Brückenlücken behandelt.\n" +"Dies geschieht durch einen Mausklick auf den Umfang des\n" +"Geometrieobjekt, das als Ausschnittobjekt verwendet wird. " + +#: AppTools/ToolCutOut.py:321 +msgid "Geometry object used to create the manual cutout." +msgstr "Geometrieobjekt zum Erstellen des manuellen Ausschnitts." + +#: AppTools/ToolCutOut.py:328 +msgid "Generate Manual Geometry" +msgstr "Manuelle Geometrie erzeugen" + +#: AppTools/ToolCutOut.py:330 +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." + +#: AppTools/ToolCutOut.py:343 +msgid "Manual Add Bridge Gaps" +msgstr "Manuelles Hinzufügen von Brückenlücken" + +#: AppTools/ToolCutOut.py:345 +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." + +#: AppTools/ToolCutOut.py:524 +msgid "" +"There is no object selected for Cutout.\n" +"Select one and try again." +msgstr "" +"Es ist kein Objekt für den Ausschnitt ausgewählt.\n" +"Wählen Sie eine aus und versuchen Sie es erneut." + +#: AppTools/ToolCutOut.py:530 AppTools/ToolCutOut.py:733 +#: AppTools/ToolCutOut.py:914 AppTools/ToolCutOut.py:996 +#: tclCommands/TclCommandGeoCutout.py:184 +msgid "Tool Diameter is zero value. Change it to a positive real number." +msgstr "" +"Werkzeugdurchmesser ist Nullwert. Ändern Sie es in eine positive reelle Zahl." + +#: AppTools/ToolCutOut.py:544 AppTools/ToolCutOut.py:748 +msgid "Number of gaps value is missing. Add it and retry." +msgstr "" +"Der Wert für die Anzahl der Lücken fehlt. Fügen Sie es hinzu und versuchen " +"Sie es erneut." + +#: AppTools/ToolCutOut.py:549 AppTools/ToolCutOut.py:752 +msgid "" +"Gaps value can be only one of: 'None', 'lr', 'tb', '2lr', '2tb', 4 or 8. " +"Fill in a correct value and retry. " +msgstr "" +"Der Lückenwert kann nur einer der folgenden Werte sein: \"Keine\", \"lr\", " +"\"tb\", \"2lr\", \"2tb\", 4 oder 8. Geben Sie einen korrekten Wert ein und " +"wiederholen Sie den Vorgang. " + +#: AppTools/ToolCutOut.py:554 AppTools/ToolCutOut.py:758 +msgid "" +"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 "" +"Bei einer Multi-Geo-Geometrie können keine Ausschnitte vorgenommen werden.\n" +"Optional kann diese Multi-Geo-Geometrie in Single-Geo-Geometrie konvertiert " +"werden.\n" +"und danach Cutout durchführen." + +#: AppTools/ToolCutOut.py:706 AppTools/ToolCutOut.py:903 +msgid "Any form CutOut operation finished." +msgstr "Jede Form CutOut-Operation ist abgeschlossen." + +#: AppTools/ToolCutOut.py:728 AppTools/ToolEtchCompensation.py:214 +#: AppTools/ToolInvertGerber.py:214 AppTools/ToolNCC.py:1603 +#: AppTools/ToolPaint.py:1396 AppTools/ToolPanelize.py:428 +#: tclCommands/TclCommandBbox.py:71 tclCommands/TclCommandNregions.py:71 +msgid "Object not found" +msgstr "Objekt nicht gefunden" + +#: AppTools/ToolCutOut.py:872 +msgid "Rectangular cutout with negative margin is not possible." +msgstr "Ein rechteckiger Ausschnitt mit negativem Rand ist nicht möglich." + +#: AppTools/ToolCutOut.py:908 +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 ..." + +#: AppTools/ToolCutOut.py:925 AppTools/ToolCutOut.py:951 +msgid "Could not retrieve Geometry object" +msgstr "Geometrieobjekt konnte nicht abgerufen werden" + +#: AppTools/ToolCutOut.py:956 +msgid "Geometry object for manual cutout not found" +msgstr "Geometrieobjekt für manuellen Ausschnitt nicht gefunden" + +#: AppTools/ToolCutOut.py:966 +msgid "Added manual Bridge Gap." +msgstr "Manuelle Brückenlücke hinzugefügt." + +#: AppTools/ToolCutOut.py:978 +msgid "Could not retrieve Gerber object" +msgstr "Gerber-Objekt konnte nicht abgerufen werden" + +#: AppTools/ToolCutOut.py:983 +msgid "" +"There is no Gerber object selected for Cutout.\n" +"Select one and try again." +msgstr "" +"Es ist kein Gerber-Objekt für den Ausschnitt ausgewählt.\n" +"Wählen Sie eine aus und versuchen Sie es erneut." + +#: AppTools/ToolCutOut.py:989 +msgid "" +"The selected object has to be of Gerber type.\n" +"Select a Gerber file and try again." +msgstr "" +"Das ausgewählte Objekt muss vom Typ Gerber sein.\n" +"Wählen Sie eine Gerber-Datei aus und versuchen Sie es erneut." + +#: AppTools/ToolCutOut.py:1024 +msgid "Geometry not supported for cutout" +msgstr "Geometrie für Ausschnitt nicht unterstützt" + +#: AppTools/ToolCutOut.py:1099 +msgid "Making manual bridge gap..." +msgstr "Manuelle Brückenlücke herstellen ..." + +#: AppTools/ToolDblSided.py:26 +msgid "2-Sided PCB" +msgstr "2-seitige PCB" + +#: AppTools/ToolDblSided.py:52 +msgid "Mirror Operation" +msgstr "Spiegelbetrieb" + +#: AppTools/ToolDblSided.py:53 +msgid "Objects to be mirrored" +msgstr "Zu spiegelnde Objekte" + +#: AppTools/ToolDblSided.py:65 +msgid "Gerber to be mirrored" +msgstr "Zu spiegelndes Gerber" + +#: AppTools/ToolDblSided.py:69 AppTools/ToolDblSided.py:97 +#: AppTools/ToolDblSided.py:127 +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." + +#: AppTools/ToolDblSided.py:93 +msgid "Excellon Object to be mirrored." +msgstr "Zu spiegelndes Excellon-Objekt." + +#: AppTools/ToolDblSided.py:122 +msgid "Geometry Obj to be mirrored." +msgstr "Geometrie-Objekt, das gespiegelt werden soll." + +#: AppTools/ToolDblSided.py:158 +msgid "Mirror Parameters" +msgstr "Spiegelparameter" + +#: AppTools/ToolDblSided.py:159 +msgid "Parameters for the mirror operation" +msgstr "Parameter für die Spiegeloperation" + +#: AppTools/ToolDblSided.py:164 +msgid "Mirror Axis" +msgstr "Spiegelachse" + +#: AppTools/ToolDblSided.py:175 +msgid "" +"The coordinates used as reference for the mirror operation.\n" +"Can be:\n" +"- Point -> a set of coordinates (x,y) around which the object is mirrored\n" +"- Box -> a set of coordinates (x, y) obtained from the center of the\n" +"bounding box of another object selected below" +msgstr "" +"Die Koordinaten, die als Referenz für die Spiegeloperation verwendet " +"werden.\n" +"Kann sein:\n" +"- Punkt -> eine Reihe von Koordinaten (x, y), um die das Objekt gespiegelt " +"wird\n" +"- Box -> ein Satz von Koordinaten (x, y), die aus der Mitte des erhalten " +"werden\n" +"Begrenzungsrahmen eines anderen unten ausgewählten Objekts" + +#: AppTools/ToolDblSided.py:189 +msgid "Point coordinates" +msgstr "Punktkoordinaten" + +#: AppTools/ToolDblSided.py:194 +msgid "" +"Add the coordinates in format (x, y) 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 coordinates " +"manually." +msgstr "" +"Fügen Sie die Koordinaten im Format (x, y) hinzu, durch die die " +"Spiegelungsachse verläuft\n" +"ausgewählt im Pass 'Spiegelachse'.\n" +"Die (x, y) -Koordinaten werden durch Drücken der SHIFT erfasst\n" +"und klicken Sie mit der linken Maustaste auf die Leinwand oder Sie können " +"die Koordinaten manuell eingeben." + +#: AppTools/ToolDblSided.py:218 +msgid "" +"It can be of type: Gerber or Excellon or Geometry.\n" +"The coordinates of the center of the bounding box are used\n" +"as reference for mirror operation." +msgstr "" +"Es kann vom Typ sein: Gerber oder Excellon oder Geometrie.\n" +"Die Koordinaten der Mitte des Begrenzungsrahmens werden verwendet\n" +"als Referenz für den Spiegelbetrieb." + +#: AppTools/ToolDblSided.py:252 +msgid "Bounds Values" +msgstr "Grenzen Werte" + +#: AppTools/ToolDblSided.py:254 +msgid "" +"Select on canvas the object(s)\n" +"for which to calculate bounds values." +msgstr "" +"Wählen Sie auf der Leinwand die Objekte aus.\n" +"für die Grenzwerte berechnet werden sollen." + +#: AppTools/ToolDblSided.py:264 +msgid "X min" +msgstr "X min" + +#: AppTools/ToolDblSided.py:266 AppTools/ToolDblSided.py:280 +msgid "Minimum location." +msgstr "Mindeststandort." + +#: AppTools/ToolDblSided.py:278 +msgid "Y min" +msgstr "Y min" + +#: AppTools/ToolDblSided.py:292 +msgid "X max" +msgstr "X max" + +#: AppTools/ToolDblSided.py:294 AppTools/ToolDblSided.py:308 +msgid "Maximum location." +msgstr "Maximaler Standort." + +#: AppTools/ToolDblSided.py:306 +msgid "Y max" +msgstr "Y max" + +#: AppTools/ToolDblSided.py:317 +msgid "Center point coordinates" +msgstr "Mittelpunktskoordinaten" + +#: AppTools/ToolDblSided.py:319 +msgid "Centroid" +msgstr "Schwerpunkt" + +#: AppTools/ToolDblSided.py:321 +msgid "" +"The center point location for the rectangular\n" +"bounding shape. Centroid. Format is (x, y)." +msgstr "" +"Die Mittelpunktposition für das Rechteck\n" +"begrenzende Form. Centroid. Das Format ist (x, y)." + +#: AppTools/ToolDblSided.py:330 +msgid "Calculate Bounds Values" +msgstr "Berechnen Sie Grenzwerte" + +#: AppTools/ToolDblSided.py:332 +msgid "" +"Calculate the enveloping rectangular shape coordinates,\n" +"for the selection of objects.\n" +"The envelope shape is parallel with the X, Y axis." +msgstr "" +"Berechnen Sie die einhüllenden rechteckigen Formkoordinaten,\n" +"zur Auswahl von Objekten.\n" +"Die Hüllkurvenform verläuft parallel zur X- und Y-Achse." + +#: AppTools/ToolDblSided.py:352 +msgid "PCB Alignment" +msgstr "PCB-Ausrichtung" + +#: AppTools/ToolDblSided.py:354 AppTools/ToolDblSided.py:456 +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." + +#: AppTools/ToolDblSided.py:361 +msgid "Drill Diameter" +msgstr "Bohrdurchmesser" + +#: AppTools/ToolDblSided.py:390 AppTools/ToolDblSided.py:397 +msgid "" +"The reference point used to create the second alignment drill\n" +"from the first alignment drill, by doing mirror.\n" +"It can be modified in the Mirror Parameters -> Reference section" +msgstr "" +"Der Referenzpunkt, der zum Erstellen des zweiten Ausrichtungsbohrers " +"verwendet wird\n" +"vom ersten Ausrichtungsbohrer durch Spiegeln.\n" +"Sie kann im Abschnitt Spiegelparameter -> Referenz geändert werden" + +#: AppTools/ToolDblSided.py:410 +msgid "Alignment Drill Coordinates" +msgstr "Ausrichtungsbohrkoordinaten" + +#: AppTools/ToolDblSided.py:412 +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 'Align " +"Axis'." +msgstr "" +"Ausrichtungslöcher (x1, y1), (x2, y2), ... auf einer Seite der Spiegelachse. " +"Für jeden Satz von (x, y) Koordinaten\n" +"Hier wird ein Paar Bohrer erstellt:\n" +"\n" +"- Ein Bohrer an den Koordinaten vom Feld\n" +"- Ein Bohrer in Spiegelposition über der oben in 'Achse ausrichten' " +"ausgewählten Achse." + +#: AppTools/ToolDblSided.py:420 +msgid "Drill coordinates" +msgstr "Bohrkoordinaten" + +#: AppTools/ToolDblSided.py:427 +msgid "" +"Add alignment drill holes coordinates in the format: (x1, y1), (x2, " +"y2), ... \n" +"on one side of the alignment 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 Koordinaten für Ausrichtungsbohrungen im folgenden Format hinzu: " +"(x1, y1), (x2, y2), ...\n" +"auf einer Seite der Ausrichtungsachse.\n" +"\n" +"Die eingestellten Koordinaten erhalten Sie:\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-tasteund klicken Sie mit der linken Maustaste auf " +"die Leinwand. Dann Strg + V im Feld.\n" +"- Drücken Sie die SHIFT-tasteund klicken Sie mit der linken Maustaste auf " +"die Leinwand. Klicken Sie dann in das Feld und dann auf Einfügen.\n" +"- durch manuelle Eingabe der Koordinaten im Format: (x1, y1), (x2, y2), ..." + +#: AppTools/ToolDblSided.py:442 +msgid "Delete Last" +msgstr "Letzte löschen" + +#: AppTools/ToolDblSided.py:444 +msgid "Delete the last coordinates tuple in the list." +msgstr "Delete the last coordinates tuple in the list." + +#: AppTools/ToolDblSided.py:454 +msgid "Create Excellon Object" +msgstr "Excellon-Objekt erstellen" + +#: AppTools/ToolDblSided.py:541 +msgid "2-Sided Tool" +msgstr "2-seitiges Werkzeug" + +#: AppTools/ToolDblSided.py:581 +msgid "" +"'Point' reference is selected and 'Point' coordinates are missing. Add them " +"and retry." +msgstr "" +"'Point'-Referenz ist ausgewählt und' Point'-Koordinaten fehlen. Fügen Sie " +"sie hinzu und versuchen Sie es erneut." + +#: AppTools/ToolDblSided.py:600 +msgid "There is no Box reference object loaded. Load one and retry." +msgstr "" +"Es ist kein Box-Referenzobjekt geladen. Laden Sie einen und versuchen Sie es " +"erneut." + +#: AppTools/ToolDblSided.py:612 +msgid "No value or wrong format in Drill Dia entry. Add it and retry." +msgstr "" +"Kein Wert oder falsches Format im Eintrag Bohrdurchmesser. Fügen Sie es " +"hinzu und versuchen Sie es erneut." + +#: AppTools/ToolDblSided.py:623 +msgid "There are no Alignment Drill Coordinates to use. Add them and retry." +msgstr "" +"Es sind keine Ausrichtungsbohrkoordinaten vorhanden. Fügen Sie sie hinzu und " +"versuchen Sie es erneut." + +#: AppTools/ToolDblSided.py:648 +msgid "Excellon object with alignment drills created..." +msgstr "Excellon-Objekt mit Ausrichtungsbohrern erstellt ..." + +#: AppTools/ToolDblSided.py:661 AppTools/ToolDblSided.py:704 +#: AppTools/ToolDblSided.py:748 +msgid "Only Gerber, Excellon and Geometry objects can be mirrored." +msgstr "Nur Gerber-, Excellon- und Geometrie-Objekte können gespiegelt werden." + +#: AppTools/ToolDblSided.py:671 AppTools/ToolDblSided.py:715 +msgid "" +"There are no Point coordinates in the Point field. Add coords and try " +"again ..." +msgstr "" +"Das Punktfeld enthält keine Punktkoordinaten. Fügen Sie Coords hinzu und " +"versuchen Sie es erneut ..." + +#: AppTools/ToolDblSided.py:681 AppTools/ToolDblSided.py:725 +#: AppTools/ToolDblSided.py:762 +msgid "There is no Box object loaded ..." +msgstr "Es ist kein Box-Objekt geladen ..." + +#: AppTools/ToolDblSided.py:691 AppTools/ToolDblSided.py:735 +#: AppTools/ToolDblSided.py:772 +msgid "was mirrored" +msgstr "wurde gespiegelt" + +#: AppTools/ToolDblSided.py:700 AppTools/ToolPunchGerber.py:533 +msgid "There is no Excellon object loaded ..." +msgstr "Es ist kein Excellon-Objekt geladen ..." + +#: AppTools/ToolDblSided.py:744 +msgid "There is no Geometry object loaded ..." +msgstr "Es wurde kein Geometrieobjekt geladen ..." + +#: AppTools/ToolDblSided.py:818 App_Main.py:4322 App_Main.py:4477 +msgid "Failed. No object(s) selected..." +msgstr "Gescheitert. Kein Objekt ausgewählt ..." + +#: AppTools/ToolDistance.py:57 AppTools/ToolDistanceMin.py:50 +msgid "Those are the units in which the distance is measured." +msgstr "Dies sind die Einheiten, in denen die Entfernung gemessen wird." + +#: AppTools/ToolDistance.py:58 AppTools/ToolDistanceMin.py:51 +msgid "METRIC (mm)" +msgstr "METRISCH (mm)" + +#: AppTools/ToolDistance.py:58 AppTools/ToolDistanceMin.py:51 +msgid "INCH (in)" +msgstr "ZOLL (in)" + +#: AppTools/ToolDistance.py:64 +msgid "Snap to center" +msgstr "Zur Mitte einrasten" + +#: AppTools/ToolDistance.py:66 +msgid "" +"Mouse cursor will snap to the center of the pad/drill\n" +"when it is hovering over the geometry of the pad/drill." +msgstr "" +"Der Mauszeiger rastet in der Mitte des Pads / Bohrers ein\n" +"wenn es über der Geometrie des Pads / Bohrers schwebt." + +#: AppTools/ToolDistance.py:76 +msgid "Start Coords" +msgstr "Starten Sie Koords" + +#: AppTools/ToolDistance.py:77 AppTools/ToolDistance.py:82 +msgid "This is measuring Start point coordinates." +msgstr "Dies ist das Messen von Startpunktkoordinaten." + +#: AppTools/ToolDistance.py:87 +msgid "Stop Coords" +msgstr "Stoppen Sie Koords" + +#: AppTools/ToolDistance.py:88 AppTools/ToolDistance.py:93 +msgid "This is the measuring Stop point coordinates." +msgstr "Dies ist die Messpunkt-Koordinate." + +#: AppTools/ToolDistance.py:98 AppTools/ToolDistanceMin.py:62 +msgid "Dx" +msgstr "Dx" + +#: AppTools/ToolDistance.py:99 AppTools/ToolDistance.py:104 +#: AppTools/ToolDistanceMin.py:63 AppTools/ToolDistanceMin.py:92 +msgid "This is the distance measured over the X axis." +msgstr "Dies ist der Abstand, der über die X-Achse gemessen wird." + +#: AppTools/ToolDistance.py:109 AppTools/ToolDistanceMin.py:65 +msgid "Dy" +msgstr "Dy" + +#: AppTools/ToolDistance.py:110 AppTools/ToolDistance.py:115 +#: AppTools/ToolDistanceMin.py:66 AppTools/ToolDistanceMin.py:97 +msgid "This is the distance measured over the Y axis." +msgstr "Dies ist die über die Y-Achse gemessene Entfernung." + +#: AppTools/ToolDistance.py:121 AppTools/ToolDistance.py:126 +#: AppTools/ToolDistanceMin.py:69 AppTools/ToolDistanceMin.py:102 +msgid "This is orientation angle of the measuring line." +msgstr "Dies ist der Orientierungswinkel der Messlinie." + +#: AppTools/ToolDistance.py:131 AppTools/ToolDistanceMin.py:71 +msgid "DISTANCE" +msgstr "ENTFERNUNG" + +#: AppTools/ToolDistance.py:132 AppTools/ToolDistance.py:137 +msgid "This is the point to point Euclidian distance." +msgstr "Dies ist die Punkt-zu-Punkt-Euklidische Entfernung." + +#: AppTools/ToolDistance.py:142 AppTools/ToolDistance.py:339 +#: AppTools/ToolDistanceMin.py:114 +msgid "Measure" +msgstr "Messen" + +#: AppTools/ToolDistance.py:274 +msgid "Working" +msgstr "Arbeiten" + +#: AppTools/ToolDistance.py:279 +msgid "MEASURING: Click on the Start point ..." +msgstr "MESSEN: Klicken Sie auf den Startpunkt ..." + +#: AppTools/ToolDistance.py:389 +msgid "Distance Tool finished." +msgstr "Distanzwerkzeug fertig." + +#: AppTools/ToolDistance.py:461 +msgid "Pads overlapped. Aborting." +msgstr "Pads überlappen sich. Abbruch." + +#: AppTools/ToolDistance.py:489 +#, fuzzy +#| msgid "Distance Tool finished." +msgid "Distance Tool cancelled." +msgstr "Distanzwerkzeug fertig." + +#: AppTools/ToolDistance.py:494 +msgid "MEASURING: Click on the Destination point ..." +msgstr "MESSEN: Klicken Sie auf den Zielpunkt ..." + +#: AppTools/ToolDistance.py:503 AppTools/ToolDistanceMin.py:284 +msgid "MEASURING" +msgstr "MESSUNG" + +#: AppTools/ToolDistance.py:504 AppTools/ToolDistanceMin.py:285 +msgid "Result" +msgstr "Ergebnis" + +#: AppTools/ToolDistanceMin.py:31 AppTools/ToolDistanceMin.py:143 +msgid "Minimum Distance Tool" +msgstr "Werkzeug für minimalen Abstand" + +#: AppTools/ToolDistanceMin.py:54 +msgid "First object point" +msgstr "Erster Objektpunkt" + +#: AppTools/ToolDistanceMin.py:55 AppTools/ToolDistanceMin.py:80 +msgid "" +"This is first object point coordinates.\n" +"This is the start point for measuring distance." +msgstr "" +"Dies sind erste Objektpunktkoordinaten.\n" +"Dies ist der Startpunkt für die Entfernungsmessung." + +#: AppTools/ToolDistanceMin.py:58 +msgid "Second object point" +msgstr "Zweiter Objektpunkt" + +#: AppTools/ToolDistanceMin.py:59 AppTools/ToolDistanceMin.py:86 +msgid "" +"This is second object point coordinates.\n" +"This is the end point for measuring distance." +msgstr "" +"Dies sind die Koordinaten des zweiten Objektpunkts.\n" +"Dies ist der Endpunkt für die Entfernungsmessung." + +#: AppTools/ToolDistanceMin.py:72 AppTools/ToolDistanceMin.py:107 +msgid "This is the point to point Euclidean distance." +msgstr "Dies ist die euklidische Distanz von Punkt zu Punkt." + +#: AppTools/ToolDistanceMin.py:74 +msgid "Half Point" +msgstr "Halber Punkt" + +#: AppTools/ToolDistanceMin.py:75 AppTools/ToolDistanceMin.py:112 +msgid "This is the middle point of the point to point Euclidean distance." +msgstr "Dies ist der Mittelpunkt der euklidischen Distanz von Punkt zu Punkt." + +#: AppTools/ToolDistanceMin.py:117 +msgid "Jump to Half Point" +msgstr "Springe zum halben Punkt" + +#: AppTools/ToolDistanceMin.py:154 +msgid "" +"Select two objects and no more, to measure the distance between them ..." +msgstr "" +"Wählen Sie zwei und nicht mehr Objekte aus, um den Abstand zwischen ihnen zu " +"messen ..." + +#: AppTools/ToolDistanceMin.py:195 AppTools/ToolDistanceMin.py:216 +#: AppTools/ToolDistanceMin.py:225 AppTools/ToolDistanceMin.py:246 +msgid "Select two objects and no more. Currently the selection has objects: " +msgstr "" +"Wählen Sie zwei Objekte und nicht mehr. Derzeit hat die Auswahl Objekte: " + +#: AppTools/ToolDistanceMin.py:293 +msgid "Objects intersects or touch at" +msgstr "Objekte schneiden sich oder berühren sich" + +#: AppTools/ToolDistanceMin.py:299 +msgid "Jumped to the half point between the two selected objects" +msgstr "Sprang zum halben Punkt zwischen den beiden ausgewählten Objekten" + +#: AppTools/ToolEtchCompensation.py:74 AppTools/ToolInvertGerber.py:74 +msgid "Gerber object that will be inverted." +msgstr "Gerber-Objekt, das invertiert wird." + +#: AppTools/ToolEtchCompensation.py:83 AppTools/ToolInvertGerber.py:83 +msgid "Parameters for this tool" +msgstr "Parameter für dieses Werkzeug" + +#: AppTools/ToolEtchCompensation.py:88 +#, fuzzy +#| msgid "Thickness" +msgid "Copper Thickness" +msgstr "Dicke" + +#: AppTools/ToolEtchCompensation.py:90 +#, fuzzy +#| msgid "" +#| "How thick the copper growth is intended to be.\n" +#| "In microns." +msgid "" +"The thickness of the copper foil.\n" +"In microns [um]." +msgstr "" +"Wie dick soll das Kupferwachstum sein.\n" +"In Mikrometern." + +#: AppTools/ToolEtchCompensation.py:101 +#, fuzzy +#| msgid "Location" +msgid "Ratio" +msgstr "Ort" + +#: AppTools/ToolEtchCompensation.py:103 +msgid "" +"The ratio of lateral etch versus depth etch.\n" +"Can be:\n" +"- custom -> the user will enter a custom value\n" +"- preselection -> value which depends on a selection of etchants" +msgstr "" + +#: AppTools/ToolEtchCompensation.py:109 +#, fuzzy +#| msgid "Selection" +msgid "PreSelection" +msgstr "Auswahl" + +#: AppTools/ToolEtchCompensation.py:121 +msgid "Compensate" +msgstr "" + +#: AppTools/ToolEtchCompensation.py:123 +msgid "" +"Will increase the copper features thickness to compensate the lateral etch." +msgstr "" + +#: AppTools/ToolEtchCompensation.py:181 AppTools/ToolInvertGerber.py:184 +msgid "Invert Tool" +msgstr "Invertiert Werkzeug" + +#: AppTools/ToolExtractDrills.py:29 AppTools/ToolExtractDrills.py:295 +msgid "Extract Drills" +msgstr "Bohrer extrahieren" + +#: AppTools/ToolExtractDrills.py:62 +msgid "Gerber from which to extract drill holes" +msgstr "Gerber, aus dem Bohrlöcher gezogen werden sollen" + +#: AppTools/ToolExtractDrills.py:297 +msgid "Extract drills from a given Gerber file." +msgstr "Extrahieren Sie Bohrer aus einer bestimmten Gerber-Datei." + +#: AppTools/ToolExtractDrills.py:478 AppTools/ToolExtractDrills.py:563 +#: AppTools/ToolExtractDrills.py:648 +msgid "No drills extracted. Try different parameters." +msgstr "Keine Bohrer extrahiert. Probieren Sie verschiedene Parameter aus." + +#: AppTools/ToolFiducials.py:56 +msgid "Fiducials Coordinates" +msgstr "Bezugspunktkoordinaten" + +#: AppTools/ToolFiducials.py:58 +msgid "" +"A table with the fiducial points coordinates,\n" +"in the format (x, y)." +msgstr "" +"Eine Tabelle der Bezugspunkte mit Koordinaten \n" +"im Format (x,z)" + +#: AppTools/ToolFiducials.py:191 +msgid "" +"- 'Auto' - automatic placement of fiducials in the corners of the bounding " +"box.\n" +" - 'Manual' - manual placement of fiducials." +msgstr "" +"\"Auto\" Die Bezugspunkte werden automatisch in den Ecken des Umrisses " +"platziert.\n" +"\"Manuell\" Die Bezugspunkte werden manuell platziert." + +#: AppTools/ToolFiducials.py:237 +msgid "Thickness of the line that makes the fiducial." +msgstr "" + +#: AppTools/ToolFiducials.py:259 +msgid "Copper Gerber" +msgstr "Gerber (Kupfer) öffnen" + +#: AppTools/ToolFiducials.py:268 +msgid "Add Fiducial" +msgstr "Bezugspunkt hinzufügen" + +#: AppTools/ToolFiducials.py:270 +msgid "Will add a polygon on the copper layer to serve as fiducial." +msgstr "Fügt ein Polygon auf die Kupferschicht als Bezugspunkt hinzu." + +#: AppTools/ToolFiducials.py:286 +msgid "Soldermask Gerber" +msgstr "Lötpastenmaske Gerber" + +#: AppTools/ToolFiducials.py:288 +msgid "The Soldermask Gerber object." +msgstr "Lötpastenmaske Gerber-Objekt." + +#: AppTools/ToolFiducials.py:300 +msgid "Add Soldermask Opening" +msgstr "Lotpastenmaske Öffnung hinzufügen" + +#: AppTools/ToolFiducials.py:302 +msgid "" +"Will add a polygon on the soldermask layer\n" +"to serve as fiducial opening.\n" +"The diameter is always double of the diameter\n" +"for the copper fiducial." +msgstr "" +"Fügt ein Polygon zur Lötpastenschicht hinzu, \n" +"welches als Öffnungs-Bezugspunkt dient.\n" +"Der Durchmesser ist immer doppelt so groß\n" +"wie der Kupfer Bezugspunkt." + +#: AppTools/ToolFiducials.py:517 +msgid "Click to add first Fiducial. Bottom Left..." +msgstr "Klicken um den ersten Bezugspunkt unten links hinzuzufügen..." + +#: AppTools/ToolFiducials.py:781 +msgid "Click to add the last fiducial. Top Right..." +msgstr "Klicken um den letzten Bezugspunkt oben rechts hinzuzufügen..." + +#: AppTools/ToolFiducials.py:786 +msgid "Click to add the second fiducial. Top Left or Bottom Right..." +msgstr "" +"Klicken um den zweiten Bezugspunkt oben links oder unten rechts " +"hinzuzufügen..." + +#: AppTools/ToolFiducials.py:789 AppTools/ToolFiducials.py:798 +msgid "Done. All fiducials have been added." +msgstr "Fertig. Alle Bezugspunkte hinzugefügt." + +#: AppTools/ToolFiducials.py:875 +msgid "Fiducials Tool exit." +msgstr "Bezugspunkttool beenden." + +#: AppTools/ToolFilm.py:42 +msgid "Film PCB" +msgstr "Film PCB" + +#: AppTools/ToolFilm.py:73 +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." + +#: AppTools/ToolFilm.py:96 +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." + +#: AppTools/ToolFilm.py:256 +msgid "Film Parameters" +msgstr "Film-Parameter" + +#: AppTools/ToolFilm.py:317 +msgid "Punch drill holes" +msgstr "Löcher stanzen" + +#: AppTools/ToolFilm.py:318 +msgid "" +"When checked the generated film will have holes in pads when\n" +"the generated film is positive. This is done to help drilling,\n" +"when done manually." +msgstr "" +"Wenn diese Option aktiviert ist, weist der erzeugte Film Löcher in den Pads " +"auf\n" +"Der erzeugte Film ist positiv. Dies geschieht, um das Bohren zu " +"erleichtern.\n" +"wenn manuell erledigt." + +#: AppTools/ToolFilm.py:336 +msgid "Source" +msgstr "Quelle" + +#: AppTools/ToolFilm.py:338 +msgid "" +"The punch hole source can be:\n" +"- Excellon -> an Excellon holes center will serve as reference.\n" +"- Pad Center -> will try to use the pads center as reference." +msgstr "" +"Die Stanzlochquelle kann sein:\n" +"- Excellon -> Ein Excellon-Lochzentrum dient als Referenz.\n" +"- Pad-Mitte -> wird versuchen, die Pad-Mitte als Referenz zu verwenden." + +#: AppTools/ToolFilm.py:343 +msgid "Pad center" +msgstr "Pad-Mitte" + +#: AppTools/ToolFilm.py:348 +msgid "Excellon Obj" +msgstr "Excellon-Objekt" + +#: AppTools/ToolFilm.py:350 +msgid "" +"Remove the geometry of Excellon from the Film to create the holes in pads." +msgstr "" +"Entfernen Sie die Geometrie von Excellon aus dem Film, um die Löcher in den " +"Pads zu erzeugen." + +#: AppTools/ToolFilm.py:364 +msgid "Punch Size" +msgstr "Lochergröße" + +#: AppTools/ToolFilm.py:365 +msgid "The value here will control how big is the punch hole in the pads." +msgstr "Der Wert hier bestimmt, wie groß das Loch in den Pads ist." + +#: AppTools/ToolFilm.py:485 +msgid "Save Film" +msgstr "Film speichern" + +#: AppTools/ToolFilm.py:487 +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 the\n" +"selected format." +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 \n" +"gewähltem Format." + +#: AppTools/ToolFilm.py:649 +msgid "" +"Using the Pad center does not work on Geometry objects. Only a Gerber object " +"has pads." +msgstr "" +"Die Verwendung der Pad-Mitte funktioniert nicht bei Geometrieobjekten. Nur " +"ein Gerber-Objekt hat Pads." + +#: AppTools/ToolFilm.py:659 +msgid "No FlatCAM object selected. Load an object for Film and retry." +msgstr "" +"Kein FlatCAM-Objekt ausgewählt. Laden Sie ein Objekt für Film und versuchen " +"Sie es erneut." + +#: AppTools/ToolFilm.py:666 +msgid "No FlatCAM object selected. Load an object for Box and retry." +msgstr "" +"Kein FlatCAM-Objekt ausgewählt. Laden Sie ein Objekt für Box und versuchen " +"Sie es erneut." + +#: AppTools/ToolFilm.py:670 +msgid "No FlatCAM object selected." +msgstr "Kein FlatCAM-Objekt ausgewählt." + +#: AppTools/ToolFilm.py:681 +msgid "Generating Film ..." +msgstr "Film wird erstellt ..." + +#: AppTools/ToolFilm.py:730 AppTools/ToolFilm.py:734 +msgid "Export positive film" +msgstr "Film positiv exportieren" + +#: AppTools/ToolFilm.py:767 +msgid "" +"No Excellon object selected. Load an object for punching reference and retry." +msgstr "" +"Kein Excellon-Objekt ausgewählt. Laden Sie ein Objekt zum Stanzen der " +"Referenz und versuchen Sie es erneut." + +#: AppTools/ToolFilm.py:791 +msgid "" +" Could not generate punched hole film because the punch hole sizeis bigger " +"than some of the apertures in the Gerber object." +msgstr "" +" Es konnte kein Lochfilm erzeugt werden, da die Lochgröße größer ist als " +"einige der Öffnungen im Gerber-Objekt." + +#: AppTools/ToolFilm.py:803 +msgid "" +"Could not generate punched hole film because the punch hole sizeis bigger " +"than some of the apertures in the Gerber object." +msgstr "" +"Es konnte kein Lochfilm erzeugt werden, da die Lochgröße größer ist als " +"einige der Öffnungen im Gerber-Objekt." + +#: AppTools/ToolFilm.py:821 +msgid "" +"Could not generate punched hole film because the newly created object " +"geometry is the same as the one in the source object geometry..." +msgstr "" +"Lochfolie konnte nicht generiert werden, da die neu erstellte " +"Objektgeometrie mit der in der Quellobjektgeometrie übereinstimmt ..." + +#: AppTools/ToolFilm.py:876 AppTools/ToolFilm.py:880 +msgid "Export negative film" +msgstr "Exportieren negativ Film" + +#: AppTools/ToolFilm.py:941 AppTools/ToolFilm.py:1124 +#: AppTools/ToolPanelize.py:441 +msgid "No object Box. Using instead" +msgstr "Keine Objektbox. Verwenden Sie stattdessen" + +#: AppTools/ToolFilm.py:1057 AppTools/ToolFilm.py:1237 +msgid "Film file exported to" +msgstr "Film-Datei exportiert nach" + +#: AppTools/ToolFilm.py:1060 AppTools/ToolFilm.py:1240 +msgid "Generating Film ... Please wait." +msgstr "Film wird erstellt ... Bitte warten Sie." + +#: AppTools/ToolImage.py:24 +msgid "Image as Object" +msgstr "Bild als Objekt" + +#: AppTools/ToolImage.py:33 +msgid "Image to PCB" +msgstr "Bild auf PCB" + +#: AppTools/ToolImage.py:56 +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." + +#: AppTools/ToolImage.py:65 +msgid "DPI value" +msgstr "DPI-Wert" + +#: AppTools/ToolImage.py:66 +msgid "Specify a DPI value for the image." +msgstr "Geben Sie einen DPI-Wert für das Bild an." + +#: AppTools/ToolImage.py:72 +msgid "Level of detail" +msgstr "Detaillierungsgrad" + +#: AppTools/ToolImage.py:81 +msgid "Image type" +msgstr "Bildtyp" + +#: AppTools/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." + +#: AppTools/ToolImage.py:92 AppTools/ToolImage.py:107 AppTools/ToolImage.py:120 +#: AppTools/ToolImage.py:133 +msgid "Mask value" +msgstr "Maskenwert" + +#: AppTools/ToolImage.py:94 +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)." + +#: AppTools/ToolImage.py:109 +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." + +#: AppTools/ToolImage.py:122 +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." + +#: AppTools/ToolImage.py:135 +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." + +#: AppTools/ToolImage.py:143 +msgid "Import image" +msgstr "Bild importieren" + +#: AppTools/ToolImage.py:145 +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." + +#: AppTools/ToolImage.py:182 +msgid "Image Tool" +msgstr "Bildwerkzeug" + +#: AppTools/ToolImage.py:234 AppTools/ToolImage.py:237 +msgid "Import IMAGE" +msgstr "BILD importieren" + +#: AppTools/ToolImage.py:277 App_Main.py:8264 App_Main.py:8311 +msgid "" +"Not supported type is picked as parameter. Only Geometry and Gerber are " +"supported" +msgstr "" +"Nicht unterstützte Art wird als Parameter ausgewählt. Nur Geometrie und " +"Gerber werden unterstützt" + +#: AppTools/ToolImage.py:285 +msgid "Importing Image" +msgstr "Bild importieren" + +#: AppTools/ToolImage.py:297 AppTools/ToolPDF.py:154 App_Main.py:8289 +#: App_Main.py:8335 App_Main.py:8399 App_Main.py:8466 App_Main.py:8532 +#: App_Main.py:8597 App_Main.py:8654 +msgid "Opened" +msgstr "Geöffnet" + +#: AppTools/ToolInvertGerber.py:123 +msgid "Invert Gerber" +msgstr "Gerber umkehren" + +#: AppTools/ToolInvertGerber.py:125 +msgid "" +"Will invert the Gerber object: areas that have copper\n" +"will be empty of copper and previous empty area will be\n" +"filled with copper." +msgstr "" +"Invertiert das Gerber-Objekt: Bereiche mit Kupfer\n" +"wird leer von Kupfer sein und der vorherige leere Bereich wird leer sein\n" +"mit Kupfer gefüllt." + +#: AppTools/ToolMove.py:102 +msgid "MOVE: Click on the Start point ..." +msgstr "Verschieben: Klicke auf den Startpunkt ..." + +#: AppTools/ToolMove.py:113 +msgid "Cancelled. No object(s) to move." +msgstr "Abgebrochen. Keine Objekte zum Bewegen." + +#: AppTools/ToolMove.py:140 +msgid "MOVE: Click on the Destination point ..." +msgstr "Verschieben: Klicken Sie auf den Zielpunkt ..." + +#: AppTools/ToolMove.py:163 +msgid "Moving..." +msgstr "Ziehen um..." + +#: AppTools/ToolMove.py:166 +msgid "No object(s) selected." +msgstr "Keine Objekte ausgewählt." + +#: AppTools/ToolMove.py:221 +msgid "Error when mouse left click." +msgstr "Fehler beim Klicken mit der linken Maustaste." + +#: AppTools/ToolNCC.py:42 +msgid "Non-Copper Clearing" +msgstr "Nicht-Kupfer-Clearing" + +#: AppTools/ToolNCC.py:88 +msgid "" +"Specify the type of object to be cleared of excess copper.\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 von überschüssigem Kupfer befreit werden " +"soll.\n" +"Es kann vom Typ Gerber oder Geometrie sein.\n" +"Was hier ausgewählt wird, bestimmt die Art\n" +"von Objekten, die das Kombinationsfeld \"Objekt\" füllen." + +#: AppTools/ToolNCC.py:110 +msgid "Object to be cleared of excess copper." +msgstr "Objekt, das von überschüssigem Kupfer befreit werden soll." + +#: AppTools/ToolNCC.py:122 +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." + +#: AppTools/ToolNCC.py:138 +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." + +#: AppTools/ToolNCC.py:146 +msgid "" +"Tool Diameter. It's value (in current FlatCAM units)\n" +"is the cut width into the material." +msgstr "" +"Werkzeugdurchmesser. Wert (in aktuellen FlatCAM-Einheiten)\n" +"ist die Schnittbreite in das Material." + +#: AppTools/ToolNCC.py:150 +msgid "" +"The Tool Type (TT) can be:\n" +"- Circular with 1 ... 4 teeth -> it is informative only. Being circular,\n" +"the cut width in material is exactly the tool diameter.\n" +"- Ball -> informative only and make reference to the Ball type endmill.\n" +"- V-Shape -> it will disable Z-Cut parameter in the resulting geometry UI " +"form\n" +"and enable two additional UI form fields in the resulting geometry: V-Tip " +"Dia and\n" +"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\n" +"column of this table.\n" +"Choosing the 'V-Shape' Tool Type automatically will select the Operation " +"Type\n" +"in the resulting geometry as Isolation." +msgstr "" +"Der Werkzeugtyp (TT) kann sein:\n" +"- Rundschreiben mit 1 ... 4 Zähnen -> nur informativ. Rundschreiben,\n" +"Die Schnittbreite im Material entspricht genau dem Werkzeugdurchmesser.\n" +"- Ball -> nur informativ und auf den Ball-Schaftfräser verweisen.\n" +"- V-Form -> Deaktiviert den Z-Cut-Parameter in der resultierenden Geometrie-" +"UI-Form\n" +"und aktivieren Sie zwei zusätzliche UI-Formularfelder in der resultierenden " +"Geometrie: V-Tip Dia und\n" +"V-Tip-Winkel. Durch Anpassen dieser beiden Werte wird der Z-Cut-Parameter " +"wie z\n" +"da die Schnittbreite in Material gleich dem Wert im Werkzeugdurchmesser ist\n" +"Spalte dieser Tabelle.\n" +"Durch automatische Auswahl des Werkzeugtyps \"V-Form\" wird der " +"Operationstyp ausgewählt\n" +"in der resultierenden Geometrie als Isolation." + +#: AppTools/ToolNCC.py:296 AppTools/ToolPaint.py:278 +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." + +#: AppTools/ToolNCC.py:318 AppTools/ToolPaint.py:300 +#: AppTools/ToolSolderPaste.py:130 +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." + +#: AppTools/ToolNCC.py:554 +msgid "" +"The type of FlatCAM object to be used as non copper clearing reference.\n" +"It can be Gerber, Excellon or Geometry." +msgstr "" +"Der Typ des FlatCAM-Objekts, das als nicht aus Kupfer stammende Clearing-" +"Referenz verwendet werden soll.\n" +"Es kann Gerber, Excellon oder Geometry sein." + +#: AppTools/ToolNCC.py:597 AppTools/ToolPaint.py:536 +msgid "Generate Geometry" +msgstr "Geometrie erzeugen" + +#: AppTools/ToolNCC.py:932 AppTools/ToolNCC.py:1431 AppTools/ToolPaint.py:857 +#: AppTools/ToolSolderPaste.py:568 AppTools/ToolSolderPaste.py:893 +#: App_Main.py:4190 +msgid "Please enter a tool diameter with non-zero value, in Float format." +msgstr "" +"Bitte geben Sie einen Werkzeugdurchmesser ungleich Null im Float-Format ein." + +#: AppTools/ToolNCC.py:936 AppTools/ToolPaint.py:861 +#: AppTools/ToolSolderPaste.py:572 App_Main.py:4194 +msgid "Adding Tool cancelled" +msgstr "Addierwerkzeug abgebrochen" + +#: AppTools/ToolNCC.py:1425 AppTools/ToolPaint.py:1183 +#: AppTools/ToolSolderPaste.py:888 +msgid "Please enter a tool diameter to add, in Float format." +msgstr "" +"Bitte geben Sie einen hinzuzufügenden Werkzeugdurchmesser im Float-Format " +"ein." + +#: AppTools/ToolNCC.py:1456 AppTools/ToolNCC.py:4065 AppTools/ToolPaint.py:1207 +#: AppTools/ToolPaint.py:3608 AppTools/ToolSolderPaste.py:917 +msgid "Cancelled. Tool already in Tool Table." +msgstr "Abgebrochen. Werkzeug bereits in der Werkzeugtabelle." + +#: AppTools/ToolNCC.py:1463 AppTools/ToolNCC.py:4082 AppTools/ToolPaint.py:1212 +#: AppTools/ToolPaint.py:3625 +msgid "New tool added to Tool Table." +msgstr "Neues Werkzeug zur Werkzeugtabelle hinzugefügt." + +#: AppTools/ToolNCC.py:1507 AppTools/ToolPaint.py:1256 +msgid "Tool from Tool Table was edited." +msgstr "Werkzeug aus Werkzeugtabelle wurde bearbeitet." + +#: AppTools/ToolNCC.py:1519 AppTools/ToolPaint.py:1268 +#: AppTools/ToolSolderPaste.py:978 +msgid "Cancelled. New diameter value is already in the Tool Table." +msgstr "" +"Abgebrochen. Der neue Durchmesserwert befindet sich bereits in der " +"Werkzeugtabelle." + +#: AppTools/ToolNCC.py:1571 AppTools/ToolPaint.py:1366 +msgid "Delete failed. Select a tool to delete." +msgstr "Löschen fehlgeschlagen. Wählen Sie ein Werkzeug zum Löschen aus." + +#: AppTools/ToolNCC.py:1577 AppTools/ToolPaint.py:1372 +msgid "Tool(s) deleted from Tool Table." +msgstr "Werkzeug(e) aus der Werkzeugtabelle gelöscht." + +#: AppTools/ToolNCC.py:1620 +msgid "Wrong Tool Dia value format entered, use a number." +msgstr "Falsches Werkzeug Dia-Wertformat eingegeben, verwenden Sie eine Zahl." + +#: AppTools/ToolNCC.py:1629 AppTools/ToolPaint.py:1423 +msgid "No selected tools in Tool Table." +msgstr "Keine ausgewählten Werkzeuge in der Werkzeugtabelle." + +#: AppTools/ToolNCC.py:1705 AppTools/ToolPaint.py:1599 +msgid "Click the end point of the paint area." +msgstr "Klicken Sie auf den Endpunkt des Malbereichs." + +#: AppTools/ToolNCC.py:1985 AppTools/ToolNCC.py:3010 +msgid "NCC Tool. Preparing non-copper polygons." +msgstr "NCC-Tool. Vorbereitung von kupferfreien Polygonen." + +#: AppTools/ToolNCC.py:2044 AppTools/ToolNCC.py:3138 +msgid "NCC Tool. Calculate 'empty' area." +msgstr "NCC-Tool. Berechnen Sie die \"leere\" Fläche." + +#: AppTools/ToolNCC.py:2063 AppTools/ToolNCC.py:2172 AppTools/ToolNCC.py:2187 +#: AppTools/ToolNCC.py:3151 AppTools/ToolNCC.py:3256 AppTools/ToolNCC.py:3271 +#: AppTools/ToolNCC.py:3537 AppTools/ToolNCC.py:3638 AppTools/ToolNCC.py:3653 +msgid "Buffering finished" +msgstr "Pufferung beendet" + +#: AppTools/ToolNCC.py:2071 AppTools/ToolNCC.py:2194 AppTools/ToolNCC.py:3159 +#: AppTools/ToolNCC.py:3278 AppTools/ToolNCC.py:3544 AppTools/ToolNCC.py:3660 +msgid "Could not get the extent of the area to be non copper cleared." +msgstr "" +"Die Ausdehnung des nicht kupferhaltigen Bereichs konnte nicht gelöscht " +"werden." + +#: AppTools/ToolNCC.py:2101 AppTools/ToolNCC.py:2180 AppTools/ToolNCC.py:3186 +#: AppTools/ToolNCC.py:3263 AppTools/ToolNCC.py:3564 AppTools/ToolNCC.py:3645 +msgid "" +"Isolation geometry is broken. Margin is less than isolation tool diameter." +msgstr "" +"Die Isolationsgeometrie ist gebrochen. Der Rand ist kleiner als der " +"Durchmesser des Isolationswerkzeugs." + +#: AppTools/ToolNCC.py:2197 AppTools/ToolNCC.py:3282 AppTools/ToolNCC.py:3663 +msgid "The selected object is not suitable for copper clearing." +msgstr "Das ausgewählte Objekt ist nicht zum Löschen von Kupfer geeignet." + +#: AppTools/ToolNCC.py:2204 AppTools/ToolNCC.py:3289 +msgid "NCC Tool. Finished calculation of 'empty' area." +msgstr "NCC-Tool. Berechnung der 'leeren' Fläche beendet." + +#: AppTools/ToolNCC.py:2247 +#, fuzzy +#| msgid "Painting polygon with method: lines." +msgid "Clearing polygon with method: lines." +msgstr "Polygon mit Methode malen: Linien." + +#: AppTools/ToolNCC.py:2257 +#, fuzzy +#| msgid "Failed. Painting polygon with method: seed." +msgid "Failed. Clearing polygon with method: seed." +msgstr "Gescheitert. Polygon mit Methode malen: Same." + +#: AppTools/ToolNCC.py:2266 +#, fuzzy +#| msgid "Failed. Painting polygon with method: standard." +msgid "Failed. Clearing polygon with method: standard." +msgstr "Gescheitert. Polygon mit Methode malen: Standard." + +#: AppTools/ToolNCC.py:2280 +#, fuzzy +#| msgid "Geometry could not be painted completely" +msgid "Geometry could not be cleared completely" +msgstr "Geometrie konnte nicht vollständig gemalt werden" + +#: AppTools/ToolNCC.py:2305 AppTools/ToolNCC.py:2307 AppTools/ToolNCC.py:2962 +#: AppTools/ToolNCC.py:2964 +msgid "Non-Copper clearing ..." +msgstr "Nicht-Kupfer-Clearing ..." + +#: AppTools/ToolNCC.py:2354 AppTools/ToolNCC.py:3106 +msgid "" +"NCC Tool. Finished non-copper polygons. Normal copper clearing task started." +msgstr "" +"NCC-Tool. Fertige kupferfreie Polygone. Normale Kupferentfernungsaufgabe " +"gestartet." + +#: AppTools/ToolNCC.py:2390 AppTools/ToolNCC.py:2638 +msgid "NCC Tool failed creating bounding box." +msgstr "Das NCC-Tool konnte keinen Begrenzungsrahmen erstellen." + +#: AppTools/ToolNCC.py:2405 AppTools/ToolNCC.py:2655 AppTools/ToolNCC.py:3302 +#: AppTools/ToolNCC.py:3688 +msgid "NCC Tool clearing with tool diameter" +msgstr "Das NCC-Werkzeug wird mit dem Werkzeugdurchmesser gelöscht" + +#: AppTools/ToolNCC.py:2405 AppTools/ToolNCC.py:2655 AppTools/ToolNCC.py:3302 +#: AppTools/ToolNCC.py:3688 +msgid "started." +msgstr "gestartet." + +#: AppTools/ToolNCC.py:2563 AppTools/ToolNCC.py:3463 +msgid "" +"There is no NCC Geometry in the file.\n" +"Usually it means that the tool diameter is too big for the painted " +"geometry.\n" +"Change the painting parameters and try again." +msgstr "" +"Die Datei enthält keine NCC-Geometrie.\n" +"In der Regel bedeutet dies, dass der Werkzeugdurchmesser für die lackierte " +"Geometrie zu groß ist.\n" +"Ändern Sie die Malparameter und versuchen Sie es erneut." + +#: AppTools/ToolNCC.py:2572 AppTools/ToolNCC.py:3472 +msgid "NCC Tool clear all done." +msgstr "NCC Tool löschen alles erledigt." + +#: AppTools/ToolNCC.py:2575 AppTools/ToolNCC.py:3475 +msgid "NCC Tool clear all done but the copper features isolation is broken for" +msgstr "" +"Das NCC-Tool löscht alles, aber die Isolierung der Kupfermerkmale ist " +"unterbrochen" + +#: AppTools/ToolNCC.py:2577 AppTools/ToolNCC.py:2863 AppTools/ToolNCC.py:3477 +#: AppTools/ToolNCC.py:3860 +msgid "tools" +msgstr "Werkzeuge" + +#: AppTools/ToolNCC.py:2859 AppTools/ToolNCC.py:3856 +msgid "NCC Tool Rest Machining clear all done." +msgstr "Die Bearbeitung der NCC-Werkzeugablagen ist abgeschlossen." + +#: AppTools/ToolNCC.py:2862 AppTools/ToolNCC.py:3859 +msgid "" +"NCC Tool Rest Machining clear all done but the copper features isolation is " +"broken for" +msgstr "" +"Die Bearbeitung der NCC-Werkzeugablagen ist abgeschlossen, die Isolierung " +"der Kupferelemente ist jedoch unterbrochen" + +#: AppTools/ToolNCC.py:2974 +msgid "NCC Tool started. Reading parameters." +msgstr "NCC Tool gestartet. Parameter lesen." + +#: AppTools/ToolNCC.py:3958 +msgid "" +"Try to use the Buffering Type = Full in Preferences -> Gerber General. " +"Reload the Gerber file after this change." +msgstr "" +"Versuchen Sie, den Puffertyp = Voll in Einstellungen -> Allgemein zu " +"verwenden. Laden Sie die Gerber-Datei nach dieser Änderung neu." + +#: AppTools/ToolNCC.py:4022 AppTools/ToolPaint.py:3565 App_Main.py:5251 +msgid "Tool from DB added in Tool Table." +msgstr "Werkzeug aus Werkzeugdatenbank zur Werkzeugtabelle hinzugefügt." + +#: AppTools/ToolOptimal.py:79 +msgid "Number of decimals kept for found distances." +msgstr "Anzahl der Dezimalstellen für gefundene Entfernungen." + +#: AppTools/ToolOptimal.py:87 +msgid "Minimum distance" +msgstr "Mindestabstand" + +#: AppTools/ToolOptimal.py:88 +msgid "Display minimum distance between copper features." +msgstr "Zeigt den Mindestabstand zwischen Kupferelementen an." + +#: AppTools/ToolOptimal.py:92 +msgid "Determined" +msgstr "Entschlossen" + +#: AppTools/ToolOptimal.py:106 +msgid "Occurring" +msgstr "Vorkommen" + +#: AppTools/ToolOptimal.py:107 +msgid "How many times this minimum is found." +msgstr "Wie oft wird dieses Minimum gefunden." + +#: AppTools/ToolOptimal.py:113 +msgid "Minimum points coordinates" +msgstr "Minimale Punktkoordinaten" + +#: AppTools/ToolOptimal.py:114 AppTools/ToolOptimal.py:120 +msgid "Coordinates for points where minimum distance was found." +msgstr "Koordinaten für Punkte, an denen der Mindestabstand gefunden wurde." + +#: AppTools/ToolOptimal.py:133 AppTools/ToolOptimal.py:209 +msgid "Jump to selected position" +msgstr "Zur ausgewählten Position springen" + +#: AppTools/ToolOptimal.py:135 AppTools/ToolOptimal.py:211 +msgid "" +"Select a position in the Locations text box and then\n" +"click this button." +msgstr "" +"Wählen Sie eine Position im Textfeld Standorte und dann\n" +"Klicken Sie auf diese Schaltfläche." + +#: AppTools/ToolOptimal.py:143 +msgid "Other distances" +msgstr "Andere Entfernungen" + +#: AppTools/ToolOptimal.py:144 +msgid "" +"Will display other distances in the Gerber file ordered from\n" +"the minimum to the maximum, not including the absolute minimum." +msgstr "" +"Zeigt andere Entfernungen in der von bestellten Gerber-Datei an\n" +"das Minimum bis zum Maximum, ohne das absolute Minimum." + +#: AppTools/ToolOptimal.py:149 +msgid "Other distances points coordinates" +msgstr "Andere Entfernungen Punkte Koordinaten" + +#: AppTools/ToolOptimal.py:150 AppTools/ToolOptimal.py:164 +#: AppTools/ToolOptimal.py:171 AppTools/ToolOptimal.py:188 +#: AppTools/ToolOptimal.py:195 +msgid "" +"Other distances and the coordinates for points\n" +"where the distance was found." +msgstr "" +"Andere Entfernungen und die Koordinaten für Punkte\n" +"wo die Entfernung gefunden wurde." + +#: AppTools/ToolOptimal.py:163 +msgid "Gerber distances" +msgstr "Gerber Entfernungen" + +#: AppTools/ToolOptimal.py:187 +msgid "Points coordinates" +msgstr "Punktkoordinaten" + +#: AppTools/ToolOptimal.py:219 +msgid "Find Minimum" +msgstr "Minimum finden" + +#: AppTools/ToolOptimal.py:221 +msgid "" +"Calculate the minimum distance between copper features,\n" +"this will allow the determination of the right tool to\n" +"use for isolation or copper clearing." +msgstr "" +"Berechnen Sie den Mindestabstand zwischen Kupferelementen.\n" +"Dies ermöglicht die Bestimmung des richtigen Werkzeugs\n" +"Verwendung zur Isolierung oder zum Löschen von Kupfer." + +#: AppTools/ToolOptimal.py:346 +msgid "Only Gerber objects can be evaluated." +msgstr "Es können nur Gerber-Objekte ausgewertet werden." + +#: AppTools/ToolOptimal.py:352 +msgid "" +"Optimal Tool. Started to search for the minimum distance between copper " +"features." +msgstr "Optimierer. Sucht Minimalabstand zwischen Kupferbereichen." + +#: AppTools/ToolOptimal.py:362 +msgid "Optimal Tool. Parsing geometry for aperture" +msgstr "Optimales Werkzeug. Analysegeometrie für Blende" + +#: AppTools/ToolOptimal.py:373 +msgid "Optimal Tool. Creating a buffer for the object geometry." +msgstr "Optimales Werkzeug. Erstellen eines Puffers für die Objektgeometrie." + +#: AppTools/ToolOptimal.py:383 +msgid "" +"The Gerber object has one Polygon as geometry.\n" +"There are no distances between geometry elements to be found." +msgstr "" +"Das Gerber-Objekt hat ein Polygon als Geometrie.\n" +"Es sind keine Abstände zwischen Geometrieelementen zu finden." + +#: AppTools/ToolOptimal.py:388 +msgid "" +"Optimal Tool. Finding the distances between each two elements. Iterations" +msgstr "" +"Optimales Werkzeug. Finden der Abstände zwischen jeweils zwei Elementen. " +"Iterationen" + +#: AppTools/ToolOptimal.py:423 +msgid "Optimal Tool. Finding the minimum distance." +msgstr "Optimales Werkzeug. Den Mindestabstand finden." + +#: AppTools/ToolOptimal.py:439 +msgid "Optimal Tool. Finished successfully." +msgstr "Optimales Werkzeug. Erfolgreich beendet." + +#: AppTools/ToolPDF.py:91 AppTools/ToolPDF.py:95 +msgid "Open PDF" +msgstr "PDF öffnen" + +#: AppTools/ToolPDF.py:98 +msgid "Open PDF cancelled" +msgstr "PDF öffnen abgebrochen" + +#: AppTools/ToolPDF.py:122 +msgid "Parsing PDF file ..." +msgstr "PDF-Datei wird analysiert ..." + +#: AppTools/ToolPDF.py:138 App_Main.py:8497 +msgid "Failed to open" +msgstr "Gescheitert zu öffnen" + +#: AppTools/ToolPDF.py:203 AppTools/ToolPcbWizard.py:445 App_Main.py:8446 +msgid "No geometry found in file" +msgstr "Keine Geometrie in der Datei gefunden" + +#: AppTools/ToolPDF.py:206 AppTools/ToolPDF.py:279 +#, python-format +msgid "Rendering PDF layer #%d ..." +msgstr "PDF-Ebene rendern #%d ..." + +#: AppTools/ToolPDF.py:210 AppTools/ToolPDF.py:283 +msgid "Open PDF file failed." +msgstr "Öffnen der PDF-Datei fehlgeschlagen." + +#: AppTools/ToolPDF.py:215 AppTools/ToolPDF.py:288 +msgid "Rendered" +msgstr "Gerendert" + +#: AppTools/ToolPaint.py:81 +msgid "" +"Specify the type of object to be painted.\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 gemalt werden soll.\n" +"Es kann vom Typ Gerber oder Geometrie sein.\n" +"Was hier ausgewählt wird, bestimmt die Art\n" +"von Objekten, die das Kombinationsfeld \"Objekt\" füllen." + +#: AppTools/ToolPaint.py:103 +msgid "Object to be painted." +msgstr "Gegenstand gemalt werden." + +#: AppTools/ToolPaint.py:116 +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." + +#: AppTools/ToolPaint.py:133 +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." + +#: AppTools/ToolPaint.py:145 +msgid "" +"The Tool Type (TT) can be:\n" +"- Circular -> it is informative only. Being circular,\n" +"the cut width in material is exactly the tool diameter.\n" +"- Ball -> informative only and make reference to the Ball type endmill.\n" +"- V-Shape -> it will disable Z-Cut parameter in the resulting geometry UI " +"form\n" +"and enable two additional UI form fields in the resulting geometry: V-Tip " +"Dia and\n" +"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\n" +"column of this table.\n" +"Choosing the 'V-Shape' Tool Type automatically will select the Operation " +"Type\n" +"in the resulting geometry as Isolation." +msgstr "" +"Der Werkzeugtyp (TT) kann sein:\n" +"- Rundschreiben mit 1 ... 4 Zähnen -> nur informativ. Rundschreiben,\n" +"Die Schnittbreite im Material entspricht genau dem Werkzeugdurchmesser.\n" +"- Ball -> nur informativ und auf den Ball-Schaftfräser verweisen.\n" +"- V-Form -> Deaktiviert den Z-Cut-Parameter in der resultierenden Geometrie-" +"UI-Form\n" +"und aktivieren Sie zwei zusätzliche UI-Formularfelder in der resultierenden " +"Geometrie: V-Tip Dia und\n" +"V-Tip-Winkel. Durch Anpassen dieser beiden Werte wird der Z-Cut-Parameter " +"wie z\n" +"da die Schnittbreite in Material gleich dem Wert im Werkzeugdurchmesser ist\n" +"Spalte dieser Tabelle.\n" +"Durch automatische Auswahl des Werkzeugtyps \"V-Form\" wird der " +"Operationstyp ausgewählt\n" +"in der resultierenden Geometrie als Isolation." + +#: AppTools/ToolPaint.py:497 +msgid "" +"The type of FlatCAM object to be used as paint reference.\n" +"It can be Gerber, Excellon or Geometry." +msgstr "" +"Der Typ des FlatCAM-Objekts, das als Malreferenz verwendet werden soll.\n" +"Es kann Gerber, Excellon oder Geometry sein." + +#: AppTools/ToolPaint.py:538 +msgid "" +"- 'Area Selection' - left mouse click to start selection of the area to be " +"painted.\n" +"Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple " +"areas.\n" +"- 'All Polygons' - the Paint will start after click.\n" +"- 'Reference Object' - will do non copper clearing within the area\n" +"specified by another object." +msgstr "" +"- 'Bereichsauswahl' - Klicken Sie mit der linken Maustaste, um den Bereich " +"auszuwählen, der gemalt werden soll.\n" +"Wenn Sie eine Änderungstaste gedrückt halten (STRG oder UMSCHALTTASTE), " +"können Sie mehrere Bereiche hinzufügen.\n" +"- 'Alle Polygone' - Der Malvorgang wird nach dem Klicken gestartet.\n" +"- 'Referenzobjekt' - löscht nicht kupferne Objekte innerhalb des Bereichs\n" +"von einem anderen Objekt angegeben." + +#: AppTools/ToolPaint.py:1392 +#, python-format +msgid "Could not retrieve object: %s" +msgstr "Objekt konnte nicht abgerufen werden: %s" + +#: AppTools/ToolPaint.py:1402 +msgid "Can't do Paint on MultiGeo geometries" +msgstr "Auf MultiGeo-Geometrien kann nicht gemalt werden" + +#: AppTools/ToolPaint.py:1432 +msgid "Click on a polygon to paint it." +msgstr "Klicken Sie auf ein Polygon um es auszufüllen." + +#: AppTools/ToolPaint.py:1452 +msgid "Click the start point of the paint area." +msgstr "Klicken Sie auf den Startpunkt des Malbereichs." + +#: AppTools/ToolPaint.py:1517 +msgid "Click to add next polygon or right click to start painting." +msgstr "" +"Klicken Sie, um die nächste Zone hinzuzufügen, oder klicken Sie mit der " +"rechten Maustaste um mit dem Ausfüllen zu beginnen." + +#: AppTools/ToolPaint.py:1530 +msgid "Click to add/remove next polygon or right click to start painting." +msgstr "" +"Klicken Sie, um die nächste Zone hinzuzufügen oder zu löschen, oder klicken " +"Sie mit der rechten Maustaste, um den Vorgang abzuschließen." + +#: AppTools/ToolPaint.py:2034 +msgid "Painting polygon with method: lines." +msgstr "Polygon mit Methode malen: Linien." + +#: AppTools/ToolPaint.py:2046 +msgid "Failed. Painting polygon with method: seed." +msgstr "Gescheitert. Polygon mit Methode malen: Same." + +#: AppTools/ToolPaint.py:2057 +msgid "Failed. Painting polygon with method: standard." +msgstr "Gescheitert. Polygon mit Methode malen: Standard." + +#: AppTools/ToolPaint.py:2073 +msgid "Geometry could not be painted completely" +msgstr "Geometrie konnte nicht vollständig gemalt werden" + +#: AppTools/ToolPaint.py:2102 AppTools/ToolPaint.py:2105 +#: AppTools/ToolPaint.py:2113 AppTools/ToolPaint.py:2416 +#: AppTools/ToolPaint.py:2419 AppTools/ToolPaint.py:2427 +#: AppTools/ToolPaint.py:2915 AppTools/ToolPaint.py:2918 +#: AppTools/ToolPaint.py:2924 +msgid "Paint Tool." +msgstr "Malwerkzeug." + +#: AppTools/ToolPaint.py:2102 AppTools/ToolPaint.py:2105 +#: AppTools/ToolPaint.py:2113 +msgid "Normal painting polygon task started." +msgstr "Normale Zeichenpolygonaufgabe gestartet." + +#: AppTools/ToolPaint.py:2103 AppTools/ToolPaint.py:2417 +#: AppTools/ToolPaint.py:2916 +msgid "Buffering geometry..." +msgstr "Geometrie puffern..." + +#: AppTools/ToolPaint.py:2125 AppTools/ToolPaint.py:2434 +#: AppTools/ToolPaint.py:2932 +msgid "No polygon found." +msgstr "Kein Polygon gefunden." + +#: AppTools/ToolPaint.py:2155 +msgid "Painting polygon..." +msgstr "Polygon malen ..." + +#: AppTools/ToolPaint.py:2165 AppTools/ToolPaint.py:2480 +#: AppTools/ToolPaint.py:2670 AppTools/ToolPaint.py:2978 +#: AppTools/ToolPaint.py:3157 +msgid "Painting with tool diameter = " +msgstr "Lackieren mit Werkzeugdurchmesser = " + +#: AppTools/ToolPaint.py:2166 AppTools/ToolPaint.py:2481 +#: AppTools/ToolPaint.py:2671 AppTools/ToolPaint.py:2979 +#: AppTools/ToolPaint.py:3158 +msgid "started" +msgstr "gestartet" + +#: AppTools/ToolPaint.py:2191 AppTools/ToolPaint.py:2507 +#: AppTools/ToolPaint.py:2697 AppTools/ToolPaint.py:3005 +#: AppTools/ToolPaint.py:3184 +msgid "Margin parameter too big. Tool is not used" +msgstr "Randparameter zu groß. Werkzeug wird nicht verwendet" + +#: AppTools/ToolPaint.py:2249 AppTools/ToolPaint.py:2576 +#: AppTools/ToolPaint.py:2754 AppTools/ToolPaint.py:3068 +#: AppTools/ToolPaint.py:3246 +msgid "" +"Could not do Paint. Try a different combination of parameters. Or a " +"different strategy of paint" +msgstr "" +"Konnte nicht malen. Probieren Sie eine andere Kombination von Parametern " +"aus. Oder eine andere Strategie der Farbe" + +#: AppTools/ToolPaint.py:2306 AppTools/ToolPaint.py:2642 +#: AppTools/ToolPaint.py:2811 AppTools/ToolPaint.py:3129 +#: AppTools/ToolPaint.py:3308 +msgid "" +"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 "" +"Die Datei enthält keine Malgeometrie.\n" +"Normalerweise bedeutet dies, dass der Werkzeugdurchmesser für die lackierte " +"Geometrie zu groß ist.\n" +"Ändern Sie die Malparameter und versuchen Sie es erneut." + +#: AppTools/ToolPaint.py:2329 +msgid "Paint Single failed." +msgstr "Das Malen eines einzelnen Polygons ist fehlgeschlagen." + +#: AppTools/ToolPaint.py:2335 +msgid "Paint Single Done." +msgstr "Malen Sie Single Done." + +#: AppTools/ToolPaint.py:2337 AppTools/ToolPaint.py:2847 +#: AppTools/ToolPaint.py:3344 +msgid "Polygon Paint started ..." +msgstr "Polygonfarbe gestartet ..." + +#: AppTools/ToolPaint.py:2416 AppTools/ToolPaint.py:2419 +#: AppTools/ToolPaint.py:2427 +msgid "Paint all polygons task started." +msgstr "Malen Sie alle Polygone Aufgabe gestartet." + +#: AppTools/ToolPaint.py:2458 AppTools/ToolPaint.py:2956 +msgid "Painting polygons..." +msgstr "Polygone malen ..." + +#: AppTools/ToolPaint.py:2651 +msgid "Paint All Done." +msgstr "Malen Sie alles fertig." + +#: AppTools/ToolPaint.py:2820 AppTools/ToolPaint.py:3317 +msgid "Paint All with Rest-Machining done." +msgstr "Malen Sie alles mit Restbearbeitung." + +#: AppTools/ToolPaint.py:2839 +msgid "Paint All failed." +msgstr "Malen Alle Polygone sind fehlgeschlagen." + +#: AppTools/ToolPaint.py:2845 +msgid "Paint Poly All Done." +msgstr "Malen Sie alle Polygone fertig." + +#: AppTools/ToolPaint.py:2915 AppTools/ToolPaint.py:2918 +#: AppTools/ToolPaint.py:2924 +msgid "Painting area task started." +msgstr "Malbereichsaufgabe gestartet." + +#: AppTools/ToolPaint.py:3138 +msgid "Paint Area Done." +msgstr "Lackierbereich fertig." + +#: AppTools/ToolPaint.py:3336 +msgid "Paint Area failed." +msgstr "Lackierbereich fehlgeschlagen." + +#: AppTools/ToolPaint.py:3342 +msgid "Paint Poly Area Done." +msgstr "Lackierbereich fertig." + +#: AppTools/ToolPanelize.py:35 +msgid "Panelize PCB" +msgstr "Panelisierung PCB" + +#: AppTools/ToolPanelize.py:55 +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." + +#: AppTools/ToolPanelize.py:88 +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." + +#: AppTools/ToolPanelize.py:100 +msgid "Penelization Reference" +msgstr "Penelisierungshinweis" + +#: AppTools/ToolPanelize.py:102 +msgid "" +"Choose the reference for panelization:\n" +"- Object = the bounding box of a different object\n" +"- Bounding Box = the bounding box of the object to be panelized\n" +"\n" +"The reference is useful when doing panelization for more than one\n" +"object. The spacings (really offsets) will be applied in reference\n" +"to this reference object therefore maintaining the panelized\n" +"objects in sync." +msgstr "" +"Wählen Sie die Referenz für die Panelisierung:\n" +"- Objekt = der Begrenzungsrahmen eines anderen Objekts\n" +"- Begrenzungsrahmen = Der Begrenzungsrahmen des zu verkleidenden Objekts\n" +"\n" +"Diese Referenz ist nützlich, wenn Sie Panels für mehr als einen erstellen\n" +"Objekt. Die Abstände (wirklich Versätze) werden als Referenz angewendet\n" +"Zu diesem Referenzobjekt gehört daher die Beibehaltung der getäfelten\n" +"Objekte synchronisieren." + +#: AppTools/ToolPanelize.py:123 +msgid "Box Type" +msgstr "Box-Typ" + +#: AppTools/ToolPanelize.py:125 +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." + +#: AppTools/ToolPanelize.py:139 +msgid "" +"The actual object that is used as 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." + +#: AppTools/ToolPanelize.py:149 +msgid "Panel Data" +msgstr "Paneldaten" + +#: AppTools/ToolPanelize.py:151 +msgid "" +"This informations will shape the resulting panel.\n" +"The number of rows and columns will set how many\n" +"duplicates of the original geometry will be generated.\n" +"\n" +"The spacings will set the distance between any two\n" +"elements of the panel array." +msgstr "" +"Diese Informationen formen das resultierende Panel.\n" +"Die Anzahl der Zeilen und Spalten legt fest, wie viele\n" +"Duplikate der ursprünglichen Geometrie werden generiert.\n" +"\n" +"Die Abstände bestimmen den Abstand zwischen zwei Elementen\n" +"Elemente des Panel-Arrays." + +#: AppTools/ToolPanelize.py:214 +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" + +#: AppTools/ToolPanelize.py:222 +msgid "Constrain panel within" +msgstr "Panel einschränken innerhalb" + +#: AppTools/ToolPanelize.py:263 +msgid "Panelize Object" +msgstr "Panelize Objekt" + +#: AppTools/ToolPanelize.py:265 AppTools/ToolRulesCheck.py:501 +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." + +#: AppTools/ToolPanelize.py:333 +msgid "Panel. Tool" +msgstr "Platte Werkzeug" + +#: AppTools/ToolPanelize.py:468 +msgid "Columns or Rows are zero value. Change them to a positive integer." +msgstr "" +"Spalten oder Zeilen haben den Wert Null. Ändern Sie sie in eine positive " +"Ganzzahl." + +#: AppTools/ToolPanelize.py:505 +msgid "Generating panel ... " +msgstr "Panel wird erstellt ... " + +#: AppTools/ToolPanelize.py:788 +msgid "Generating panel ... Adding the Gerber code." +msgstr "Panel wird generiert ... Hinzufügen des Gerber-Codes." + +#: AppTools/ToolPanelize.py:796 +msgid "Generating panel... Spawning copies" +msgstr "Panel wird erstellt ... Kopien werden erstellt" + +#: AppTools/ToolPanelize.py:803 +msgid "Panel done..." +msgstr "Panel fertig ..." + +#: AppTools/ToolPanelize.py:806 +#, python-brace-format +msgid "" +"{text} Too big for the constrain area. Final panel has {col} columns and " +"{row} rows" +msgstr "" +"{text} Zu groß für den Einschränkungsbereich. Das letzte Panel enthält {col} " +"Spalten und {row} Zeilen" + +#: AppTools/ToolPanelize.py:815 +msgid "Panel created successfully." +msgstr "Panel erfolgreich erstellt." + +#: AppTools/ToolPcbWizard.py:31 +msgid "PcbWizard Import Tool" +msgstr "PCBWizard Werkzeug importieren" + +#: AppTools/ToolPcbWizard.py:40 +msgid "Import 2-file Excellon" +msgstr "Importieren Sie 2-Datei-Excellon" + +#: AppTools/ToolPcbWizard.py:51 +msgid "Load files" +msgstr "Dateien laden" + +#: AppTools/ToolPcbWizard.py:57 +msgid "Excellon file" +msgstr "Excellon-Datei" + +#: AppTools/ToolPcbWizard.py:59 +msgid "" +"Load the Excellon file.\n" +"Usually it has a .DRL extension" +msgstr "" +"Laden Sie die Excellon-Datei.\n" +"Normalerweise hat es die Erweiterung .DRL" + +#: AppTools/ToolPcbWizard.py:65 +msgid "INF file" +msgstr "INF-Datei" + +#: AppTools/ToolPcbWizard.py:67 +msgid "Load the INF file." +msgstr "Laden Sie die INF-Datei." + +#: AppTools/ToolPcbWizard.py:79 +msgid "Tool Number" +msgstr "Werkzeugnummer" + +#: AppTools/ToolPcbWizard.py:81 +msgid "Tool diameter in file units." +msgstr "Werkzeugdurchmesser in Feileneinheiten." + +#: AppTools/ToolPcbWizard.py:87 +msgid "Excellon format" +msgstr "Excellon format" + +#: AppTools/ToolPcbWizard.py:95 +msgid "Int. digits" +msgstr "Ganzzahlige Ziffern" + +#: AppTools/ToolPcbWizard.py:97 +msgid "The number of digits for the integral part of the coordinates." +msgstr "Die Anzahl der Ziffern für den integralen Teil der Koordinaten." + +#: AppTools/ToolPcbWizard.py:104 +msgid "Frac. digits" +msgstr "Nachkommastellen" + +#: AppTools/ToolPcbWizard.py:106 +msgid "The number of digits for the fractional part of the coordinates." +msgstr "Die Anzahl der Stellen für den gebrochenen Teil der Koordinaten." + +#: AppTools/ToolPcbWizard.py:113 +msgid "No Suppression" +msgstr "Keine Unterdrück" + +#: AppTools/ToolPcbWizard.py:114 +msgid "Zeros supp." +msgstr "Nullunterdrück." + +#: AppTools/ToolPcbWizard.py:116 +msgid "" +"The type of zeros suppression used.\n" +"Can be of type:\n" +"- LZ = leading zeros are kept\n" +"- TZ = trailing zeros are kept\n" +"- No Suppression = no zero suppression" +msgstr "" +"Die Art der Unterdrückung von Nullen.\n" +"Kann vom Typ sein:\n" +"- LZ = führende Nullen werden beibehalten\n" +"- TZ = nachfolgende Nullen bleiben erhalten\n" +"- Keine Unterdrückung = keine Nullunterdrückung" + +#: AppTools/ToolPcbWizard.py:129 +msgid "" +"The type of units that the coordinates and tool\n" +"diameters are using. Can be INCH or MM." +msgstr "" +"Die Art der Einheiten, die die Koordinaten und das Werkzeug haben\n" +"Durchmesser verwenden. Kann INCH oder MM sein." + +#: AppTools/ToolPcbWizard.py:136 +msgid "Import Excellon" +msgstr "Excellon importieren" + +#: AppTools/ToolPcbWizard.py:138 +msgid "" +"Import in FlatCAM an Excellon file\n" +"that store it's information's in 2 files.\n" +"One usually has .DRL extension while\n" +"the other has .INF extension." +msgstr "" +"Importieren Sie in FlatCAM eine Excellon-Datei\n" +"das speichert seine Informationen in 2 Dateien.\n" +"Normalerweise hat man eine .DRL-Erweiterung\n" +"der andere hat die Erweiterung .INF." + +#: AppTools/ToolPcbWizard.py:197 +msgid "PCBWizard Tool" +msgstr "PCBWizard Werkzeug" + +#: AppTools/ToolPcbWizard.py:291 AppTools/ToolPcbWizard.py:295 +msgid "Load PcbWizard Excellon file" +msgstr "PcbWizard Excellon-Datei laden" + +#: AppTools/ToolPcbWizard.py:314 AppTools/ToolPcbWizard.py:318 +msgid "Load PcbWizard INF file" +msgstr "Laden Sie die PcbWizard INF-Datei" + +#: AppTools/ToolPcbWizard.py:366 +msgid "" +"The INF file does not contain the tool table.\n" +"Try to open the Excellon file from File -> Open -> Excellon\n" +"and edit the drill diameters manually." +msgstr "" +"Die INF-Datei enthält keine Werkzeugtabelle.\n" +"Versuchen Sie, die Excellon-Datei über Datei -> Öffnen -> Excellon zu " +"öffnen\n" +"und bearbeiten Sie die Bohrerdurchmesser manuell." + +#: AppTools/ToolPcbWizard.py:387 +msgid "PcbWizard .INF file loaded." +msgstr "PcbWizard-INF-Datei wurde geladen." + +#: AppTools/ToolPcbWizard.py:392 +msgid "Main PcbWizard Excellon file loaded." +msgstr "Haupt-PcbWizard Excellon-Datei geladen." + +#: AppTools/ToolPcbWizard.py:424 App_Main.py:8424 +msgid "This is not Excellon file." +msgstr "Dies ist keine Excellon-Datei." + +#: AppTools/ToolPcbWizard.py:427 +msgid "Cannot parse file" +msgstr "Datei kann nicht analysiert werden" + +#: AppTools/ToolPcbWizard.py:450 +msgid "Importing Excellon." +msgstr "Excellon importieren." + +#: AppTools/ToolPcbWizard.py:457 +msgid "Import Excellon file failed." +msgstr "Import der Excellon-Datei ist fehlgeschlagen." + +#: AppTools/ToolPcbWizard.py:464 +msgid "Imported" +msgstr "Importiert" + +#: AppTools/ToolPcbWizard.py:467 +msgid "Excellon merging is in progress. Please wait..." +msgstr "Das Zusammenführen von Excellons ist im Gange. Warten Sie mal..." + +#: AppTools/ToolPcbWizard.py:469 +msgid "The imported Excellon file is empty." +msgstr "Die importierte Excellon-Datei ist Keine." + +#: AppTools/ToolProperties.py:116 App_Main.py:4664 App_Main.py:6718 +#: App_Main.py:6813 App_Main.py:6854 App_Main.py:6895 App_Main.py:6936 +#: App_Main.py:6977 App_Main.py:7021 App_Main.py:7065 App_Main.py:7585 +#: App_Main.py:7589 +msgid "No object selected." +msgstr "Kein Objekt ausgewählt." + +#: AppTools/ToolProperties.py:131 +msgid "Object Properties are displayed." +msgstr "Objekteigenschaften werden angezeigt." + +#: AppTools/ToolProperties.py:136 +msgid "Properties Tool" +msgstr "Eigenschaftenwerkzeug" + +#: AppTools/ToolProperties.py:150 +msgid "TYPE" +msgstr "TYP" + +#: AppTools/ToolProperties.py:151 +msgid "NAME" +msgstr "NAME" + +#: AppTools/ToolProperties.py:153 +msgid "Dimensions" +msgstr "Dimensionen" + +#: AppTools/ToolProperties.py:181 +msgid "Geo Type" +msgstr "Geo-Typ" + +#: AppTools/ToolProperties.py:184 +msgid "Single-Geo" +msgstr "Einzehln Geo" + +#: AppTools/ToolProperties.py:185 +msgid "Multi-Geo" +msgstr "Mehrfache Geo" + +#: AppTools/ToolProperties.py:196 +msgid "Calculating dimensions ... Please wait." +msgstr "Bemaßung wird berechnet ... Bitte warten." + +#: AppTools/ToolProperties.py:339 AppTools/ToolProperties.py:343 +#: AppTools/ToolProperties.py:345 +msgid "Inch" +msgstr "Zoll" + +#: AppTools/ToolProperties.py:339 AppTools/ToolProperties.py:344 +#: AppTools/ToolProperties.py:346 +msgid "Metric" +msgstr "Metrisch" + +#: AppTools/ToolProperties.py:421 AppTools/ToolProperties.py:486 +msgid "Drills number" +msgstr "Bohrernummer" + +#: AppTools/ToolProperties.py:422 AppTools/ToolProperties.py:488 +msgid "Slots number" +msgstr "Slotnummer" + +#: AppTools/ToolProperties.py:424 +msgid "Drills total number:" +msgstr "Gesamtzahl Bohrer:" + +#: AppTools/ToolProperties.py:425 +msgid "Slots total number:" +msgstr "Gesamtzahl der slots:" + +#: AppTools/ToolProperties.py:452 AppTools/ToolProperties.py:455 +#: AppTools/ToolProperties.py:458 AppTools/ToolProperties.py:483 +msgid "Present" +msgstr "Vorhanden" + +#: AppTools/ToolProperties.py:453 AppTools/ToolProperties.py:484 +msgid "Solid Geometry" +msgstr "Festkörpergeometrie" + +#: AppTools/ToolProperties.py:456 +msgid "GCode Text" +msgstr "GCode Text" + +#: AppTools/ToolProperties.py:459 +msgid "GCode Geometry" +msgstr "GCode Geometrie" + +#: AppTools/ToolProperties.py:462 +msgid "Data" +msgstr "Daten" + +#: AppTools/ToolProperties.py:495 +msgid "Depth of Cut" +msgstr "Tiefe des Schnitts" + +#: AppTools/ToolProperties.py:507 +msgid "Clearance Height" +msgstr "Freilaufhöhe" + +#: AppTools/ToolProperties.py:539 +msgid "Routing time" +msgstr "Berechnungszeit" + +#: AppTools/ToolProperties.py:546 +msgid "Travelled distance" +msgstr "Zurückgelegte Strecke" + +#: AppTools/ToolProperties.py:564 +msgid "Width" +msgstr "Breite" + +#: AppTools/ToolProperties.py:570 AppTools/ToolProperties.py:578 +msgid "Box Area" +msgstr "Feld Bereich" + +#: AppTools/ToolProperties.py:573 AppTools/ToolProperties.py:581 +msgid "Convex_Hull Area" +msgstr "Konvexer Rumpfbereich" + +#: AppTools/ToolProperties.py:588 AppTools/ToolProperties.py:591 +msgid "Copper Area" +msgstr "Kupferareal" + +#: AppTools/ToolPunchGerber.py:30 AppTools/ToolPunchGerber.py:323 +msgid "Punch Gerber" +msgstr "Schlag Gerber" + +#: AppTools/ToolPunchGerber.py:65 +msgid "Gerber into which to punch holes" +msgstr "Gerber, in den Löcher gestanzt werden können" + +#: AppTools/ToolPunchGerber.py:85 +msgid "ALL" +msgstr "ALLE" + +#: AppTools/ToolPunchGerber.py:166 +msgid "" +"Remove the geometry of Excellon from the Gerber to create the holes in pads." +msgstr "" +"Entfernen Sie die Geometrie von Excellon aus dem Gerber, um die Löcher in " +"den Pads zu erstellen." + +#: AppTools/ToolPunchGerber.py:325 +msgid "" +"Create a Gerber object from the selected object, within\n" +"the specified box." +msgstr "" +"Erstellen Sie innerhalb des ausgewählten Objekts ein Gerber-Objekt\n" +"das angegebene Feld." + +#: AppTools/ToolPunchGerber.py:425 +msgid "Punch Tool" +msgstr "Stanzwerkzeug" + +#: AppTools/ToolPunchGerber.py:599 +msgid "The value of the fixed diameter is 0.0. Aborting." +msgstr "Der Wert des festen Durchmessers beträgt 0,0. Abbruch." + +#: AppTools/ToolPunchGerber.py:602 +msgid "" +"Could not generate punched hole Gerber because the punch hole size is bigger " +"than some of the apertures in the Gerber object." +msgstr "" +"Stanzloch Gerber konnte nicht generiert werden, da die Stanzlochgröße größer " +"ist als einige der Öffnungen im Gerber-Objekt." + +#: AppTools/ToolPunchGerber.py:665 +msgid "" +"Could not generate punched hole Gerber because the newly created object " +"geometry is the same as the one in the source object geometry..." +msgstr "" +"Stanzloch Gerber konnte nicht generiert werden, da die neu erstellte " +"Objektgeometrie mit der in der Quellobjektgeometrie übereinstimmt ..." + +#: AppTools/ToolQRCode.py:80 +msgid "Gerber Object to which the QRCode will be added." +msgstr "Gerber-Objekt zu dem der QRCode hinzugefügt wird." + +#: AppTools/ToolQRCode.py:93 +msgid "QRCode Parameters" +msgstr "QRCode Parameter" + +#: AppTools/ToolQRCode.py:95 +msgid "The parameters used to shape the QRCode." +msgstr "Parameter zum Aussehen des QRCodes." + +#: AppTools/ToolQRCode.py:207 +msgid "Export QRCode" +msgstr "QRCode exportieren" + +#: AppTools/ToolQRCode.py:209 +msgid "" +"Show a set of controls allowing to export the QRCode\n" +"to a SVG file or an PNG file." +msgstr "" +"Zeigt einen Satz von Bedienelementen um den QRCode\n" +"in eine SVG oder ein PNG File zu exportieren." + +#: AppTools/ToolQRCode.py:248 +msgid "Transparent back color" +msgstr "Transparente Hintergrundfarbe" + +#: AppTools/ToolQRCode.py:273 +msgid "Export QRCode SVG" +msgstr "QRCode als SVG exportieren" + +#: AppTools/ToolQRCode.py:275 +msgid "Export a SVG file with the QRCode content." +msgstr "Export als SVG Code mit dem QRCode Inhalt." + +#: AppTools/ToolQRCode.py:286 +msgid "Export QRCode PNG" +msgstr "G-Code als PNG exportieren" + +#: AppTools/ToolQRCode.py:288 +msgid "Export a PNG image file with the QRCode content." +msgstr "Exportiert den QRCode als PNG Datei." + +#: AppTools/ToolQRCode.py:299 +msgid "Insert QRCode" +msgstr "QRCode einfügen" + +#: AppTools/ToolQRCode.py:301 +msgid "Create the QRCode object." +msgstr "Erzeugen des QRCode Objektes." + +#: AppTools/ToolQRCode.py:415 AppTools/ToolQRCode.py:750 +#: AppTools/ToolQRCode.py:799 +msgid "Cancelled. There is no QRCode Data in the text box." +msgstr "Abgebrochen. Es befindet sich kein QRCode im Feld." + +#: AppTools/ToolQRCode.py:434 +msgid "Generating QRCode geometry" +msgstr "QRCode Geometrie erzeugen" + +#: AppTools/ToolQRCode.py:474 +msgid "Click on the Destination point ..." +msgstr "Klicken Sie auf den Zielpunkt ..." + +#: AppTools/ToolQRCode.py:589 +msgid "QRCode Tool done." +msgstr "QRCode Tool fertig." + +#: AppTools/ToolQRCode.py:782 AppTools/ToolQRCode.py:786 +msgid "Export PNG" +msgstr "PNG exportieren" + +#: AppTools/ToolQRCode.py:829 AppTools/ToolQRCode.py:833 App_Main.py:6746 +#: App_Main.py:6750 +msgid "Export SVG" +msgstr "SVG exportieren" + +#: AppTools/ToolRulesCheck.py:33 +msgid "Check Rules" +msgstr "Überprüfen Sie die Regeln" + +#: AppTools/ToolRulesCheck.py:61 +msgid "Gerber Files" +msgstr "Gerber-Dateien" + +#: AppTools/ToolRulesCheck.py:63 +msgid "Gerber objects for which to check rules." +msgstr "Gerber-Objekte, für die Regeln überprüft werden sollen." + +#: AppTools/ToolRulesCheck.py:78 +msgid "Top" +msgstr "Oberst" + +#: AppTools/ToolRulesCheck.py:80 +msgid "The Top Gerber Copper object for which rules are checked." +msgstr "Das Top Gerber Copper-Objekt, für das Regeln überprüft werden." + +#: AppTools/ToolRulesCheck.py:96 +msgid "Bottom" +msgstr "Unterseite" + +#: AppTools/ToolRulesCheck.py:98 +msgid "The Bottom Gerber Copper object for which rules are checked." +msgstr "Das untere Gerber Copper-Objekt, für das Regeln überprüft werden." + +#: AppTools/ToolRulesCheck.py:114 +msgid "SM Top" +msgstr "SM Oberst" + +#: AppTools/ToolRulesCheck.py:116 +msgid "The Top Gerber Solder Mask object for which rules are checked." +msgstr "Das oberste Gerber-Lötmaskenobjekt, für das Regeln überprüft werden." + +#: AppTools/ToolRulesCheck.py:132 +msgid "SM Bottom" +msgstr "SM unten" + +#: AppTools/ToolRulesCheck.py:134 +msgid "The Bottom Gerber Solder Mask object for which rules are checked." +msgstr "Das untere Gerber-Lötmaskenobjekt, für das Regeln überprüft werden." + +#: AppTools/ToolRulesCheck.py:150 +msgid "Silk Top" +msgstr "Siebdruck Oben" + +#: AppTools/ToolRulesCheck.py:152 +msgid "The Top Gerber Silkscreen object for which rules are checked." +msgstr "Das oberste Gerber-Siebdruck-Objekt, für das Regeln überprüft werden." + +#: AppTools/ToolRulesCheck.py:168 +msgid "Silk Bottom" +msgstr "Siebdruck unten" + +#: AppTools/ToolRulesCheck.py:170 +msgid "The Bottom Gerber Silkscreen object for which rules are checked." +msgstr "Das untere Gerber-Siebdruck-Objekt, für das Regeln überprüft werden." + +#: AppTools/ToolRulesCheck.py:188 +msgid "The Gerber Outline (Cutout) object for which rules are checked." +msgstr "" +"Das Gerber-Gliederungsobjekt (Ausschnitt), für das Regeln überprüft werden." + +#: AppTools/ToolRulesCheck.py:199 +msgid "Excellon Objects" +msgstr "Excellon-Objekte" + +#: AppTools/ToolRulesCheck.py:201 +msgid "Excellon objects for which to check rules." +msgstr "Excellon-Objekte, für die Regeln überprüft werden sollen." + +#: AppTools/ToolRulesCheck.py:213 +msgid "Excellon 1" +msgstr "Excellon 1" + +#: AppTools/ToolRulesCheck.py:215 +msgid "" +"Excellon object for which to check rules.\n" +"Holds the plated holes or a general Excellon file content." +msgstr "" +"Excellon-Objekt, für das Regeln überprüft werden sollen.\n" +"Enthält die plattierten Löcher oder einen allgemeinen Excellon-Dateiinhalt." + +#: AppTools/ToolRulesCheck.py:232 +msgid "Excellon 2" +msgstr "Excellon 2" + +#: AppTools/ToolRulesCheck.py:234 +msgid "" +"Excellon object for which to check rules.\n" +"Holds the non-plated holes." +msgstr "" +"Excellon-Objekt, für das Regeln überprüft werden sollen.\n" +"Hält die nicht plattierten Löcher." + +#: AppTools/ToolRulesCheck.py:247 +msgid "All Rules" +msgstr "Alle Regeln" + +#: AppTools/ToolRulesCheck.py:249 +msgid "This check/uncheck all the rules below." +msgstr "" +"Hiermit können Sie alle unten aufgeführten Regeln aktivieren / deaktivieren." + +#: AppTools/ToolRulesCheck.py:499 +msgid "Run Rules Check" +msgstr "Führen Sie die Regelprüfung durch" + +#: AppTools/ToolRulesCheck.py:1158 AppTools/ToolRulesCheck.py:1218 +#: AppTools/ToolRulesCheck.py:1255 AppTools/ToolRulesCheck.py:1327 +#: AppTools/ToolRulesCheck.py:1381 AppTools/ToolRulesCheck.py:1419 +#: AppTools/ToolRulesCheck.py:1484 +msgid "Value is not valid." +msgstr "Wert ist ungültig." + +#: AppTools/ToolRulesCheck.py:1172 +msgid "TOP -> Copper to Copper clearance" +msgstr "TOP -> Kupfer zu Kupfer Abstand" + +#: AppTools/ToolRulesCheck.py:1183 +msgid "BOTTOM -> Copper to Copper clearance" +msgstr "UNTEN -> Kupfer zu Kupfer Abstand" + +#: AppTools/ToolRulesCheck.py:1188 AppTools/ToolRulesCheck.py:1282 +#: AppTools/ToolRulesCheck.py:1446 +msgid "" +"At least one Gerber object has to be selected for this rule but none is " +"selected." +msgstr "" +"Für diese Regel muss mindestens ein Gerber-Objekt ausgewählt sein, aber " +"keines." + +#: AppTools/ToolRulesCheck.py:1224 +msgid "" +"One of the copper Gerber objects or the Outline Gerber object is not valid." +msgstr "" +"Eines der Kupfer-Gerber-Objekte oder das Umriss-Gerber-Objekt ist ungültig." + +#: AppTools/ToolRulesCheck.py:1237 AppTools/ToolRulesCheck.py:1401 +msgid "" +"Outline Gerber object presence is mandatory for this rule but it is not " +"selected." +msgstr "" +"Das Vorhandensein von Gerber-Objekten ist für diese Regel obligatorisch, " +"jedoch nicht ausgewählt." + +#: AppTools/ToolRulesCheck.py:1254 AppTools/ToolRulesCheck.py:1281 +msgid "Silk to Silk clearance" +msgstr "Siebdruck zu siebdruck freiheit" + +#: AppTools/ToolRulesCheck.py:1267 +msgid "TOP -> Silk to Silk clearance" +msgstr "TOP -> Siebdruck zu Siebdruck Abstand" + +#: AppTools/ToolRulesCheck.py:1277 +msgid "BOTTOM -> Silk to Silk clearance" +msgstr "UNTEN -> Abstand von Siebdruck zu Siebdruck" + +#: AppTools/ToolRulesCheck.py:1333 +msgid "One or more of the Gerber objects is not valid." +msgstr "Eines oder mehrere der Gerber-Objekte sind ungültig." + +#: AppTools/ToolRulesCheck.py:1341 +msgid "TOP -> Silk to Solder Mask Clearance" +msgstr "TOP -> Abstand von Siebdruck zu Lötmaske" + +#: AppTools/ToolRulesCheck.py:1347 +msgid "BOTTOM -> Silk to Solder Mask Clearance" +msgstr "UNTEN -> Abstand von Siebdruck zu Lötmaske" + +#: AppTools/ToolRulesCheck.py:1351 +msgid "" +"Both Silk and Solder Mask Gerber objects has to be either both Top or both " +"Bottom." +msgstr "" +"Sowohl Siebdruck- als auch Lötmasken-Gerber-Objekte müssen entweder beide " +"oben oder beide unten sein." + +#: AppTools/ToolRulesCheck.py:1387 +msgid "" +"One of the Silk Gerber objects or the Outline Gerber object is not valid." +msgstr "" +"Eines der Siebdruck-Gerber-Objekte oder das Gliederung-Gerber-Objekt ist " +"ungültig." + +#: AppTools/ToolRulesCheck.py:1431 +msgid "TOP -> Minimum Solder Mask Sliver" +msgstr "TOP -> Minimum Lötmaskenband" + +#: AppTools/ToolRulesCheck.py:1441 +msgid "BOTTOM -> Minimum Solder Mask Sliver" +msgstr "UNTEN-> Minimum Lötmaskenband" + +#: AppTools/ToolRulesCheck.py:1490 +msgid "One of the Copper Gerber objects or the Excellon objects is not valid." +msgstr "" +"Eines der Kupfer-Gerber-Objekte oder der Excellon-Objekte ist ungültig." + +#: AppTools/ToolRulesCheck.py:1506 +msgid "" +"Excellon object presence is mandatory for this rule but none is selected." +msgstr "" +"Das Vorhandensein von Excellon-Objekten ist für diese Regel obligatorisch, " +"es ist jedoch keine ausgewählt." + +#: AppTools/ToolRulesCheck.py:1579 AppTools/ToolRulesCheck.py:1592 +#: AppTools/ToolRulesCheck.py:1603 AppTools/ToolRulesCheck.py:1616 +msgid "STATUS" +msgstr "STATUS" + +#: AppTools/ToolRulesCheck.py:1582 AppTools/ToolRulesCheck.py:1606 +msgid "FAILED" +msgstr "GESCHEITERT" + +#: AppTools/ToolRulesCheck.py:1595 AppTools/ToolRulesCheck.py:1619 +msgid "PASSED" +msgstr "BESTANDEN" + +#: AppTools/ToolRulesCheck.py:1596 AppTools/ToolRulesCheck.py:1620 +msgid "Violations: There are no violations for the current rule." +msgstr "Verstöße: Für die aktuelle Regel gibt es keine Verstöße." + +#: AppTools/ToolShell.py:74 AppTools/ToolShell.py:76 +msgid "...processing..." +msgstr "...wird bearbeitet..." + +#: AppTools/ToolSolderPaste.py:37 +msgid "Solder Paste Tool" +msgstr "Lötpaste-Werkzeug" + +#: AppTools/ToolSolderPaste.py:69 +msgid "Gerber Solder paste object. " +msgstr "Gerber Lötpastenobjekt. " + +#: AppTools/ToolSolderPaste.py:76 +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." + +#: AppTools/ToolSolderPaste.py:91 +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." + +#: AppTools/ToolSolderPaste.py:98 +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." + +#: AppTools/ToolSolderPaste.py:105 +msgid "New Nozzle Tool" +msgstr "Neues Düsenwerkzeug" + +#: AppTools/ToolSolderPaste.py:124 +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." + +#: AppTools/ToolSolderPaste.py:136 +msgid "Generate solder paste dispensing geometry." +msgstr "Generieren Sie Lotpastendispensiergeometrie." + +#: AppTools/ToolSolderPaste.py:155 +msgid "STEP 1" +msgstr "SCHRITT 1" + +#: AppTools/ToolSolderPaste.py:157 +msgid "" +"First step is to select a number of nozzle tools for usage\n" +"and then optionally modify the GCode parameters below." +msgstr "" +"Zunächst müssen Sie eine Reihe von Düsenwerkzeugen auswählen\n" +"und ändern Sie dann optional die GCode-Parameter." + +#: AppTools/ToolSolderPaste.py:160 +msgid "" +"Select tools.\n" +"Modify parameters." +msgstr "" +"Werkzeuge auswählen.\n" +"Parameter ändern." + +#: AppTools/ToolSolderPaste.py:280 +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)." + +#: AppTools/ToolSolderPaste.py:350 +msgid "" +"Generate GCode for Solder Paste dispensing\n" +"on PCB pads." +msgstr "" +"Generieren Sie GCode für die Lotpastendosierung\n" +"auf PCB-Pads." + +#: AppTools/ToolSolderPaste.py:371 +msgid "STEP 2" +msgstr "SCHRITT 2" + +#: AppTools/ToolSolderPaste.py:373 +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." + +#: AppTools/ToolSolderPaste.py:390 +msgid "Geo Result" +msgstr "Geo-Ergebnis" + +#: AppTools/ToolSolderPaste.py:392 +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." + +#: AppTools/ToolSolderPaste.py:401 +msgid "STEP 3" +msgstr "SCHRITT 3" + +#: AppTools/ToolSolderPaste.py:403 +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." + +#: AppTools/ToolSolderPaste.py:424 +msgid "CNC Result" +msgstr "CNC-Ergebnis" + +#: AppTools/ToolSolderPaste.py:426 +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." + +#: AppTools/ToolSolderPaste.py:436 +msgid "View GCode" +msgstr "GCode anzeigen" + +#: AppTools/ToolSolderPaste.py:438 +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." + +#: AppTools/ToolSolderPaste.py:448 +msgid "Save GCode" +msgstr "Speichern Sie GCode" + +#: AppTools/ToolSolderPaste.py:450 +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." + +#: AppTools/ToolSolderPaste.py:460 +msgid "STEP 4" +msgstr "SCHRITT 4" + +#: AppTools/ToolSolderPaste.py:462 +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." + +#: AppTools/ToolSolderPaste.py:922 +msgid "New Nozzle tool added to Tool Table." +msgstr "Neues Düsenwerkzeug zur Werkzeugtabelle hinzugefügt." + +#: AppTools/ToolSolderPaste.py:965 +msgid "Nozzle tool from Tool Table was edited." +msgstr "Das Düsenwerkzeug aus der Werkzeugtabelle wurde bearbeitet." + +#: AppTools/ToolSolderPaste.py:1024 +msgid "Delete failed. Select a Nozzle tool to delete." +msgstr "Löschen fehlgeschlagen. Wählen Sie ein Düsenwerkzeug zum Löschen aus." + +#: AppTools/ToolSolderPaste.py:1030 +msgid "Nozzle tool(s) deleted from Tool Table." +msgstr "Düsenwerkzeug (e) aus der Werkzeugtabelle gelöscht." + +#: AppTools/ToolSolderPaste.py:1086 +msgid "No SolderPaste mask Gerber object loaded." +msgstr "Keine Lötpastenmaske Gerber-Objekt geladen." + +#: AppTools/ToolSolderPaste.py:1104 +msgid "Creating Solder Paste dispensing geometry." +msgstr "Erstellen einer Lotpastenspendergeometrie." + +#: AppTools/ToolSolderPaste.py:1117 +msgid "No Nozzle tools in the tool table." +msgstr "Nein Düsenwerkzeuge in der Werkzeugtabelle." + +#: AppTools/ToolSolderPaste.py:1243 +msgid "Cancelled. Empty file, it has no geometry..." +msgstr "Abgebrochen. Leere Datei hat keine Geometrie ..." + +#: AppTools/ToolSolderPaste.py:1246 +msgid "Solder Paste geometry generated successfully" +msgstr "Lotpastengeometrie erfolgreich generiert" + +#: AppTools/ToolSolderPaste.py:1253 +msgid "Some or all pads have no solder due of inadequate nozzle diameters..." +msgstr "" +"Einige oder alle Pads haben wegen unzureichender Düsendurchmesser keine " +"Lötstellen ..." + +#: AppTools/ToolSolderPaste.py:1267 +msgid "Generating Solder Paste dispensing geometry..." +msgstr "Lötpasten-Dosiergeometrie erzeugen ..." + +#: AppTools/ToolSolderPaste.py:1287 +msgid "There is no Geometry object available." +msgstr "Es ist kein Geometrieobjekt verfügbar." + +#: AppTools/ToolSolderPaste.py:1292 +msgid "This Geometry can't be processed. NOT a solder_paste_tool geometry." +msgstr "" +"Diese Geometrie kann nicht verarbeitet werden. KEINE Geometrie " +"\"Lötpaste_Tool\"." + +#: AppTools/ToolSolderPaste.py:1328 +msgid "An internal error has ocurred. See shell.\n" +msgstr "Ein interner Fehler ist aufgetreten. Siehe Konsole.\n" + +#: AppTools/ToolSolderPaste.py:1393 +msgid "ToolSolderPaste CNCjob created" +msgstr "Werkzeuglötpaste CNC-Auftrag erstellt" + +#: AppTools/ToolSolderPaste.py:1412 +msgid "SP GCode Editor" +msgstr "SP GCode-Editor" + +#: AppTools/ToolSolderPaste.py:1424 AppTools/ToolSolderPaste.py:1429 +#: AppTools/ToolSolderPaste.py:1484 +msgid "" +"This CNCJob object can't be processed. NOT a solder_paste_tool CNCJob object." +msgstr "" +"Dieses CNCJob-Objekt kann nicht verarbeitet werden. KEIN lot_paste_tool " +"CNCJob Objekt." + +#: AppTools/ToolSolderPaste.py:1454 +msgid "No Gcode in the object" +msgstr "Kein Gcode im Objekt" + +#: AppTools/ToolSolderPaste.py:1494 +msgid "Export GCode ..." +msgstr "GCode exportieren ..." + +#: AppTools/ToolSolderPaste.py:1542 +msgid "Solder paste dispenser GCode file saved to" +msgstr "Lotpastenspender GCode-Datei gespeichert in" + +#: AppTools/ToolSub.py:65 +msgid "Gerber Objects" +msgstr "Gerber-Objekte" + +#: AppTools/ToolSub.py:78 +msgid "" +"Gerber object from which to subtract\n" +"the subtractor Gerber object." +msgstr "" +"Gerber-Objekt, von dem subtrahiert werden soll\n" +"der Subtrahierer Gerber Objekt." + +#: AppTools/ToolSub.py:91 AppTools/ToolSub.py:146 +msgid "Subtractor" +msgstr "Subtraktor" + +#: AppTools/ToolSub.py:93 +msgid "" +"Gerber object that will be subtracted\n" +"from the target Gerber object." +msgstr "" +"Gerber-Objekt, das abgezogen wird\n" +"vom Zielobjekt Gerber." + +#: AppTools/ToolSub.py:100 +msgid "Subtract Gerber" +msgstr "Gerber abziehen" + +#: AppTools/ToolSub.py:102 +msgid "" +"Will remove the area occupied by the subtractor\n" +"Gerber from the Target Gerber.\n" +"Can be used to remove the overlapping silkscreen\n" +"over the soldermask." +msgstr "" +"Entfernt den vom Subtrahierer belegten Bereich\n" +"Gerber vom Target Gerber.\n" +"Kann verwendet werden, um den überlappenden Siebdruck zu entfernen\n" +"über der Lötmaske." + +#: AppTools/ToolSub.py:120 +msgid "Geometry Objects" +msgstr "Geometrieobjekte" + +#: AppTools/ToolSub.py:133 +msgid "" +"Geometry object from which to subtract\n" +"the subtractor Geometry object." +msgstr "" +"Geometrieobjekt, von dem subtrahiert werden soll\n" +"das Subtrahierer-Geometrieobjekt." + +#: AppTools/ToolSub.py:148 +msgid "" +"Geometry object that will be subtracted\n" +"from the target Geometry object." +msgstr "" +"Geometrieobjekt, das subtrahiert wird\n" +"aus dem Zielobjekt Geometrie." + +#: AppTools/ToolSub.py:156 +msgid "" +"Checking this will close the paths cut by the Geometry subtractor object." +msgstr "" +"Wenn Sie dies aktivieren, werden die vom Geometrie-Subtrahierer-Objekt " +"geschnittenen Pfade geschlossen." + +#: AppTools/ToolSub.py:159 +msgid "Subtract Geometry" +msgstr "Geometrie subtrahieren" + +#: AppTools/ToolSub.py:161 +msgid "" +"Will remove the area occupied by the subtractor\n" +"Geometry from the Target Geometry." +msgstr "" +"Entfernt den vom Subtrahierer belegten Bereich\n" +"Geometrie aus der Zielgeometrie." + +#: AppTools/ToolSub.py:263 +msgid "Sub Tool" +msgstr "Sub. Werkzeug" + +#: AppTools/ToolSub.py:284 AppTools/ToolSub.py:489 +msgid "No Target object loaded." +msgstr "Kein Zielobjekt geladen." + +#: AppTools/ToolSub.py:287 +msgid "Loading geometry from Gerber objects." +msgstr "Lade Geometrien aus Gerber Objekten." + +#: AppTools/ToolSub.py:299 AppTools/ToolSub.py:504 +msgid "No Subtractor object loaded." +msgstr "Es wurde kein Subtrahiererobjekt geladen." + +# Whatever a Subtractor Gerber is, could not translate +#: AppTools/ToolSub.py:331 +msgid "Processing geometry from Subtractor Gerber object." +msgstr "Verarbeite Geomtrie des Subtractor Gerber Objekts." + +#: AppTools/ToolSub.py:352 +msgid "Parsing geometry for aperture" +msgstr "Analysegeometrie für Blende" + +# whatever aperture means here.... +#: AppTools/ToolSub.py:413 +msgid "Finished parsing geometry for aperture" +msgstr "Einlesen der aperture Geometrie fertiggestellt" + +#: AppTools/ToolSub.py:458 AppTools/ToolSub.py:661 +msgid "Generating new object ..." +msgstr "Neues Objekt erzeugen ..." + +#: AppTools/ToolSub.py:462 AppTools/ToolSub.py:665 AppTools/ToolSub.py:746 +msgid "Generating new object failed." +msgstr "Das Generieren eines neuen Objekts ist fehlgeschlagen." + +#: AppTools/ToolSub.py:467 AppTools/ToolSub.py:671 +msgid "Created" +msgstr "Erstellt" + +#: AppTools/ToolSub.py:518 +msgid "Currently, the Subtractor geometry cannot be of type Multigeo." +msgstr "Derzeit kann die Subtrahierergeometrie nicht vom Typ Multi-Geo sein." + +#: AppTools/ToolSub.py:563 +msgid "Parsing solid_geometry ..." +msgstr "Analyse von solid_geometry ..." + +#: AppTools/ToolSub.py:565 +msgid "Parsing solid_geometry for tool" +msgstr "Analysieren der solid_geometry für das Werkzeug" + +#: AppTools/ToolTransform.py:23 +msgid "Object Transform" +msgstr "Objekttransformation" + +#: AppTools/ToolTransform.py:78 +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." + +#: AppTools/ToolTransform.py:99 AppTools/ToolTransform.py:120 +msgid "" +"Angle for Skew action, in degrees.\n" +"Float number between -360 and 360." +msgstr "" +"Winkel für Schrägstellung in Grad.\n" +"Gleitkommazahl zwischen -360 und 360." + +#: AppTools/ToolTransform.py:109 AppTools/ToolTransform.py:130 +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." + +#: AppTools/ToolTransform.py:159 AppTools/ToolTransform.py:179 +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." + +#: AppTools/ToolTransform.py:228 AppTools/ToolTransform.py:248 +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" + +#: AppTools/ToolTransform.py:268 AppTools/ToolTransform.py:273 +msgid "Flip the selected object(s) over the X axis." +msgstr "Drehen Sie die ausgewählten Objekte über die X-Achse." + +#: AppTools/ToolTransform.py:297 +msgid "Ref. Point" +msgstr "Anhaltspunkt" + +#: AppTools/ToolTransform.py:348 +msgid "" +"Create the buffer effect on each geometry,\n" +"element from the selected object, using the distance." +msgstr "" +"Erstellen Sie den Puffereffekt für jede Geometrie.\n" +"Element aus dem ausgewählten Objekt unter Verwendung des Abstands." + +#: AppTools/ToolTransform.py:374 +msgid "" +"Create the buffer effect on each geometry,\n" +"element from the selected object, using the factor." +msgstr "" +"Erstellen Sie den Puffereffekt für jede Geometrie.\n" +"Element aus dem ausgewählten Objekt unter Verwendung des Faktors." + +#: AppTools/ToolTransform.py:479 +msgid "Buffer D" +msgstr "Puffer E" + +#: AppTools/ToolTransform.py:480 +msgid "Buffer F" +msgstr "Puffer F" + +#: AppTools/ToolTransform.py:557 +msgid "Rotate transformation can not be done for a value of 0." +msgstr "" +"Bei einem Wert von 0 kann keine Rotationstransformation durchgeführt werden." + +#: AppTools/ToolTransform.py:596 AppTools/ToolTransform.py:619 +msgid "Scale transformation can not be done for a factor of 0 or 1." +msgstr "" +"Eine Skalentransformation kann für einen Faktor von 0 oder 1 nicht " +"durchgeführt werden." + +#: AppTools/ToolTransform.py:634 AppTools/ToolTransform.py:644 +msgid "Offset transformation can not be done for a value of 0." +msgstr "" +"Bei einem Wert von 0 kann keine Offset-Transformation durchgeführt werden." + +#: AppTools/ToolTransform.py:676 +msgid "No object selected. Please Select an object to rotate!" +msgstr "Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum Drehen aus!" + +#: AppTools/ToolTransform.py:702 +msgid "CNCJob objects can't be rotated." +msgstr "CNCJob-Objekte können nicht gedreht werden." + +#: AppTools/ToolTransform.py:710 +msgid "Rotate done" +msgstr "Fertig drehen" + +#: AppTools/ToolTransform.py:713 AppTools/ToolTransform.py:783 +#: AppTools/ToolTransform.py:833 AppTools/ToolTransform.py:887 +#: AppTools/ToolTransform.py:917 AppTools/ToolTransform.py:953 +msgid "Due of" +msgstr "Aufgrund von" + +#: AppTools/ToolTransform.py:713 AppTools/ToolTransform.py:783 +#: AppTools/ToolTransform.py:833 AppTools/ToolTransform.py:887 +#: AppTools/ToolTransform.py:917 AppTools/ToolTransform.py:953 +msgid "action was not executed." +msgstr "Aktion wurde nicht ausgeführt." + +#: AppTools/ToolTransform.py:725 +msgid "No object selected. Please Select an object to flip" +msgstr "Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt aus" + +#: AppTools/ToolTransform.py:758 +msgid "CNCJob objects can't be mirrored/flipped." +msgstr "CNCJob-Objekte können nicht gespiegelt / gespiegelt werden." + +#: AppTools/ToolTransform.py:793 +msgid "Skew transformation can not be done for 0, 90 and 180 degrees." +msgstr "" +"Die Neigungstransformation kann nicht für 0, 90 und 180 Grad durchgeführt " +"werden." + +#: AppTools/ToolTransform.py:798 +msgid "No object selected. Please Select an object to shear/skew!" +msgstr "" +"Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum Scheren / Schrägen!" + +#: AppTools/ToolTransform.py:818 +msgid "CNCJob objects can't be skewed." +msgstr "CNCJob-Objekte können nicht verzerrt werden." + +#: AppTools/ToolTransform.py:830 +msgid "Skew on the" +msgstr "Schräg auf die" + +#: AppTools/ToolTransform.py:830 AppTools/ToolTransform.py:884 +#: AppTools/ToolTransform.py:914 +msgid "axis done" +msgstr "Achse fertig" + +#: AppTools/ToolTransform.py:844 +msgid "No object selected. Please Select an object to scale!" +msgstr "Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum Skalieren!" + +#: AppTools/ToolTransform.py:875 +msgid "CNCJob objects can't be scaled." +msgstr "CNCJob-Objekte können nicht skaliert werden." + +#: AppTools/ToolTransform.py:884 +msgid "Scale on the" +msgstr "Skalieren Sie auf der" + +#: AppTools/ToolTransform.py:894 +msgid "No object selected. Please Select an object to offset!" +msgstr "Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum Versetzen aus!" + +#: AppTools/ToolTransform.py:901 +msgid "CNCJob objects can't be offset." +msgstr "CNCJob-Objekte können nicht versetzt werden." + +#: AppTools/ToolTransform.py:914 +msgid "Offset on the" +msgstr "Offset auf dem" + +#: AppTools/ToolTransform.py:924 +msgid "No object selected. Please Select an object to buffer!" +msgstr "Kein Objekt ausgewählt. Bitte wählen Sie ein Objekt zum Puffern aus!" + +#: AppTools/ToolTransform.py:927 +msgid "Applying Buffer" +msgstr "Anwenden von Puffer" + +#: AppTools/ToolTransform.py:931 +msgid "CNCJob objects can't be buffered." +msgstr "CNCJob-Objekte können nicht gepuffert werden." + +#: AppTools/ToolTransform.py:948 +msgid "Buffer done" +msgstr "Puffer fertig" + +#: AppTranslation.py:104 +msgid "The application will restart." +msgstr "Die Anwendung wird neu gestartet." + +#: AppTranslation.py:106 +msgid "Are you sure do you want to change the current language to" +msgstr "Möchten Sie die aktuelle Sprache wirklich in ändern" + +#: AppTranslation.py:107 +msgid "Apply Language ..." +msgstr "Sprache anwenden ..." + +#: AppTranslation.py:201 App_Main.py:3047 +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?" + +#: AppTranslation.py:204 App_Main.py:3050 App_Main.py:6334 +msgid "Save changes" +msgstr "Änderungen speichern" + +#: App_Main.py:477 msgid "FlatCAM is initializing ..." msgstr "FlatCAM wird initialisiert ..." -#: FlatCAMApp.py:639 +#: App_Main.py:620 msgid "Could not find the Language files. The App strings are missing." msgstr "" "Die Sprachdateien konnten nicht gefunden werden. Die App-Zeichenfolgen " "fehlen." -#: FlatCAMApp.py:709 +#: App_Main.py:692 msgid "" "FlatCAM is initializing ...\n" "Canvas initialization started." @@ -35,7 +16929,7 @@ msgstr "" "FlatCAM wird initialisiert ...\n" "Die Canvas-Initialisierung wurde gestartet." -#: FlatCAMApp.py:729 +#: App_Main.py:712 msgid "" "FlatCAM is initializing ...\n" "Canvas initialization started.\n" @@ -45,64 +16939,45 @@ msgstr "" "Die Canvas-Initialisierung wurde gestartet.\n" "Canvas-Initialisierung abgeschlossen in" -#: FlatCAMApp.py:1593 FlatCAMApp.py:7451 +#: App_Main.py:1542 App_Main.py:6445 msgid "New Project - Not saved" msgstr "Neues Projekt - Nicht gespeichert" -#: FlatCAMApp.py:1689 +#: App_Main.py:1643 msgid "" "Found old default preferences files. Please reboot the application to update." msgstr "" "Alte Einstellungsdatei gefunden. Bitte starten Sie Flatcam neu um die " "Einstellungen zu aktualisieren." -#: FlatCAMApp.py:1740 FlatCAMApp.py:2512 FlatCAMApp.py:2547 FlatCAMApp.py:2594 -#: FlatCAMApp.py:4540 FlatCAMApp.py:7535 FlatCAMApp.py:7572 FlatCAMApp.py:7614 -#: FlatCAMApp.py:7643 FlatCAMApp.py:7684 FlatCAMApp.py:7709 FlatCAMApp.py:7761 -#: FlatCAMApp.py:7796 FlatCAMApp.py:7841 FlatCAMApp.py:7882 FlatCAMApp.py:7923 -#: FlatCAMApp.py:7964 FlatCAMApp.py:8005 FlatCAMApp.py:8049 FlatCAMApp.py:8105 -#: FlatCAMApp.py:8137 FlatCAMApp.py:8169 FlatCAMApp.py:8402 FlatCAMApp.py:8440 -#: FlatCAMApp.py:8483 FlatCAMApp.py:8560 FlatCAMApp.py:8615 -#: FlatCAMBookmark.py:300 FlatCAMBookmark.py:342 FlatCAMDB.py:663 -#: FlatCAMDB.py:709 FlatCAMDB.py:2125 FlatCAMDB.py:2171 -#: flatcamEditors/FlatCAMExcEditor.py:1023 -#: flatcamEditors/FlatCAMExcEditor.py:1091 -#: flatcamEditors/FlatCAMTextEditor.py:223 flatcamGUI/FlatCAMGUI.py:3443 -#: flatcamGUI/FlatCAMGUI.py:3659 flatcamGUI/FlatCAMGUI.py:3874 -#: flatcamObjects/ObjectCollection.py:126 flatcamTools/ToolFilm.py:754 -#: flatcamTools/ToolFilm.py:900 flatcamTools/ToolImage.py:247 -#: flatcamTools/ToolMove.py:269 flatcamTools/ToolPcbWizard.py:301 -#: flatcamTools/ToolPcbWizard.py:324 flatcamTools/ToolQRCode.py:791 -#: flatcamTools/ToolQRCode.py:838 -msgid "Cancelled." -msgstr "Abgebrochen." - -#: FlatCAMApp.py:1756 +#: App_Main.py:1710 msgid "Open Config file failed." msgstr "Öffnen der Config-Datei ist fehlgeschlagen." -#: FlatCAMApp.py:1771 +#: App_Main.py:1725 msgid "Open Script file failed." msgstr "Open Script-Datei ist fehlgeschlagen." -#: FlatCAMApp.py:1797 +#: App_Main.py:1751 msgid "Open Excellon file failed." msgstr "Öffnen der Excellon-Datei fehlgeschlagen." -#: FlatCAMApp.py:1810 +#: App_Main.py:1764 msgid "Open GCode file failed." msgstr "Öffnen der GCode-Datei fehlgeschlagen." -#: FlatCAMApp.py:1823 +#: App_Main.py:1777 msgid "Open Gerber file failed." msgstr "Öffnen der Gerber-Datei fehlgeschlagen." -#: FlatCAMApp.py:2131 -msgid "Select a Geometry, Gerber or Excellon Object to edit." +#: App_Main.py:2095 +#, fuzzy +#| msgid "Select a Geometry, Gerber or Excellon Object to edit." +msgid "Select a Geometry, Gerber, Excellon or CNCJob Object to edit." msgstr "" "Wählen Sie ein zu bearbeitendes Geometrie-, Gerber- oder Excellon-Objekt aus." -#: FlatCAMApp.py:2146 +#: App_Main.py:2110 msgid "" "Simultaneous editing of tools geometry in a MultiGeo Geometry is not " "possible.\n" @@ -112,199 +16987,93 @@ msgstr "" "Geometrie ist nicht möglich.\n" "Bearbeiten Sie jeweils nur eine Geometrie." -#: FlatCAMApp.py:2204 +#: App_Main.py:2176 msgid "Editor is activated ..." msgstr "Editor wurde aktiviert ..." -#: FlatCAMApp.py:2225 +#: App_Main.py:2197 msgid "Do you want to save the edited object?" msgstr "Möchten Sie das bearbeitete Objekt speichern?" -#: FlatCAMApp.py:2226 flatcamGUI/FlatCAMGUI.py:2288 -msgid "Close Editor" -msgstr "Editor schließen" - -#: FlatCAMApp.py:2229 FlatCAMApp.py:3518 FlatCAMApp.py:6085 FlatCAMApp.py:7345 -#: FlatCAMTranslation.py:109 FlatCAMTranslation.py:207 -#: flatcamGUI/FlatCAMGUI.py:2519 -#: flatcamGUI/preferences/PreferencesUIManager.py:1122 -msgid "Yes" -msgstr "Ja" - -#: FlatCAMApp.py:2230 FlatCAMApp.py:3519 FlatCAMApp.py:6086 FlatCAMApp.py:7346 -#: FlatCAMTranslation.py:110 FlatCAMTranslation.py:208 -#: flatcamGUI/FlatCAMGUI.py:2520 -#: flatcamGUI/preferences/PreferencesUIManager.py:1123 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:164 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:150 -#: flatcamTools/ToolNCC.py:182 flatcamTools/ToolPaint.py:166 -msgid "No" -msgstr "Nein" - -#: FlatCAMApp.py:2231 FlatCAMApp.py:3520 FlatCAMApp.py:4478 FlatCAMApp.py:5103 -#: FlatCAMApp.py:7347 FlatCAMDB.py:128 FlatCAMDB.py:1689 -#: flatcamGUI/FlatCAMGUI.py:1347 -msgid "Cancel" -msgstr "Abbrechen" - -#: FlatCAMApp.py:2263 +#: App_Main.py:2235 msgid "Object empty after edit." msgstr "Das Objekt ist nach der Bearbeitung leer." -#: FlatCAMApp.py:2267 FlatCAMApp.py:2288 FlatCAMApp.py:2310 +#: App_Main.py:2239 App_Main.py:2260 App_Main.py:2282 msgid "Editor exited. Editor content saved." msgstr "Editor beendet. Editorinhalt gespeichert." -#: FlatCAMApp.py:2314 FlatCAMApp.py:2337 FlatCAMApp.py:2355 +#: App_Main.py:2286 App_Main.py:2309 App_Main.py:2327 msgid "Select a Gerber, Geometry or Excellon Object to update." msgstr "" "Wählen Sie ein Gerber-, Geometrie- oder Excellon-Objekt zum Aktualisieren " "aus." -#: FlatCAMApp.py:2317 +#: App_Main.py:2289 msgid "is updated, returning to App..." msgstr "wurde aktualisiert..." -#: FlatCAMApp.py:2324 +#: App_Main.py:2296 msgid "Editor exited. Editor content was not saved." msgstr "Editor beendet. Der Inhalt des Editors wurde nicht gespeichert." -#: FlatCAMApp.py:2504 FlatCAMApp.py:2508 +#: App_Main.py:2422 App_Main.py:2426 msgid "Import FlatCAM Preferences" msgstr "FlatCAM-Voreinstellungen importieren" -#: FlatCAMApp.py:2519 +#: App_Main.py:2437 msgid "Imported Defaults from" msgstr "Voreinstellungen wurden importiert von" -#: FlatCAMApp.py:2539 FlatCAMApp.py:2544 +#: App_Main.py:2457 App_Main.py:2462 msgid "Export FlatCAM Preferences" msgstr "FlatCAM-Voreinstellungen exportieren" -#: FlatCAMApp.py:2558 FlatCAMApp.py:2626 -#: flatcamGUI/preferences/PreferencesUIManager.py:1018 -msgid "Failed to write defaults to file." -msgstr "Fehler beim Schreiben der Voreinstellungen in die Datei." - -#: FlatCAMApp.py:2564 +#: App_Main.py:2482 msgid "Exported preferences to" msgstr "Exportierte Einstellungen nach" -#: FlatCAMApp.py:2584 FlatCAMApp.py:2589 +#: App_Main.py:2502 App_Main.py:2507 msgid "Save to file" msgstr "Speichern unter" -#: FlatCAMApp.py:2602 FlatCAMApp.py:8859 FlatCAMApp.py:8907 FlatCAMApp.py:9032 -#: FlatCAMApp.py:9168 FlatCAMBookmark.py:308 FlatCAMDB.py:671 FlatCAMDB.py:2133 -#: flatcamEditors/FlatCAMTextEditor.py:276 flatcamObjects/FlatCAMCNCJob.py:959 -#: flatcamTools/ToolFilm.py:1031 flatcamTools/ToolFilm.py:1212 -#: flatcamTools/ToolSolderPaste.py:1534 -msgid "" -"Permission denied, saving not possible.\n" -"Most likely another app is holding the file open and not accessible." -msgstr "" -"Berechtigung verweigert, Speichern nicht möglich.\n" -"Wahrscheinlich hält eine andere App die Datei offen oder ist geschützt." - -#: FlatCAMApp.py:2613 +#: App_Main.py:2531 msgid "Could not load the file." msgstr "Die Datei konnte nicht geladen werden." -#: FlatCAMApp.py:2629 +#: App_Main.py:2547 msgid "Exported file to" msgstr "Exportierte Datei nach" -#: FlatCAMApp.py:2712 +#: App_Main.py:2584 msgid "Failed to open recent files file for writing." msgstr "Fehler beim Öffnen der zuletzt geöffneten Datei zum Schreiben." -#: FlatCAMApp.py:2723 +#: App_Main.py:2595 msgid "Failed to open recent projects file for writing." msgstr "Fehler beim Öffnen der letzten Projektdatei zum Schreiben." -#: FlatCAMApp.py:2806 FlatCAMApp.py:9377 FlatCAMApp.py:9441 FlatCAMApp.py:9572 -#: FlatCAMApp.py:9637 FlatCAMApp.py:10287 -#: flatcamEditors/FlatCAMGrbEditor.py:4364 -#: flatcamObjects/FlatCAMGeometry.py:1725 flatcamParsers/ParseExcellon.py:897 -#: flatcamTools/ToolPcbWizard.py:432 -msgid "An internal error has occurred. See shell.\n" -msgstr "Ein interner Fehler ist aufgetreten. Siehe Shell.\n" - -#: FlatCAMApp.py:2807 -#, python-brace-format -msgid "" -"Object ({kind}) failed because: {error} \n" -"\n" -msgstr "" -"Objekt ({kind}) gescheitert weil: {error} \n" -"\n" - -#: FlatCAMApp.py:2822 -msgid "Converting units to " -msgstr "Einheiten umrechnen in " - -#: FlatCAMApp.py:2931 -msgid "CREATE A NEW FLATCAM TCL SCRIPT" -msgstr "NEUES FLATCAL TCL SCRIPT ERZEUGEN" - -#: FlatCAMApp.py:2932 -msgid "TCL Tutorial is here" -msgstr "Das TCL Tutorial ist hier" - -#: FlatCAMApp.py:2934 -msgid "FlatCAM commands list" -msgstr "FlatCAM Befehlsliste" - -#: FlatCAMApp.py:2935 -msgid "" -"Type >help< followed by Run Code for a list of FlatCAM Tcl Commands " -"(displayed in Tcl Shell)." -msgstr "" -"Geben Sie >help< gefolgt von Run Code ein, um eine Liste der FlatCAM Tcl-" -"Befehle anzuzeigen (angezeigt in der Tcl-Shell)." - -#: FlatCAMApp.py:2982 FlatCAMApp.py:2988 FlatCAMApp.py:2994 FlatCAMApp.py:3000 -#: FlatCAMApp.py:3006 FlatCAMApp.py:3012 -msgid "created/selected" -msgstr "erstellt / ausgewählt" - -#: FlatCAMApp.py:3027 FlatCAMApp.py:5189 flatcamObjects/FlatCAMObj.py:248 -#: flatcamObjects/FlatCAMObj.py:279 flatcamObjects/FlatCAMObj.py:295 -#: flatcamObjects/FlatCAMObj.py:375 flatcamTools/ToolCopperThieving.py:1481 -#: flatcamTools/ToolFiducials.py:809 flatcamTools/ToolMove.py:229 -#: flatcamTools/ToolQRCode.py:728 -msgid "Plotting" -msgstr "Plotten" - -#: FlatCAMApp.py:3090 flatcamGUI/FlatCAMGUI.py:545 -msgid "About FlatCAM" -msgstr "Über FlatCAM" - -#: FlatCAMApp.py:3116 +#: App_Main.py:2650 msgid "2D Computer-Aided Printed Circuit Board Manufacturing" msgstr "2D-Computer-Aided-Printed-Circuit-Board-Herstellung" -#: FlatCAMApp.py:3117 +#: App_Main.py:2651 msgid "Development" msgstr "Entwicklung" -#: FlatCAMApp.py:3118 +#: App_Main.py:2652 msgid "DOWNLOAD" msgstr "HERUNTERLADEN" -#: FlatCAMApp.py:3119 +#: App_Main.py:2653 msgid "Issue tracker" msgstr "Problem Tracker" -#: FlatCAMApp.py:3123 FlatCAMApp.py:3484 flatcamGUI/GUIElements.py:2583 -msgid "Close" -msgstr "Schließen" - -#: FlatCAMApp.py:3138 +#: App_Main.py:2672 msgid "Licensed under the MIT license" msgstr "Lizenziert unter der MIT-Lizenz" -#: FlatCAMApp.py:3147 +#: App_Main.py:2681 msgid "" "Permission is hereby granted, free of charge, to any person obtaining a " "copy\n" @@ -359,7 +17128,7 @@ msgstr "" "ZUSAMMENHANG MIT DER\n" " SOFTWARE ODER SONSTIGER VERWENDUNG DER SOFTWARE ENTSTANDEN." -#: FlatCAMApp.py:3169 +#: App_Main.py:2703 msgid "" "Some of the icons used are from the following sources:
    Icons by FreepikoNline Web FontsoNline Web Fonts" -#: FlatCAMApp.py:3202 +#: App_Main.py:2736 msgid "Splash" msgstr "Begrüßungsbildschirm" -#: FlatCAMApp.py:3208 +#: App_Main.py:2742 msgid "Programmers" msgstr "Programmierer" -#: FlatCAMApp.py:3214 +#: App_Main.py:2748 msgid "Translators" msgstr "Übersetzer" -#: FlatCAMApp.py:3220 +#: App_Main.py:2754 msgid "License" msgstr "Lizenz" -#: FlatCAMApp.py:3226 +#: App_Main.py:2760 msgid "Attributions" msgstr "Zuschreibungen" -#: FlatCAMApp.py:3249 +#: App_Main.py:2783 msgid "Programmer" msgstr "Programmierer" -#: FlatCAMApp.py:3250 +#: App_Main.py:2784 msgid "Status" msgstr "Status" -#: FlatCAMApp.py:3251 FlatCAMApp.py:3331 +#: App_Main.py:2785 App_Main.py:2865 msgid "E-mail" msgstr "Email" -#: FlatCAMApp.py:3259 +#: App_Main.py:2788 +msgid "Program Author" +msgstr "Programmautor" + +#: App_Main.py:2793 msgid "BETA Maintainer >= 2019" msgstr "Betreuer >= 2019" -#: FlatCAMApp.py:3328 +#: App_Main.py:2862 msgid "Language" msgstr "Sprache" -#: FlatCAMApp.py:3329 +#: App_Main.py:2863 msgid "Translator" msgstr "Übersetzer" -#: FlatCAMApp.py:3330 +#: App_Main.py:2864 msgid "Corrections" msgstr "Korrekturen" -#: FlatCAMApp.py:3455 FlatCAMApp.py:3464 flatcamGUI/FlatCAMGUI.py:527 -msgid "Bookmarks Manager" -msgstr "Lesezeichen verwalten" - -#: FlatCAMApp.py:3475 +#: App_Main.py:3009 msgid "" "This entry will resolve to another website if:\n" "\n" @@ -448,48 +17217,32 @@ msgstr "" "Wenn Sie keine Informationen zu FlatCAM beta erhalten können\n" "Verwenden Sie den Link zum YouTube-Kanal im Menü Hilfe." -#: FlatCAMApp.py:3482 +#: App_Main.py:3016 msgid "Alternative website" msgstr "Alternative Website" -#: FlatCAMApp.py:3508 flatcamGUI/FlatCAMGUI.py:4267 -msgid "Application is saving the project. Please wait ..." -msgstr "Anwendung speichert das Projekt. Warten Sie mal ..." - -#: FlatCAMApp.py:3513 FlatCAMTranslation.py:202 -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:3516 FlatCAMApp.py:7343 FlatCAMTranslation.py:205 -msgid "Save changes" -msgstr "Änderungen speichern" - -#: FlatCAMApp.py:3778 +#: App_Main.py:3315 msgid "Selected Excellon file extensions registered with FlatCAM." msgstr "" "Ausgewählte Excellon-Dateierweiterungen, die bei FlatCAM registriert sind." -#: FlatCAMApp.py:3800 +#: App_Main.py:3337 msgid "Selected GCode file extensions registered with FlatCAM." msgstr "" "Ausgewählte GCode-Dateierweiterungen, die bei FlatCAM registriert sind." -#: FlatCAMApp.py:3822 +#: App_Main.py:3359 msgid "Selected Gerber file extensions registered with FlatCAM." msgstr "" "Ausgewählte Gerber-Dateierweiterungen, die bei FlatCAM registriert sind." -#: FlatCAMApp.py:4010 FlatCAMApp.py:4069 FlatCAMApp.py:4097 +#: App_Main.py:3547 App_Main.py:3606 App_Main.py:3634 msgid "At least two objects are required for join. Objects currently selected" msgstr "" "Zum Verbinden sind mindestens zwei Objekte erforderlich. Derzeit ausgewählte " "Objekte" -#: FlatCAMApp.py:4019 +#: App_Main.py:3556 msgid "" "Failed join. The Geometry objects are of different types.\n" "At least one is MultiGeo type and the other is SingleGeo type. A possibility " @@ -507,52 +17260,52 @@ msgstr "" "und das Ergebnis entspricht möglicherweise nicht dem, was erwartet wurde.\n" "Überprüfen Sie den generierten GCODE." -#: FlatCAMApp.py:4031 FlatCAMApp.py:4041 +#: App_Main.py:3568 App_Main.py:3578 msgid "Geometry merging finished" msgstr "Zusammenführung der Geometrien beendet" -#: FlatCAMApp.py:4064 +#: App_Main.py:3601 msgid "Failed. Excellon joining works only on Excellon objects." msgstr "" "Gescheitert. Die Zusammenfügung von Excellon funktioniert nur bei Excellon-" "Objekten." -#: FlatCAMApp.py:4074 +#: App_Main.py:3611 msgid "Excellon merging finished" msgstr "Excellon-Bearbeitung abgeschlossen" -#: FlatCAMApp.py:4092 +#: App_Main.py:3629 msgid "Failed. Gerber joining works only on Gerber objects." msgstr "" "Gescheitert. Das Zusammenfügen für Gerber-Objekte funktioniert nur bei " "Gerber-Objekten." -#: FlatCAMApp.py:4102 +#: App_Main.py:3639 msgid "Gerber merging finished" msgstr "Erledigt. Gerber-Bearbeitung beendet" -#: FlatCAMApp.py:4122 FlatCAMApp.py:4159 +#: App_Main.py:3659 App_Main.py:3696 msgid "Failed. Select a Geometry Object and try again." msgstr "" "Gescheitert. Wählen Sie ein Geometrieobjekt aus und versuchen Sie es erneut." -#: FlatCAMApp.py:4126 FlatCAMApp.py:4164 +#: App_Main.py:3663 App_Main.py:3701 msgid "Expected a GeometryObject, got" msgstr "Erwartet ein GeometryObject, bekam" -#: FlatCAMApp.py:4141 +#: App_Main.py:3678 msgid "A Geometry object was converted to MultiGeo type." msgstr "Ein Geometrieobjekt wurde in den MultiGeo-Typ konvertiert." -#: FlatCAMApp.py:4179 +#: App_Main.py:3716 msgid "A Geometry object was converted to SingleGeo type." msgstr "Ein Geometrieobjekt wurde in den SingleGeo-Typ konvertiert." -#: FlatCAMApp.py:4472 +#: App_Main.py:3922 msgid "Toggle Units" msgstr "Einheiten wechseln" -#: FlatCAMApp.py:4474 +#: App_Main.py:3924 msgid "" "Changing the units of the project\n" "will scale all objects.\n" @@ -564,32 +17317,68 @@ msgstr "" "aller Objekte entsprechend skaliert.\n" "Wollen Sie Fortsetzen?" -#: FlatCAMApp.py:4477 FlatCAMApp.py:5025 FlatCAMApp.py:5102 FlatCAMApp.py:7728 -#: FlatCAMApp.py:7742 FlatCAMApp.py:8075 FlatCAMApp.py:8085 +#: App_Main.py:3927 App_Main.py:4201 App_Main.py:4278 App_Main.py:6722 +#: App_Main.py:6736 App_Main.py:7069 App_Main.py:7079 msgid "Ok" msgstr "Ok" -#: FlatCAMApp.py:4526 +#: App_Main.py:3977 msgid "Converted units to" msgstr "Einheiten wurden umgerechnet in" -#: FlatCAMApp.py:4928 +#: App_Main.py:4019 +#, fuzzy +#| msgid "All plots enabled." +msgid "Axis enabled." +msgstr "Alle Diagramme aktiviert." + +#: App_Main.py:4031 +#, fuzzy +#| msgid "All plots disabled." +msgid "Axis disabled." +msgstr "Alle Diagramme sind deaktiviert." + +#: App_Main.py:4039 +#, fuzzy +#| msgid "Disabled" +msgid "HUD disabled." +msgstr "Deaktiviert" + +#: App_Main.py:4041 +#, fuzzy +#| msgid "Enabled" +msgid "HUD enabled." +msgstr "Aktiviert" + +#: App_Main.py:4065 +#, fuzzy +#| msgid "Workspace Settings" +msgid "Grid enabled." +msgstr "Arbeitsbereichseinstellungen" + +#: App_Main.py:4080 +#, fuzzy +#| msgid "Workspace Settings" +msgid "Grid disabled." +msgstr "Arbeitsbereichseinstellungen" + +#: App_Main.py:4101 msgid "Detachable Tabs" msgstr "Abnehmbare Laschen" -#: FlatCAMApp.py:5014 flatcamTools/ToolNCC.py:932 flatcamTools/ToolNCC.py:1431 -#: flatcamTools/ToolPaint.py:858 flatcamTools/ToolSolderPaste.py:568 -#: flatcamTools/ToolSolderPaste.py:893 -msgid "Please enter a tool diameter with non-zero value, in Float format." -msgstr "" -"Bitte geben Sie einen Werkzeugdurchmesser ungleich Null im Float-Format ein." +#: App_Main.py:4130 +#, fuzzy +#| msgid "Workspace Settings" +msgid "Workspace enabled." +msgstr "Arbeitsbereichseinstellungen" -#: FlatCAMApp.py:5018 flatcamTools/ToolNCC.py:936 flatcamTools/ToolPaint.py:862 -#: flatcamTools/ToolSolderPaste.py:572 -msgid "Adding Tool cancelled" -msgstr "Addierwerkzeug abgebrochen" +#: App_Main.py:4133 +#, fuzzy +#| msgid "Workspace Settings" +msgid "Workspace disabled." +msgstr "Arbeitsbereichseinstellungen" -#: FlatCAMApp.py:5021 +#: App_Main.py:4197 msgid "" "Adding Tool works only when Advanced is checked.\n" "Go to Preferences -> General - Show Advanced Options." @@ -598,11 +17387,11 @@ msgstr "" "ist.\n" "Gehen Sie zu Einstellungen -> Allgemein - Erweiterte Optionen anzeigen." -#: FlatCAMApp.py:5097 +#: App_Main.py:4273 msgid "Delete objects" msgstr "Objekte löschen" -#: FlatCAMApp.py:5100 +#: App_Main.py:4276 msgid "" "Are you sure you want to permanently delete\n" "the selected objects?" @@ -610,153 +17399,92 @@ msgstr "" "Möchten Sie die ausgewählten Objekte\n" "wirklich dauerhaft löschen?" -#: FlatCAMApp.py:5138 +#: App_Main.py:4314 msgid "Object(s) deleted" msgstr "Objekt (e) gelöscht" -#: FlatCAMApp.py:5142 FlatCAMApp.py:5297 flatcamTools/ToolDblSided.py:818 -msgid "Failed. No object(s) selected..." -msgstr "Gescheitert. Kein Objekt ausgewählt ..." - -#: FlatCAMApp.py:5144 +#: App_Main.py:4324 msgid "Save the work in Editor and try again ..." msgstr "Speichern Sie den Editor und versuchen Sie es erneut ..." -#: FlatCAMApp.py:5173 +#: App_Main.py:4353 msgid "Object deleted" msgstr "Objekt (e) gelöscht" -#: FlatCAMApp.py:5200 +#: App_Main.py:4380 msgid "Click to set the origin ..." msgstr "Klicken Sie hier, um den Ursprung festzulegen ..." -#: FlatCAMApp.py:5222 +#: App_Main.py:4402 msgid "Setting Origin..." msgstr "Ursprung setzten ..." -#: FlatCAMApp.py:5235 FlatCAMApp.py:5337 +#: App_Main.py:4415 App_Main.py:4517 msgid "Origin set" msgstr "Ursprung gesetzt" -#: FlatCAMApp.py:5252 +#: App_Main.py:4432 msgid "Origin coordinates specified but incomplete." msgstr "Ursprungskoordinaten angegeben, aber unvollständig." -#: FlatCAMApp.py:5293 +#: App_Main.py:4473 msgid "Moving to Origin..." msgstr "Umzug zum Ursprung ..." -#: FlatCAMApp.py:5374 +#: App_Main.py:4554 msgid "Jump to ..." msgstr "Springen zu ..." -#: FlatCAMApp.py:5375 +#: App_Main.py:4555 msgid "Enter the coordinates in format X,Y:" msgstr "Geben Sie die Koordinaten im Format X, Y ein:" -#: FlatCAMApp.py:5385 +#: App_Main.py:4565 msgid "Wrong coordinates. Enter coordinates in format: X,Y" msgstr "Falsche Koordinaten. Koordinaten im Format eingeben: X, Y" -#: FlatCAMApp.py:5463 FlatCAMApp.py:5612 -#: flatcamEditors/FlatCAMExcEditor.py:3624 -#: flatcamEditors/FlatCAMExcEditor.py:3632 -#: flatcamEditors/FlatCAMGeoEditor.py:4349 -#: flatcamEditors/FlatCAMGeoEditor.py:4363 -#: flatcamEditors/FlatCAMGrbEditor.py:1087 -#: flatcamEditors/FlatCAMGrbEditor.py:1204 -#: flatcamEditors/FlatCAMGrbEditor.py:1490 -#: flatcamEditors/FlatCAMGrbEditor.py:1759 -#: flatcamEditors/FlatCAMGrbEditor.py:4622 -#: flatcamEditors/FlatCAMGrbEditor.py:4637 flatcamGUI/FlatCAMGUI.py:3424 -#: flatcamGUI/FlatCAMGUI.py:3436 flatcamTools/ToolAlignObjects.py:393 -#: flatcamTools/ToolAlignObjects.py:415 -msgid "Done." -msgstr "Fertig." - -#: FlatCAMApp.py:5478 FlatCAMApp.py:7724 FlatCAMApp.py:7819 FlatCAMApp.py:7860 -#: FlatCAMApp.py:7901 FlatCAMApp.py:7942 FlatCAMApp.py:7983 FlatCAMApp.py:8027 -#: FlatCAMApp.py:8071 FlatCAMApp.py:8593 FlatCAMApp.py:8597 -#: flatcamTools/ToolProperties.py:116 -msgid "No object selected." -msgstr "Kein Objekt ausgewählt." - -#: FlatCAMApp.py:5497 +#: App_Main.py:4683 msgid "Bottom-Left" msgstr "Unten links" -#: FlatCAMApp.py:5498 flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py:131 -#: flatcamTools/ToolCalibration.py:159 -msgid "Top-Left" -msgstr "Oben links" - -#: FlatCAMApp.py:5499 flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py:132 -#: flatcamTools/ToolCalibration.py:160 -msgid "Bottom-Right" -msgstr "Unten rechts" - -#: FlatCAMApp.py:5500 +#: App_Main.py:4686 msgid "Top-Right" msgstr "Oben rechts" -#: FlatCAMApp.py:5501 flatcamGUI/ObjectUI.py:2706 -msgid "Center" -msgstr "Center" - -#: FlatCAMApp.py:5521 +#: App_Main.py:4707 msgid "Locate ..." msgstr "Lokalisieren ..." -#: FlatCAMApp.py:5779 FlatCAMApp.py:5856 +#: App_Main.py:4970 App_Main.py:5047 msgid "No object is selected. Select an object and try again." msgstr "" "Es ist kein Objekt ausgewählt. Wählen Sie ein Objekt und versuchen Sie es " "erneut." -#: FlatCAMApp.py:5882 +#: App_Main.py:5073 msgid "" "Aborting. The current task will be gracefully closed as soon as possible..." msgstr "" "Abbrechen. Die aktuelle Aufgabe wird so schnell wie möglich ordnungsgemäß " "abgeschlossen ..." -#: FlatCAMApp.py:5888 +#: App_Main.py:5079 msgid "The current task was gracefully closed on user request..." msgstr "" "Die aktuelle Aufgabe wurde auf Benutzeranforderung ordnungsgemäß " "geschlossen ..." -#: FlatCAMApp.py:5916 flatcamGUI/preferences/PreferencesUIManager.py:905 -#: flatcamGUI/preferences/PreferencesUIManager.py:949 -#: flatcamGUI/preferences/PreferencesUIManager.py:970 -#: flatcamGUI/preferences/PreferencesUIManager.py:1075 -msgid "Preferences" -msgstr "Einstellungen" - -#: FlatCAMApp.py:5981 FlatCAMApp.py:6009 FlatCAMApp.py:6036 FlatCAMApp.py:6056 -#: FlatCAMDB.py:738 FlatCAMDB.py:913 FlatCAMDB.py:2200 FlatCAMDB.py:2418 -#: flatcamObjects/FlatCAMGeometry.py:890 flatcamTools/ToolNCC.py:3963 -#: flatcamTools/ToolNCC.py:4047 flatcamTools/ToolPaint.py:3553 -#: flatcamTools/ToolPaint.py:3638 -msgid "Tools Database" -msgstr "Werkzeugdatenbank" - -#: FlatCAMApp.py:6033 +#: App_Main.py:5224 msgid "Tools in Tools Database edited but not saved." msgstr "Werkzeugdatenbank geschlossen ohne zu speichern." -#: FlatCAMApp.py:6060 flatcamTools/ToolNCC.py:3970 -#: flatcamTools/ToolPaint.py:3560 -msgid "Tool from DB added in Tool Table." -msgstr "Werkzeug aus Werkzeugdatenbank zur Werkzeugtabelle hinzugefügt." - -#: FlatCAMApp.py:6062 +#: App_Main.py:5253 msgid "Adding tool from DB is not allowed for this object." msgstr "" "Das Hinzufügen von Werkzeugen aus der Datenbank ist für dieses Objekt nicht " "zulässig." -#: FlatCAMApp.py:6080 +#: App_Main.py:5271 msgid "" "One or more Tools are edited.\n" "Do you want to update the Tools Database?" @@ -764,175 +17492,113 @@ msgstr "" "Ein oder mehrere Werkzeuge wurden geändert.\n" "Möchten Sie die Werkzeugdatenbank aktualisieren?" -#: FlatCAMApp.py:6082 +#: App_Main.py:5273 msgid "Save Tools Database" msgstr "Werkzeugdatenbank speichern" -#: FlatCAMApp.py:6135 +#: App_Main.py:5326 msgid "No object selected to Flip on Y axis." msgstr "Kein Objekt ausgewählt, um auf der Y-Achse zu spiegeln." -#: FlatCAMApp.py:6161 +#: App_Main.py:5352 msgid "Flip on Y axis done." msgstr "Y-Achse spiegeln fertig." -#: FlatCAMApp.py:6163 FlatCAMApp.py:6211 -#: flatcamEditors/FlatCAMGrbEditor.py:6059 -msgid "Flip action was not executed." -msgstr "Flip-Aktion wurde nicht ausgeführt." - -#: FlatCAMApp.py:6183 +#: App_Main.py:5374 msgid "No object selected to Flip on X axis." msgstr "Es wurde kein Objekt zum Spiegeln auf der X-Achse ausgewählt." -#: FlatCAMApp.py:6209 +#: App_Main.py:5400 msgid "Flip on X axis done." msgstr "Flip on X axis done." -#: FlatCAMApp.py:6231 +#: App_Main.py:5422 msgid "No object selected to Rotate." msgstr "Es wurde kein Objekt zum Drehen ausgewählt." -#: FlatCAMApp.py:6234 FlatCAMApp.py:6287 FlatCAMApp.py:6326 +#: App_Main.py:5425 App_Main.py:5476 App_Main.py:5513 msgid "Transform" msgstr "Verwandeln" -#: FlatCAMApp.py:6234 FlatCAMApp.py:6287 FlatCAMApp.py:6326 +#: App_Main.py:5425 App_Main.py:5476 App_Main.py:5513 msgid "Enter the Angle value:" msgstr "Geben Sie den Winkelwert ein:" -#: FlatCAMApp.py:6265 +#: App_Main.py:5455 msgid "Rotation done." msgstr "Rotation abgeschlossen." -#: FlatCAMApp.py:6267 +#: App_Main.py:5457 msgid "Rotation movement was not executed." msgstr "Drehbewegung wurde nicht ausgeführt." -#: FlatCAMApp.py:6285 +#: App_Main.py:5474 msgid "No object selected to Skew/Shear on X axis." msgstr "Auf der X-Achse wurde kein Objekt zum Neigen / Schneiden ausgewählt." -#: FlatCAMApp.py:6307 +#: App_Main.py:5495 msgid "Skew on X axis done." msgstr "Neigung auf der X-Achse." -#: FlatCAMApp.py:6324 +#: App_Main.py:5511 msgid "No object selected to Skew/Shear on Y axis." msgstr "Kein Objekt für Neigung / Schneiden auf der Y-Achse ausgewählt." -#: FlatCAMApp.py:6346 +#: App_Main.py:5532 msgid "Skew on Y axis done." msgstr "Neigung auf der Y-Achse." -#: FlatCAMApp.py:6497 FlatCAMApp.py:6544 flatcamGUI/FlatCAMGUI.py:503 -#: flatcamGUI/FlatCAMGUI.py:1728 -msgid "Select All" -msgstr "Select All" - -#: FlatCAMApp.py:6501 FlatCAMApp.py:6548 flatcamGUI/FlatCAMGUI.py:505 -msgid "Deselect All" -msgstr "Alle abwählen" - -#: FlatCAMApp.py:6564 -msgid "All objects are selected." -msgstr "Alle Objekte werden ausgewählt." - -#: FlatCAMApp.py:6574 -msgid "Objects selection is cleared." -msgstr "Die Objektauswahl wird gelöscht." - -#: FlatCAMApp.py:6594 flatcamGUI/FlatCAMGUI.py:1721 -msgid "Grid On/Off" -msgstr "Raster ein/aus" - -#: FlatCAMApp.py:6606 flatcamEditors/FlatCAMGeoEditor.py:939 -#: flatcamEditors/FlatCAMGrbEditor.py:2583 -#: flatcamEditors/FlatCAMGrbEditor.py:5641 flatcamGUI/ObjectUI.py:1595 -#: flatcamTools/ToolDblSided.py:192 flatcamTools/ToolDblSided.py:425 -#: flatcamTools/ToolNCC.py:294 flatcamTools/ToolNCC.py:631 -#: flatcamTools/ToolPaint.py:277 flatcamTools/ToolPaint.py:676 -#: flatcamTools/ToolSolderPaste.py:122 flatcamTools/ToolSolderPaste.py:597 -#: flatcamTools/ToolTransform.py:478 -msgid "Add" -msgstr "Hinzufügen" - -#: FlatCAMApp.py:6608 flatcamEditors/FlatCAMGrbEditor.py:2588 -#: flatcamEditors/FlatCAMGrbEditor.py:2736 flatcamGUI/FlatCAMGUI.py:751 -#: flatcamGUI/FlatCAMGUI.py:1074 flatcamGUI/FlatCAMGUI.py:2141 -#: flatcamGUI/FlatCAMGUI.py:2284 flatcamGUI/FlatCAMGUI.py:2777 -#: flatcamGUI/ObjectUI.py:1623 flatcamObjects/FlatCAMGeometry.py:505 -#: flatcamTools/ToolNCC.py:316 flatcamTools/ToolNCC.py:637 -#: flatcamTools/ToolPaint.py:299 flatcamTools/ToolPaint.py:682 -#: flatcamTools/ToolSolderPaste.py:128 flatcamTools/ToolSolderPaste.py:600 -msgid "Delete" -msgstr "Löschen" - -#: FlatCAMApp.py:6624 +#: App_Main.py:5611 msgid "New Grid ..." msgstr "Neues Raster ..." -#: FlatCAMApp.py:6625 +#: App_Main.py:5612 msgid "Enter a Grid Value:" msgstr "Geben Sie einen Rasterwert ein:" -#: FlatCAMApp.py:6633 FlatCAMApp.py:6660 +#: App_Main.py:5620 App_Main.py:5644 msgid "Please enter a grid value with non-zero value, in Float format." msgstr "" "Bitte geben Sie im Float-Format einen Rasterwert mit einem Wert ungleich " "Null ein." -#: FlatCAMApp.py:6639 +#: App_Main.py:5625 msgid "New Grid added" msgstr "Neues Raster" -#: FlatCAMApp.py:6642 +#: App_Main.py:5627 msgid "Grid already exists" msgstr "Netz existiert bereits" -#: FlatCAMApp.py:6645 +#: App_Main.py:5629 msgid "Adding New Grid cancelled" msgstr "Neues Netz wurde abgebrochen" -#: FlatCAMApp.py:6667 +#: App_Main.py:5650 msgid " Grid Value does not exist" msgstr " Rasterwert existiert nicht" -#: FlatCAMApp.py:6670 +#: App_Main.py:5652 msgid "Grid Value deleted" msgstr "Rasterwert gelöscht" -#: FlatCAMApp.py:6673 +#: App_Main.py:5654 msgid "Delete Grid value cancelled" msgstr "Rasterwert löschen abgebrochen" -#: FlatCAMApp.py:6679 +#: App_Main.py:5660 msgid "Key Shortcut List" msgstr "Tastenkürzel Liste" -#: FlatCAMApp.py:6713 +#: App_Main.py:5694 msgid " No object selected to copy it's name" msgstr " Kein Objekt zum Kopieren des Namens ausgewählt" -#: FlatCAMApp.py:6717 +#: App_Main.py:5698 msgid "Name copied on clipboard ..." msgstr "Name in Zwischenablage kopiert ..." -#: FlatCAMApp.py:6930 flatcamEditors/FlatCAMGrbEditor.py:4554 -msgid "Coordinates copied to clipboard." -msgstr "Koordinaten in die Zwischenablage kopiert." - -#: FlatCAMApp.py:7167 FlatCAMApp.py:7173 FlatCAMApp.py:7179 FlatCAMApp.py:7185 -#: flatcamObjects/ObjectCollection.py:922 -#: flatcamObjects/ObjectCollection.py:928 -#: flatcamObjects/ObjectCollection.py:934 -#: flatcamObjects/ObjectCollection.py:940 -#: flatcamObjects/ObjectCollection.py:946 -#: flatcamObjects/ObjectCollection.py:952 -msgid "selected" -msgstr "ausgewählt" - -#: FlatCAMApp.py:7340 +#: App_Main.py:6331 msgid "" "There are files/objects opened in FlatCAM.\n" "Creating a New project will delete them.\n" @@ -942,17 +17608,12 @@ msgstr "" "Wenn Sie ein neues Projekt erstellen, werden diese gelöscht.\n" "Möchten Sie das Projekt speichern?" -#: FlatCAMApp.py:7361 +#: App_Main.py:6352 msgid "New Project created" msgstr "Neues Projekt erstellt" -#: FlatCAMApp.py:7519 FlatCAMApp.py:7523 flatcamGUI/FlatCAMGUI.py:836 -#: flatcamGUI/FlatCAMGUI.py:2544 -msgid "Open Gerber" -msgstr "Gerber öffnen" - -#: FlatCAMApp.py:7528 FlatCAMApp.py:7565 FlatCAMApp.py:7607 FlatCAMApp.py:7677 -#: FlatCAMApp.py:8462 FlatCAMApp.py:9675 FlatCAMApp.py:9737 +#: App_Main.py:6522 App_Main.py:6559 App_Main.py:6601 App_Main.py:6671 +#: App_Main.py:7454 App_Main.py:8667 App_Main.py:8729 msgid "" "Canvas initialization started.\n" "Canvas initialization finished in" @@ -960,351 +17621,296 @@ msgstr "" "Die Canvas-Initialisierung wurde gestartet.\n" "Canvas-Initialisierung abgeschlossen in" -#: FlatCAMApp.py:7530 +#: App_Main.py:6524 msgid "Opening Gerber file." msgstr "Gerber-Datei öffnen." -#: FlatCAMApp.py:7557 FlatCAMApp.py:7561 flatcamGUI/FlatCAMGUI.py:838 -#: flatcamGUI/FlatCAMGUI.py:2546 -msgid "Open Excellon" -msgstr "Excellon öffnen" - -#: FlatCAMApp.py:7567 +#: App_Main.py:6561 msgid "Opening Excellon file." msgstr "Excellon-Datei öffnen." -#: FlatCAMApp.py:7598 FlatCAMApp.py:7602 +#: App_Main.py:6592 App_Main.py:6596 msgid "Open G-Code" msgstr "G-Code öffnen" -#: FlatCAMApp.py:7609 +#: App_Main.py:6603 msgid "Opening G-Code file." msgstr "Öffnen der G-Code-Datei." -#: FlatCAMApp.py:7632 FlatCAMApp.py:7635 flatcamGUI/FlatCAMGUI.py:1730 -msgid "Open Project" -msgstr "Projekt öffnen" - -#: FlatCAMApp.py:7668 FlatCAMApp.py:7672 +#: App_Main.py:6662 App_Main.py:6666 msgid "Open HPGL2" msgstr "HPGL2 öffnen" -#: FlatCAMApp.py:7679 +#: App_Main.py:6673 msgid "Opening HPGL2 file." msgstr "HPGL2-Datei öffnen." -#: FlatCAMApp.py:7702 FlatCAMApp.py:7705 +#: App_Main.py:6696 App_Main.py:6699 msgid "Open Configuration File" msgstr "Einstellungsdatei öffne" -#: FlatCAMApp.py:7725 FlatCAMApp.py:8072 +#: App_Main.py:6719 App_Main.py:7066 msgid "Please Select a Geometry object to export" msgstr "Bitte wählen Sie ein Geometrieobjekt zum Exportieren aus" -#: FlatCAMApp.py:7739 +#: App_Main.py:6733 msgid "Only Geometry, Gerber and CNCJob objects can be used." msgstr "Es können nur Geometrie-, Gerber- und CNCJob-Objekte verwendet werden." -#: FlatCAMApp.py:7752 FlatCAMApp.py:7756 flatcamTools/ToolQRCode.py:829 -#: flatcamTools/ToolQRCode.py:833 -msgid "Export SVG" -msgstr "SVG exportieren" - -#: FlatCAMApp.py:7781 +#: App_Main.py:6775 msgid "Data must be a 3D array with last dimension 3 or 4" msgstr "Daten müssen ein 3D-Array mit der letzten Dimension 3 oder 4 sein" -#: FlatCAMApp.py:7787 FlatCAMApp.py:7791 +#: App_Main.py:6781 App_Main.py:6785 msgid "Export PNG Image" msgstr "PNG-Bild exportieren" -#: FlatCAMApp.py:7824 FlatCAMApp.py:8032 +#: App_Main.py:6818 App_Main.py:7026 msgid "Failed. Only Gerber objects can be saved as Gerber files..." msgstr "" "Fehlgeschlagen. Nur Gerber-Objekte können als Gerber-Dateien gespeichert " "werden ..." -#: FlatCAMApp.py:7836 +#: App_Main.py:6830 msgid "Save Gerber source file" msgstr "Gerber-Quelldatei speichern" -#: FlatCAMApp.py:7865 +#: App_Main.py:6859 msgid "Failed. Only Script objects can be saved as TCL Script files..." msgstr "" "Gescheitert. Nur Skriptobjekte können als TCL-Skriptdateien gespeichert " "werden ..." -#: FlatCAMApp.py:7877 +#: App_Main.py:6871 msgid "Save Script source file" msgstr "Speichern Sie die Quelldatei des Skripts" -#: FlatCAMApp.py:7906 +#: App_Main.py:6900 msgid "Failed. Only Document objects can be saved as Document files..." msgstr "" "Gescheitert. Nur Dokumentobjekte können als Dokumentdateien gespeichert " "werden ..." -#: FlatCAMApp.py:7918 +#: App_Main.py:6912 msgid "Save Document source file" msgstr "Speichern Sie die Quelldatei des Dokuments" -#: FlatCAMApp.py:7947 FlatCAMApp.py:7988 FlatCAMApp.py:8945 +#: App_Main.py:6941 App_Main.py:6982 App_Main.py:7937 msgid "Failed. Only Excellon objects can be saved as Excellon files..." msgstr "" "Fehlgeschlagen. Nur Excellon-Objekte können als Excellon-Dateien gespeichert " "werden ..." -#: FlatCAMApp.py:7955 FlatCAMApp.py:7959 +#: App_Main.py:6949 App_Main.py:6953 msgid "Save Excellon source file" msgstr "Speichern Sie die Excellon-Quelldatei" -#: FlatCAMApp.py:7996 FlatCAMApp.py:8000 +#: App_Main.py:6990 App_Main.py:6994 msgid "Export Excellon" msgstr "Excellon exportieren" -#: FlatCAMApp.py:8040 FlatCAMApp.py:8044 +#: App_Main.py:7034 App_Main.py:7038 msgid "Export Gerber" msgstr "Gerber exportieren" -#: FlatCAMApp.py:8082 +#: App_Main.py:7076 msgid "Only Geometry objects can be used." msgstr "Es können nur Geometrieobjekte verwendet werden." -#: FlatCAMApp.py:8096 FlatCAMApp.py:8100 +#: App_Main.py:7090 App_Main.py:7094 msgid "Export DXF" msgstr "DXF exportieren" -#: FlatCAMApp.py:8125 FlatCAMApp.py:8128 +#: App_Main.py:7119 App_Main.py:7122 msgid "Import SVG" msgstr "SVG importieren" -#: FlatCAMApp.py:8156 FlatCAMApp.py:8160 +#: App_Main.py:7150 App_Main.py:7154 msgid "Import DXF" msgstr "Importieren Sie DXF" -#: FlatCAMApp.py:8210 +#: App_Main.py:7204 msgid "Viewing the source code of the selected object." msgstr "Anzeigen des Quellcodes des ausgewählten Objekts." -#: FlatCAMApp.py:8211 flatcamObjects/FlatCAMCNCJob.py:548 -#: flatcamObjects/FlatCAMScript.py:134 -msgid "Loading..." -msgstr "Wird geladen..." - -#: FlatCAMApp.py:8217 FlatCAMApp.py:8221 +#: App_Main.py:7211 App_Main.py:7215 msgid "Select an Gerber or Excellon file to view it's source file." msgstr "" "Wählen Sie eine Gerber- oder Excellon-Datei aus, um die Quelldatei " "anzuzeigen." -#: FlatCAMApp.py:8235 +#: App_Main.py:7229 msgid "Source Editor" msgstr "Quelleditor" -#: FlatCAMApp.py:8275 FlatCAMApp.py:8282 +#: App_Main.py:7269 App_Main.py:7276 msgid "There is no selected object for which to see it's source file code." msgstr "" "Es gibt kein ausgewähltes Objekt, für das man seinen Quelldateien sehen kann." -#: FlatCAMApp.py:8294 +#: App_Main.py:7288 msgid "Failed to load the source code for the selected object" msgstr "Fehler beim Laden des Quellcodes für das ausgewählte Objekt" -#: FlatCAMApp.py:8308 flatcamObjects/FlatCAMCNCJob.py:562 -msgid "Code Editor" -msgstr "Code-Editor" - -#: FlatCAMApp.py:8330 +#: App_Main.py:7324 msgid "Go to Line ..." msgstr "Gehe zur Linie ..." -#: FlatCAMApp.py:8331 +#: App_Main.py:7325 msgid "Line:" msgstr "Linie:" -#: FlatCAMApp.py:8360 +#: App_Main.py:7352 msgid "New TCL script file created in Code Editor." msgstr "Neue TCL-Skriptdatei, die im Code-Editor erstellt wurde." -#: FlatCAMApp.py:8396 FlatCAMApp.py:8398 FlatCAMApp.py:8434 FlatCAMApp.py:8436 +#: App_Main.py:7388 App_Main.py:7390 App_Main.py:7426 App_Main.py:7428 msgid "Open TCL script" msgstr "Öffnen Sie das TCL-Skript" -#: FlatCAMApp.py:8464 +#: App_Main.py:7456 msgid "Executing ScriptObject file." msgstr "Ausführen der ScriptObject-Datei." -#: FlatCAMApp.py:8472 FlatCAMApp.py:8475 +#: App_Main.py:7464 App_Main.py:7467 msgid "Run TCL script" msgstr "Führen Sie das TCL-Skript aus" -#: FlatCAMApp.py:8498 +#: App_Main.py:7490 msgid "TCL script file opened in Code Editor and executed." msgstr "TCL-Skriptdatei im Code-Editor geöffnet und ausgeführt." -#: FlatCAMApp.py:8549 FlatCAMApp.py:8555 +#: App_Main.py:7541 App_Main.py:7547 msgid "Save Project As ..." msgstr "Projekt speichern als ..." -#: FlatCAMApp.py:8551 flatcamGUI/FlatCAMGUI.py:1134 -#: flatcamGUI/FlatCAMGUI.py:2176 -msgid "Project" -msgstr "Projekt" - -#: FlatCAMApp.py:8590 +#: App_Main.py:7582 msgid "FlatCAM objects print" msgstr "FlatCAM-Objekte werden gedruckt" -#: FlatCAMApp.py:8603 FlatCAMApp.py:8610 +#: App_Main.py:7595 App_Main.py:7602 msgid "Save Object as PDF ..." msgstr "Objekt als PDF speichern ..." -#: FlatCAMApp.py:8619 +#: App_Main.py:7611 msgid "Printing PDF ... Please wait." msgstr "PDF wird gedruckt ... Bitte warten." -#: FlatCAMApp.py:8798 +#: App_Main.py:7790 msgid "PDF file saved to" msgstr "PDF-Datei gespeichert in" -#: FlatCAMApp.py:8823 +#: App_Main.py:7815 msgid "Exporting SVG" msgstr "SVG exportieren" -#: FlatCAMApp.py:8866 +#: App_Main.py:7858 msgid "SVG file exported to" msgstr "SVG-Datei exportiert nach" -#: FlatCAMApp.py:8892 +#: App_Main.py:7884 msgid "" "Save cancelled because source file is empty. Try to export the Gerber file." msgstr "" "Speichern abgebrochen, da die Quelldatei leer ist. Versuchen Sie einen " "Export der Gerber Datei." -#: FlatCAMApp.py:9039 +#: App_Main.py:8031 msgid "Excellon file exported to" msgstr "Excellon-Datei exportiert nach" -#: FlatCAMApp.py:9048 +#: App_Main.py:8040 msgid "Exporting Excellon" msgstr "Excellon exportieren" -#: FlatCAMApp.py:9053 FlatCAMApp.py:9060 +#: App_Main.py:8045 App_Main.py:8052 msgid "Could not export Excellon file." msgstr "Excellon-Datei konnte nicht exportiert werden." -#: FlatCAMApp.py:9175 +#: App_Main.py:8167 msgid "Gerber file exported to" msgstr "Gerberdatei exportiert nach" -#: FlatCAMApp.py:9183 +#: App_Main.py:8175 msgid "Exporting Gerber" msgstr "Gerber exportieren" -#: FlatCAMApp.py:9188 FlatCAMApp.py:9195 +#: App_Main.py:8180 App_Main.py:8187 msgid "Could not export Gerber file." msgstr "Gerber-Datei konnte nicht exportiert werden." -#: FlatCAMApp.py:9230 +#: App_Main.py:8222 msgid "DXF file exported to" msgstr "DXF-Datei exportiert nach" -#: FlatCAMApp.py:9236 +#: App_Main.py:8228 msgid "Exporting DXF" msgstr "DXF exportieren" -#: FlatCAMApp.py:9241 FlatCAMApp.py:9248 +#: App_Main.py:8233 App_Main.py:8240 msgid "Could not export DXF file." msgstr "DXF-Datei konnte nicht exportiert werden." -#: FlatCAMApp.py:9272 FlatCAMApp.py:9319 flatcamTools/ToolImage.py:277 -msgid "" -"Not supported type is picked as parameter. Only Geometry and Gerber are " -"supported" -msgstr "" -"Nicht unterstützte Art wird als Parameter ausgewählt. Nur Geometrie und " -"Gerber werden unterstützt" - -#: FlatCAMApp.py:9282 +#: App_Main.py:8274 msgid "Importing SVG" msgstr "SVG importieren" -#: FlatCAMApp.py:9290 FlatCAMApp.py:9336 +#: App_Main.py:8282 App_Main.py:8328 msgid "Import failed." msgstr "Import fehlgeschlagen." -#: FlatCAMApp.py:9297 FlatCAMApp.py:9343 FlatCAMApp.py:9407 FlatCAMApp.py:9474 -#: FlatCAMApp.py:9540 FlatCAMApp.py:9605 FlatCAMApp.py:9662 -#: flatcamTools/ToolImage.py:297 flatcamTools/ToolPDF.py:225 -msgid "Opened" -msgstr "Geöffnet" - -#: FlatCAMApp.py:9328 +#: App_Main.py:8320 msgid "Importing DXF" msgstr "DXF importieren" -#: FlatCAMApp.py:9369 FlatCAMApp.py:9564 FlatCAMApp.py:9629 +#: App_Main.py:8361 App_Main.py:8556 App_Main.py:8621 msgid "Failed to open file" msgstr "Datei konnte nicht geöffnet werden" -#: FlatCAMApp.py:9372 FlatCAMApp.py:9567 FlatCAMApp.py:9632 +#: App_Main.py:8364 App_Main.py:8559 App_Main.py:8624 msgid "Failed to parse file" msgstr "Datei konnte nicht analysiert werden" -#: FlatCAMApp.py:9384 +#: App_Main.py:8376 msgid "Object is not Gerber file or empty. Aborting object creation." msgstr "" "Objekt ist keine Gerberdatei oder leer. Objekterstellung wird abgebrochen." -#: FlatCAMApp.py:9389 +#: App_Main.py:8381 msgid "Opening Gerber" msgstr "Gerber öffnen" -#: FlatCAMApp.py:9400 +#: App_Main.py:8392 msgid "Open Gerber failed. Probable not a Gerber file." msgstr "Open Gerber ist fehlgeschlagen. Wahrscheinlich keine Gerber-Datei." -#: FlatCAMApp.py:9432 flatcamTools/ToolPcbWizard.py:424 -msgid "This is not Excellon file." -msgstr "Dies ist keine Excellon-Datei." - -#: FlatCAMApp.py:9436 +#: App_Main.py:8428 msgid "Cannot open file" msgstr "Kann Datei nicht öffnen" -#: FlatCAMApp.py:9454 flatcamTools/ToolPDF.py:275 -#: flatcamTools/ToolPcbWizard.py:445 -msgid "No geometry found in file" -msgstr "Keine Geometrie in der Datei gefunden" - -#: FlatCAMApp.py:9457 +#: App_Main.py:8449 msgid "Opening Excellon." msgstr "Eröffnung Excellon." -#: FlatCAMApp.py:9467 +#: App_Main.py:8459 msgid "Open Excellon file failed. Probable not an Excellon file." msgstr "" "Die Excellon-Datei konnte nicht geöffnet werden. Wahrscheinlich keine " "Excellon-Datei." -#: FlatCAMApp.py:9499 +#: App_Main.py:8491 msgid "Reading GCode file" msgstr "GCode-Datei wird gelesen" -#: FlatCAMApp.py:9505 -msgid "Failed to open" -msgstr "Gescheitert zu öffnen" - -#: FlatCAMApp.py:9512 +#: App_Main.py:8504 msgid "This is not GCODE" msgstr "Dies ist kein GCODE" -#: FlatCAMApp.py:9517 +#: App_Main.py:8509 msgid "Opening G-Code." msgstr "G-Code öffnen." -#: FlatCAMApp.py:9530 +#: App_Main.py:8522 msgid "" "Failed to create CNCJob Object. Probable not a GCode file. Try to load it " "from File menu.\n" @@ -1316,127 +17922,133 @@ msgstr "" "Der Versuch, ein FlatCAM CNCJob-Objekt aus einer G-Code-Datei zu erstellen, " "ist während der Verarbeitung fehlgeschlagen" -#: FlatCAMApp.py:9586 +#: App_Main.py:8578 msgid "Object is not HPGL2 file or empty. Aborting object creation." msgstr "" "Objekt ist keine HPGL2-Datei oder leer. Objekterstellung wird abgebrochen." -#: FlatCAMApp.py:9591 +#: App_Main.py:8583 msgid "Opening HPGL2" msgstr "HPGL2 öffnen" -#: FlatCAMApp.py:9598 +#: App_Main.py:8590 msgid " Open HPGL2 failed. Probable not a HPGL2 file." msgstr " HPGL2 öffnen ist fehlgeschlagen. Wahrscheinlich keine HPGL2-Datei." -#: FlatCAMApp.py:9624 +#: App_Main.py:8616 msgid "TCL script file opened in Code Editor." msgstr "TCL-Skriptdatei im Code-Editor geöffnet." -#: FlatCAMApp.py:9644 +#: App_Main.py:8636 msgid "Opening TCL Script..." msgstr "TCL-Skript wird geöffnet ..." -#: FlatCAMApp.py:9655 +#: App_Main.py:8647 msgid "Failed to open TCL Script." msgstr "TCL-Skript konnte nicht geöffnet werden." -#: FlatCAMApp.py:9677 +#: App_Main.py:8669 msgid "Opening FlatCAM Config file." msgstr "Öffnen der FlatCAM Config-Datei." -#: FlatCAMApp.py:9705 +#: App_Main.py:8697 msgid "Failed to open config file" msgstr "Fehler beim Öffnen der Konfigurationsdatei" -#: FlatCAMApp.py:9734 +#: App_Main.py:8726 msgid "Loading Project ... Please Wait ..." msgstr "Projekt wird geladen ... Bitte warten ..." -#: FlatCAMApp.py:9739 +#: App_Main.py:8731 msgid "Opening FlatCAM Project file." msgstr "Öffnen der FlatCAM-Projektdatei." -#: FlatCAMApp.py:9754 FlatCAMApp.py:9758 FlatCAMApp.py:9775 +#: App_Main.py:8746 App_Main.py:8750 App_Main.py:8767 msgid "Failed to open project file" msgstr "Projektdatei konnte nicht geöffnet werden" -#: FlatCAMApp.py:9812 +#: App_Main.py:8804 msgid "Loading Project ... restoring" msgstr "Projekt wird geladen ... wird wiederhergestellt" -#: FlatCAMApp.py:9822 +#: App_Main.py:8814 msgid "Project loaded from" msgstr "Projekt geladen von" -#: FlatCAMApp.py:9846 +#: App_Main.py:8840 msgid "Redrawing all objects" msgstr "Alle Objekte neu zeichnen" -#: FlatCAMApp.py:9934 +#: App_Main.py:8928 msgid "Failed to load recent item list." msgstr "Fehler beim Laden der letzten Elementliste." -#: FlatCAMApp.py:9941 +#: App_Main.py:8935 msgid "Failed to parse recent item list." msgstr "Liste der letzten Artikel konnte nicht analysiert werden." -#: FlatCAMApp.py:9951 +#: App_Main.py:8945 msgid "Failed to load recent projects item list." msgstr "Fehler beim Laden der Artikelliste der letzten Projekte." -#: FlatCAMApp.py:9958 +#: App_Main.py:8952 msgid "Failed to parse recent project item list." msgstr "" "Fehler beim Analysieren der Liste der zuletzt verwendeten Projektelemente." -#: FlatCAMApp.py:10019 +#: App_Main.py:9013 msgid "Clear Recent projects" msgstr "Letzte Projekte löschen" -#: FlatCAMApp.py:10043 +#: App_Main.py:9037 msgid "Clear Recent files" msgstr "Letzte Dateien löschen" -#: FlatCAMApp.py:10065 flatcamGUI/FlatCAMGUI.py:1363 -msgid "Shortcut Key List" -msgstr " Liste der Tastenkombinationen " - -#: FlatCAMApp.py:10145 +#: App_Main.py:9139 msgid "Selected Tab - Choose an Item from Project Tab" msgstr "" "Ausgewählte Registerkarte - Wählen Sie ein Element auf der Registerkarte " "\"Projekt\" aus" -#: FlatCAMApp.py:10146 +#: App_Main.py:9140 msgid "Details" msgstr "Einzelheiten" -#: FlatCAMApp.py:10148 +#: App_Main.py:9142 msgid "The normal flow when working in FlatCAM is the following:" msgstr "Der normale Ablauf beim Arbeiten in FlatCAM ist der folgende:" -#: FlatCAMApp.py:10149 +#: App_Main.py:9143 +#, fuzzy +#| msgid "" +#| "Load/Import a Gerber, Excellon, Gcode, DXF, Raster Image or SVG file into " +#| "FlatCAM using either the toolbars, key shortcuts or even dragging and " +#| "dropping the files on the GUI." msgid "" "Load/Import a Gerber, Excellon, Gcode, DXF, Raster Image or SVG file into " "FlatCAM using either the toolbars, key shortcuts or even dragging and " -"dropping the files on the GUI." +"dropping the files on the AppGUI." msgstr "" "Laden / Importieren Sie eine Gerber-, Excellon-, Gcode-, DXF-, Rasterbild- " "oder SVG-Datei mithilfe der Symbolleisten, Tastenkombinationen oder durch " "Ziehen und Ablegen der Dateien auf der GUI in FlatCAM." -#: FlatCAMApp.py:10152 +#: App_Main.py:9146 +#, fuzzy +#| msgid "" +#| "You can also load a FlatCAM project by double clicking on the project " +#| "file, drag and drop of the file into the FLATCAM GUI or through the menu " +#| "(or toolbar) actions offered within the app." msgid "" "You can also load a FlatCAM project by double clicking on the project file, " -"drag and drop of the file into the FLATCAM GUI or through the menu (or " +"drag and drop of the file into the FLATCAM AppGUI or through the menu (or " "toolbar) actions offered within the app." msgstr "" "Sie können ein FlatCAM-Projekt auch laden, indem Sie auf die Projektdatei " "doppelklicken, sie per Drag & Drop in die FLATCAM-Benutzeroberfläche ziehen " "oder über die in der App angebotenen Menü- (oder Symbolleisten-) Aktionen." -#: FlatCAMApp.py:10155 +#: App_Main.py:9149 msgid "" "Once an object is available in the Project Tab, by selecting it and then " "focusing on SELECTED TAB (more simpler is to double click the object name in " @@ -1449,7 +18061,7 @@ msgstr "" "AUSGEWÄHLTES TAB mit den Objekteigenschaften entsprechend der Art " "aktualisiert: Gerber, Excellon-, Geometrie- oder CNCJob-Objekt." -#: FlatCAMApp.py:10159 +#: App_Main.py:9153 msgid "" "If the selection of the object is done on the canvas by single click " "instead, and the SELECTED TAB is in focus, again the object properties will " @@ -1464,7 +18076,7 @@ msgstr "" "doppelklicken, um das Ausgewählte Registerkarte zu öffnen und es zu füllen, " "selbst wenn es unscharf war." -#: FlatCAMApp.py:10163 +#: App_Main.py:9157 msgid "" "You can change the parameters in this screen and the flow direction is like " "this:" @@ -1472,7 +18084,7 @@ msgstr "" "Sie können die Parameter in diesem Bildschirm ändern und die Flussrichtung " "ist wie folgt:" -#: FlatCAMApp.py:10164 +#: App_Main.py:9158 msgid "" "Gerber/Excellon Object --> Change Parameter --> Generate Geometry --> " "Geometry Object --> Add tools (change param in Selected Tab) --> Generate " @@ -1485,7 +18097,7 @@ msgstr "" "überprüfen (über CNC bearbeiten) Code) und / oder GCode anhängen / " "voranstellen (ebenfalls in Ausgewählte Registerkarte) -> GCode speichern." -#: FlatCAMApp.py:10168 +#: App_Main.py:9162 msgid "" "A list of key shortcuts is available through an menu entry in Help --> " "Shortcuts List or through its own key shortcut: F3." @@ -1494,33 +18106,33 @@ msgstr "" "der Hilfe -> Liste der Tastenkombinationen oder über eine eigene " "Tastenkombination: F3." -#: FlatCAMApp.py:10232 +#: App_Main.py:9226 msgid "Failed checking for latest version. Could not connect." msgstr "" "Fehler bei der Suche nach der neuesten Version. Konnte keine Verbindung " "herstellen." -#: FlatCAMApp.py:10239 +#: App_Main.py:9233 msgid "Could not parse information about latest version." msgstr "Informationen zur neuesten Version konnten nicht analysiert werden." -#: FlatCAMApp.py:10249 +#: App_Main.py:9243 msgid "FlatCAM is up to date!" msgstr "FlatCAM ist auf dem neuesten Version!" -#: FlatCAMApp.py:10254 +#: App_Main.py:9248 msgid "Newer Version Available" msgstr "Neuere Version verfügbar" -#: FlatCAMApp.py:10256 +#: App_Main.py:9250 msgid "There is a newer version of FlatCAM available for download:" msgstr "Es gibt eine neuere Version von FlatCAM zum Download:" -#: FlatCAMApp.py:10260 +#: App_Main.py:9254 msgid "info" msgstr "Info" -#: FlatCAMApp.py:10288 +#: App_Main.py:9282 msgid "" "OpenGL canvas initialization failed. HW or HW configuration not supported." "Change the graphic engine to Legacy(2D) in Edit -> Preferences -> General " @@ -1532,129 +18144,75 @@ msgstr "" "Einstellungen -> Registerkarte Allgemein in Legacy (2D).\n" "\n" -#: FlatCAMApp.py:10367 +#: App_Main.py:9360 msgid "All plots disabled." msgstr "Alle Diagramme sind deaktiviert." -#: FlatCAMApp.py:10374 +#: App_Main.py:9367 msgid "All non selected plots disabled." msgstr "Alle nicht ausgewählten Diagramme sind deaktiviert." -#: FlatCAMApp.py:10381 +#: App_Main.py:9374 msgid "All plots enabled." msgstr "Alle Diagramme aktiviert." -#: FlatCAMApp.py:10387 +#: App_Main.py:9380 msgid "Selected plots enabled..." msgstr "Ausgewählte Diagramme aktiviert ..." -#: FlatCAMApp.py:10395 +#: App_Main.py:9388 msgid "Selected plots disabled..." msgstr "Ausgewählte Diagramme deaktiviert ..." -#: FlatCAMApp.py:10428 +#: App_Main.py:9421 msgid "Enabling plots ..." msgstr "Diagramm aktivieren..." -#: FlatCAMApp.py:10480 +#: App_Main.py:9470 msgid "Disabling plots ..." msgstr "Diagramm deaktivieren..." -#: FlatCAMApp.py:10503 +#: App_Main.py:9493 msgid "Working ..." msgstr "Arbeiten ..." -#: FlatCAMApp.py:10558 flatcamGUI/FlatCAMGUI.py:703 -msgid "Red" -msgstr "Rote" - -#: FlatCAMApp.py:10560 flatcamGUI/FlatCAMGUI.py:706 -msgid "Blue" -msgstr "Blau" - -#: FlatCAMApp.py:10563 flatcamGUI/FlatCAMGUI.py:709 -msgid "Yellow" -msgstr "Gelb" - -#: FlatCAMApp.py:10565 flatcamGUI/FlatCAMGUI.py:712 -msgid "Green" -msgstr "Grün" - -#: FlatCAMApp.py:10567 flatcamGUI/FlatCAMGUI.py:715 -msgid "Purple" -msgstr "Lila" - -#: FlatCAMApp.py:10569 flatcamGUI/FlatCAMGUI.py:718 -msgid "Brown" -msgstr "Braun" - -#: FlatCAMApp.py:10571 FlatCAMApp.py:10627 flatcamGUI/FlatCAMGUI.py:721 -msgid "White" -msgstr "Weiß" - -#: FlatCAMApp.py:10573 flatcamGUI/FlatCAMGUI.py:724 -msgid "Black" -msgstr "Schwarz" - -#: FlatCAMApp.py:10576 flatcamGUI/FlatCAMGUI.py:729 -msgid "Custom" -msgstr "Maßgeschn." - -#: FlatCAMApp.py:10586 flatcamGUI/FlatCAMGUI.py:737 -msgid "Default" -msgstr "Standard" - -#: FlatCAMApp.py:10610 flatcamGUI/FlatCAMGUI.py:734 -msgid "Opacity" -msgstr "Opazität" - -#: FlatCAMApp.py:10612 +#: App_Main.py:9602 msgid "Set alpha level ..." msgstr "Alpha-Level einstellen ..." -#: FlatCAMApp.py:10612 -#: flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:131 -#: flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:133 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:220 -#: flatcamTools/ToolExtractDrills.py:164 flatcamTools/ToolExtractDrills.py:285 -#: flatcamTools/ToolPunchGerber.py:192 flatcamTools/ToolPunchGerber.py:308 -#: flatcamTools/ToolTransform.py:357 -msgid "Value" -msgstr "Wert" - -#: FlatCAMApp.py:10666 +#: App_Main.py:9656 msgid "Saving FlatCAM Project" msgstr "FlatCAM-Projekt speichern" -#: FlatCAMApp.py:10687 FlatCAMApp.py:10723 +#: App_Main.py:9677 App_Main.py:9713 msgid "Project saved to" msgstr "Projekt gespeichert in" -#: FlatCAMApp.py:10694 +#: App_Main.py:9684 msgid "The object is used by another application." msgstr "Das Objekt wird von einer anderen Anwendung verwendet." -#: FlatCAMApp.py:10708 +#: App_Main.py:9698 msgid "Failed to verify project file" msgstr "Fehler beim Überprüfen der Projektdatei" -#: FlatCAMApp.py:10708 FlatCAMApp.py:10716 FlatCAMApp.py:10726 +#: App_Main.py:9698 App_Main.py:9706 App_Main.py:9716 msgid "Retry to save it." msgstr "Versuchen Sie erneut, es zu speichern." -#: FlatCAMApp.py:10716 FlatCAMApp.py:10726 +#: App_Main.py:9706 App_Main.py:9716 msgid "Failed to parse saved project file" msgstr "Fehler beim Parsen der Projektdatei" -#: FlatCAMBookmark.py:57 FlatCAMBookmark.py:84 +#: Bookmark.py:57 Bookmark.py:84 msgid "Title" msgstr "Titel" -#: FlatCAMBookmark.py:58 FlatCAMBookmark.py:88 +#: Bookmark.py:58 Bookmark.py:88 msgid "Web Link" msgstr "Weblink" -#: FlatCAMBookmark.py:62 +#: Bookmark.py:62 msgid "" "Index.\n" "The rows in gray color will populate the Bookmarks menu.\n" @@ -1664,7 +18222,7 @@ msgstr "" "Die grauen Zeilen füllen das Lesezeichen-Menü.\n" "Die Anzahl der grauen Zeilen wird in den Einstellungen festgelegt." -#: FlatCAMBookmark.py:66 +#: Bookmark.py:66 msgid "" "Description of the link that is set as an menu action.\n" "Try to keep it short because it is installed as a menu item." @@ -1672,1106 +18230,186 @@ msgstr "" "Beschreibung des Links, der als Menüaktion festgelegt wird.\n" "Versuchen Sie es kurz zu halten, da es als Menüelement installiert ist." -#: FlatCAMBookmark.py:69 +#: Bookmark.py:69 msgid "Web Link. E.g: https://your_website.org " msgstr "Weblink. ZB: https://your_website.org " -#: FlatCAMBookmark.py:78 +#: Bookmark.py:78 msgid "New Bookmark" msgstr "Neues Lesezeichen" -#: FlatCAMBookmark.py:97 +#: Bookmark.py:97 msgid "Add Entry" msgstr "Eintrag hinzufügen" -#: FlatCAMBookmark.py:98 +#: Bookmark.py:98 msgid "Remove Entry" msgstr "Eintrag entfernen" -#: FlatCAMBookmark.py:99 +#: Bookmark.py:99 msgid "Export List" msgstr "Liste exportieren" -#: FlatCAMBookmark.py:100 +#: Bookmark.py:100 msgid "Import List" msgstr "Liste importieren" -#: FlatCAMBookmark.py:190 +#: Bookmark.py:190 msgid "Title entry is empty." msgstr "Kein Titel eingegeben." -#: FlatCAMBookmark.py:199 +#: Bookmark.py:199 msgid "Web link entry is empty." msgstr "Keine Internetadresse angegeben." -#: FlatCAMBookmark.py:207 +#: Bookmark.py:207 msgid "Either the Title or the Weblink already in the table." msgstr "" "Entweder Titel oder Internetadresse sind bereits in der Tabelle vorhanden." -#: FlatCAMBookmark.py:227 +#: Bookmark.py:227 msgid "Bookmark added." msgstr "Lesezeichen verwalten." -#: FlatCAMBookmark.py:244 +#: Bookmark.py:244 msgid "This bookmark can not be removed" msgstr "Dieses Lesezeichen kann nicht entfernt werden" -#: FlatCAMBookmark.py:275 +#: Bookmark.py:275 msgid "Bookmark removed." msgstr "Lesezeichen entfernt." -#: FlatCAMBookmark.py:290 +#: Bookmark.py:290 msgid "Export FlatCAM Bookmarks" msgstr "Export der FlatCAM-Lesezeichen" -#: FlatCAMBookmark.py:293 flatcamGUI/FlatCAMGUI.py:524 -msgid "Bookmarks" -msgstr "Lesezeichen" - -#: FlatCAMBookmark.py:319 FlatCAMBookmark.py:349 +#: Bookmark.py:319 Bookmark.py:349 msgid "Could not load bookmarks file." msgstr "Die Lesezeichen-Datei konnte nicht geladen werden." -#: FlatCAMBookmark.py:329 +#: Bookmark.py:329 msgid "Failed to write bookmarks to file." msgstr "Fehler beim Schreiben der Lesezeichen in die Datei." -#: FlatCAMBookmark.py:331 +#: Bookmark.py:331 msgid "Exported bookmarks to" msgstr "Exportierte Lesezeichen nach" -#: FlatCAMBookmark.py:337 +#: Bookmark.py:337 msgid "Import FlatCAM Bookmarks" msgstr "Importieren Sie FlatCAM-Lesezeichen" -#: FlatCAMBookmark.py:356 +#: Bookmark.py:356 msgid "Imported Bookmarks from" msgstr "Importierte Lesezeichen von" -#: FlatCAMCommon.py:29 +#: Common.py:37 msgid "The user requested a graceful exit of the current task." msgstr "" "Der Benutzer hat einen ordnungsgemäßen Abschluss der aktuellen Aufgabe " "angefordert." -#: FlatCAMDB.py:86 -msgid "Add Geometry Tool in DB" -msgstr "Geometriewerkzeug in DB hinzufügen" +#: Common.py:250 +msgid "Click the end point of the area." +msgstr "Klicken Sie auf den Endpunkt des Bereichs." -#: FlatCAMDB.py:88 FlatCAMDB.py:1643 -msgid "" -"Add a new tool in the Tools Database.\n" -"It will be used in the Geometry UI.\n" -"You can edit it after it is added." +#: Common.py:386 +msgid "Exclusion areas added. Checking overlap with the object geometry ..." msgstr "" -"Fügen Sie der Werkzeugdatenbank ein neues Werkzeug hinzu\n" -"Es wird in der Geometrie-Benutzeroberfläche verwendet.\n" -"Danach können Sie es modifizieren." -#: FlatCAMDB.py:102 FlatCAMDB.py:1657 -msgid "Delete Tool from DB" -msgstr "Werkzeug aus DB löschen" - -#: FlatCAMDB.py:104 FlatCAMDB.py:1659 -msgid "Remove a selection of tools in the Tools Database." -msgstr "Eine Auswahl von Werkzeugen aus der Werkzeugdatenbank entfernen." - -#: FlatCAMDB.py:108 FlatCAMDB.py:1663 -msgid "Export DB" -msgstr "DB exportieren" - -#: FlatCAMDB.py:110 FlatCAMDB.py:1665 -msgid "Save the Tools Database to a custom text file." -msgstr "Werkzeugdatenbank als Textdatei speichern." - -#: FlatCAMDB.py:114 FlatCAMDB.py:1669 -msgid "Import DB" -msgstr "Importieren Sie DB" - -#: FlatCAMDB.py:116 FlatCAMDB.py:1671 -msgid "Load the Tools Database information's from a custom text file." -msgstr "Werkzeugdatenbank aus einer Textdatei importieren." - -#: FlatCAMDB.py:120 FlatCAMDB.py:1681 -msgid "Add Tool from Tools DB" -msgstr "Werkzeug aus Werkzeugdatenbank hinzufügen" - -#: FlatCAMDB.py:122 FlatCAMDB.py:1683 -msgid "" -"Add a new tool in the Tools Table of the\n" -"active Geometry object after selecting a tool\n" -"in the Tools Database." +#: Common.py:392 +msgid "Failed. Exclusion areas intersects the object geometry ..." msgstr "" -"Fügen Sie ein neues Werkzeug in die Werkzeugtabelle der\n" -"aktiven Geometrie hinzu, nachdem Sie das Werkzeug in\n" -"der Werkzeugdatenbank ausgewählt haben." -#: FlatCAMDB.py:158 FlatCAMDB.py:833 FlatCAMDB.py:1087 -msgid "Tool Name" -msgstr "Werkzeugname" - -#: FlatCAMDB.py:159 FlatCAMDB.py:835 FlatCAMDB.py:1100 -#: flatcamEditors/FlatCAMExcEditor.py:1604 flatcamGUI/ObjectUI.py:1345 -#: flatcamGUI/ObjectUI.py:1583 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:132 -#: flatcamTools/ToolNCC.py:278 flatcamTools/ToolNCC.py:287 -#: flatcamTools/ToolPaint.py:261 -msgid "Tool Dia" -msgstr "Werkzeugdurchm" - -#: FlatCAMDB.py:160 FlatCAMDB.py:837 FlatCAMDB.py:1281 -#: flatcamGUI/ObjectUI.py:1558 -msgid "Tool Offset" -msgstr "Werkzeugversatz" - -#: FlatCAMDB.py:161 FlatCAMDB.py:839 FlatCAMDB.py:1298 -msgid "Custom Offset" -msgstr "Selbstdefinierter Werkzeugversatz" - -#: FlatCAMDB.py:162 FlatCAMDB.py:841 FlatCAMDB.py:1265 -#: flatcamGUI/ObjectUI.py:309 -#: flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:67 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:53 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:62 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:72 -#: flatcamTools/ToolNCC.py:213 flatcamTools/ToolNCC.py:227 -#: flatcamTools/ToolPaint.py:196 -msgid "Tool Type" -msgstr "Werkzeugtyp" - -#: FlatCAMDB.py:163 FlatCAMDB.py:843 FlatCAMDB.py:1113 -msgid "Tool Shape" -msgstr "Werkzeugform" - -#: FlatCAMDB.py:164 FlatCAMDB.py:846 FlatCAMDB.py:1129 -#: flatcamGUI/ObjectUI.py:350 flatcamGUI/ObjectUI.py:900 -#: flatcamGUI/ObjectUI.py:1703 flatcamGUI/ObjectUI.py:2336 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:93 -#: flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py:48 -#: flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:107 -#: flatcamGUI/preferences/tools/ToolsCalculatorsPrefGroupUI.py:78 -#: flatcamGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:58 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:98 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:105 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:113 -#: flatcamTools/ToolCalculators.py:114 flatcamTools/ToolCutOut.py:138 -#: flatcamTools/ToolNCC.py:260 flatcamTools/ToolNCC.py:268 -#: flatcamTools/ToolPaint.py:243 -msgid "Cut Z" -msgstr "Schnitttiefe Z" - -#: FlatCAMDB.py:165 FlatCAMDB.py:848 FlatCAMDB.py:1143 -msgid "MultiDepth" -msgstr "Mehrfache Durchgänge" - -# Abbrev. unclear: Depth Per Pass? -# Perhaps better not translate -#: FlatCAMDB.py:166 FlatCAMDB.py:850 FlatCAMDB.py:1156 -msgid "DPP" -msgstr "DPP" - -#: FlatCAMDB.py:167 FlatCAMDB.py:852 FlatCAMDB.py:1312 -msgid "V-Dia" -msgstr "V-Durchm." - -#: FlatCAMDB.py:168 FlatCAMDB.py:854 FlatCAMDB.py:1326 -msgid "V-Angle" -msgstr "Winkel der V-Form" - -#: FlatCAMDB.py:169 FlatCAMDB.py:856 FlatCAMDB.py:1170 -#: flatcamGUI/ObjectUI.py:946 flatcamGUI/ObjectUI.py:1750 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:134 -#: flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py:101 -#: flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py:61 -#: flatcamObjects/FlatCAMExcellon.py:1316 -#: flatcamObjects/FlatCAMGeometry.py:1606 flatcamTools/ToolCalibration.py:74 -msgid "Travel Z" -msgstr "Bewegungshöhe Z (Travel)" - -# I think this is FeedRate XY -#: FlatCAMDB.py:170 FlatCAMDB.py:858 -msgid "FR" -msgstr "Vorschub (XY)" - -#: FlatCAMDB.py:171 FlatCAMDB.py:860 -msgid "FR Z" -msgstr "Vorschub (Z)" - -#: FlatCAMDB.py:172 FlatCAMDB.py:862 FlatCAMDB.py:1340 -msgid "FR Rapids" -msgstr "Vorschub ohne Last" - -#: FlatCAMDB.py:173 FlatCAMDB.py:864 FlatCAMDB.py:1213 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:222 -msgid "Spindle Speed" -msgstr "Drehgeschwindigkeit" - -#: FlatCAMDB.py:174 FlatCAMDB.py:866 FlatCAMDB.py:1228 -#: flatcamGUI/ObjectUI.py:1064 flatcamGUI/ObjectUI.py:1857 -msgid "Dwell" -msgstr "Warten zum Beschleunigen" - -#: FlatCAMDB.py:175 FlatCAMDB.py:868 FlatCAMDB.py:1241 -msgid "Dwelltime" -msgstr "Wartezeit zum Beschleunigen" - -#: FlatCAMDB.py:176 FlatCAMDB.py:870 flatcamGUI/ObjectUI.py:2014 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:257 -#: flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py:254 -#: flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:237 -#: flatcamTools/ToolSolderPaste.py:335 -msgid "Preprocessor" -msgstr "Postprozessor" - -#: FlatCAMDB.py:177 FlatCAMDB.py:872 FlatCAMDB.py:1356 -msgid "ExtraCut" -msgstr "Zusätzlicher Schnitt" - -#: FlatCAMDB.py:178 FlatCAMDB.py:874 FlatCAMDB.py:1371 -msgid "E-Cut Length" -msgstr "Extra Schnittlänge" - -#: FlatCAMDB.py:179 FlatCAMDB.py:876 -msgid "Toolchange" -msgstr "Werkzeugwechsel" - -#: FlatCAMDB.py:180 FlatCAMDB.py:878 -msgid "Toolchange XY" -msgstr "Werkzeugwechsel XY" - -#: FlatCAMDB.py:181 FlatCAMDB.py:880 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:160 -#: flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py:131 -#: flatcamGUI/preferences/tools/Tools2CalPrefGroupUI.py:98 -#: flatcamTools/ToolCalibration.py:111 -msgid "Toolchange Z" -msgstr "Werkzeugwechsel Z" - -#: FlatCAMDB.py:182 FlatCAMDB.py:882 flatcamGUI/ObjectUI.py:1193 -#: flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:69 -#: flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:54 -msgid "Start Z" -msgstr "Start Z" - -#: FlatCAMDB.py:183 FlatCAMDB.py:885 -msgid "End Z" -msgstr "Ende Z" - -#: FlatCAMDB.py:187 -msgid "Tool Index." -msgstr "Werkzeugverzeichnis." - -#: FlatCAMDB.py:189 FlatCAMDB.py:1089 -msgid "" -"Tool name.\n" -"This is not used in the app, it's function\n" -"is to serve as a note for the user." -msgstr "" -"Werkzeugname\n" -"Wird in der App nicht verwendet,\n" -"sondern dient als Kommentar für den Nutzer." - -#: FlatCAMDB.py:193 FlatCAMDB.py:1102 -msgid "Tool Diameter." -msgstr "Werkzeugdurchmesser." - -#: FlatCAMDB.py:195 FlatCAMDB.py:1283 -msgid "" -"Tool Offset.\n" -"Can be of a few types:\n" -"Path = zero offset\n" -"In = offset inside by half of tool diameter\n" -"Out = offset outside by half of tool diameter\n" -"Custom = custom offset using the Custom Offset value" -msgstr "" -"Werkzeug Offest.\n" -"Folgende Typen sind erlaubt:\n" -"Path: kein Offset\n" -"In: Offset einen halben Werkzeugdurchmesser innerhalb.\n" -"Out: Offset einen halben Werkzeugdurchmesser ausserhalb\n" -"Custom: selbstdefinierter Wert im Feld \"Selbstdefinierter Offset\"" - -#: FlatCAMDB.py:202 FlatCAMDB.py:1300 -msgid "" -"Custom Offset.\n" -"A value to be used as offset from the current path." -msgstr "" -"Selbstdefinierter Offset.\n" -"Ein Wert der als Offset zum aktellen Pfad hinzugefügt wird." - -#: FlatCAMDB.py:205 FlatCAMDB.py:1267 -msgid "" -"Tool Type.\n" -"Can be:\n" -"Iso = isolation cut\n" -"Rough = rough cut, low feedrate, multiple passes\n" -"Finish = finishing cut, high feedrate" -msgstr "" -"Werkzeugart.\n" -"Erlaubt sind:\n" -"Iso: Isolationsschnitte\n" -"Rough: Roughen, um viel Material abzutragen, geringer Vorschub, viele " -"Durchgänge\n" -"Finish: Finishing, hoher Vorschub" - -#: FlatCAMDB.py:211 FlatCAMDB.py:1115 -msgid "" -"Tool Shape. \n" -"Can be:\n" -"C1 ... C4 = circular tool with x flutes\n" -"B = ball tip milling tool\n" -"V = v-shape milling tool" -msgstr "" -"Werkzeugform.\n" -"Erlaubt sind:\n" -"C1 … C4: Runde Form mit x Schneiden\n" -"B: Kugelförmig\n" -"V: V-Förmig" - -#: FlatCAMDB.py:217 FlatCAMDB.py:1131 -msgid "" -"Cutting Depth.\n" -"The depth at which to cut into material." -msgstr "" -"Schneidtiefe.\n" -"Eindringtiefe in das Material." - -# MultiDepth is hard to translate, cause it is somewhat artificial. If you need to abbreviate perhaps "MehrfDurchg" could suffice, but stays ugly. -#: FlatCAMDB.py:220 FlatCAMDB.py:1145 -msgid "" -"Multi Depth.\n" -"Selecting this will allow cutting in multiple passes,\n" -"each pass adding a DPP parameter depth." -msgstr "" -"Mehrfache Durchgänge.\n" -"Wenn ausgewählt wird der Schnitt in mehreren Stufen\n" -"durchgeführt. Die Schnitttiefe jedes Schnittes ist in DPP angegeben." - -#: FlatCAMDB.py:224 FlatCAMDB.py:1158 -msgid "" -"DPP. Depth per Pass.\n" -"The value used to cut into material on each pass." -msgstr "" -"DPP: Tiefe pro Schnitt. Definiert die einzelne Schnitttiefe in mehrfachen " -"Durchgängen." - -#: FlatCAMDB.py:227 FlatCAMDB.py:1314 -msgid "" -"V-Dia.\n" -"Diameter of the tip for V-Shape Tools." -msgstr "" -"V-Durchmesser.\n" -"Durchmesser der Spitze eines V-Förmigen Werkzeugs." - -# Typo in english? V-Angle, missing n? -#: FlatCAMDB.py:230 FlatCAMDB.py:1328 -msgid "" -"V-Agle.\n" -"Angle at the tip for the V-Shape Tools." -msgstr "" -"V-Winkel.\n" -"Öffnungswinkel an der Spitze eine V-Förmigen Werkzeugs." - -#: FlatCAMDB.py:233 FlatCAMDB.py:1172 -msgid "" -"Clearance Height.\n" -"Height at which the milling bit will travel between cuts,\n" -"above the surface of the material, avoiding all fixtures." -msgstr "" -"Freilauf Höhe.\n" -"Die Höhe in der das Fräswerkzeug sich zwischen den Schnitten \n" -"frei bewegen kann ohne auf Hindernisse zu stossen." - -#: FlatCAMDB.py:237 -msgid "" -"FR. Feedrate\n" -"The speed on XY plane used while cutting into material." -msgstr "" -"FR: Feedrate\n" -"Geschwindkeit beim fräsen. Angegeben in cm pro Minute." - -#: FlatCAMDB.py:240 -msgid "" -"FR Z. Feedrate Z\n" -"The speed on Z plane." -msgstr "" -"FR Z: Feedrate Z:\n" -"Geschwindigkeit beim Fräsen in Z-Richtung." - -#: FlatCAMDB.py:243 FlatCAMDB.py:1342 -msgid "" -"FR Rapids. Feedrate Rapids\n" -"Speed used while moving as fast as possible.\n" -"This is used only by some devices that can't use\n" -"the G0 g-code command. Mostly 3D printers." -msgstr "" -"FR Rapids: Feedrate ohne Last\n" -"Geschwindigkeit die ohne Last gefahren werden kann.\n" -"Wird benutzt bei Geräten die das G0 Kommando nicht \n" -"unterstützen (oft 3D Drucker)." - -#: FlatCAMDB.py:248 FlatCAMDB.py:1215 -msgid "" -"Spindle Speed.\n" -"If it's left empty it will not be used.\n" -"The speed of the spindle in RPM." -msgstr "" -"Drehzahl.\n" -"Drehzahl des Fräsmotors in U/min.\n" -"Wird nicht benutzt, wenn leer." - -#: FlatCAMDB.py:252 FlatCAMDB.py:1230 -msgid "" -"Dwell.\n" -"Check this if a delay is needed to allow\n" -"the spindle motor to reach it's set speed." -msgstr "" -"Verweilen.\n" -"Überprüfen Sie dies, wenn eine Verzögerung erforderlich ist\n" -"Der Spindelmotor erreicht die eingestellte Drehzahl." - -#: FlatCAMDB.py:256 FlatCAMDB.py:1243 -msgid "" -"Dwell Time.\n" -"A delay used to allow the motor spindle reach it's set speed." -msgstr "" -"Verweilzeit.\n" -"Eine Verzögerung, mit der die Motorspindel ihre eingestellte Drehzahl " -"erreicht." - -#: FlatCAMDB.py:259 -msgid "" -"Preprocessor.\n" -"A selection of files that will alter the generated G-code\n" -"to fit for a number of use cases." -msgstr "" -"Präprozessor.\n" -"Diese Dateien werden den erzeugten G-Code modifizieren\n" -"um eine große Anzahl Anwendungsmöglichkeiten zu unterstützen." - -#: FlatCAMDB.py:263 FlatCAMDB.py:1358 -msgid "" -"Extra Cut.\n" -"If checked, after a isolation is finished an extra cut\n" -"will be added where the start and end of isolation meet\n" -"such as that this point is covered by this extra cut to\n" -"ensure a complete isolation." -msgstr "" -"Zusatzschnitt.\n" -"Wenn gewählt, wird nach dem Isolationsschnitt ein Zusatzschnitt\n" -"durchgeführt, um Start und Endpunkt definitiv zu verbinden und \n" -"so eine vollständige Isolation zu gewährleisten." - -#: FlatCAMDB.py:269 FlatCAMDB.py:1373 -msgid "" -"Extra Cut length.\n" -"If checked, after a isolation is finished an extra cut\n" -"will be added where the start and end of isolation meet\n" -"such as that this point is covered by this extra cut to\n" -"ensure a complete isolation. This is the length of\n" -"the extra cut." -msgstr "" -"Zusatzschnitt.\n" -"Wenn gewählt, wird nach dem Isolationsschnitt ein Zusatzschnitt\n" -"durchgeführt, um Start und Endpunkt definitiv zu verbinden und \n" -"so eine vollständige Isolation zu gewährleisten." - -#: FlatCAMDB.py:276 -msgid "" -"Toolchange.\n" -"It will create a toolchange event.\n" -"The kind of toolchange is determined by\n" -"the preprocessor file." -msgstr "" -"Werkzeugwechsel.\n" -"Löst ein Werkzeugwechselereignis aus.\n" -"Die Art wie der Werkzeugwechsel durchgeführt wird\n" -"hängt vom gewählten Präprozessor ab." - -#: FlatCAMDB.py:281 -msgid "" -"Toolchange XY.\n" -"A set of coordinates in the format (x, y).\n" -"Will determine the cartesian position of the point\n" -"where the tool change event take place." -msgstr "" -"Werkzeugwechsel XY\n" -"Ein Satz von Koordinaten im Format (x,y).\n" -"An der Position dieses Punktes wird ein \n" -"Werkzeugwechselereignis ausgelöst." - -# Is this really the height of where a toolchange event takes place or is it the position of where to go to for being able to change the tool? -#: FlatCAMDB.py:286 -msgid "" -"Toolchange Z.\n" -"The position on Z plane where the tool change event take place." -msgstr "" -"Werkzeugwechsel Z.\n" -"Die Position in der Z Ebene an der ein Werkzeugwechselereignis ausgelöst " -"wird." - -#: FlatCAMDB.py:289 -msgid "" -"Start Z.\n" -"If it's left empty it will not be used.\n" -"A position on Z plane to move immediately after job start." -msgstr "" -"Start Z.\n" -"Nicht benutzt wenn leer.\n" -"Die Z-Position die zum Start angefahren wird." - -#: FlatCAMDB.py:293 -msgid "" -"End Z.\n" -"A position on Z plane to move immediately after job stop." -msgstr "" -"End Z.\n" -"Die Z-Position die bei Beendigung des Jobs angefahren wird." - -#: FlatCAMDB.py:305 FlatCAMDB.py:682 FlatCAMDB.py:716 FlatCAMDB.py:1898 -#: FlatCAMDB.py:2144 FlatCAMDB.py:2178 -msgid "Could not load Tools DB file." -msgstr "Werkzeugdatenbank konnte nicht geladen werden." - -#: FlatCAMDB.py:313 FlatCAMDB.py:724 FlatCAMDB.py:1906 FlatCAMDB.py:2186 -msgid "Failed to parse Tools DB file." -msgstr "Formatfehler beim Einlesen der Werkzeugdatenbank." - -#: FlatCAMDB.py:316 FlatCAMDB.py:727 FlatCAMDB.py:1909 FlatCAMDB.py:2189 -msgid "Loaded FlatCAM Tools DB from" -msgstr "Geladene FlatCAM Tools DB von" - -#: FlatCAMDB.py:322 FlatCAMDB.py:1823 -msgid "Add to DB" -msgstr "Hinzufügen" - -#: FlatCAMDB.py:324 FlatCAMDB.py:1826 -msgid "Copy from DB" -msgstr "Von Datenbank kopieren" - -#: FlatCAMDB.py:326 FlatCAMDB.py:1829 -msgid "Delete from DB" -msgstr "Aus Datenbank löschen" - -#: FlatCAMDB.py:603 FlatCAMDB.py:2044 -msgid "Tool added to DB." -msgstr "Werkzeug wurde zur Werkzeugdatenbank hinzugefügt." - -#: FlatCAMDB.py:624 FlatCAMDB.py:2077 -msgid "Tool copied from Tools DB." -msgstr "Das Werkzeug wurde aus der Werkzeugdatenbank kopiert." - -#: FlatCAMDB.py:642 FlatCAMDB.py:2104 -msgid "Tool removed from Tools DB." -msgstr "Werkzeug wurde aus der Werkzeugdatenbank gelöscht." - -#: FlatCAMDB.py:653 FlatCAMDB.py:2115 -msgid "Export Tools Database" -msgstr "Werkzeugdatenbank exportieren" - -#: FlatCAMDB.py:656 FlatCAMDB.py:2118 -msgid "Tools_Database" -msgstr "Werkzeugdatenbank" - -#: FlatCAMDB.py:693 FlatCAMDB.py:696 FlatCAMDB.py:748 FlatCAMDB.py:2155 -#: FlatCAMDB.py:2158 FlatCAMDB.py:2211 -msgid "Failed to write Tools DB to file." -msgstr "Fehler beim Schreiben der Werkzeugdatenbank in eine Datei." - -#: FlatCAMDB.py:699 FlatCAMDB.py:2161 -msgid "Exported Tools DB to" -msgstr "Werkzeugdatenbank wurde exportiert nach" - -#: FlatCAMDB.py:706 FlatCAMDB.py:2168 -msgid "Import FlatCAM Tools DB" -msgstr "Import der FlatCAM-Werkzeugdatenbank" - -#: FlatCAMDB.py:752 FlatCAMDB.py:2215 -msgid "Saved Tools DB." -msgstr "Datenbank der gespeicherten Werkzeuge." - -#: FlatCAMDB.py:899 FlatCAMDB.py:2405 -msgid "No Tool/row selected in the Tools Database table" -msgstr "" -"Gescheitert. Kein Werkzeug (keine Spalte) in der Werkzeugtabelle ausgewählt" - -#: FlatCAMDB.py:917 FlatCAMDB.py:2422 -msgid "Cancelled adding tool from DB." -msgstr "Hinzufügen aus der Datenbank wurde abgebrochen." - -#: FlatCAMDB.py:1018 -msgid "Basic Geo Parameters" -msgstr "Grundlegende Geoparameter" - -#: FlatCAMDB.py:1030 -msgid "Advanced Geo Parameters" -msgstr "Erweiterte Geoparameter" - -#: FlatCAMDB.py:1042 -msgid "NCC Parameters" -msgstr "NCC-Parameter" - -#: FlatCAMDB.py:1054 -msgid "Paint Parameters" -msgstr "Lackparameter" - -#: FlatCAMDB.py:1185 flatcamGUI/ObjectUI.py:967 flatcamGUI/ObjectUI.py:1769 -#: flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py:185 -#: flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:148 -#: flatcamTools/ToolSolderPaste.py:253 -msgid "Feedrate X-Y" -msgstr "Vorschub X-Y" - -#: FlatCAMDB.py:1187 -msgid "" -"Feedrate X-Y. Feedrate\n" -"The speed on XY plane used while cutting into material." -msgstr "" -"Vorschub X-Y. Vorschubgeschwindigkeit\n" -"Die Geschwindigkeit in der XY-Ebene, die beim Schneiden in Material " -"verwendet wird." - -#: FlatCAMDB.py:1199 flatcamGUI/ObjectUI.py:982 flatcamGUI/ObjectUI.py:1783 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:207 -#: flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py:200 -#: flatcamGUI/preferences/tools/ToolsSolderpastePrefGroupUI.py:161 -#: flatcamTools/ToolSolderPaste.py:265 -msgid "Feedrate Z" -msgstr "Vorschub Z" - -#: FlatCAMDB.py:1201 -msgid "" -"Feedrate Z\n" -"The speed on Z plane." -msgstr "" -"Vorschub Z.\n" -"Die Geschwindigkeit in der Z-Ebene." - -#: FlatCAMDB.py:1399 flatcamGUI/ObjectUI.py:845 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:46 -#: flatcamTools/ToolNCC.py:341 -msgid "Operation" -msgstr "Operation" - -#: FlatCAMDB.py:1401 flatcamTools/ToolNCC.py:343 -msgid "" -"The 'Operation' can be:\n" -"- Isolation -> will ensure that the non-copper clearing is always complete.\n" -"If it's not successful then the non-copper clearing will fail, too.\n" -"- Clear -> the regular non-copper clearing." -msgstr "" -"Die 'Operation' kann sein:\n" -"- Isolierung-> stellt sicher, dass das Löschen ohne Kupfer immer " -"abgeschlossen ist.\n" -"Wenn dies nicht erfolgreich ist, schlägt auch das Löschen ohne Kupfer fehl.\n" -"- Klären-> das reguläre Nicht-Kupfer-löschen." - -#: FlatCAMDB.py:1408 flatcamEditors/FlatCAMGrbEditor.py:2742 -#: flatcamGUI/GUIElements.py:2577 flatcamTools/ToolNCC.py:350 -msgid "Clear" -msgstr "Klären" - -#: FlatCAMDB.py:1409 flatcamTools/ToolNCC.py:351 flatcamTools/ToolNCC.py:1623 -msgid "Isolation" -msgstr "Isolation" - -#: FlatCAMDB.py:1417 flatcamGUI/ObjectUI.py:409 flatcamGUI/ObjectUI.py:867 -#: flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py:62 -#: flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:56 -#: flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py:95 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:137 -#: flatcamTools/ToolNCC.py:359 -msgid "Milling Type" -msgstr "Fräsart" - -#: FlatCAMDB.py:1419 FlatCAMDB.py:1427 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:139 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:147 -#: flatcamTools/ToolNCC.py:361 flatcamTools/ToolNCC.py:369 -msgid "" -"Milling type when the selected tool is of type: 'iso_op':\n" -"- climb / best for precision milling and to reduce tool usage\n" -"- conventional / useful when there is no backlash compensation" -msgstr "" -"Frästyp, wenn das ausgewählte Werkzeug vom Typ 'iso_op' ist:\n" -"- Besteigung / am besten zum Präzisionsfräsen und zur Reduzierung des " -"Werkzeugverbrauchs\n" -"- konventionell / nützlich, wenn kein Spielausgleich vorhanden ist" - -#: FlatCAMDB.py:1424 flatcamGUI/ObjectUI.py:415 -#: flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:62 -#: flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py:102 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:144 -#: flatcamTools/ToolNCC.py:366 -msgid "Climb" -msgstr "Steigen" - -# Cannot translate without context. -#: FlatCAMDB.py:1425 flatcamGUI/ObjectUI.py:416 -#: flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:63 -#: flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py:103 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:145 -#: flatcamTools/ToolNCC.py:367 -msgid "Conventional" -msgstr "Konventionell" - -#: FlatCAMDB.py:1437 FlatCAMDB.py:1546 flatcamEditors/FlatCAMGeoEditor.py:451 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:182 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:163 -#: flatcamTools/ToolNCC.py:382 flatcamTools/ToolPaint.py:329 -msgid "Overlap" -msgstr "Überlappung" - -# Double -#: FlatCAMDB.py:1439 flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:184 -#: flatcamTools/ToolNCC.py:384 -msgid "" -"How much (percentage) of the tool width to overlap each tool pass.\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 CNC.\n" -"Higher values = slow processing and slow execution on CNC\n" -"due of too many paths." -msgstr "" -"Wie viel (Prozent) der Werkzeugbreite, um jeden Werkzeugdurchlauf zu " -"überlappen.\n" -"Passen Sie den Wert beginnend mit niedrigeren Werten an\n" -"und es zu erhöhen, wenn noch Bereiche sind, die geräumt werden sollen\n" -"ungeklärt.\n" -"Niedrigere Werte = schnellere Verarbeitung, schnellere Ausführung auf CNC.\n" -"Höhere Werte = langsame Verarbeitung und langsame Ausführung auf CNC\n" -"wegen zu vieler Wege." - -#: FlatCAMDB.py:1458 FlatCAMDB.py:1567 flatcamEditors/FlatCAMGeoEditor.py:471 -#: flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:72 -#: flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:229 -#: flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:59 -#: flatcamGUI/preferences/tools/Tools2InvertPrefGroupUI.py:45 -#: flatcamGUI/preferences/tools/Tools2InvertPrefGroupUI.py:53 -#: flatcamGUI/preferences/tools/ToolsCutoutPrefGroupUI.py:115 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:202 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:183 -#: flatcamTools/ToolCopperThieving.py:111 -#: flatcamTools/ToolCopperThieving.py:362 flatcamTools/ToolCutOut.py:190 -#: flatcamTools/ToolFiducials.py:172 flatcamTools/ToolInvertGerber.py:88 -#: flatcamTools/ToolInvertGerber.py:96 flatcamTools/ToolNCC.py:403 -#: flatcamTools/ToolPaint.py:350 -msgid "Margin" -msgstr "Marge" - -#: FlatCAMDB.py:1460 -#: flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:74 -#: flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:61 -#: flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:125 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:204 -#: flatcamTools/ToolCopperThieving.py:113 flatcamTools/ToolFiducials.py:174 -#: flatcamTools/ToolFiducials.py:237 flatcamTools/ToolNCC.py:405 -msgid "Bounding box margin." -msgstr "Begrenzungsrahmenrand." - -#: FlatCAMDB.py:1471 FlatCAMDB.py:1582 flatcamEditors/FlatCAMGeoEditor.py:485 -#: flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:105 -#: flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:106 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:215 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:198 -#: flatcamTools/ToolExtractDrills.py:128 flatcamTools/ToolNCC.py:416 -#: flatcamTools/ToolPaint.py:365 flatcamTools/ToolPunchGerber.py:139 -msgid "Method" -msgstr "Methode" - -#: FlatCAMDB.py:1473 flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:217 -#: flatcamTools/ToolNCC.py:418 -msgid "" -"Algorithm for copper clearing:\n" -"- Standard: Fixed step inwards.\n" -"- Seed-based: Outwards from seed.\n" -"- Line-based: Parallel lines." -msgstr "" -"Algorithmus zur Kupferreinigung:\n" -"- Standard: Schritt nach innen behoben.\n" -"- Samenbasiert: Aus dem Samen heraus.\n" -"- Linienbasiert: Parallele Linien." - -#: FlatCAMDB.py:1481 FlatCAMDB.py:1596 flatcamEditors/FlatCAMGeoEditor.py:499 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:230 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 -#: flatcamTools/ToolNCC.py:431 flatcamTools/ToolNCC.py:2395 -#: flatcamTools/ToolNCC.py:2424 flatcamTools/ToolNCC.py:2693 -#: flatcamTools/ToolNCC.py:2725 flatcamTools/ToolPaint.py:390 -#: flatcamTools/ToolPaint.py:1834 tclCommands/TclCommandCopperClear.py:126 -#: tclCommands/TclCommandCopperClear.py:134 tclCommands/TclCommandPaint.py:125 -msgid "Standard" -msgstr "Standard" - -#: FlatCAMDB.py:1481 FlatCAMDB.py:1596 defaults.py:395 defaults.py:427 -#: flatcamEditors/FlatCAMGeoEditor.py:499 -#: flatcamEditors/FlatCAMGeoEditor.py:569 -#: flatcamEditors/FlatCAMGeoEditor.py:5152 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:230 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 -#: flatcamTools/ToolNCC.py:431 flatcamTools/ToolNCC.py:2401 -#: flatcamTools/ToolNCC.py:2429 flatcamTools/ToolNCC.py:2699 -#: flatcamTools/ToolNCC.py:2731 flatcamTools/ToolPaint.py:390 -#: flatcamTools/ToolPaint.py:1848 tclCommands/TclCommandCopperClear.py:128 -#: tclCommands/TclCommandCopperClear.py:136 tclCommands/TclCommandPaint.py:127 -msgid "Seed" -msgstr "Keim" - -#: FlatCAMDB.py:1481 FlatCAMDB.py:1596 flatcamEditors/FlatCAMGeoEditor.py:499 -#: flatcamEditors/FlatCAMGeoEditor.py:5156 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:230 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 -#: flatcamTools/ToolNCC.py:431 flatcamTools/ToolPaint.py:390 -#: flatcamTools/ToolPaint.py:699 flatcamTools/ToolPaint.py:1862 -#: tclCommands/TclCommandCopperClear.py:130 tclCommands/TclCommandPaint.py:129 -msgid "Lines" -msgstr "Linien" - -#: FlatCAMDB.py:1489 FlatCAMDB.py:1607 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:237 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:224 -#: flatcamTools/ToolNCC.py:439 flatcamTools/ToolPaint.py:401 -msgid "Connect" -msgstr "Verbinden" - -#: FlatCAMDB.py:1493 FlatCAMDB.py:1610 flatcamEditors/FlatCAMGeoEditor.py:508 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:239 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:226 -#: flatcamTools/ToolNCC.py:443 flatcamTools/ToolPaint.py:404 -msgid "" -"Draw lines between resulting\n" -"segments to minimize tool lifts." -msgstr "" -"Zeichnen Sie Linien zwischen den Ergebnissen\n" -"Segmente, um Werkzeuglifte zu minimieren." - -#: FlatCAMDB.py:1499 FlatCAMDB.py:1614 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:246 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:232 -#: flatcamTools/ToolNCC.py:449 flatcamTools/ToolPaint.py:408 -msgid "Contour" -msgstr "Kontur" - -#: FlatCAMDB.py:1503 FlatCAMDB.py:1617 flatcamEditors/FlatCAMGeoEditor.py:518 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:248 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:234 -#: flatcamTools/ToolNCC.py:453 flatcamTools/ToolPaint.py:411 -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." - -#: FlatCAMDB.py:1509 flatcamEditors/FlatCAMGeoEditor.py:612 -#: flatcamEditors/FlatCAMGrbEditor.py:5311 flatcamGUI/ObjectUI.py:143 -#: flatcamGUI/ObjectUI.py:1497 flatcamGUI/ObjectUI.py:2326 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:255 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:142 -#: flatcamTools/ToolNCC.py:459 flatcamTools/ToolTransform.py:28 -msgid "Offset" -msgstr "Versatz" - -#: FlatCAMDB.py:1513 flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:257 -#: flatcamTools/ToolNCC.py:463 -msgid "" -"If used, it will add an offset to the copper features.\n" -"The copper clearing will finish to a distance\n" -"from the copper features.\n" -"The value can be between 0 and 10 FlatCAM units." -msgstr "" -"Bei Verwendung wird den Kupferelementen ein Offset hinzugefügt.\n" -"Die Kupferreinigung wird bis zu einer gewissen Entfernung enden\n" -"von den Kupfermerkmalen.\n" -"Der Wert kann zwischen 0 und 10 FlatCAM-Einheiten liegen." - -# 3rd Time -#: FlatCAMDB.py:1548 flatcamEditors/FlatCAMGeoEditor.py:453 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:165 -#: flatcamTools/ToolPaint.py:331 -msgid "" -"How much (percentage) of the tool width to overlap each tool pass.\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 CNC.\n" -"Higher values = slow processing and slow execution on CNC\n" -"due of too many paths." -msgstr "" -"Wie viel (Prozent) der Werkzeugbreite, um jeden Werkzeugdurchlauf zu " -"überlappen.\n" -"Passen Sie den Wert beginnend mit niedrigeren Werten an\n" -"und erhöhen, wenn Bereiche, die gestrichen werden sollen, noch vorhanden " -"sind\n" -"nicht gemalt.\n" -"Niedrigere Werte = schnellere Verarbeitung, schnellere Ausführung auf CNC.\n" -"Höhere Werte = langsame Verarbeitung und langsame Ausführung auf CNC\n" -"wegen zu vieler Wege." - -#: FlatCAMDB.py:1569 flatcamEditors/FlatCAMGeoEditor.py:473 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:185 -#: flatcamTools/ToolPaint.py:352 -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." - -#: FlatCAMDB.py:1584 flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:200 -#: flatcamTools/ToolPaint.py:367 -msgid "" -"Algorithm for painting:\n" -"- Standard: Fixed step inwards.\n" -"- Seed-based: Outwards from seed.\n" -"- Line-based: Parallel lines.\n" -"- Laser-lines: Active only for Gerber objects.\n" -"Will create lines that follow the traces.\n" -"- Combo: In case of failure a new method will be picked from the above\n" -"in the order specified." -msgstr "" -"Algorithmus zum Malen:\n" -"- Standard: Schritt nach innen behoben.\n" -"- Samenbasiert: Aus dem Samen heraus.\n" -"- Linienbasiert: Parallele Linien.\n" -"- Laserlinien: Nur für Gerber-Objekte aktiv.\n" -"Erstellt Linien, die den Spuren folgen.\n" -"- Combo: Im Fehlerfall wird eine neue Methode aus den oben genannten " -"ausgewählt\n" -"in der angegebenen Reihenfolge." - -#: FlatCAMDB.py:1596 FlatCAMDB.py:1598 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 -#: flatcamTools/ToolPaint.py:390 flatcamTools/ToolPaint.py:392 -#: flatcamTools/ToolPaint.py:693 flatcamTools/ToolPaint.py:698 -#: flatcamTools/ToolPaint.py:1876 tclCommands/TclCommandPaint.py:131 -msgid "Laser_lines" -msgstr "LaserlinienLinien" - -#: FlatCAMDB.py:1596 flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:217 -#: flatcamTools/ToolPaint.py:390 flatcamTools/ToolPaint.py:2027 -#: tclCommands/TclCommandPaint.py:133 -msgid "Combo" -msgstr "Combo" - -#: FlatCAMDB.py:1641 -msgid "Add Tool in DB" -msgstr "Werkzeug in DB hinzufügen" - -#: FlatCAMDB.py:1675 -msgid "Save DB" -msgstr "Speichern DB" - -#: FlatCAMDB.py:1677 -msgid "Save the Tools Database information's." -msgstr "Speichern Sie die Tools-Datenbankinformationen." - -#: FlatCAMProcess.py:172 -msgid "processes running." -msgstr "laufende Prozesse." - -#: FlatCAMTool.py:245 FlatCAMTool.py:252 flatcamGUI/ObjectUI.py:157 -#: flatcamGUI/ObjectUI.py:164 -msgid "Edited value is out of range" -msgstr "Der bearbeitete Wert liegt außerhalb des Bereichs" - -#: FlatCAMTool.py:247 FlatCAMTool.py:254 flatcamGUI/ObjectUI.py:159 -#: flatcamGUI/ObjectUI.py:166 -msgid "Edited value is within limits." -msgstr "Der bearbeitete Wert liegt innerhalb der Grenzen." - -#: FlatCAMTranslation.py:104 -msgid "The application will restart." -msgstr "Die Anwendung wird neu gestartet." - -#: FlatCAMTranslation.py:106 -msgid "Are you sure do you want to change the current language to" -msgstr "Möchten Sie die aktuelle Sprache wirklich in ändern" - -#: FlatCAMTranslation.py:107 -msgid "Apply Language ..." -msgstr "Sprache anwenden ..." +#: Common.py:396 +#, fuzzy +#| msgid "Exclusion areas" +msgid "Exclusion areas added." +msgstr "Ausschlussbereiche" + +#: Common.py:405 +#, fuzzy +#| msgid "Exclusion areas" +msgid "With Exclusion areas." +msgstr "Ausschlussbereiche" + +#: Common.py:435 +msgid "Cancelled. Area exclusion drawing was interrupted." +msgstr "Abgebrochen. Die Bereichsausschlusszeichnung wurde unterbrochen." + +#: Common.py:527 Common.py:575 +#, fuzzy +#| msgid "All objects are selected." +msgid "All exclusion zones deleted." +msgstr "Alle Objekte werden ausgewählt." + +#: Common.py:562 +#, fuzzy +#| msgid "Delete all exclusion areas." +msgid "Selected exclusion zones deleted." +msgstr "Löschen Sie alle Ausschlussbereiche." #: assets/linux/flatcam-beta.desktop:3 msgid "FlatCAM Beta" msgstr "FlatCAM Beta" -#: assets/linux/flatcam-beta.desktop:7 -msgid "./assets/icon.png" -msgstr "./assets/icon.png" - #: assets/linux/flatcam-beta.desktop:8 msgid "G-Code from GERBERS" msgstr "G-Code von GERBERS" -#: camlib.py:597 +#: camlib.py:596 msgid "self.solid_geometry is neither BaseGeometry or list." msgstr "self.solid_geometry ist weder BaseGeometry noch eine Liste." -#: camlib.py:970 +#: camlib.py:971 msgid "Pass" msgstr "Pass" -#: camlib.py:981 flatcamGUI/preferences/gerber/GerberAdvOptPrefGroupUI.py:146 -#: flatcamObjects/FlatCAMGerber.py:497 flatcamTools/ToolCopperThieving.py:1016 -#: flatcamTools/ToolCopperThieving.py:1205 -#: flatcamTools/ToolCopperThieving.py:1217 flatcamTools/ToolNCC.py:2050 -#: flatcamTools/ToolNCC.py:2158 flatcamTools/ToolNCC.py:2172 -#: flatcamTools/ToolNCC.py:3103 flatcamTools/ToolNCC.py:3208 -#: flatcamTools/ToolNCC.py:3223 flatcamTools/ToolNCC.py:3489 -#: flatcamTools/ToolNCC.py:3590 flatcamTools/ToolNCC.py:3605 -msgid "Buffering" -msgstr "Pufferung" - -#: camlib.py:990 +#: camlib.py:991 msgid "Get Exteriors" msgstr "Holen Sie sich das Äußere" -#: camlib.py:993 +#: camlib.py:994 msgid "Get Interiors" msgstr "Holen Sie sich Innenräume" -#: camlib.py:2172 +#: camlib.py:2174 msgid "Object was mirrored" msgstr "Objekt wurde gespiegelt" -#: camlib.py:2174 +#: camlib.py:2176 msgid "Failed to mirror. No object selected" msgstr "Spiegelung fehlgeschlagen Kein Objekt ausgewählt" -#: camlib.py:2239 +#: camlib.py:2241 msgid "Object was rotated" msgstr "Objekt wurde gedreht" -#: camlib.py:2241 +#: camlib.py:2243 msgid "Failed to rotate. No object selected" msgstr "Fehler beim Drehen. Kein Objekt ausgewählt" -#: camlib.py:2307 +#: camlib.py:2309 msgid "Object was skewed" msgstr "Objekt war schief" -#: camlib.py:2309 +#: camlib.py:2311 msgid "Failed to skew. No object selected" msgstr "Fehler beim Neigen Kein Objekt ausgewählt" -#: camlib.py:2385 +#: camlib.py:2387 msgid "Object was buffered" msgstr "Objekt wurde gepuffert" -#: camlib.py:2387 +#: camlib.py:2389 msgid "Failed to buffer. No object selected" msgstr "Fehler beim Puffern. Kein Objekt ausgewählt" -#: camlib.py:2594 +#: camlib.py:2597 msgid "There is no such parameter" msgstr "Es gibt keinen solchen Parameter" -#: camlib.py:2654 camlib.py:2887 camlib.py:3116 camlib.py:3338 +#: camlib.py:2657 camlib.py:2898 camlib.py:3127 camlib.py:3349 msgid "" "The Cut Z parameter has positive value. It is the depth value to drill into " "material.\n" @@ -2786,14 +18424,14 @@ msgstr "" "einen negativen Wert. \n" "Überprüfen Sie den resultierenden CNC-Code (Gcode usw.)." -#: camlib.py:2662 camlib.py:2897 camlib.py:3126 camlib.py:3348 camlib.py:3631 -#: camlib.py:4017 +#: camlib.py:2665 camlib.py:2908 camlib.py:3137 camlib.py:3359 camlib.py:3650 +#: camlib.py:4045 msgid "The Cut Z parameter is zero. There will be no cut, skipping file" msgstr "" "Der Parameter Cut Z ist Null. Es wird kein Schnitt ausgeführt, und die Datei " "wird übersprungen" -#: camlib.py:2673 camlib.py:3985 +#: camlib.py:2680 camlib.py:4013 msgid "" "The Toolchange X,Y field in Edit -> Preferences has to be in the format (x, " "y) \n" @@ -2803,7 +18441,7 @@ msgstr "" "(x, y) sein\n" "Aber jetzt gibt es nur einen Wert, nicht zwei. " -#: camlib.py:2682 camlib.py:3582 camlib.py:3967 +#: camlib.py:2693 camlib.py:3597 camlib.py:3991 msgid "" "The End Move X,Y field in Edit -> Preferences has to be in the format (x, y) " "but now there is only one value, not two." @@ -2811,31 +18449,31 @@ msgstr "" "Das Feld Endverschiebung X, Y unter Bearbeiten -> Einstellungen muss das " "Format (x, y) haben, aber jetzt gibt es nur einen Wert, nicht zwei." -#: camlib.py:2770 +#: camlib.py:2781 msgid "Creating a list of points to drill..." msgstr "Erstellen einer Liste von Punkten zum Bohren ..." -#: camlib.py:2860 camlib.py:3729 camlib.py:4121 +#: camlib.py:2871 camlib.py:3748 camlib.py:4149 msgid "Starting G-Code" msgstr "G-Code starten" -#: camlib.py:3001 camlib.py:3220 camlib.py:3384 camlib.py:3742 camlib.py:4132 +#: camlib.py:3012 camlib.py:3231 camlib.py:3395 camlib.py:3761 camlib.py:4160 msgid "Starting G-Code for tool with diameter" msgstr "Start-G-Code für Werkzeug mit Durchmesser" -#: camlib.py:3084 camlib.py:3302 camlib.py:3470 +#: camlib.py:3095 camlib.py:3313 camlib.py:3481 msgid "G91 coordinates not implemented" msgstr "G91 Koordinaten nicht implementiert" -#: camlib.py:3090 camlib.py:3309 camlib.py:3475 +#: camlib.py:3101 camlib.py:3320 camlib.py:3486 msgid "The loaded Excellon file has no drills" msgstr "Die geladene Excellon-Datei hat keine Bohrer" -#: camlib.py:3498 +#: camlib.py:3509 msgid "Finished G-Code generation..." msgstr "Fertige G-Code-Generierung ..." -#: camlib.py:3600 +#: camlib.py:3619 msgid "" "The Toolchange X,Y field in Edit -> Preferences has to be in the format (x, " "y) \n" @@ -2845,7 +18483,7 @@ msgstr "" "das Format (x, y) haben.\n" "Aber jetzt gibt es nur einen Wert, nicht zwei." -#: camlib.py:3614 camlib.py:4000 +#: camlib.py:3633 camlib.py:4028 msgid "" "Cut_Z parameter is None or zero. Most likely a bad combinations of other " "parameters." @@ -2853,7 +18491,7 @@ msgstr "" "Der Parameter Cut_Z ist None oder Null. Höchstwahrscheinlich eine schlechte " "Kombination anderer Parameter." -#: camlib.py:3623 camlib.py:4009 +#: camlib.py:3642 camlib.py:4037 msgid "" "The Cut Z parameter has positive value. It is the depth value to cut into " "material.\n" @@ -2868,11 +18506,11 @@ msgstr "" "einen negativen Wert. \n" "Überprüfen Sie den resultierenden CNC-Code (Gcode usw.)." -#: camlib.py:3636 camlib.py:4023 +#: camlib.py:3655 camlib.py:4051 msgid "Travel Z parameter is None or zero." msgstr "Der Parameter für den Travel Z ist Kein oder Null." -#: camlib.py:3641 camlib.py:4028 +#: camlib.py:3660 camlib.py:4056 msgid "" "The Travel Z parameter has negative value. It is the height value to travel " "between cuts.\n" @@ -2886,36 +18524,36 @@ msgstr "" "einen Tippfehler handelt, konvertiert die App den Wert in einen positiven " "Wert. Überprüfen Sie den resultierenden CNC-Code (Gcode usw.)." -#: camlib.py:3649 camlib.py:4036 +#: camlib.py:3668 camlib.py:4064 msgid "The Z Travel parameter is zero. This is dangerous, skipping file" msgstr "" "Der Parameter Z-Weg ist Null. Dies ist gefährlich, da die %s Datei " "übersprungen wird" -#: camlib.py:3668 camlib.py:4059 +#: camlib.py:3687 camlib.py:4087 msgid "Indexing geometry before generating G-Code..." msgstr "Indizierung der Geometrie vor dem Generieren von G-Code ..." -#: camlib.py:3812 camlib.py:4201 +#: camlib.py:3831 camlib.py:4229 msgid "Finished G-Code generation" msgstr "Fertige G-Code-Generierung" -#: camlib.py:3812 +#: camlib.py:3831 msgid "paths traced" msgstr "Pfade verfolgt" -#: camlib.py:3862 +#: camlib.py:3881 msgid "Expected a Geometry, got" msgstr "Erwartet eine Geometrie, erhalten" -#: camlib.py:3869 +#: camlib.py:3888 msgid "" "Trying to generate a CNC Job from a Geometry object without solid_geometry." msgstr "" "Der Versuch, einen CNC-Auftrag aus einem Geometrieobjekt ohne solid_geometry " "zu generieren." -#: camlib.py:3910 +#: camlib.py:3929 msgid "" "The Tool Offset value is too negative to use for the current_geometry.\n" "Raise the value (in module) and try again." @@ -2924,15709 +18562,46 @@ msgstr "" "Geometrie verwendet zu werden.\n" "Erhöhen Sie den Wert (im Modul) und versuchen Sie es erneut." -#: camlib.py:4201 +#: camlib.py:4229 msgid " paths traced." msgstr " Pfade verfolgt." -#: camlib.py:4229 +#: camlib.py:4257 msgid "There is no tool data in the SolderPaste geometry." msgstr "In der SolderPaste-Geometrie sind keine Werkzeugdaten vorhanden." -#: camlib.py:4318 +#: camlib.py:4346 msgid "Finished SolderPaste G-Code generation" msgstr "Fertige G-Code-Generierung für Lötpaste" -#: camlib.py:4318 +#: camlib.py:4346 msgid "paths traced." msgstr "paths traced." -#: camlib.py:4578 +#: camlib.py:4606 msgid "Parsing GCode file. Number of lines" msgstr "Analysieren der GCode-Datei. Anzahl der Zeilen" -#: camlib.py:4685 +#: camlib.py:4713 msgid "Creating Geometry from the parsed GCode file. " msgstr "Erstellen von Geometrie aus der analysierten GCode-Datei. " -#: camlib.py:4828 camlib.py:5118 camlib.py:5229 camlib.py:5385 +#: camlib.py:4856 camlib.py:5079 camlib.py:5190 camlib.py:5346 msgid "G91 coordinates not implemented ..." msgstr "G91 Koordinaten nicht implementiert ..." -#: camlib.py:4960 +#: camlib.py:4921 msgid "Unifying Geometry from parsed Geometry segments" msgstr "Vereinheitlichen von Geometrie aus analysierten Geometriesegmenten" -#: defaults.py:401 -#: flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:86 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:309 -#: flatcamTools/ToolCopperThieving.py:125 flatcamTools/ToolNCC.py:535 -#: flatcamTools/ToolNCC.py:1306 flatcamTools/ToolNCC.py:1634 -#: flatcamTools/ToolNCC.py:1919 flatcamTools/ToolNCC.py:1983 -#: flatcamTools/ToolNCC.py:2967 flatcamTools/ToolNCC.py:2976 -#: tclCommands/TclCommandCopperClear.py:190 -msgid "Itself" -msgstr "Selbst" - -#: defaults.py:428 flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:280 -#: flatcamTools/ToolPaint.py:486 flatcamTools/ToolPaint.py:1427 -#: tclCommands/TclCommandPaint.py:162 -msgid "All Polygons" -msgstr "Alle Polygone" - -#: defaults.py:739 +#: defaults.py:756 msgid "Could not load defaults file." msgstr "Voreinstellungen konnte nicht geladen werden." -#: defaults.py:752 +#: defaults.py:769 msgid "Failed to parse defaults file." msgstr "Fehler beim Einlesen der Voreinstellungen." -#: flatcamEditors/FlatCAMExcEditor.py:50 flatcamEditors/FlatCAMExcEditor.py:74 -#: flatcamEditors/FlatCAMExcEditor.py:168 -#: flatcamEditors/FlatCAMExcEditor.py:385 -#: flatcamEditors/FlatCAMExcEditor.py:589 -#: flatcamEditors/FlatCAMGrbEditor.py:243 -#: flatcamEditors/FlatCAMGrbEditor.py:250 -msgid "Click to place ..." -msgstr "Klicken um zu platzieren ..." - -#: flatcamEditors/FlatCAMExcEditor.py:58 -msgid "To add a drill first select a tool" -msgstr "Um einen Bohrer hinzuzufügen, wählen Sie zuerst ein Werkzeug aus" - -#: flatcamEditors/FlatCAMExcEditor.py:122 -msgid "Done. Drill added." -msgstr "Erledigt. Bohrer hinzugefügt." - -#: flatcamEditors/FlatCAMExcEditor.py:176 -msgid "To add an Drill Array first select a tool in Tool Table" -msgstr "" -"Um ein Bohr-Array hinzuzufügen, wählen Sie zunächst ein Werkzeug in der " -"Werkzeugtabelle aus" - -#: flatcamEditors/FlatCAMExcEditor.py:192 -#: flatcamEditors/FlatCAMExcEditor.py:415 -#: flatcamEditors/FlatCAMExcEditor.py:636 -#: flatcamEditors/FlatCAMExcEditor.py:1151 -#: flatcamEditors/FlatCAMExcEditor.py:1178 -#: flatcamEditors/FlatCAMGrbEditor.py:473 -#: flatcamEditors/FlatCAMGrbEditor.py:1937 -#: flatcamEditors/FlatCAMGrbEditor.py:1967 -msgid "Click on target location ..." -msgstr "Klicken Sie auf den Zielort ..." - -#: flatcamEditors/FlatCAMExcEditor.py:211 -msgid "Click on the Drill Circular Array Start position" -msgstr "Klicken Sie auf die Startposition des Bohrkreis-Arrays" - -#: flatcamEditors/FlatCAMExcEditor.py:233 -#: flatcamEditors/FlatCAMExcEditor.py:677 -#: flatcamEditors/FlatCAMGrbEditor.py:518 -msgid "The value is not Float. Check for comma instead of dot separator." -msgstr "" -"Der Wert ist nicht Real. Überprüfen Sie das Komma anstelle des Trennzeichens." - -#: flatcamEditors/FlatCAMExcEditor.py:237 -msgid "The value is mistyped. Check the value" -msgstr "Der Wert ist falsch geschrieben. Überprüfen Sie den Wert" - -#: flatcamEditors/FlatCAMExcEditor.py:336 -msgid "Too many drills for the selected spacing angle." -msgstr "Zu viele Bohrer für den ausgewählten Abstandswinkel." - -#: flatcamEditors/FlatCAMExcEditor.py:354 -msgid "Done. Drill Array added." -msgstr "Erledigt. Bohrfeld hinzugefügt." - -#: flatcamEditors/FlatCAMExcEditor.py:394 -msgid "To add a slot first select a tool" -msgstr "Um einen Steckplatz hinzuzufügen, wählen Sie zunächst ein Werkzeug aus" - -#: flatcamEditors/FlatCAMExcEditor.py:454 -#: flatcamEditors/FlatCAMExcEditor.py:461 -#: flatcamEditors/FlatCAMExcEditor.py:742 -#: flatcamEditors/FlatCAMExcEditor.py:749 -msgid "Value is missing or wrong format. Add it and retry." -msgstr "" -"Wert fehlt oder falsches Format. Fügen Sie es hinzu und versuchen Sie es " -"erneut." - -#: flatcamEditors/FlatCAMExcEditor.py:559 -msgid "Done. Adding Slot completed." -msgstr "Erledigt. Das Hinzufügen des Slots ist abgeschlossen." - -#: flatcamEditors/FlatCAMExcEditor.py:597 -msgid "To add an Slot Array first select a tool in Tool Table" -msgstr "" -"Um ein Schlitze-Array hinzuzufügen, wählen Sie zunächst ein Werkzeug in der " -"Werkzeugtabelle aus" - -#: flatcamEditors/FlatCAMExcEditor.py:655 -msgid "Click on the Slot Circular Array Start position" -msgstr "Klicken Sie auf die kreisförmige Startposition des Arrays" - -#: flatcamEditors/FlatCAMExcEditor.py:680 -#: flatcamEditors/FlatCAMGrbEditor.py:521 -msgid "The value is mistyped. Check the value." -msgstr "Der Wert ist falsch geschrieben. Überprüfen Sie den Wert." - -#: flatcamEditors/FlatCAMExcEditor.py:859 -msgid "Too many Slots for the selected spacing angle." -msgstr "Zu viele Slots für den ausgewählten Abstandswinkel." - -#: flatcamEditors/FlatCAMExcEditor.py:882 -msgid "Done. Slot Array added." -msgstr "Erledigt. Schlitze Array hinzugefügt." - -#: flatcamEditors/FlatCAMExcEditor.py:904 -msgid "Click on the Drill(s) to resize ..." -msgstr "Klicken Sie auf die Bohrer, um die Größe zu ändern ..." - -#: flatcamEditors/FlatCAMExcEditor.py:934 -msgid "Resize drill(s) failed. Please enter a diameter for resize." -msgstr "" -"Die Größe der Bohrer ist fehlgeschlagen. Bitte geben Sie einen Durchmesser " -"für die Größenänderung ein." - -#: flatcamEditors/FlatCAMExcEditor.py:1112 -msgid "Done. Drill/Slot Resize completed." -msgstr "Getan. Bohrer / Schlitz Größenänderung abgeschlossen." - -#: flatcamEditors/FlatCAMExcEditor.py:1115 -msgid "Cancelled. No drills/slots selected for resize ..." -msgstr "Abgebrochen. Keine Bohrer / Schlitze für Größenänderung ausgewählt ..." - -#: flatcamEditors/FlatCAMExcEditor.py:1153 -#: flatcamEditors/FlatCAMGrbEditor.py:1939 -msgid "Click on reference location ..." -msgstr "Klicken Sie auf die Referenzposition ..." - -#: flatcamEditors/FlatCAMExcEditor.py:1210 -msgid "Done. Drill(s) Move completed." -msgstr "Erledigt. Bohrer Bewegen abgeschlossen." - -#: flatcamEditors/FlatCAMExcEditor.py:1318 -msgid "Done. Drill(s) copied." -msgstr "Erledigt. Bohrer kopiert." - -#: flatcamEditors/FlatCAMExcEditor.py:1557 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:26 -msgid "Excellon Editor" -msgstr "Excellon Editor" - -#: flatcamEditors/FlatCAMExcEditor.py:1564 -#: flatcamEditors/FlatCAMGrbEditor.py:2462 -msgid "Name:" -msgstr "Name:" - -#: flatcamEditors/FlatCAMExcEditor.py:1570 flatcamGUI/ObjectUI.py:761 -#: flatcamGUI/ObjectUI.py:1465 flatcamTools/ToolNCC.py:120 -#: flatcamTools/ToolPaint.py:115 flatcamTools/ToolSolderPaste.py:74 -msgid "Tools Table" -msgstr "Werkzeugtabelle" - -#: flatcamEditors/FlatCAMExcEditor.py:1572 flatcamGUI/ObjectUI.py:763 -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:1584 -#: flatcamEditors/FlatCAMExcEditor.py:3066 flatcamGUI/ObjectUI.py:781 -#: flatcamObjects/FlatCAMExcellon.py:1098 -#: flatcamObjects/FlatCAMExcellon.py:1188 -#: flatcamObjects/FlatCAMExcellon.py:1373 flatcamTools/ToolNCC.py:132 -#: flatcamTools/ToolPaint.py:128 flatcamTools/ToolPcbWizard.py:76 -#: flatcamTools/ToolProperties.py:416 flatcamTools/ToolProperties.py:476 -#: flatcamTools/ToolSolderPaste.py:85 tclCommands/TclCommandDrillcncjob.py:193 -msgid "Diameter" -msgstr "Durchmesser" - -#: flatcamEditors/FlatCAMExcEditor.py:1592 -msgid "Add/Delete Tool" -msgstr "Werkzeug hinzufügen / löschen" - -#: flatcamEditors/FlatCAMExcEditor.py:1594 -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:1606 flatcamGUI/ObjectUI.py:1585 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:57 -msgid "Diameter for the new tool" -msgstr "Durchmesser für das neue Werkzeug" - -#: flatcamEditors/FlatCAMExcEditor.py:1616 -msgid "Add Tool" -msgstr "Werkzeug hinzufügen" - -#: flatcamEditors/FlatCAMExcEditor.py:1618 -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:1630 -msgid "Delete Tool" -msgstr "Werkzeug löschen" - -#: flatcamEditors/FlatCAMExcEditor.py:1632 -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:1650 flatcamGUI/FlatCAMGUI.py:2019 -msgid "Resize Drill(s)" -msgstr "Größe der Bohrer ändern" - -#: flatcamEditors/FlatCAMExcEditor.py:1652 -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:1659 -msgid "Resize Dia" -msgstr "Durchmesser ändern" - -#: flatcamEditors/FlatCAMExcEditor.py:1661 -msgid "Diameter to resize to." -msgstr "Durchmesser zur Größenänderung." - -#: flatcamEditors/FlatCAMExcEditor.py:1672 -msgid "Resize" -msgstr "Größe ändern" - -#: flatcamEditors/FlatCAMExcEditor.py:1674 -msgid "Resize drill(s)" -msgstr "Bohrer verkleinern" - -#: flatcamEditors/FlatCAMExcEditor.py:1699 flatcamGUI/FlatCAMGUI.py:2018 -#: flatcamGUI/FlatCAMGUI.py:2270 -msgid "Add Drill Array" -msgstr "Bohrer-Array hinzufügen" - -#: flatcamEditors/FlatCAMExcEditor.py:1701 -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:1707 -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:1710 -#: flatcamEditors/FlatCAMExcEditor.py:1924 -#: flatcamEditors/FlatCAMGrbEditor.py:2775 -msgid "Linear" -msgstr "Linear" - -#: flatcamEditors/FlatCAMExcEditor.py:1711 -#: flatcamEditors/FlatCAMExcEditor.py:1925 -#: flatcamEditors/FlatCAMGrbEditor.py:2776 flatcamGUI/ObjectUI.py:316 -#: flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:52 -#: flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:149 -#: flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:107 -#: flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:52 -#: flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:151 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:61 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:70 -#: flatcamTools/ToolExtractDrills.py:78 flatcamTools/ToolExtractDrills.py:201 -#: flatcamTools/ToolFiducials.py:220 flatcamTools/ToolNCC.py:221 -#: flatcamTools/ToolPaint.py:204 flatcamTools/ToolPunchGerber.py:89 -#: flatcamTools/ToolPunchGerber.py:229 -msgid "Circular" -msgstr "Kreisförmig" - -#: flatcamEditors/FlatCAMExcEditor.py:1719 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:68 -msgid "Nr of drills" -msgstr "Anzahl der Bohrer" - -#: flatcamEditors/FlatCAMExcEditor.py:1720 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:70 -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:1738 -#: flatcamEditors/FlatCAMExcEditor.py:1788 -#: flatcamEditors/FlatCAMExcEditor.py:1860 -#: flatcamEditors/FlatCAMExcEditor.py:1953 -#: flatcamEditors/FlatCAMExcEditor.py:2004 -#: flatcamEditors/FlatCAMGrbEditor.py:1573 -#: flatcamEditors/FlatCAMGrbEditor.py:2804 -#: flatcamEditors/FlatCAMGrbEditor.py:2853 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:178 -msgid "Direction" -msgstr "Richtung" - -#: flatcamEditors/FlatCAMExcEditor.py:1740 -#: flatcamEditors/FlatCAMExcEditor.py:1955 -#: flatcamEditors/FlatCAMGrbEditor.py:2806 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:86 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:234 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:122 -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:1747 -#: flatcamEditors/FlatCAMExcEditor.py:1869 -#: flatcamEditors/FlatCAMExcEditor.py:1962 -#: flatcamEditors/FlatCAMGrbEditor.py:2813 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:92 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:187 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:240 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:128 -#: flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py:208 -#: flatcamTools/ToolFilm.py:256 -msgid "X" -msgstr "X" - -#: flatcamEditors/FlatCAMExcEditor.py:1748 -#: flatcamEditors/FlatCAMExcEditor.py:1870 -#: flatcamEditors/FlatCAMExcEditor.py:1963 -#: flatcamEditors/FlatCAMGrbEditor.py:2814 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:93 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:188 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:241 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:129 -#: flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py:209 -#: flatcamTools/ToolFilm.py:257 -msgid "Y" -msgstr "Y" - -#: flatcamEditors/FlatCAMExcEditor.py:1749 -#: flatcamEditors/FlatCAMExcEditor.py:1766 -#: flatcamEditors/FlatCAMExcEditor.py:1800 -#: flatcamEditors/FlatCAMExcEditor.py:1871 -#: flatcamEditors/FlatCAMExcEditor.py:1875 -#: flatcamEditors/FlatCAMExcEditor.py:1964 -#: flatcamEditors/FlatCAMExcEditor.py:1982 -#: flatcamEditors/FlatCAMExcEditor.py:2016 -#: flatcamEditors/FlatCAMGrbEditor.py:2815 -#: flatcamEditors/FlatCAMGrbEditor.py:2832 -#: flatcamEditors/FlatCAMGrbEditor.py:2868 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:94 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:113 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:189 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:194 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:242 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:263 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:130 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:148 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:53 -#: flatcamTools/ToolDistance.py:120 flatcamTools/ToolDistanceMin.py:69 -#: flatcamTools/ToolTransform.py:60 -msgid "Angle" -msgstr "Winkel" - -#: flatcamEditors/FlatCAMExcEditor.py:1753 -#: flatcamEditors/FlatCAMExcEditor.py:1968 -#: flatcamEditors/FlatCAMGrbEditor.py:2819 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:100 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:248 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:136 -msgid "Pitch" -msgstr "Abstand" - -#: flatcamEditors/FlatCAMExcEditor.py:1755 -#: flatcamEditors/FlatCAMExcEditor.py:1970 -#: flatcamEditors/FlatCAMGrbEditor.py:2821 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:102 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:250 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:138 -msgid "Pitch = Distance between elements of the array." -msgstr "Abstand = Abstand zwischen Elementen des Arrays." - -#: flatcamEditors/FlatCAMExcEditor.py:1768 -#: flatcamEditors/FlatCAMExcEditor.py:1984 -msgid "" -"Angle at which the linear array is placed.\n" -"The precision is of max 2 decimals.\n" -"Min value is: -360 degrees.\n" -"Max value is: 360.00 degrees." -msgstr "" -"Winkel, bei dem das lineare Feld platziert wird.\n" -"Die Genauigkeit beträgt maximal 2 Dezimalstellen.\n" -"Der Mindestwert beträgt -360 Grad.\n" -"Maximalwert ist: 360.00 Grad." - -#: flatcamEditors/FlatCAMExcEditor.py:1789 -#: flatcamEditors/FlatCAMExcEditor.py:2005 -#: flatcamEditors/FlatCAMGrbEditor.py:2855 -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:1796 -#: flatcamEditors/FlatCAMExcEditor.py:2012 -#: flatcamEditors/FlatCAMGrbEditor.py:2863 -#: flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:129 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:136 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:286 -#: flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:142 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:170 -msgid "CW" -msgstr "CW" - -#: flatcamEditors/FlatCAMExcEditor.py:1797 -#: flatcamEditors/FlatCAMExcEditor.py:2013 -#: flatcamEditors/FlatCAMGrbEditor.py:2864 -#: flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py:130 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:137 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:287 -#: flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:143 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:171 -msgid "CCW" -msgstr "CCW" - -#: flatcamEditors/FlatCAMExcEditor.py:1801 -#: flatcamEditors/FlatCAMExcEditor.py:2017 -#: flatcamEditors/FlatCAMGrbEditor.py:2870 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:115 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:145 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:265 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:295 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:150 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:179 -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:1835 -msgid "Slot Parameters" -msgstr "Schlitze-Parameter" - -#: flatcamEditors/FlatCAMExcEditor.py:1837 -msgid "" -"Parameters for adding a slot (hole with oval shape)\n" -"either single or as an part of an array." -msgstr "" -"Parameter zum Hinzufügen eines Schlitzes (Loch mit ovaler Form)\n" -"entweder einzeln oder als Teil eines Arrays." - -#: flatcamEditors/FlatCAMExcEditor.py:1846 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:162 -#: flatcamTools/ToolProperties.py:559 -msgid "Length" -msgstr "Länge" - -#: flatcamEditors/FlatCAMExcEditor.py:1848 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:164 -msgid "Length = The length of the slot." -msgstr "Länge = Die Länge des Schlitzes." - -#: flatcamEditors/FlatCAMExcEditor.py:1862 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:180 -msgid "" -"Direction on which the slot is oriented:\n" -"- 'X' - horizontal axis \n" -"- 'Y' - vertical axis or \n" -"- 'Angle' - a custom angle for the slot inclination" -msgstr "" -"Richtung, in die der Steckplatz ausgerichtet ist:\n" -"- 'X' - horizontale Achse\n" -"- 'Y' - vertikale Achse oder\n" -"- 'Winkel' - Ein benutzerdefinierter Winkel für die Schlitzneigung" - -#: flatcamEditors/FlatCAMExcEditor.py:1877 -msgid "" -"Angle at which the slot is placed.\n" -"The precision is of max 2 decimals.\n" -"Min value is: -360 degrees.\n" -"Max value is: 360.00 degrees." -msgstr "" -"Winkel, in dem der Schlitz platziert ist.\n" -"Die Genauigkeit beträgt maximal 2 Dezimalstellen.\n" -"Der Mindestwert beträgt: -360 Grad.\n" -"Maximaler Wert ist: 360.00 Grad." - -#: flatcamEditors/FlatCAMExcEditor.py:1910 -msgid "Slot Array Parameters" -msgstr "Schlitzes Array-Parameter" - -#: flatcamEditors/FlatCAMExcEditor.py:1912 -msgid "Parameters for the array of slots (linear or circular array)" -msgstr "" -"Parameter für das Array von Schlitzes (lineares oder kreisförmiges Array)" - -#: flatcamEditors/FlatCAMExcEditor.py:1921 -msgid "" -"Select the type of slot array to create.\n" -"It can be Linear X(Y) or Circular" -msgstr "" -"Wählen Sie den Typ des zu erstellenden Slot-Arrays.\n" -"Es kann ein lineares X (Y) oder ein kreisförmiges sein" - -#: flatcamEditors/FlatCAMExcEditor.py:1933 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:219 -msgid "Nr of slots" -msgstr "Anzahl der Slots" - -#: flatcamEditors/FlatCAMExcEditor.py:1934 -#: flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py:221 -msgid "Specify how many slots to be in the array." -msgstr "Geben Sie an, wie viele Steckplätze sich im Array befinden sollen." - -#: flatcamEditors/FlatCAMExcEditor.py:2452 -#: flatcamObjects/FlatCAMExcellon.py:410 -msgid "Total Drills" -msgstr "Bohrungen insgesamt" - -#: flatcamEditors/FlatCAMExcEditor.py:2484 -#: flatcamObjects/FlatCAMExcellon.py:441 -msgid "Total Slots" -msgstr "Schlitz insgesamt" - -#: flatcamEditors/FlatCAMExcEditor.py:2559 -#: flatcamEditors/FlatCAMGeoEditor.py:1076 -#: flatcamEditors/FlatCAMGeoEditor.py:1117 -#: flatcamEditors/FlatCAMGeoEditor.py:1145 -#: flatcamEditors/FlatCAMGeoEditor.py:1173 -#: flatcamEditors/FlatCAMGeoEditor.py:1217 -#: flatcamEditors/FlatCAMGeoEditor.py:1252 -#: flatcamEditors/FlatCAMGeoEditor.py:1280 -#: flatcamObjects/FlatCAMGeometry.py:599 flatcamObjects/FlatCAMGeometry.py:1033 -#: flatcamObjects/FlatCAMGeometry.py:1780 -#: flatcamObjects/FlatCAMGeometry.py:2424 flatcamTools/ToolNCC.py:1498 -#: flatcamTools/ToolPaint.py:1249 flatcamTools/ToolPaint.py:1420 -#: flatcamTools/ToolSolderPaste.py:883 flatcamTools/ToolSolderPaste.py:956 -msgid "Wrong value format entered, use a number." -msgstr "Falsches Wertformat eingegeben, eine Zahl verwenden." - -#: flatcamEditors/FlatCAMExcEditor.py:2570 -msgid "" -"Tool already in the original or actual tool list.\n" -"Save and reedit Excellon if you need to add this tool. " -msgstr "" -"Werkzeug bereits in der ursprünglichen oder tatsächlichen Werkzeugliste.\n" -"Speichern Sie Excellon und bearbeiten Sie es erneut, wenn Sie dieses Tool " -"hinzufügen müssen. " - -#: flatcamEditors/FlatCAMExcEditor.py:2579 flatcamGUI/FlatCAMGUI.py:4071 -msgid "Added new tool with dia" -msgstr "Neues Werkzeug mit Durchmesser hinzugefügt" - -#: flatcamEditors/FlatCAMExcEditor.py:2612 -msgid "Select a tool in Tool Table" -msgstr "Wählen Sie ein Werkzeug in der Werkzeugtabelle aus" - -#: flatcamEditors/FlatCAMExcEditor.py:2642 -msgid "Deleted tool with diameter" -msgstr "Gelöschtes Werkzeug mit Durchmesser" - -#: flatcamEditors/FlatCAMExcEditor.py:2790 -msgid "Done. Tool edit completed." -msgstr "Erledigt. Werkzeugbearbeitung abgeschlossen." - -#: flatcamEditors/FlatCAMExcEditor.py:3352 -msgid "There are no Tools definitions in the file. Aborting Excellon creation." -msgstr "" -"Die Datei enthält keine Werkzeugdefinitionen. Abbruch der Excellon-" -"Erstellung." - -#: flatcamEditors/FlatCAMExcEditor.py:3356 -msgid "An internal error has ocurred. See Shell.\n" -msgstr "" -"Ein interner Fehler ist aufgetreten. Siehe Shell.\n" -"\n" - -#: flatcamEditors/FlatCAMExcEditor.py:3361 -msgid "Creating Excellon." -msgstr "Excellon erstellen." - -#: flatcamEditors/FlatCAMExcEditor.py:3373 -msgid "Excellon editing finished." -msgstr "Excellon-Bearbeitung abgeschlossen." - -#: flatcamEditors/FlatCAMExcEditor.py:3390 -msgid "Cancelled. There is no Tool/Drill selected" -msgstr "Abgebrochen. Es ist kein Werkzeug / Bohrer ausgewählt" - -#: flatcamEditors/FlatCAMExcEditor.py:4003 -msgid "Done. Drill(s) deleted." -msgstr "Erledigt. Bohrer gelöscht." - -#: flatcamEditors/FlatCAMExcEditor.py:4076 -#: flatcamEditors/FlatCAMExcEditor.py:4086 -#: flatcamEditors/FlatCAMGrbEditor.py:5063 -msgid "Click on the circular array Center position" -msgstr "Klicken Sie auf die kreisförmige Anordnung in der Mitte" - -#: flatcamEditors/FlatCAMGeoEditor.py:85 -msgid "Buffer distance:" -msgstr "Pufferabstand:" - -#: flatcamEditors/FlatCAMGeoEditor.py:86 -msgid "Buffer corner:" -msgstr "Pufferecke:" - -#: flatcamEditors/FlatCAMGeoEditor.py:88 -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:94 -#: flatcamEditors/FlatCAMGrbEditor.py:2631 -msgid "Round" -msgstr "Runden" - -#: flatcamEditors/FlatCAMGeoEditor.py:95 -#: flatcamEditors/FlatCAMGrbEditor.py:2632 flatcamGUI/ObjectUI.py:2073 -#: flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:217 -#: flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:68 -#: flatcamGUI/preferences/tools/Tools2EDrillsPrefGroupUI.py:175 -#: flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:68 -#: flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:177 -#: flatcamGUI/preferences/tools/Tools2QRCodePrefGroupUI.py:143 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:327 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:291 -#: flatcamTools/ToolExtractDrills.py:94 flatcamTools/ToolExtractDrills.py:227 -#: flatcamTools/ToolNCC.py:583 flatcamTools/ToolPaint.py:527 -#: flatcamTools/ToolPunchGerber.py:105 flatcamTools/ToolPunchGerber.py:255 -#: flatcamTools/ToolQRCode.py:198 -msgid "Square" -msgstr "Quadrat" - -#: flatcamEditors/FlatCAMGeoEditor.py:96 -#: flatcamEditors/FlatCAMGrbEditor.py:2633 -msgid "Beveled" -msgstr "Abgeschrägt" - -#: flatcamEditors/FlatCAMGeoEditor.py:103 -msgid "Buffer Interior" -msgstr "Pufferinnenraum" - -#: flatcamEditors/FlatCAMGeoEditor.py:105 -msgid "Buffer Exterior" -msgstr "Puffer außen" - -#: flatcamEditors/FlatCAMGeoEditor.py:111 -msgid "Full Buffer" -msgstr "Voller Puffer" - -#: flatcamEditors/FlatCAMGeoEditor.py:132 -#: flatcamEditors/FlatCAMGeoEditor.py:3017 flatcamGUI/FlatCAMGUI.py:1928 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:190 -msgid "Buffer Tool" -msgstr "Pufferwerkzeug" - -#: flatcamEditors/FlatCAMGeoEditor.py:144 -#: flatcamEditors/FlatCAMGeoEditor.py:161 -#: flatcamEditors/FlatCAMGeoEditor.py:178 -#: flatcamEditors/FlatCAMGeoEditor.py:3036 -#: flatcamEditors/FlatCAMGeoEditor.py:3064 -#: flatcamEditors/FlatCAMGeoEditor.py:3092 -#: flatcamEditors/FlatCAMGrbEditor.py:5116 -msgid "Buffer distance value is missing or wrong format. Add it and retry." -msgstr "" -"Pufferabstandswert fehlt oder falsches Format. Fügen Sie es hinzu und " -"versuchen Sie es erneut." - -#: flatcamEditors/FlatCAMGeoEditor.py:242 -msgid "Font" -msgstr "Schrift" - -#: flatcamEditors/FlatCAMGeoEditor.py:323 flatcamGUI/FlatCAMGUI.py:2208 -msgid "Text" -msgstr "Text" - -#: flatcamEditors/FlatCAMGeoEditor.py:349 -msgid "Text Tool" -msgstr "Textwerkzeug" - -#: flatcamEditors/FlatCAMGeoEditor.py:405 flatcamGUI/FlatCAMGUI.py:511 -#: flatcamGUI/FlatCAMGUI.py:1158 flatcamGUI/ObjectUI.py:818 -#: flatcamGUI/ObjectUI.py:1662 flatcamObjects/FlatCAMExcellon.py:742 -#: flatcamObjects/FlatCAMExcellon.py:1084 flatcamObjects/FlatCAMGeometry.py:759 -#: flatcamTools/ToolNCC.py:331 flatcamTools/ToolNCC.py:797 -#: flatcamTools/ToolPaint.py:314 flatcamTools/ToolPaint.py:767 -msgid "Tool" -msgstr "Werkzeug" - -#: flatcamEditors/FlatCAMGeoEditor.py:439 flatcamGUI/ObjectUI.py:364 -#: flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py:43 -msgid "Tool dia" -msgstr "Werkzeugdurchmesser" - -#: flatcamEditors/FlatCAMGeoEditor.py:441 -msgid "Diameter of the tool to be used in the operation." -msgstr "Durchmesser des im Betrieb zu verwendenden Werkzeugs." - -#: flatcamEditors/FlatCAMGeoEditor.py:487 -msgid "" -"Algorithm to paint the polygons:\n" -"- Standard: Fixed step inwards.\n" -"- Seed-based: Outwards from seed.\n" -"- Line-based: Parallel lines." -msgstr "" -"Algorithmus zum Malen der Polygone:\n" -"- Standard: Schritt nach innen behoben.\n" -"- Samenbasiert: Aus dem Samen heraus.\n" -"- Linienbasiert: Parallele Linien." - -#: flatcamEditors/FlatCAMGeoEditor.py:506 -msgid "Connect:" -msgstr "Verbinden:" - -#: flatcamEditors/FlatCAMGeoEditor.py:516 -msgid "Contour:" -msgstr "Kontur:" - -#: flatcamEditors/FlatCAMGeoEditor.py:529 flatcamGUI/FlatCAMGUI.py:2212 -msgid "Paint" -msgstr "Malen" - -#: flatcamEditors/FlatCAMGeoEditor.py:547 flatcamGUI/FlatCAMGUI.py:924 -#: flatcamGUI/FlatCAMGUI.py:2628 flatcamGUI/ObjectUI.py:2139 -#: flatcamTools/ToolPaint.py:43 flatcamTools/ToolPaint.py:738 -msgid "Paint Tool" -msgstr "Werkzeug Malen" - -#: flatcamEditors/FlatCAMGeoEditor.py:583 -#: flatcamEditors/FlatCAMGeoEditor.py:1055 -#: flatcamEditors/FlatCAMGeoEditor.py:3024 -#: flatcamEditors/FlatCAMGeoEditor.py:3052 -#: flatcamEditors/FlatCAMGeoEditor.py:3080 -#: flatcamEditors/FlatCAMGeoEditor.py:4502 -#: flatcamEditors/FlatCAMGrbEditor.py:5767 -msgid "Cancelled. No shape selected." -msgstr "Abgebrochen. Keine Form ausgewählt." - -#: flatcamEditors/FlatCAMGeoEditor.py:596 -#: flatcamEditors/FlatCAMGeoEditor.py:3042 -#: flatcamEditors/FlatCAMGeoEditor.py:3070 -#: flatcamEditors/FlatCAMGeoEditor.py:3098 -#: flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py:59 -#: flatcamTools/ToolProperties.py:117 flatcamTools/ToolProperties.py:162 -msgid "Tools" -msgstr "Werkzeuge" - -#: flatcamEditors/FlatCAMGeoEditor.py:607 -#: flatcamEditors/FlatCAMGeoEditor.py:991 -#: flatcamEditors/FlatCAMGrbEditor.py:5306 -#: flatcamEditors/FlatCAMGrbEditor.py:5703 flatcamGUI/FlatCAMGUI.py:945 -#: flatcamGUI/FlatCAMGUI.py:2649 flatcamTools/ToolTransform.py:460 -msgid "Transform Tool" -msgstr "Werkzeug Umwandeln" - -#: flatcamEditors/FlatCAMGeoEditor.py:608 -#: flatcamEditors/FlatCAMGeoEditor.py:673 -#: flatcamEditors/FlatCAMGrbEditor.py:5307 -#: flatcamEditors/FlatCAMGrbEditor.py:5372 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:45 -#: flatcamTools/ToolTransform.py:24 flatcamTools/ToolTransform.py:466 -msgid "Rotate" -msgstr "Drehen" - -#: flatcamEditors/FlatCAMGeoEditor.py:609 -#: flatcamEditors/FlatCAMGrbEditor.py:5308 flatcamTools/ToolTransform.py:25 -msgid "Skew/Shear" -msgstr "Neigung/Schere" - -#: flatcamEditors/FlatCAMGeoEditor.py:610 -#: flatcamEditors/FlatCAMGrbEditor.py:2680 -#: flatcamEditors/FlatCAMGrbEditor.py:5309 flatcamGUI/FlatCAMGUI.py:1063 -#: flatcamGUI/FlatCAMGUI.py:2140 flatcamGUI/FlatCAMGUI.py:2255 -#: flatcamGUI/FlatCAMGUI.py:2767 flatcamGUI/ObjectUI.py:125 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:95 -#: flatcamTools/ToolTransform.py:26 -msgid "Scale" -msgstr "Skalieren" - -#: flatcamEditors/FlatCAMGeoEditor.py:611 -#: flatcamEditors/FlatCAMGrbEditor.py:5310 flatcamTools/ToolTransform.py:27 -msgid "Mirror (Flip)" -msgstr "Spiegeln (Flip)" - -#: flatcamEditors/FlatCAMGeoEditor.py:625 -#: flatcamEditors/FlatCAMGrbEditor.py:5324 flatcamGUI/FlatCAMGUI.py:856 -#: flatcamGUI/FlatCAMGUI.py:2564 -msgid "Editor" -msgstr "Editor" - -#: flatcamEditors/FlatCAMGeoEditor.py:657 -#: flatcamEditors/FlatCAMGrbEditor.py:5356 -msgid "Angle:" -msgstr "Winkel:" - -#: flatcamEditors/FlatCAMGeoEditor.py:659 -#: flatcamEditors/FlatCAMGrbEditor.py:5358 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:55 -#: flatcamTools/ToolTransform.py:62 -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:675 -#: flatcamEditors/FlatCAMGrbEditor.py:5374 -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:698 -#: flatcamEditors/FlatCAMGrbEditor.py:5397 -msgid "Angle X:" -msgstr "Winkel X:" - -#: flatcamEditors/FlatCAMGeoEditor.py:700 -#: flatcamEditors/FlatCAMGeoEditor.py:720 -#: flatcamEditors/FlatCAMGrbEditor.py:5399 -#: flatcamEditors/FlatCAMGrbEditor.py:5419 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:74 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:88 -#: flatcamTools/ToolCalibration.py:505 flatcamTools/ToolCalibration.py:518 -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:711 -#: flatcamEditors/FlatCAMGrbEditor.py:5410 flatcamTools/ToolTransform.py:467 -msgid "Skew X" -msgstr "Neigung X" - -#: flatcamEditors/FlatCAMGeoEditor.py:713 -#: flatcamEditors/FlatCAMGeoEditor.py:733 -#: flatcamEditors/FlatCAMGrbEditor.py:5412 -#: flatcamEditors/FlatCAMGrbEditor.py:5432 -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:718 -#: flatcamEditors/FlatCAMGrbEditor.py:5417 -msgid "Angle Y:" -msgstr "Winkel Y:" - -#: flatcamEditors/FlatCAMGeoEditor.py:731 -#: flatcamEditors/FlatCAMGrbEditor.py:5430 flatcamTools/ToolTransform.py:468 -msgid "Skew Y" -msgstr "Neigung Y" - -#: flatcamEditors/FlatCAMGeoEditor.py:759 -#: flatcamEditors/FlatCAMGrbEditor.py:5458 -msgid "Factor X:" -msgstr "Faktor X:" - -#: flatcamEditors/FlatCAMGeoEditor.py:761 -#: flatcamEditors/FlatCAMGrbEditor.py:5460 flatcamTools/ToolCalibration.py:469 -msgid "Factor for Scale action over X axis." -msgstr "Faktor für die Skalierungsaktion über der X-Achse." - -#: flatcamEditors/FlatCAMGeoEditor.py:771 -#: flatcamEditors/FlatCAMGrbEditor.py:5470 flatcamTools/ToolTransform.py:469 -msgid "Scale X" -msgstr "Maßstab X" - -#: flatcamEditors/FlatCAMGeoEditor.py:773 -#: flatcamEditors/FlatCAMGeoEditor.py:792 -#: flatcamEditors/FlatCAMGrbEditor.py:5472 -#: flatcamEditors/FlatCAMGrbEditor.py:5491 -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:778 -#: flatcamEditors/FlatCAMGrbEditor.py:5477 -msgid "Factor Y:" -msgstr "Faktor Y:" - -#: flatcamEditors/FlatCAMGeoEditor.py:780 -#: flatcamEditors/FlatCAMGrbEditor.py:5479 flatcamTools/ToolCalibration.py:481 -msgid "Factor for Scale action over Y axis." -msgstr "Faktor für die Skalierungsaktion über der Y-Achse." - -#: flatcamEditors/FlatCAMGeoEditor.py:790 -#: flatcamEditors/FlatCAMGrbEditor.py:5489 flatcamTools/ToolTransform.py:470 -msgid "Scale Y" -msgstr "Maßstab Y" - -#: flatcamEditors/FlatCAMGeoEditor.py:799 -#: flatcamEditors/FlatCAMGrbEditor.py:5498 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:124 -#: flatcamTools/ToolTransform.py:189 -msgid "Link" -msgstr "Verknüpfung" - -#: flatcamEditors/FlatCAMGeoEditor.py:801 -#: flatcamEditors/FlatCAMGrbEditor.py:5500 -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:807 -#: flatcamEditors/FlatCAMGrbEditor.py:5506 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:132 -#: flatcamTools/ToolTransform.py:196 -msgid "Scale Reference" -msgstr "Skalenreferenz" - -#: flatcamEditors/FlatCAMGeoEditor.py:809 -#: flatcamEditors/FlatCAMGrbEditor.py:5508 -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:837 -#: flatcamEditors/FlatCAMGrbEditor.py:5537 -msgid "Value X:" -msgstr "Wert X:" - -#: flatcamEditors/FlatCAMGeoEditor.py:839 -#: flatcamEditors/FlatCAMGrbEditor.py:5539 -msgid "Value for Offset action on X axis." -msgstr "Wert für die Offset-Aktion auf der X-Achse." - -#: flatcamEditors/FlatCAMGeoEditor.py:849 -#: flatcamEditors/FlatCAMGrbEditor.py:5549 flatcamTools/ToolTransform.py:473 -msgid "Offset X" -msgstr "Versatz X" - -#: flatcamEditors/FlatCAMGeoEditor.py:851 -#: flatcamEditors/FlatCAMGeoEditor.py:871 -#: flatcamEditors/FlatCAMGrbEditor.py:5551 -#: flatcamEditors/FlatCAMGrbEditor.py:5571 -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:857 -#: flatcamEditors/FlatCAMGrbEditor.py:5557 -msgid "Value Y:" -msgstr "Wert Y:" - -#: flatcamEditors/FlatCAMGeoEditor.py:859 -#: flatcamEditors/FlatCAMGrbEditor.py:5559 -msgid "Value for Offset action on Y axis." -msgstr "Wert für die Offset-Aktion auf der Y-Achse." - -#: flatcamEditors/FlatCAMGeoEditor.py:869 -#: flatcamEditors/FlatCAMGrbEditor.py:5569 flatcamTools/ToolTransform.py:474 -msgid "Offset Y" -msgstr "Versatz Y" - -#: flatcamEditors/FlatCAMGeoEditor.py:900 -#: flatcamEditors/FlatCAMGrbEditor.py:5600 flatcamTools/ToolTransform.py:475 -msgid "Flip on X" -msgstr "Flip auf X" - -#: flatcamEditors/FlatCAMGeoEditor.py:902 -#: flatcamEditors/FlatCAMGeoEditor.py:909 -#: flatcamEditors/FlatCAMGrbEditor.py:5602 -#: flatcamEditors/FlatCAMGrbEditor.py:5609 -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:907 -#: flatcamEditors/FlatCAMGrbEditor.py:5607 flatcamTools/ToolTransform.py:476 -msgid "Flip on Y" -msgstr "Flip auf Y" - -#: flatcamEditors/FlatCAMGeoEditor.py:915 -#: flatcamEditors/FlatCAMGrbEditor.py:5615 -msgid "Ref Pt" -msgstr "Ref. Pt" - -#: flatcamEditors/FlatCAMGeoEditor.py:917 -#: flatcamEditors/FlatCAMGrbEditor.py:5617 -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:929 -#: flatcamEditors/FlatCAMGrbEditor.py:5629 -msgid "Point:" -msgstr "Punkt:" - -#: flatcamEditors/FlatCAMGeoEditor.py:931 -#: flatcamEditors/FlatCAMGrbEditor.py:5631 flatcamTools/ToolTransform.py:299 -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:941 -#: flatcamEditors/FlatCAMGrbEditor.py:5643 flatcamTools/ToolTransform.py:309 -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:1304 -#: flatcamEditors/FlatCAMGrbEditor.py:5951 -msgid "No shape selected. Please Select a shape to rotate!" -msgstr "Keine Form ausgewählt Bitte wählen Sie eine Form zum Drehen aus!" - -#: flatcamEditors/FlatCAMGeoEditor.py:1307 -#: flatcamEditors/FlatCAMGrbEditor.py:5954 flatcamTools/ToolTransform.py:679 -msgid "Appying Rotate" -msgstr "Anwenden Drehen" - -#: flatcamEditors/FlatCAMGeoEditor.py:1333 -#: flatcamEditors/FlatCAMGrbEditor.py:5986 -msgid "Done. Rotate completed." -msgstr "Erledigt. Drehen abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:1335 -msgid "Rotation action was not executed" -msgstr "Rotationsaktion wurde nicht ausgeführt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1354 -#: flatcamEditors/FlatCAMGrbEditor.py:6005 -msgid "No shape selected. Please Select a shape to flip!" -msgstr "Keine Form ausgewählt. Bitte wählen Sie eine Form zum Kippen!" - -#: flatcamEditors/FlatCAMGeoEditor.py:1357 -#: flatcamEditors/FlatCAMGrbEditor.py:6008 flatcamTools/ToolTransform.py:728 -msgid "Applying Flip" -msgstr "Flip anwenden" - -#: flatcamEditors/FlatCAMGeoEditor.py:1386 -#: flatcamEditors/FlatCAMGrbEditor.py:6046 flatcamTools/ToolTransform.py:769 -msgid "Flip on the Y axis done" -msgstr "Spiegeln Sie die Y-Achse bereit" - -#: flatcamEditors/FlatCAMGeoEditor.py:1390 -#: flatcamEditors/FlatCAMGrbEditor.py:6055 flatcamTools/ToolTransform.py:778 -msgid "Flip on the X axis done" -msgstr "Spiegeln Sie die X-Achse bereit" - -#: flatcamEditors/FlatCAMGeoEditor.py:1398 -msgid "Flip action was not executed" -msgstr "Spiegeln-Aktion wurde nicht ausgeführt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1416 -#: flatcamEditors/FlatCAMGrbEditor.py:6075 -msgid "No shape selected. Please Select a shape to shear/skew!" -msgstr "" -"Keine Form ausgewählt. Bitte wählen Sie eine Form zum Scheren / " -"Schrägstellen!" - -#: flatcamEditors/FlatCAMGeoEditor.py:1419 -#: flatcamEditors/FlatCAMGrbEditor.py:6078 flatcamTools/ToolTransform.py:801 -msgid "Applying Skew" -msgstr "Schräglauf anwenden" - -#: flatcamEditors/FlatCAMGeoEditor.py:1442 -#: flatcamEditors/FlatCAMGrbEditor.py:6112 -msgid "Skew on the X axis done" -msgstr "Schrägstellung auf der X-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1444 -#: flatcamEditors/FlatCAMGrbEditor.py:6114 -msgid "Skew on the Y axis done" -msgstr "Schrägstellung auf der Y-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1447 -msgid "Skew action was not executed" -msgstr "Die Versatzaktion wurde nicht ausgeführt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1469 -#: flatcamEditors/FlatCAMGrbEditor.py:6136 -msgid "No shape selected. Please Select a shape to scale!" -msgstr "Keine Form ausgewählt. Bitte wählen Sie eine zu skalierende Form!" - -#: flatcamEditors/FlatCAMGeoEditor.py:1472 -#: flatcamEditors/FlatCAMGrbEditor.py:6139 flatcamTools/ToolTransform.py:847 -msgid "Applying Scale" -msgstr "Maßstab anwenden" - -#: flatcamEditors/FlatCAMGeoEditor.py:1504 -#: flatcamEditors/FlatCAMGrbEditor.py:6176 -msgid "Scale on the X axis done" -msgstr "Skalieren auf der X-Achse erledigt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1506 -#: flatcamEditors/FlatCAMGrbEditor.py:6178 -msgid "Scale on the Y axis done" -msgstr "Skalieren auf der Y-Achse erledigt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1508 -msgid "Scale action was not executed" -msgstr "Skalierungsaktion wurde nicht ausgeführt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1523 -#: flatcamEditors/FlatCAMGrbEditor.py:6195 -msgid "No shape selected. Please Select a shape to offset!" -msgstr "Keine Form ausgewählt. Bitte wählen Sie eine zu versetzende Form!" - -#: flatcamEditors/FlatCAMGeoEditor.py:1526 -#: flatcamEditors/FlatCAMGrbEditor.py:6198 flatcamTools/ToolTransform.py:897 -msgid "Applying Offset" -msgstr "Offsetdruck anwenden" - -#: flatcamEditors/FlatCAMGeoEditor.py:1536 -#: flatcamEditors/FlatCAMGrbEditor.py:6219 -msgid "Offset on the X axis done" -msgstr "Versatz auf der X-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1538 -#: flatcamEditors/FlatCAMGrbEditor.py:6221 -msgid "Offset on the Y axis done" -msgstr "Versatz auf der Y-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1541 -msgid "Offset action was not executed" -msgstr "Offsetaktion wurde nicht ausgeführt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1545 -#: flatcamEditors/FlatCAMGrbEditor.py:6228 -msgid "Rotate ..." -msgstr "Drehen ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:1546 -#: flatcamEditors/FlatCAMGeoEditor.py:1601 -#: flatcamEditors/FlatCAMGeoEditor.py:1618 -#: flatcamEditors/FlatCAMGrbEditor.py:6229 -#: flatcamEditors/FlatCAMGrbEditor.py:6278 -#: flatcamEditors/FlatCAMGrbEditor.py:6293 -msgid "Enter an Angle Value (degrees)" -msgstr "Geben Sie einen Winkelwert (Grad) ein" - -#: flatcamEditors/FlatCAMGeoEditor.py:1555 -#: flatcamEditors/FlatCAMGrbEditor.py:6237 -msgid "Geometry shape rotate done" -msgstr "Geometrieform drehen fertig" - -#: flatcamEditors/FlatCAMGeoEditor.py:1559 -#: flatcamEditors/FlatCAMGrbEditor.py:6240 -msgid "Geometry shape rotate cancelled" -msgstr "Geometrieform drehen abgebrochen" - -#: flatcamEditors/FlatCAMGeoEditor.py:1564 -#: flatcamEditors/FlatCAMGrbEditor.py:6245 -msgid "Offset on X axis ..." -msgstr "Versatz auf der X-Achse ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:1565 -#: flatcamEditors/FlatCAMGeoEditor.py:1584 -#: flatcamEditors/FlatCAMGrbEditor.py:6246 -#: flatcamEditors/FlatCAMGrbEditor.py:6263 -msgid "Enter a distance Value" -msgstr "Geben Sie einen Abstandswert ein" - -#: flatcamEditors/FlatCAMGeoEditor.py:1574 -#: flatcamEditors/FlatCAMGrbEditor.py:6254 -msgid "Geometry shape offset on X axis done" -msgstr "Geometrieformversatz auf der X-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1578 -#: flatcamEditors/FlatCAMGrbEditor.py:6257 -msgid "Geometry shape offset X cancelled" -msgstr "[WARNING_NOTCL] Geometrieformversatz X abgebrochen" - -#: flatcamEditors/FlatCAMGeoEditor.py:1583 -#: flatcamEditors/FlatCAMGrbEditor.py:6262 -msgid "Offset on Y axis ..." -msgstr "Versatz auf der Y-Achse ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:1593 -#: flatcamEditors/FlatCAMGrbEditor.py:6271 -msgid "Geometry shape offset on Y axis done" -msgstr "Geometrieformversatz auf Y-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1597 -msgid "Geometry shape offset on Y axis canceled" -msgstr "Geometrieformversatz auf Y-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1600 -#: flatcamEditors/FlatCAMGrbEditor.py:6277 -msgid "Skew on X axis ..." -msgstr "Neigung auf der X-Achse ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:1610 -#: flatcamEditors/FlatCAMGrbEditor.py:6286 -msgid "Geometry shape skew on X axis done" -msgstr "Geometrieformversatz auf X-Achse" - -#: flatcamEditors/FlatCAMGeoEditor.py:1614 -msgid "Geometry shape skew on X axis canceled" -msgstr "Geometrieformversatz auf X-Achse" - -#: flatcamEditors/FlatCAMGeoEditor.py:1617 -#: flatcamEditors/FlatCAMGrbEditor.py:6292 -msgid "Skew on Y axis ..." -msgstr "Neigung auf der Y-Achse ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:1627 -#: flatcamEditors/FlatCAMGrbEditor.py:6301 -msgid "Geometry shape skew on Y axis done" -msgstr "Geometrieformversatz auf Y-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:1631 -msgid "Geometry shape skew on Y axis canceled" -msgstr "Geometrieformversatz auf Y-Achse erfolgt" - -#: flatcamEditors/FlatCAMGeoEditor.py:2008 -#: flatcamEditors/FlatCAMGeoEditor.py:2079 -#: flatcamEditors/FlatCAMGrbEditor.py:1437 -#: flatcamEditors/FlatCAMGrbEditor.py:1515 -msgid "Click on Center point ..." -msgstr "Klicken Sie auf Mittelpunkt." - -#: flatcamEditors/FlatCAMGeoEditor.py:2021 -#: flatcamEditors/FlatCAMGrbEditor.py:1447 -msgid "Click on Perimeter point to complete ..." -msgstr "Klicken Sie auf Umfangspunkt, um den Vorgang abzuschließen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2053 -msgid "Done. Adding Circle completed." -msgstr "Erledigt. Hinzufügen des Kreises abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2107 -#: flatcamEditors/FlatCAMGrbEditor.py:1548 -msgid "Click on Start point ..." -msgstr "Klicken Sie auf Startpunkt ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2109 -#: flatcamEditors/FlatCAMGrbEditor.py:1550 -msgid "Click on Point3 ..." -msgstr "Klicken Sie auf Punkt3 ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2111 -#: flatcamEditors/FlatCAMGrbEditor.py:1552 -msgid "Click on Stop point ..." -msgstr "Klicken Sie auf Haltepunkt ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2116 -#: flatcamEditors/FlatCAMGrbEditor.py:1557 -msgid "Click on Stop point to complete ..." -msgstr "Klicken Sie auf Stopp, um den Vorgang abzuschließen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2118 -#: flatcamEditors/FlatCAMGrbEditor.py:1559 -msgid "Click on Point2 to complete ..." -msgstr "Klicken Sie auf Punkt2, um den Vorgang abzuschließen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2120 -#: flatcamEditors/FlatCAMGrbEditor.py:1561 -msgid "Click on Center point to complete ..." -msgstr "Klicken Sie auf Mittelpunkt, um den Vorgang abzuschließen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2132 -#, python-format -msgid "Direction: %s" -msgstr "Richtung: %s" - -#: flatcamEditors/FlatCAMGeoEditor.py:2146 -#: flatcamEditors/FlatCAMGrbEditor.py:1587 -msgid "Mode: Start -> Stop -> Center. Click on Start point ..." -msgstr "Modus: Start -> Stopp -> Zentrieren. Klicken Sie auf Startpunkt ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2149 -#: flatcamEditors/FlatCAMGrbEditor.py:1590 -msgid "Mode: Point1 -> Point3 -> Point2. Click on Point1 ..." -msgstr "Modus: Punkt 1 -> Punkt 3 -> Punkt 2. Klicken Sie auf Punkt1 ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2152 -#: flatcamEditors/FlatCAMGrbEditor.py:1593 -msgid "Mode: Center -> Start -> Stop. Click on Center point ..." -msgstr "Modus: Mitte -> Start -> Stopp. Klicken Sie auf Mittelpunkt." - -#: flatcamEditors/FlatCAMGeoEditor.py:2293 -msgid "Done. Arc completed." -msgstr "Erledigt. Arc abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2324 -#: flatcamEditors/FlatCAMGeoEditor.py:2397 -msgid "Click on 1st corner ..." -msgstr "Klicken Sie auf die 1. Ecke ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2336 -msgid "Click on opposite corner to complete ..." -msgstr "" -"Klicken Sie auf die gegenüberliegende Ecke, um den Vorgang abzuschließen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2366 -msgid "Done. Rectangle completed." -msgstr "Erledigt. Rechteck fertiggestellt." - -#: flatcamEditors/FlatCAMGeoEditor.py:2410 -#: flatcamObjects/FlatCAMGeometry.py:2648 flatcamTools/ToolNCC.py:1733 -#: flatcamTools/ToolPaint.py:1628 -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:2441 -msgid "Done. Polygon completed." -msgstr "Erledigt. Polygon fertiggestellt." - -#: flatcamEditors/FlatCAMGeoEditor.py:2455 -#: flatcamEditors/FlatCAMGeoEditor.py:2520 -#: flatcamEditors/FlatCAMGrbEditor.py:1113 -#: flatcamEditors/FlatCAMGrbEditor.py:1324 -msgid "Backtracked one point ..." -msgstr "Einen Punkt zurückverfolgt ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2498 -msgid "Done. Path completed." -msgstr "Getan. Pfad abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2657 -msgid "No shape selected. Select a shape to explode" -msgstr "Keine Form ausgewählt. Wählen Sie eine Form zum Auflösen aus" - -#: flatcamEditors/FlatCAMGeoEditor.py:2690 -msgid "Done. Polygons exploded into lines." -msgstr "Getan. Polygone explodierten in Linien." - -#: flatcamEditors/FlatCAMGeoEditor.py:2722 -msgid "MOVE: No shape selected. Select a shape to move" -msgstr "Bewegen: Keine Form ausgewählt. Wähle eine Form zum Bewegen aus" - -#: flatcamEditors/FlatCAMGeoEditor.py:2725 -#: flatcamEditors/FlatCAMGeoEditor.py:2745 -msgid " MOVE: Click on reference point ..." -msgstr " Bewegen: Referenzpunkt anklicken ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2730 -msgid " Click on destination point ..." -msgstr " Klicken Sie auf den Zielpunkt ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2770 -msgid "Done. Geometry(s) Move completed." -msgstr "Erledigt. Geometrie(n) Bewegung abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2903 -msgid "Done. Geometry(s) Copy completed." -msgstr "Erledigt. Geometrie(n) Kopieren abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2934 -#: flatcamEditors/FlatCAMGrbEditor.py:899 -msgid "Click on 1st point ..." -msgstr "Klicken Sie auf den 1. Punkt ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:2958 -msgid "" -"Font not supported. Only Regular, Bold, Italic and BoldItalic are supported. " -"Error" -msgstr "" -"Schrift wird nicht unterstützt. Es werden nur Regular, Bold, Italic und " -"BoldItalic unterstützt. Error" - -#: flatcamEditors/FlatCAMGeoEditor.py:2966 -msgid "No text to add." -msgstr "Kein Text zum Hinzufügen." - -#: flatcamEditors/FlatCAMGeoEditor.py:2976 -msgid " Done. Adding Text completed." -msgstr " Erledigt. Hinzufügen von Text abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:3013 -msgid "Create buffer geometry ..." -msgstr "Puffergeometrie erstellen ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:3048 -#: flatcamEditors/FlatCAMGrbEditor.py:5160 -msgid "Done. Buffer Tool completed." -msgstr "Erledigt. Pufferwerkzeug abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:3076 -msgid "Done. Buffer Int Tool completed." -msgstr "Erledigt. Innenpufferwerkzeug abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:3104 -msgid "Done. Buffer Ext Tool completed." -msgstr "Erledigt. Außenpufferwerkzeug abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:3153 -#: flatcamEditors/FlatCAMGrbEditor.py:2153 -msgid "Select a shape to act as deletion area ..." -msgstr "Wählen Sie eine Form als Löschbereich aus ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:3155 -#: flatcamEditors/FlatCAMGeoEditor.py:3181 -#: flatcamEditors/FlatCAMGeoEditor.py:3187 -#: flatcamEditors/FlatCAMGrbEditor.py:2155 -msgid "Click to pick-up the erase shape..." -msgstr "Klicken Sie, um die Löschform aufzunehmen ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:3191 -#: flatcamEditors/FlatCAMGrbEditor.py:2214 -msgid "Click to erase ..." -msgstr "Klicken zum Löschen ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:3220 -#: flatcamEditors/FlatCAMGrbEditor.py:2247 -msgid "Done. Eraser tool action completed." -msgstr "Erledigt. Radiergummi-Aktion abgeschlossen." - -#: flatcamEditors/FlatCAMGeoEditor.py:3270 -msgid "Create Paint geometry ..." -msgstr "Malen geometrie erstellen ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:3283 -#: flatcamEditors/FlatCAMGrbEditor.py:2410 -msgid "Shape transformations ..." -msgstr "Formtransformationen ..." - -#: flatcamEditors/FlatCAMGeoEditor.py:3339 -#: flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py:27 -msgid "Geometry Editor" -msgstr "Geo-Editor" - -#: flatcamEditors/FlatCAMGeoEditor.py:3345 -#: flatcamEditors/FlatCAMGrbEditor.py:2488 -#: flatcamEditors/FlatCAMGrbEditor.py:3969 flatcamGUI/ObjectUI.py:263 -#: flatcamGUI/ObjectUI.py:1497 flatcamGUI/ObjectUI.py:2326 -#: flatcamTools/ToolCutOut.py:95 -msgid "Type" -msgstr "Typ" - -#: flatcamEditors/FlatCAMGeoEditor.py:3345 flatcamGUI/ObjectUI.py:218 -#: flatcamGUI/ObjectUI.py:742 flatcamGUI/ObjectUI.py:1433 -#: flatcamGUI/ObjectUI.py:2235 flatcamGUI/ObjectUI.py:2539 -#: flatcamGUI/ObjectUI.py:2606 flatcamTools/ToolCalibration.py:234 -#: flatcamTools/ToolFiducials.py:73 -msgid "Name" -msgstr "Name" - -#: flatcamEditors/FlatCAMGeoEditor.py:3587 -msgid "Ring" -msgstr "Ring" - -#: flatcamEditors/FlatCAMGeoEditor.py:3589 -msgid "Line" -msgstr "Linie" - -#: flatcamEditors/FlatCAMGeoEditor.py:3591 flatcamGUI/FlatCAMGUI.py:2202 -#: flatcamGUI/ObjectUI.py:2074 -#: flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py:218 -#: flatcamGUI/preferences/tools/ToolsNCCPrefGroupUI.py:328 -#: flatcamGUI/preferences/tools/ToolsPaintPrefGroupUI.py:292 -#: flatcamTools/ToolNCC.py:584 flatcamTools/ToolPaint.py:528 -msgid "Polygon" -msgstr "Polygon" - -#: flatcamEditors/FlatCAMGeoEditor.py:3593 -msgid "Multi-Line" -msgstr "Mehrzeilig" - -#: flatcamEditors/FlatCAMGeoEditor.py:3595 -msgid "Multi-Polygon" -msgstr "Multi-Polygon" - -#: flatcamEditors/FlatCAMGeoEditor.py:3602 -msgid "Geo Elem" -msgstr "Geoelement" - -#: flatcamEditors/FlatCAMGeoEditor.py:4076 -msgid "Editing MultiGeo Geometry, tool" -msgstr "Bearbeiten von MultiGeo Geometry, Werkzeug" - -#: flatcamEditors/FlatCAMGeoEditor.py:4078 -msgid "with diameter" -msgstr "mit Durchmesser" - -#: flatcamEditors/FlatCAMGeoEditor.py:4509 flatcamGUI/FlatCAMGUI.py:3753 -#: flatcamGUI/FlatCAMGUI.py:3799 flatcamGUI/FlatCAMGUI.py:3817 -#: flatcamGUI/FlatCAMGUI.py:3961 flatcamGUI/FlatCAMGUI.py:4000 -#: flatcamGUI/FlatCAMGUI.py:4012 flatcamGUI/FlatCAMGUI.py:4029 -msgid "Click on target point." -msgstr "Klicken Sie auf den Zielpunkt." - -#: flatcamEditors/FlatCAMGeoEditor.py:4823 -#: flatcamEditors/FlatCAMGeoEditor.py:4858 -msgid "A selection of at least 2 geo items is required to do Intersection." -msgstr "" -"Eine Auswahl von mindestens 2 Geo-Elementen ist erforderlich, um die " -"Kreuzung durchzuführen." - -#: flatcamEditors/FlatCAMGeoEditor.py:4944 -#: flatcamEditors/FlatCAMGeoEditor.py:5048 -msgid "" -"Negative buffer value is not accepted. Use Buffer interior to generate an " -"'inside' shape" -msgstr "" -"Negativer Pufferwert wird nicht akzeptiert. Verwenden Sie den " -"Pufferinnenraum, um eine Innenform zu erzeugen" - -#: flatcamEditors/FlatCAMGeoEditor.py:4954 -#: flatcamEditors/FlatCAMGeoEditor.py:5007 -#: flatcamEditors/FlatCAMGeoEditor.py:5057 -msgid "Nothing selected for buffering." -msgstr "Nichts ist für die Pufferung ausgewählt." - -#: flatcamEditors/FlatCAMGeoEditor.py:4959 -#: flatcamEditors/FlatCAMGeoEditor.py:5011 -#: flatcamEditors/FlatCAMGeoEditor.py:5062 -msgid "Invalid distance for buffering." -msgstr "Ungültige Entfernung zum Puffern." - -#: flatcamEditors/FlatCAMGeoEditor.py:4983 -#: flatcamEditors/FlatCAMGeoEditor.py:5082 -msgid "Failed, the result is empty. Choose a different buffer value." -msgstr "" -"Fehlgeschlagen, das Ergebnis ist leer. Wählen Sie einen anderen Pufferwert." - -#: flatcamEditors/FlatCAMGeoEditor.py:4994 -msgid "Full buffer geometry created." -msgstr "Volle Puffergeometrie erstellt." - -#: flatcamEditors/FlatCAMGeoEditor.py:5000 -msgid "Negative buffer value is not accepted." -msgstr "Negativer Pufferwert wird nicht akzeptiert." - -#: flatcamEditors/FlatCAMGeoEditor.py:5031 -msgid "Failed, the result is empty. Choose a smaller buffer value." -msgstr "" -"Fehlgeschlagen, das Ergebnis ist leer. Wählen Sie einen kleineren Pufferwert." - -#: flatcamEditors/FlatCAMGeoEditor.py:5041 -msgid "Interior buffer geometry created." -msgstr "Innere Puffergeometrie erstellt." - -#: flatcamEditors/FlatCAMGeoEditor.py:5092 -msgid "Exterior buffer geometry created." -msgstr "Außenpuffergeometrie erstellt." - -#: flatcamEditors/FlatCAMGeoEditor.py:5098 -#, python-format -msgid "Could not do Paint. Overlap value has to be less than 100%%." -msgstr "Konnte nicht Malen. Der Überlappungswert muss kleiner als 100 %% sein." - -#: flatcamEditors/FlatCAMGeoEditor.py:5105 -msgid "Nothing selected for painting." -msgstr "Nichts zum Malen ausgewählt." - -#: flatcamEditors/FlatCAMGeoEditor.py:5111 -msgid "Invalid value for" -msgstr "Ungültiger Wert für" - -#: flatcamEditors/FlatCAMGeoEditor.py:5170 -msgid "" -"Could not do Paint. Try a different combination of parameters. Or a " -"different method of Paint" -msgstr "" -"Konnte nicht malen. Probieren Sie eine andere Kombination von Parametern " -"aus. Oder eine andere Malmethode" - -#: flatcamEditors/FlatCAMGeoEditor.py:5181 -msgid "Paint done." -msgstr "Malen fertig." - -#: flatcamEditors/FlatCAMGrbEditor.py:213 -msgid "To add an Pad first select a aperture in Aperture Table" -msgstr "" -"Um ein Pad hinzuzufügen, wählen Sie zunächst eine Blende in der Aperture " -"Table aus" - -#: flatcamEditors/FlatCAMGrbEditor.py:220 -#: flatcamEditors/FlatCAMGrbEditor.py:420 -msgid "Aperture size is zero. It needs to be greater than zero." -msgstr "Die Größe der Blende ist Null. Es muss größer als Null sein." - -#: flatcamEditors/FlatCAMGrbEditor.py:373 -#: flatcamEditors/FlatCAMGrbEditor.py:686 -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:385 -msgid "Done. Adding Pad completed." -msgstr "Erledigt. Hinzufügen von Pad abgeschlossen." - -#: flatcamEditors/FlatCAMGrbEditor.py:412 -msgid "To add an Pad Array first select a aperture in Aperture Table" -msgstr "" -"Um ein Pad-Array hinzuzufügen, wählen Sie zunächst eine Blende in der " -"Aperture-Tabelle aus" - -#: flatcamEditors/FlatCAMGrbEditor.py:492 -msgid "Click on the Pad Circular Array Start position" -msgstr "Klicken Sie auf die Startposition des Pad-Kreis-Arrays" - -#: flatcamEditors/FlatCAMGrbEditor.py:712 -msgid "Too many Pads for the selected spacing angle." -msgstr "Zu viele Pad für den ausgewählten Abstandswinkel." - -#: flatcamEditors/FlatCAMGrbEditor.py:735 -msgid "Done. Pad Array added." -msgstr "Erledigt. Pad Array hinzugefügt." - -#: flatcamEditors/FlatCAMGrbEditor.py:760 -msgid "Select shape(s) and then click ..." -msgstr "Wählen Sie die Form (en) aus und klicken Sie dann auf ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:772 -msgid "Failed. Nothing selected." -msgstr "Gescheitert. Nichts ausgewählt." - -#: flatcamEditors/FlatCAMGrbEditor.py:788 -msgid "" -"Failed. Poligonize works only on geometries belonging to the same aperture." -msgstr "" -"Gescheitert. Poligonize funktioniert nur bei Geometrien, die zur selben " -"Apertur gehören." - -#: flatcamEditors/FlatCAMGrbEditor.py:842 -msgid "Done. Poligonize completed." -msgstr "Erledigt. Poligonize abgeschlossen." - -#: flatcamEditors/FlatCAMGrbEditor.py:897 -#: flatcamEditors/FlatCAMGrbEditor.py:1130 -#: flatcamEditors/FlatCAMGrbEditor.py:1154 -msgid "Corner Mode 1: 45 degrees ..." -msgstr "Eckmodus 1: 45 Grad ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:909 -#: flatcamEditors/FlatCAMGrbEditor.py:1239 -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/FlatCAMGrbEditor.py:1118 -#: flatcamEditors/FlatCAMGrbEditor.py:1151 -msgid "Corner Mode 2: Reverse 45 degrees ..." -msgstr "Eckmodus 2: 45 Grad umkehren ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1121 -#: flatcamEditors/FlatCAMGrbEditor.py:1148 -msgid "Corner Mode 3: 90 degrees ..." -msgstr "Eckmodus 3: 90 Grad ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1124 -#: flatcamEditors/FlatCAMGrbEditor.py:1145 -msgid "Corner Mode 4: Reverse 90 degrees ..." -msgstr "Eckmodus 4: Um 90 Grad umkehren ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1127 -#: flatcamEditors/FlatCAMGrbEditor.py:1142 -msgid "Corner Mode 5: Free angle ..." -msgstr "Eckmodus 5: Freiwinkel ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1184 -#: flatcamEditors/FlatCAMGrbEditor.py:1360 -#: flatcamEditors/FlatCAMGrbEditor.py:1399 -msgid "Track Mode 1: 45 degrees ..." -msgstr "Spurmodus 1: 45 Grad ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1340 -#: flatcamEditors/FlatCAMGrbEditor.py:1394 -msgid "Track Mode 2: Reverse 45 degrees ..." -msgstr "Spurmodus 2: 45 Grad umkehren ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1345 -#: flatcamEditors/FlatCAMGrbEditor.py:1389 -msgid "Track Mode 3: 90 degrees ..." -msgstr "Spurmodus 3: 90 Grad ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1350 -#: flatcamEditors/FlatCAMGrbEditor.py:1384 -msgid "Track Mode 4: Reverse 90 degrees ..." -msgstr "Spurmodus 4: Um 90 Grad umkehren ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1355 -#: flatcamEditors/FlatCAMGrbEditor.py:1379 -msgid "Track Mode 5: Free angle ..." -msgstr "Spurmodus 5: Freiwinkel ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1780 -msgid "Scale the selected Gerber apertures ..." -msgstr "Skalieren Sie die ausgewählten Gerber-Öffnungen ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1822 -msgid "Buffer the selected apertures ..." -msgstr "Die ausgewählten Öffnungen puffern ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1864 -msgid "Mark polygon areas in the edited Gerber ..." -msgstr "Markiere Polygonbereiche im bearbeiteten Gerber ..." - -#: flatcamEditors/FlatCAMGrbEditor.py:1930 -msgid "Nothing selected to move" -msgstr "Nichts zum Bewegen ausgewählt" - -#: flatcamEditors/FlatCAMGrbEditor.py:2055 -msgid "Done. Apertures Move completed." -msgstr "Erledigt. Öffnungsbewegung abgeschlossen." - -#: flatcamEditors/FlatCAMGrbEditor.py:2137 -msgid "Done. Apertures copied." -msgstr "Erledigt. Blende kopiert." - -#: flatcamEditors/FlatCAMGrbEditor.py:2455 flatcamGUI/FlatCAMGUI.py:2233 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:27 -msgid "Gerber Editor" -msgstr "Gerber-Editor" - -#: flatcamEditors/FlatCAMGrbEditor.py:2475 flatcamGUI/ObjectUI.py:228 -#: flatcamTools/ToolProperties.py:159 -msgid "Apertures" -msgstr "Öffnungen" - -#: flatcamEditors/FlatCAMGrbEditor.py:2477 flatcamGUI/ObjectUI.py:230 -msgid "Apertures Table for the Gerber Object." -msgstr "Blendentabelle für das Gerberobjekt." - -#: flatcamEditors/FlatCAMGrbEditor.py:2488 -#: flatcamEditors/FlatCAMGrbEditor.py:3969 flatcamGUI/ObjectUI.py:263 -msgid "Code" -msgstr "Code" - -#: flatcamEditors/FlatCAMGrbEditor.py:2488 -#: flatcamEditors/FlatCAMGrbEditor.py:3969 flatcamGUI/ObjectUI.py:263 -#: flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py:103 -#: flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:167 -#: flatcamGUI/preferences/tools/Tools2CThievingPrefGroupUI.py:196 -#: flatcamGUI/preferences/tools/Tools2FiducialsPrefGroupUI.py:43 -#: flatcamTools/ToolCopperThieving.py:261 -#: flatcamTools/ToolCopperThieving.py:301 flatcamTools/ToolFiducials.py:156 -msgid "Size" -msgstr "Größe" - -#: flatcamEditors/FlatCAMGrbEditor.py:2488 -#: flatcamEditors/FlatCAMGrbEditor.py:3969 flatcamGUI/ObjectUI.py:263 -msgid "Dim" -msgstr "Maße" - -#: flatcamEditors/FlatCAMGrbEditor.py:2493 flatcamGUI/ObjectUI.py:267 -msgid "Index" -msgstr "Index" - -#: flatcamEditors/FlatCAMGrbEditor.py:2495 -#: flatcamEditors/FlatCAMGrbEditor.py:2524 flatcamGUI/ObjectUI.py:269 -msgid "Aperture Code" -msgstr "Öffnungscode" - -#: flatcamEditors/FlatCAMGrbEditor.py:2497 flatcamGUI/ObjectUI.py:271 -msgid "Type of aperture: circular, rectangle, macros etc" -msgstr "Öffnungsart: kreisförmig, rechteckig, Makros usw" - -#: flatcamEditors/FlatCAMGrbEditor.py:2499 flatcamGUI/ObjectUI.py:273 -msgid "Aperture Size:" -msgstr "Öffnungsgröße:" - -#: flatcamEditors/FlatCAMGrbEditor.py:2501 flatcamGUI/ObjectUI.py:275 -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:2525 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:58 -msgid "Code for the new aperture" -msgstr "Code für die neue Blende" - -#: flatcamEditors/FlatCAMGrbEditor.py:2534 -msgid "Aperture Size" -msgstr "Öffnungsgröße" - -#: flatcamEditors/FlatCAMGrbEditor.py:2536 -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:2550 -msgid "Aperture Type" -msgstr "Blendentyp" - -#: flatcamEditors/FlatCAMGrbEditor.py:2552 -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:2563 -msgid "Aperture Dim" -msgstr "Öffnungsmaße" - -#: flatcamEditors/FlatCAMGrbEditor.py:2565 -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:2574 -msgid "Add/Delete Aperture" -msgstr "Blende hinzufügen / löschen" - -#: flatcamEditors/FlatCAMGrbEditor.py:2576 -msgid "Add/Delete an aperture in the aperture table" -msgstr "Eine Blende in der Blendentabelle hinzufügen / löschen" - -#: flatcamEditors/FlatCAMGrbEditor.py:2585 -msgid "Add a new aperture to the aperture list." -msgstr "Fügen Sie der Blendenliste eine neue Blende hinzu." - -#: flatcamEditors/FlatCAMGrbEditor.py:2590 -msgid "Delete a aperture in the aperture list" -msgstr "Löschen Sie eine Blende in der Blendenliste" - -#: flatcamEditors/FlatCAMGrbEditor.py:2607 -msgid "Buffer Aperture" -msgstr "Pufferblende" - -#: flatcamEditors/FlatCAMGrbEditor.py:2609 -msgid "Buffer a aperture in the aperture list" -msgstr "Puffern Sie eine Blende in der Blendenliste" - -#: flatcamEditors/FlatCAMGrbEditor.py:2622 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:194 -msgid "Buffer distance" -msgstr "Pufferabstand" - -#: flatcamEditors/FlatCAMGrbEditor.py:2623 -msgid "Buffer corner" -msgstr "Pufferecke" - -#: flatcamEditors/FlatCAMGrbEditor.py:2625 -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:2640 flatcamGUI/FlatCAMGUI.py:1061 -#: flatcamGUI/FlatCAMGUI.py:2138 flatcamGUI/FlatCAMGUI.py:2210 -#: flatcamGUI/FlatCAMGUI.py:2253 flatcamGUI/FlatCAMGUI.py:2765 -#: flatcamGUI/preferences/tools/ToolsTransformPrefGroupUI.py:200 -#: flatcamTools/ToolTransform.py:29 -msgid "Buffer" -msgstr "Puffer" - -#: flatcamEditors/FlatCAMGrbEditor.py:2655 -msgid "Scale Aperture" -msgstr "Skalenöffnung" - -#: flatcamEditors/FlatCAMGrbEditor.py:2657 -msgid "Scale a aperture in the aperture list" -msgstr "Skalieren Sie eine Blende in der Blendenliste" - -#: flatcamEditors/FlatCAMGrbEditor.py:2665 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:209 -msgid "Scale factor" -msgstr "Skalierungsfaktor" - -#: flatcamEditors/FlatCAMGrbEditor.py:2667 -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:2695 -msgid "Mark polygons" -msgstr "Polygone markieren" - -#: flatcamEditors/FlatCAMGrbEditor.py:2697 -msgid "Mark the polygon areas." -msgstr "Markieren Sie die Polygonbereiche." - -#: flatcamEditors/FlatCAMGrbEditor.py:2705 -msgid "Area UPPER threshold" -msgstr "Flächenobergrenze" - -#: flatcamEditors/FlatCAMGrbEditor.py:2707 -msgid "" -"The threshold value, all areas less than this are marked.\n" -"Can have a value between 0.0000 and 9999.9999" -msgstr "" -"Der Schwellenwert, alle Bereiche, die darunter liegen, sind markiert.\n" -"Kann einen Wert zwischen 0,0000 und 9999,9999 haben" - -#: flatcamEditors/FlatCAMGrbEditor.py:2714 -msgid "Area LOWER threshold" -msgstr "Bereichsuntergrenze" - -#: flatcamEditors/FlatCAMGrbEditor.py:2716 -msgid "" -"The threshold value, all areas more than this are marked.\n" -"Can have a value between 0.0000 and 9999.9999" -msgstr "" -"Mit dem Schwellwert sind alle Bereiche gekennzeichnet, die darüber " -"hinausgehen.\n" -"Kann einen Wert zwischen 0,0000 und 9999,9999 haben" - -#: flatcamEditors/FlatCAMGrbEditor.py:2730 -msgid "Mark" -msgstr "Kennzeichen" - -#: flatcamEditors/FlatCAMGrbEditor.py:2732 -msgid "Mark the polygons that fit within limits." -msgstr "Markieren Sie die Polygone, die in Grenzen passen." - -#: flatcamEditors/FlatCAMGrbEditor.py:2738 -msgid "Delete all the marked polygons." -msgstr "Löschen Sie alle markierten Polygone." - -#: flatcamEditors/FlatCAMGrbEditor.py:2744 -msgid "Clear all the markings." -msgstr "Alle Markierungen entfernen." - -#: flatcamEditors/FlatCAMGrbEditor.py:2764 flatcamGUI/FlatCAMGUI.py:1046 -#: flatcamGUI/FlatCAMGUI.py:2138 flatcamGUI/FlatCAMGUI.py:2750 -msgid "Add Pad Array" -msgstr "Pad-Array hinzufügen" - -#: flatcamEditors/FlatCAMGrbEditor.py:2766 -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:2772 -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:2783 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:95 -msgid "Nr of pads" -msgstr "Anzahl der Pads" - -#: flatcamEditors/FlatCAMGrbEditor.py:2785 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:97 -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:2834 -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/FlatCAMGrbEditor.py:3328 -#: flatcamEditors/FlatCAMGrbEditor.py:3332 -msgid "Aperture code value is missing or wrong format. Add it and retry." -msgstr "" -"Blendencodewert fehlt oder falsches Format. Fügen Sie es hinzu und versuchen " -"Sie es erneut." - -#: flatcamEditors/FlatCAMGrbEditor.py:3368 -msgid "" -"Aperture dimensions value is missing or wrong format. Add it in format " -"(width, height) and retry." -msgstr "" -"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:3381 -msgid "Aperture size value is missing or wrong format. Add it and retry." -msgstr "" -"Der Wert für die Blendengröße fehlt oder das Format ist falsch. Fügen Sie es " -"hinzu und versuchen Sie es erneut." - -#: flatcamEditors/FlatCAMGrbEditor.py:3392 -msgid "Aperture already in the aperture table." -msgstr "Blende bereits in der Blendentabelle." - -#: flatcamEditors/FlatCAMGrbEditor.py:3399 -msgid "Added new aperture with code" -msgstr "Neue Blende mit Code hinzugefügt" - -#: flatcamEditors/FlatCAMGrbEditor.py:3431 -msgid " Select an aperture in Aperture Table" -msgstr " Wählen Sie in Blende Table eine Blende aus" - -#: flatcamEditors/FlatCAMGrbEditor.py:3439 -msgid "Select an aperture in Aperture Table -->" -msgstr "Wählen Sie in Blende Table eine Blende aus -->" - -#: flatcamEditors/FlatCAMGrbEditor.py:3453 -msgid "Deleted aperture with code" -msgstr "Blende mit Code gelöscht" - -#: flatcamEditors/FlatCAMGrbEditor.py:3521 -msgid "Dimensions need two float values separated by comma." -msgstr "Bemaßungen benötigen zwei durch Komma getrennte Gleitkommawerte." - -#: flatcamEditors/FlatCAMGrbEditor.py:3530 -msgid "Dimensions edited." -msgstr "Abmessungen bearbeitet." - -#: flatcamEditors/FlatCAMGrbEditor.py:4084 -msgid "Loading Gerber into Editor" -msgstr "Gerber File wird in den Editor geladen" - -#: flatcamEditors/FlatCAMGrbEditor.py:4212 -msgid "Setting up the UI" -msgstr "UI wird initialisiert" - -#: flatcamEditors/FlatCAMGrbEditor.py:4213 -msgid "Adding geometry finished. Preparing the GUI" -msgstr "Geometrie wurde hinzugefügt. User Interface wird vorbereitet" - -#: flatcamEditors/FlatCAMGrbEditor.py:4222 -msgid "Finished loading the Gerber object into the editor." -msgstr "Gerber-Objekte wurde in den Editor geladen." - -#: flatcamEditors/FlatCAMGrbEditor.py:4361 -msgid "" -"There are no Aperture definitions in the file. Aborting Gerber creation." -msgstr "" -"Die Datei enthält keine Aperture-Definitionen. Abbruch der Gerber-Erstellung." - -#: flatcamEditors/FlatCAMGrbEditor.py:4371 -msgid "Creating Gerber." -msgstr "Gerber erstellen." - -#: flatcamEditors/FlatCAMGrbEditor.py:4380 -msgid "Done. Gerber editing finished." -msgstr "Erledigt. Gerber-Bearbeitung beendet." - -#: flatcamEditors/FlatCAMGrbEditor.py:4398 -msgid "Cancelled. No aperture is selected" -msgstr "Abgebrochen. Es ist keine Blende ausgewählt" - -#: flatcamEditors/FlatCAMGrbEditor.py:4992 -msgid "Failed. No aperture geometry is selected." -msgstr "Gescheitert. Es ist keine Aperturgeometrie ausgewählt." - -#: flatcamEditors/FlatCAMGrbEditor.py:5001 -#: flatcamEditors/FlatCAMGrbEditor.py:5272 -msgid "Done. Apertures geometry deleted." -msgstr "Fertig. Blendengeometrie gelöscht." - -#: flatcamEditors/FlatCAMGrbEditor.py:5144 -msgid "No aperture to buffer. Select at least one aperture and try again." -msgstr "" -"Keine Blende zum Puffern Wählen Sie mindestens eine Blende und versuchen Sie " -"es erneut." - -#: flatcamEditors/FlatCAMGrbEditor.py:5156 -msgid "Failed." -msgstr "Gescheitert." - -#: flatcamEditors/FlatCAMGrbEditor.py:5175 -msgid "Scale factor value is missing or wrong format. Add it and retry." -msgstr "" -"Der Skalierungsfaktor ist nicht vorhanden oder das Format ist falsch. Fügen " -"Sie es hinzu und versuchen Sie es erneut." - -#: flatcamEditors/FlatCAMGrbEditor.py:5207 -msgid "No aperture to scale. Select at least one aperture and try again." -msgstr "" -"Keine zu skalierende Blende Wählen Sie mindestens eine Blende und versuchen " -"Sie es erneut." - -#: flatcamEditors/FlatCAMGrbEditor.py:5223 -msgid "Done. Scale Tool completed." -msgstr "Erledigt. Skalierungswerkzeug abgeschlossen." - -#: flatcamEditors/FlatCAMGrbEditor.py:5261 -msgid "Polygons marked." -msgstr "Polygone markiert." - -#: flatcamEditors/FlatCAMGrbEditor.py:5264 -msgid "No polygons were marked. None fit within the limits." -msgstr "Es wurden keine Polygone markiert. Keiner passt in die Grenzen." - -#: flatcamEditors/FlatCAMGrbEditor.py:5988 -msgid "Rotation action was not executed." -msgstr "Rotationsaktion wurde nicht ausgeführt." - -#: flatcamEditors/FlatCAMGrbEditor.py:6116 -msgid "Skew action was not executed." -msgstr "Die Versatzaktion wurde nicht ausgeführt." - -#: flatcamEditors/FlatCAMGrbEditor.py:6181 -msgid "Scale action was not executed." -msgstr "Skalierungsaktion wurde nicht ausgeführt." - -#: flatcamEditors/FlatCAMGrbEditor.py:6224 -msgid "Offset action was not executed." -msgstr "Offsetaktion wurde nicht ausgeführt." - -#: flatcamEditors/FlatCAMGrbEditor.py:6274 -msgid "Geometry shape offset Y cancelled" -msgstr "Geometrieform-Versatz Y abgebrochen" - -#: flatcamEditors/FlatCAMGrbEditor.py:6289 -msgid "Geometry shape skew X cancelled" -msgstr "Geometrieformverzerren X abgebrochen" - -#: flatcamEditors/FlatCAMGrbEditor.py:6304 -msgid "Geometry shape skew Y cancelled" -msgstr "Geometrieformverzerren Y abgebrochen" - -#: flatcamEditors/FlatCAMTextEditor.py:74 -msgid "Print Preview" -msgstr "Druckvorschau" - -#: flatcamEditors/FlatCAMTextEditor.py:75 -msgid "Open a OS standard Preview Print window." -msgstr "" -"Öffnen Sie ein Standardfenster für die Druckvorschau des Betriebssystems." - -#: flatcamEditors/FlatCAMTextEditor.py:78 -msgid "Print Code" -msgstr "Code drucken" - -#: flatcamEditors/FlatCAMTextEditor.py:79 -msgid "Open a OS standard Print window." -msgstr "Öffnen Sie ein Betriebssystem-Standard-Druckfenster." - -#: flatcamEditors/FlatCAMTextEditor.py:81 -msgid "Find in Code" -msgstr "Im Code suchen" - -#: flatcamEditors/FlatCAMTextEditor.py:82 -msgid "Will search and highlight in yellow the string in the Find box." -msgstr "Sucht und hebt die Zeichenfolge im Feld Suchen gelb hervor." - -#: flatcamEditors/FlatCAMTextEditor.py:86 -msgid "Find box. Enter here the strings to be searched in the text." -msgstr "" -"Suchfeld. Geben Sie hier die Zeichenfolgen ein, nach denen im Text gesucht " -"werden soll." - -#: flatcamEditors/FlatCAMTextEditor.py:88 -msgid "Replace With" -msgstr "Ersetzen mit" - -#: flatcamEditors/FlatCAMTextEditor.py:89 -msgid "" -"Will replace the string from the Find box with the one in the Replace box." -msgstr "" -"Ersetzt die Zeichenfolge aus dem Feld Suchen durch die Zeichenfolge aus dem " -"Feld Ersetzen." - -#: flatcamEditors/FlatCAMTextEditor.py:93 -msgid "String to replace the one in the Find box throughout the text." -msgstr "" -"Zeichenfolge, die die Zeichenfolge im Feld Suchen im gesamten Text ersetzt." - -#: flatcamEditors/FlatCAMTextEditor.py:95 flatcamGUI/ObjectUI.py:486 -#: flatcamGUI/ObjectUI.py:2219 -#: flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:54 -#: flatcamGUI/preferences/gerber/GerberOptPrefGroupUI.py:88 -msgid "All" -msgstr "Alles" - -#: flatcamEditors/FlatCAMTextEditor.py:96 -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\" .." - -#: flatcamEditors/FlatCAMTextEditor.py:99 -msgid "Copy All" -msgstr "Kopiere alles" - -#: flatcamEditors/FlatCAMTextEditor.py:100 -msgid "Will copy all the text in the Code Editor to the clipboard." -msgstr "Kopiert den gesamten Text im Code-Editor in die Zwischenablage." - -#: flatcamEditors/FlatCAMTextEditor.py:103 -msgid "Open Code" -msgstr "Code öffnen" - -#: flatcamEditors/FlatCAMTextEditor.py:104 -msgid "Will open a text file in the editor." -msgstr "Öffnet eine Textdatei im Editor." - -#: flatcamEditors/FlatCAMTextEditor.py:106 -msgid "Save Code" -msgstr "Code speichern" - -#: flatcamEditors/FlatCAMTextEditor.py:107 -msgid "Will save the text in the editor into a file." -msgstr "Speichert den Text im Editor in einer Datei." - -#: flatcamEditors/FlatCAMTextEditor.py:109 -msgid "Run Code" -msgstr "Code ausführen" - -#: flatcamEditors/FlatCAMTextEditor.py:110 -msgid "Will run the TCL commands found in the text file, one by one." -msgstr "Führt die in der Textdatei enthaltenen TCL-Befehle nacheinander aus." - -#: flatcamEditors/FlatCAMTextEditor.py:184 -msgid "Open file" -msgstr "Datei öffnen" - -#: flatcamEditors/FlatCAMTextEditor.py:215 -#: flatcamEditors/FlatCAMTextEditor.py:220 -msgid "Export Code ..." -msgstr "Code exportieren ..." - -#: flatcamEditors/FlatCAMTextEditor.py:272 flatcamObjects/FlatCAMCNCJob.py:955 -#: flatcamTools/ToolSolderPaste.py:1530 -msgid "No such file or directory" -msgstr "Keine solche Datei oder Ordner" - -#: flatcamEditors/FlatCAMTextEditor.py:284 flatcamObjects/FlatCAMCNCJob.py:969 -msgid "Saved to" -msgstr "Gespeichert in" - -#: flatcamEditors/FlatCAMTextEditor.py:334 -msgid "Code Editor content copied to clipboard ..." -msgstr "Code Editor Inhalt in die Zwischenablage kopiert ..." - -#: flatcamGUI/FlatCAMGUI.py:78 flatcamGUI/FlatCAMGUI.py:80 -#: flatcamGUI/FlatCAMGUI.py:2163 -msgid "Toggle Panel" -msgstr "Panel umschalten" - -#: flatcamGUI/FlatCAMGUI.py:90 -msgid "File" -msgstr "Datei" - -#: flatcamGUI/FlatCAMGUI.py:95 -msgid "&New Project ...\tCtrl+N" -msgstr "&Neues Projekt ...\\STRG+N" - -#: flatcamGUI/FlatCAMGUI.py:97 -msgid "Will create a new, blank project" -msgstr "Erzeugt ein neues leeres Projekt" - -#: flatcamGUI/FlatCAMGUI.py:102 -msgid "&New" -msgstr "&Neu" - -#: flatcamGUI/FlatCAMGUI.py:106 -msgid "Geometry\tN" -msgstr "Geometrie\tN" - -#: flatcamGUI/FlatCAMGUI.py:108 -msgid "Will create a new, empty Geometry Object." -msgstr "Erzeugt ein neues, leeres Geometrieobjekt." - -#: flatcamGUI/FlatCAMGUI.py:111 -msgid "Gerber\tB" -msgstr "Gerber\tB" - -#: flatcamGUI/FlatCAMGUI.py:113 -msgid "Will create a new, empty Gerber Object." -msgstr "Erzeugt ein neues, leeres Gerber-Objekt." - -#: flatcamGUI/FlatCAMGUI.py:116 -msgid "Excellon\tL" -msgstr "Excellon\tL" - -#: flatcamGUI/FlatCAMGUI.py:118 -msgid "Will create a new, empty Excellon Object." -msgstr "Erzeugt ein neues, leeres Excellon-Objekt." - -#: flatcamGUI/FlatCAMGUI.py:123 -msgid "Document\tD" -msgstr "Dokumentieren\tD" - -#: flatcamGUI/FlatCAMGUI.py:125 -msgid "Will create a new, empty Document Object." -msgstr "Erstellt ein neues, leeres Dokumentobjekt." - -#: flatcamGUI/FlatCAMGUI.py:129 flatcamGUI/FlatCAMGUI.py:4420 -#: flatcamTools/ToolPcbWizard.py:62 flatcamTools/ToolPcbWizard.py:69 -msgid "Open" -msgstr "Öffnen" - -#: flatcamGUI/FlatCAMGUI.py:134 -msgid "Open &Project ..." -msgstr "&Projekt öffnen..." - -#: flatcamGUI/FlatCAMGUI.py:140 flatcamGUI/FlatCAMGUI.py:4430 -msgid "Open &Gerber ...\tCtrl+G" -msgstr "&Gerber öffnen...\\STRG+G" - -#: flatcamGUI/FlatCAMGUI.py:145 flatcamGUI/FlatCAMGUI.py:4435 -msgid "Open &Excellon ...\tCtrl+E" -msgstr "&Excellon öffnen...\\STRG+E" - -#: flatcamGUI/FlatCAMGUI.py:150 flatcamGUI/FlatCAMGUI.py:4440 -msgid "Open G-&Code ..." -msgstr "G-&Code öffnen..." - -#: flatcamGUI/FlatCAMGUI.py:157 -msgid "Open Config ..." -msgstr "Config öffnen..." - -#: flatcamGUI/FlatCAMGUI.py:162 -msgid "Recent projects" -msgstr "Letzte Projekte" - -#: flatcamGUI/FlatCAMGUI.py:164 -msgid "Recent files" -msgstr "Neueste Dateien" - -#: flatcamGUI/FlatCAMGUI.py:167 flatcamGUI/FlatCAMGUI.py:753 -#: flatcamGUI/FlatCAMGUI.py:1339 -msgid "Save" -msgstr "Speichern" - -#: flatcamGUI/FlatCAMGUI.py:171 -msgid "&Save Project ...\tCtrl+S" -msgstr "Projekt speichern ...\\STRG+S" - -#: flatcamGUI/FlatCAMGUI.py:176 -msgid "Save Project &As ...\tCtrl+Shift+S" -msgstr "Projekt speichern als ...\\STRG+Shift+S" - -#: flatcamGUI/FlatCAMGUI.py:191 -msgid "Scripting" -msgstr "Scripting" - -#: flatcamGUI/FlatCAMGUI.py:195 flatcamGUI/FlatCAMGUI.py:903 -#: flatcamGUI/FlatCAMGUI.py:2607 -msgid "New Script ..." -msgstr "Neues Skript ..." - -#: flatcamGUI/FlatCAMGUI.py:197 flatcamGUI/FlatCAMGUI.py:905 -#: flatcamGUI/FlatCAMGUI.py:2609 -msgid "Open Script ..." -msgstr "Skript öffnen ..." - -#: flatcamGUI/FlatCAMGUI.py:199 -msgid "Open Example ..." -msgstr "Beispiel öffnen ..." - -#: flatcamGUI/FlatCAMGUI.py:201 flatcamGUI/FlatCAMGUI.py:907 -#: flatcamGUI/FlatCAMGUI.py:2611 flatcamGUI/FlatCAMGUI.py:4409 -msgid "Run Script ..." -msgstr "Skript ausführen ..." - -#: flatcamGUI/FlatCAMGUI.py:203 flatcamGUI/FlatCAMGUI.py:4411 -msgid "" -"Will run the opened Tcl Script thus\n" -"enabling the automation of certain\n" -"functions of FlatCAM." -msgstr "" -"Das geöffnete Tcl-Skript wird ausgeführt.\n" -"Ermöglichung der Automatisierung bestimmter\n" -"Funktionen von FlatCAM." - -#: flatcamGUI/FlatCAMGUI.py:218 -msgid "Import" -msgstr "Importieren" - -#: flatcamGUI/FlatCAMGUI.py:220 -msgid "&SVG as Geometry Object ..." -msgstr "&SVG als Geometrieobjekt ..." - -#: flatcamGUI/FlatCAMGUI.py:223 -msgid "&SVG as Gerber Object ..." -msgstr "&SVG als Gerberobjekt ..." - -#: flatcamGUI/FlatCAMGUI.py:228 -msgid "&DXF as Geometry Object ..." -msgstr "&DXF als Geometrieobjekt ..." - -#: flatcamGUI/FlatCAMGUI.py:231 -msgid "&DXF as Gerber Object ..." -msgstr "&DXF als Gerberobjekt ..." - -#: flatcamGUI/FlatCAMGUI.py:235 -msgid "HPGL2 as Geometry Object ..." -msgstr "HPGL2 als Geometrieobjekt ..." - -#: flatcamGUI/FlatCAMGUI.py:241 -msgid "Export" -msgstr "Exportieren" - -#: flatcamGUI/FlatCAMGUI.py:245 -msgid "Export &SVG ..." -msgstr "SVG exportieren ..." - -#: flatcamGUI/FlatCAMGUI.py:249 -msgid "Export DXF ..." -msgstr "DXF exportieren ..." - -#: flatcamGUI/FlatCAMGUI.py:255 -msgid "Export &PNG ..." -msgstr "PNG exportieren ..." - -#: flatcamGUI/FlatCAMGUI.py:257 -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 die\n" -"Bildinformationen des FlatCAM-Plotbereiches." - -#: flatcamGUI/FlatCAMGUI.py:266 -msgid "Export &Excellon ..." -msgstr "Excellon exportieren ..." - -#: flatcamGUI/FlatCAMGUI.py:268 -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:275 -msgid "Export &Gerber ..." -msgstr "Gerber exportieren ..." - -#: flatcamGUI/FlatCAMGUI.py:277 -msgid "" -"Will export an Gerber Object as Gerber file,\n" -"the coordinates format, the file units and zeros\n" -"are set in Preferences -> Gerber Export." -msgstr "" -"Exportiert ein Gerber-Objekt als Gerber-Datei.\n" -"das Koordinatenformat, die Dateieinheiten und Nullen\n" -"werden in den Einstellungen -> Gerber Export eingestellt." - -#: flatcamGUI/FlatCAMGUI.py:287 -msgid "Backup" -msgstr "Sicherungskopie" - -#: flatcamGUI/FlatCAMGUI.py:292 -msgid "Import Preferences from file ..." -msgstr "Einstellungen aus Datei importieren ..." - -#: flatcamGUI/FlatCAMGUI.py:298 -msgid "Export Preferences to file ..." -msgstr "Einstellungen in Datei exportieren ..." - -#: flatcamGUI/FlatCAMGUI.py:306 -#: flatcamGUI/preferences/PreferencesUIManager.py:1119 -msgid "Save Preferences" -msgstr "Einstellungen speichern" - -#: flatcamGUI/FlatCAMGUI.py:312 flatcamGUI/FlatCAMGUI.py:1730 -msgid "Print (PDF)" -msgstr "Drucken (PDF)" - -#: flatcamGUI/FlatCAMGUI.py:320 -msgid "E&xit" -msgstr "Ausgang" - -#: flatcamGUI/FlatCAMGUI.py:328 flatcamGUI/FlatCAMGUI.py:747 -#: flatcamGUI/FlatCAMGUI.py:2286 -msgid "Edit" -msgstr "Bearbeiten" - -#: flatcamGUI/FlatCAMGUI.py:332 -msgid "Edit Object\tE" -msgstr "Objekt bearbeiten\tE" - -#: flatcamGUI/FlatCAMGUI.py:334 -msgid "Close Editor\tCtrl+S" -msgstr "Schließen Sie Editor\tSTRG+S" - -#: flatcamGUI/FlatCAMGUI.py:343 -msgid "Conversion" -msgstr "Umwandlung" - -#: flatcamGUI/FlatCAMGUI.py:345 -msgid "&Join Geo/Gerber/Exc -> Geo" -msgstr "Geo/Gerber/Exc -> Geo zusammenfassen" - -#: flatcamGUI/FlatCAMGUI.py:347 -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:354 -msgid "Join Excellon(s) -> Excellon" -msgstr "Excellon(s) -> Excellon zusammenfassen" - -#: flatcamGUI/FlatCAMGUI.py:356 -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:359 -msgid "Join Gerber(s) -> Gerber" -msgstr "Gerber(s) -> Gerber zusammenfassen" - -#: flatcamGUI/FlatCAMGUI.py:361 -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:366 -msgid "Convert Single to MultiGeo" -msgstr "Konvertieren Sie Single in MultiGeo" - -#: flatcamGUI/FlatCAMGUI.py:368 -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:372 -msgid "Convert Multi to SingleGeo" -msgstr "Konvertieren Sie Multi in SingleGeo" - -#: flatcamGUI/FlatCAMGUI.py:374 -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:381 -msgid "Convert Any to Geo" -msgstr "Konvertieren Sie Any zu Geo" - -#: flatcamGUI/FlatCAMGUI.py:384 -msgid "Convert Any to Gerber" -msgstr "Konvertieren Sie Any zu Gerber" - -#: flatcamGUI/FlatCAMGUI.py:390 -msgid "&Copy\tCtrl+C" -msgstr "Kopieren\tSTRG+C" - -#: flatcamGUI/FlatCAMGUI.py:395 -msgid "&Delete\tDEL" -msgstr "Löschen\tDEL" - -#: flatcamGUI/FlatCAMGUI.py:400 -msgid "Se&t Origin\tO" -msgstr "Ursprung festlegen\tO" - -#: flatcamGUI/FlatCAMGUI.py:402 -msgid "Move to Origin\tShift+O" -msgstr "Zum Ursprung wechseln\tShift+O" - -#: flatcamGUI/FlatCAMGUI.py:405 -msgid "Jump to Location\tJ" -msgstr "Zum Ort springen\tJ" - -#: flatcamGUI/FlatCAMGUI.py:407 -msgid "Locate in Object\tShift+J" -msgstr "Suchen Sie im Objekt\tShift+J" - -#: flatcamGUI/FlatCAMGUI.py:412 -msgid "Toggle Units\tQ" -msgstr "Einheiten umschalten\tQ" - -#: flatcamGUI/FlatCAMGUI.py:414 -msgid "&Select All\tCtrl+A" -msgstr "Alles auswählen\tSTRG+A" - -#: flatcamGUI/FlatCAMGUI.py:419 -msgid "&Preferences\tShift+P" -msgstr "Einstellungen\tShift+P" - -#: flatcamGUI/FlatCAMGUI.py:425 flatcamTools/ToolProperties.py:155 -msgid "Options" -msgstr "Optionen" - -#: flatcamGUI/FlatCAMGUI.py:427 -msgid "&Rotate Selection\tShift+(R)" -msgstr "Auswahl drehen\tShift+(R)" - -#: flatcamGUI/FlatCAMGUI.py:432 -msgid "&Skew on X axis\tShift+X" -msgstr "Neigung auf der X-Achse\tShift+X" - -#: flatcamGUI/FlatCAMGUI.py:434 -msgid "S&kew on Y axis\tShift+Y" -msgstr "Neigung auf der Y-Achse\tShift+Y" - -#: flatcamGUI/FlatCAMGUI.py:439 -msgid "Flip on &X axis\tX" -msgstr "X-Achse kippen\tX" - -#: flatcamGUI/FlatCAMGUI.py:441 -msgid "Flip on &Y axis\tY" -msgstr "Y-Achse kippen\tY" - -#: flatcamGUI/FlatCAMGUI.py:446 -msgid "View source\tAlt+S" -msgstr "Quelltext anzeigen\tAlt+S" - -#: flatcamGUI/FlatCAMGUI.py:448 -msgid "Tools DataBase\tCtrl+D" -msgstr "Werkzeugdatenbank\tSTRG+D" - -#: flatcamGUI/FlatCAMGUI.py:455 flatcamGUI/FlatCAMGUI.py:2183 -msgid "View" -msgstr "Aussicht" - -#: flatcamGUI/FlatCAMGUI.py:457 -msgid "Enable all plots\tAlt+1" -msgstr "Alle Diagramme aktivieren\tAlt+1" - -#: flatcamGUI/FlatCAMGUI.py:459 -msgid "Disable all plots\tAlt+2" -msgstr "Alle Diagramme deaktivieren\tAlt+2" - -#: flatcamGUI/FlatCAMGUI.py:461 -msgid "Disable non-selected\tAlt+3" -msgstr "Nicht ausgewählte Diagramme deaktivieren\tAlt+3" - -#: flatcamGUI/FlatCAMGUI.py:465 -msgid "&Zoom Fit\tV" -msgstr "Passed zoomen\tV" - -#: flatcamGUI/FlatCAMGUI.py:467 -msgid "&Zoom In\t=" -msgstr "Hineinzoomen\t=" - -#: flatcamGUI/FlatCAMGUI.py:469 -msgid "&Zoom Out\t-" -msgstr "Rauszoomen\t-" - -#: flatcamGUI/FlatCAMGUI.py:474 -msgid "Redraw All\tF5" -msgstr "Alles neu zeichnen\tF5" - -#: flatcamGUI/FlatCAMGUI.py:478 -msgid "Toggle Code Editor\tShift+E" -msgstr "Code-Editor umschalten\tShift+E" - -#: flatcamGUI/FlatCAMGUI.py:481 -msgid "&Toggle FullScreen\tAlt+F10" -msgstr "FullScreen umschalten\tAlt+F10" - -#: flatcamGUI/FlatCAMGUI.py:483 -msgid "&Toggle Plot Area\tCtrl+F10" -msgstr "Plotbereich umschalten\tSTRG+F10" - -#: flatcamGUI/FlatCAMGUI.py:485 -msgid "&Toggle Project/Sel/Tool\t`" -msgstr "Projekt/Auswahl/Werkzeug umschalten\t`" - -#: flatcamGUI/FlatCAMGUI.py:489 -msgid "&Toggle Grid Snap\tG" -msgstr "Schaltet den Rasterfang ein\tG" - -#: flatcamGUI/FlatCAMGUI.py:491 -msgid "&Toggle Grid Lines\tAlt+G" -msgstr "Gitterlinien umschalten\tAlt+G" - -#: flatcamGUI/FlatCAMGUI.py:493 -msgid "&Toggle Axis\tShift+G" -msgstr "Achse umschalten\tShift+G" - -#: flatcamGUI/FlatCAMGUI.py:495 -msgid "Toggle Workspace\tShift+W" -msgstr "Arbeitsbereich umschalten\tShift+W" - -#: flatcamGUI/FlatCAMGUI.py:500 -msgid "Objects" -msgstr "Objekte" - -#: flatcamGUI/FlatCAMGUI.py:514 -msgid "&Command Line\tS" -msgstr "Befehlszeile\tS" - -#: flatcamGUI/FlatCAMGUI.py:519 -msgid "Help" -msgstr "Hilfe" - -#: flatcamGUI/FlatCAMGUI.py:521 -msgid "Online Help\tF1" -msgstr "Onlinehilfe\tF1" - -#: flatcamGUI/FlatCAMGUI.py:531 -msgid "Report a bug" -msgstr "Einen Fehler melden" - -#: flatcamGUI/FlatCAMGUI.py:534 -msgid "Excellon Specification" -msgstr "Excellon-Spezifikation" - -#: flatcamGUI/FlatCAMGUI.py:536 -msgid "Gerber Specification" -msgstr "Gerber-Spezifikation" - -#: flatcamGUI/FlatCAMGUI.py:541 -msgid "Shortcuts List\tF3" -msgstr "Tastenkürzel Liste\tF3" - -#: flatcamGUI/FlatCAMGUI.py:543 -msgid "YouTube Channel\tF4" -msgstr "Youtube Kanal\tF4" - -#: flatcamGUI/FlatCAMGUI.py:554 -msgid "Add Circle\tO" -msgstr "Kreis hinzufügen\tO" - -#: flatcamGUI/FlatCAMGUI.py:557 -msgid "Add Arc\tA" -msgstr "Bogen hinzufügen\tA" - -#: flatcamGUI/FlatCAMGUI.py:560 -msgid "Add Rectangle\tR" -msgstr "Rechteck hinzufügen\tR" - -#: flatcamGUI/FlatCAMGUI.py:563 -msgid "Add Polygon\tN" -msgstr "Polygon hinzufügen\tN" - -#: flatcamGUI/FlatCAMGUI.py:566 -msgid "Add Path\tP" -msgstr "Pfad hinzufügen\tP" - -#: flatcamGUI/FlatCAMGUI.py:569 -msgid "Add Text\tT" -msgstr "Text hinzufügen\tT" - -#: flatcamGUI/FlatCAMGUI.py:572 -msgid "Polygon Union\tU" -msgstr "Polygon-Vereinigung\tU" - -#: flatcamGUI/FlatCAMGUI.py:574 -msgid "Polygon Intersection\tE" -msgstr "Polygonschnitt\tE" - -#: flatcamGUI/FlatCAMGUI.py:576 -msgid "Polygon Subtraction\tS" -msgstr "Polygon-Subtraktion\tS" - -#: flatcamGUI/FlatCAMGUI.py:580 -msgid "Cut Path\tX" -msgstr "Pfad ausschneiden\tX" - -#: flatcamGUI/FlatCAMGUI.py:584 -msgid "Copy Geom\tC" -msgstr "Geometrie kopieren\tC" - -#: flatcamGUI/FlatCAMGUI.py:586 -msgid "Delete Shape\tDEL" -msgstr "Form löschen\tDEL" - -#: flatcamGUI/FlatCAMGUI.py:590 flatcamGUI/FlatCAMGUI.py:677 -msgid "Move\tM" -msgstr "Bewegung\tM" - -#: flatcamGUI/FlatCAMGUI.py:592 -msgid "Buffer Tool\tB" -msgstr "Pufferwerkzeug\tB" - -#: flatcamGUI/FlatCAMGUI.py:595 -msgid "Paint Tool\tI" -msgstr "Malenwerkzeug\tI" - -#: flatcamGUI/FlatCAMGUI.py:598 -msgid "Transform Tool\tAlt+R" -msgstr "Transformationswerkzeug\tAlt+R" - -#: flatcamGUI/FlatCAMGUI.py:602 -msgid "Toggle Corner Snap\tK" -msgstr "Eckfang umschalten\tK" - -#: flatcamGUI/FlatCAMGUI.py:608 -msgid ">Excellon Editor<" -msgstr ">Excellon Editor<" - -#: flatcamGUI/FlatCAMGUI.py:612 -msgid "Add Drill Array\tA" -msgstr "Bohrfeld hinzufügen\tA" - -#: flatcamGUI/FlatCAMGUI.py:614 -msgid "Add Drill\tD" -msgstr "Bohrer hinzufügen\tD" - -#: flatcamGUI/FlatCAMGUI.py:618 -msgid "Add Slot Array\tQ" -msgstr "Steckplatz-Array hinzufügen\tQ" - -#: flatcamGUI/FlatCAMGUI.py:620 -msgid "Add Slot\tW" -msgstr "Slot hinzufügen\tW" - -#: flatcamGUI/FlatCAMGUI.py:624 -msgid "Resize Drill(S)\tR" -msgstr "Bohrer verkleinern\tR" - -#: flatcamGUI/FlatCAMGUI.py:627 flatcamGUI/FlatCAMGUI.py:671 -msgid "Copy\tC" -msgstr "Kopieren\tC" - -#: flatcamGUI/FlatCAMGUI.py:629 flatcamGUI/FlatCAMGUI.py:673 -msgid "Delete\tDEL" -msgstr "Löschen\tDEL" - -#: flatcamGUI/FlatCAMGUI.py:634 -msgid "Move Drill(s)\tM" -msgstr "Bohrer verschieben\tM" - -#: flatcamGUI/FlatCAMGUI.py:639 -msgid ">Gerber Editor<" -msgstr ">Gerber-Editor<" - -#: flatcamGUI/FlatCAMGUI.py:643 -msgid "Add Pad\tP" -msgstr "Pad hinzufügen\tP" - -#: flatcamGUI/FlatCAMGUI.py:645 -msgid "Add Pad Array\tA" -msgstr "Pad-Array hinzufügen\tA" - -#: flatcamGUI/FlatCAMGUI.py:647 -msgid "Add Track\tT" -msgstr "Track hinzufügen\tA" - -#: flatcamGUI/FlatCAMGUI.py:649 -msgid "Add Region\tN" -msgstr "Region hinzufügen\tN" - -#: flatcamGUI/FlatCAMGUI.py:653 -msgid "Poligonize\tAlt+N" -msgstr "Polygonisieren\tAlt+N" - -#: flatcamGUI/FlatCAMGUI.py:655 -msgid "Add SemiDisc\tE" -msgstr "Halbschibe hinzufügen\tE" - -#: flatcamGUI/FlatCAMGUI.py:657 -msgid "Add Disc\tD" -msgstr "Schibe hinzufügen\tD" - -#: flatcamGUI/FlatCAMGUI.py:659 -msgid "Buffer\tB" -msgstr "Puffer\tB" - -#: flatcamGUI/FlatCAMGUI.py:661 -msgid "Scale\tS" -msgstr "Skalieren\tS" - -#: flatcamGUI/FlatCAMGUI.py:663 -msgid "Mark Area\tAlt+A" -msgstr "Bereich markieren\tAlt+A" - -#: flatcamGUI/FlatCAMGUI.py:665 -msgid "Eraser\tCtrl+E" -msgstr "Radiergummi\tSTRG+E" - -#: flatcamGUI/FlatCAMGUI.py:667 -msgid "Transform\tAlt+R" -msgstr "Transformationswerkzeug\tSTRG+R" - -#: flatcamGUI/FlatCAMGUI.py:694 -msgid "Enable Plot" -msgstr "Diagramm aktivieren" - -#: flatcamGUI/FlatCAMGUI.py:696 -msgid "Disable Plot" -msgstr "Diagramm deaktivieren" - -#: flatcamGUI/FlatCAMGUI.py:700 -msgid "Set Color" -msgstr "Farbsatz" - -#: flatcamGUI/FlatCAMGUI.py:742 -msgid "Generate CNC" -msgstr "CNC generieren" - -#: flatcamGUI/FlatCAMGUI.py:744 -msgid "View Source" -msgstr "Quelltext anzeigen" - -#: flatcamGUI/FlatCAMGUI.py:749 flatcamGUI/FlatCAMGUI.py:863 -#: flatcamGUI/FlatCAMGUI.py:1072 flatcamGUI/FlatCAMGUI.py:2138 -#: flatcamGUI/FlatCAMGUI.py:2282 flatcamGUI/FlatCAMGUI.py:2572 -#: flatcamGUI/FlatCAMGUI.py:2775 flatcamGUI/ObjectUI.py:1617 -#: flatcamObjects/FlatCAMGeometry.py:502 flatcamTools/ToolPanelize.py:540 -#: flatcamTools/ToolPanelize.py:567 flatcamTools/ToolPanelize.py:660 -#: flatcamTools/ToolPanelize.py:689 flatcamTools/ToolPanelize.py:751 -msgid "Copy" -msgstr "Kopieren" - -#: flatcamGUI/FlatCAMGUI.py:757 flatcamGUI/FlatCAMGUI.py:2295 -#: flatcamTools/ToolProperties.py:31 -msgid "Properties" -msgstr "Eigenschaften" - -#: flatcamGUI/FlatCAMGUI.py:786 -msgid "File Toolbar" -msgstr "Dateisymbolleiste" - -#: flatcamGUI/FlatCAMGUI.py:790 -msgid "Edit Toolbar" -msgstr "Symbolleiste bearbeiten" - -#: flatcamGUI/FlatCAMGUI.py:794 -msgid "View Toolbar" -msgstr "Symbolleiste anzeigen" - -#: flatcamGUI/FlatCAMGUI.py:798 -msgid "Shell Toolbar" -msgstr "Shell-Symbolleiste" - -#: flatcamGUI/FlatCAMGUI.py:802 -msgid "Tools Toolbar" -msgstr "Werkzeugleiste" - -#: flatcamGUI/FlatCAMGUI.py:806 -msgid "Excellon Editor Toolbar" -msgstr "Excellon Editor-Symbolleiste" - -#: flatcamGUI/FlatCAMGUI.py:812 -msgid "Geometry Editor Toolbar" -msgstr "Geometrie Editor-Symbolleiste" - -#: flatcamGUI/FlatCAMGUI.py:816 -msgid "Gerber Editor Toolbar" -msgstr "Gerber Editor-Symbolleiste" - -#: flatcamGUI/FlatCAMGUI.py:820 -msgid "Grid Toolbar" -msgstr "Raster-Symbolleiste" - -#: flatcamGUI/FlatCAMGUI.py:841 flatcamGUI/FlatCAMGUI.py:2549 -msgid "Open project" -msgstr "Projekt öffnen" - -#: flatcamGUI/FlatCAMGUI.py:843 flatcamGUI/FlatCAMGUI.py:2551 -msgid "Save project" -msgstr "Projekt speichern" - -#: flatcamGUI/FlatCAMGUI.py:849 flatcamGUI/FlatCAMGUI.py:2557 -msgid "New Blank Geometry" -msgstr "Neue Geometrie erstellen" - -#: flatcamGUI/FlatCAMGUI.py:851 flatcamGUI/FlatCAMGUI.py:2559 -msgid "New Blank Gerber" -msgstr "Neues Gerber erstellen" - -#: flatcamGUI/FlatCAMGUI.py:853 flatcamGUI/FlatCAMGUI.py:2561 -msgid "New Blank Excellon" -msgstr "Neuen Excellon erstellen" - -#: flatcamGUI/FlatCAMGUI.py:858 flatcamGUI/FlatCAMGUI.py:2567 -msgid "Save Object and close the Editor" -msgstr "Speichern Sie das Objekt und schließen Sie den Editor" - -#: flatcamGUI/FlatCAMGUI.py:865 flatcamGUI/FlatCAMGUI.py:2574 -msgid "&Delete" -msgstr "&Löschen" - -#: flatcamGUI/FlatCAMGUI.py:868 flatcamGUI/FlatCAMGUI.py:1729 -#: flatcamGUI/FlatCAMGUI.py:1935 flatcamGUI/FlatCAMGUI.py:2577 -#: flatcamTools/ToolDistance.py:35 flatcamTools/ToolDistance.py:195 -msgid "Distance Tool" -msgstr "Entfernungswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:870 flatcamGUI/FlatCAMGUI.py:2579 -msgid "Distance Min Tool" -msgstr "Werkzeug für Mindestabstand" - -#: flatcamGUI/FlatCAMGUI.py:872 flatcamGUI/FlatCAMGUI.py:1722 -#: flatcamGUI/FlatCAMGUI.py:2581 -msgid "Set Origin" -msgstr "Nullpunkt festlegen" - -#: flatcamGUI/FlatCAMGUI.py:874 -msgid "Move to Origin" -msgstr "Zum Ursprung wechseln" - -#: flatcamGUI/FlatCAMGUI.py:877 flatcamGUI/FlatCAMGUI.py:2583 -msgid "Jump to Location" -msgstr "Zur Position springen\tJ" - -#: flatcamGUI/FlatCAMGUI.py:879 flatcamGUI/FlatCAMGUI.py:1734 -#: flatcamGUI/FlatCAMGUI.py:2585 -msgid "Locate in Object" -msgstr "Suchen Sie im Objekt" - -#: flatcamGUI/FlatCAMGUI.py:885 flatcamGUI/FlatCAMGUI.py:2591 -msgid "&Replot" -msgstr "Neuzeichnen &R" - -#: flatcamGUI/FlatCAMGUI.py:887 flatcamGUI/FlatCAMGUI.py:2593 -msgid "&Clear plot" -msgstr "Darstellung löschen &C" - -#: flatcamGUI/FlatCAMGUI.py:889 flatcamGUI/FlatCAMGUI.py:1725 -#: flatcamGUI/FlatCAMGUI.py:2595 -msgid "Zoom In" -msgstr "Hineinzoomen" - -#: flatcamGUI/FlatCAMGUI.py:891 flatcamGUI/FlatCAMGUI.py:1725 -#: flatcamGUI/FlatCAMGUI.py:2597 -msgid "Zoom Out" -msgstr "Rauszoomen" - -#: flatcamGUI/FlatCAMGUI.py:893 flatcamGUI/FlatCAMGUI.py:1724 -#: flatcamGUI/FlatCAMGUI.py:2185 flatcamGUI/FlatCAMGUI.py:2599 -msgid "Zoom Fit" -msgstr "Passend zoomen" - -#: flatcamGUI/FlatCAMGUI.py:901 flatcamGUI/FlatCAMGUI.py:2605 -msgid "&Command Line" -msgstr "Befehlszeile" - -#: flatcamGUI/FlatCAMGUI.py:913 flatcamGUI/FlatCAMGUI.py:2617 -msgid "2Sided Tool" -msgstr "2Seitiges Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:915 flatcamGUI/FlatCAMGUI.py:1740 -#: flatcamGUI/FlatCAMGUI.py:2619 -msgid "Align Objects Tool" -msgstr "Werkzeug \"Objekte ausrichten\"" - -#: flatcamGUI/FlatCAMGUI.py:917 flatcamGUI/FlatCAMGUI.py:1741 -#: flatcamGUI/FlatCAMGUI.py:2621 flatcamTools/ToolExtractDrills.py:393 -msgid "Extract Drills Tool" -msgstr "Bohrer Extrahieren Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:920 flatcamGUI/ObjectUI.py:596 -#: flatcamTools/ToolCutOut.py:440 -msgid "Cutout Tool" -msgstr "Ausschnittwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:922 flatcamGUI/FlatCAMGUI.py:2626 -#: flatcamGUI/ObjectUI.py:574 flatcamGUI/ObjectUI.py:2157 -#: flatcamTools/ToolNCC.py:974 -msgid "NCC Tool" -msgstr "NCC Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:928 flatcamGUI/FlatCAMGUI.py:2632 -msgid "Panel Tool" -msgstr "Platte Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:930 flatcamGUI/FlatCAMGUI.py:2634 -#: flatcamTools/ToolFilm.py:586 -msgid "Film Tool" -msgstr "Filmwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:932 flatcamGUI/FlatCAMGUI.py:2636 -#: flatcamTools/ToolSolderPaste.py:553 -msgid "SolderPaste Tool" -msgstr "Lötpaste-Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:934 flatcamGUI/FlatCAMGUI.py:2638 -#: flatcamTools/ToolSub.py:35 -msgid "Subtract Tool" -msgstr "Subtraktionswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:936 flatcamGUI/FlatCAMGUI.py:2640 -#: flatcamTools/ToolRulesCheck.py:616 -msgid "Rules Tool" -msgstr "Regelwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:938 flatcamGUI/FlatCAMGUI.py:1743 -#: flatcamGUI/FlatCAMGUI.py:2642 flatcamTools/ToolOptimal.py:33 -#: flatcamTools/ToolOptimal.py:307 -msgid "Optimal Tool" -msgstr "Optimierungswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:943 flatcamGUI/FlatCAMGUI.py:1740 -#: flatcamGUI/FlatCAMGUI.py:2647 -msgid "Calculators Tool" -msgstr "Rechnerwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:947 flatcamGUI/FlatCAMGUI.py:1744 -#: flatcamGUI/FlatCAMGUI.py:2651 flatcamTools/ToolQRCode.py:43 -#: flatcamTools/ToolQRCode.py:382 -msgid "QRCode Tool" -msgstr "QRCode Werkzeug" - -# Really don't know -#: flatcamGUI/FlatCAMGUI.py:949 flatcamGUI/FlatCAMGUI.py:2653 -#: flatcamTools/ToolCopperThieving.py:39 flatcamTools/ToolCopperThieving.py:568 -msgid "Copper Thieving Tool" -msgstr "Copper Thieving Werkzeug" - -# Really don't know -#: flatcamGUI/FlatCAMGUI.py:952 flatcamGUI/FlatCAMGUI.py:1741 -#: flatcamGUI/FlatCAMGUI.py:2656 flatcamTools/ToolFiducials.py:33 -#: flatcamTools/ToolFiducials.py:395 -msgid "Fiducials Tool" -msgstr "Passermarken-Tool" - -#: flatcamGUI/FlatCAMGUI.py:954 flatcamGUI/FlatCAMGUI.py:2658 -#: flatcamTools/ToolCalibration.py:37 flatcamTools/ToolCalibration.py:759 -msgid "Calibration Tool" -msgstr "Kalibierungswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:956 flatcamGUI/FlatCAMGUI.py:1741 -#: flatcamGUI/FlatCAMGUI.py:2660 -msgid "Punch Gerber Tool" -msgstr "Stanzen Sie das Gerber-Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:958 flatcamGUI/FlatCAMGUI.py:2662 -#: flatcamTools/ToolInvertGerber.py:31 -msgid "Invert Gerber Tool" -msgstr "Invertieren Sie das Gerber-Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:964 flatcamGUI/FlatCAMGUI.py:990 -#: flatcamGUI/FlatCAMGUI.py:1042 flatcamGUI/FlatCAMGUI.py:2668 -#: flatcamGUI/FlatCAMGUI.py:2746 -msgid "Select" -msgstr "Wählen" - -#: flatcamGUI/FlatCAMGUI.py:966 flatcamGUI/FlatCAMGUI.py:2670 -msgid "Add Drill Hole" -msgstr "Bohrloch hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:968 flatcamGUI/FlatCAMGUI.py:2672 -msgid "Add Drill Hole Array" -msgstr "Bohrlochfeld hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:970 flatcamGUI/FlatCAMGUI.py:2020 -#: flatcamGUI/FlatCAMGUI.py:2273 flatcamGUI/FlatCAMGUI.py:2676 -msgid "Add Slot" -msgstr "Steckplatz hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:972 flatcamGUI/FlatCAMGUI.py:2019 -#: flatcamGUI/FlatCAMGUI.py:2275 flatcamGUI/FlatCAMGUI.py:2678 -msgid "Add Slot Array" -msgstr "Steckplatz-Array hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:974 flatcamGUI/FlatCAMGUI.py:2278 -#: flatcamGUI/FlatCAMGUI.py:2674 -msgid "Resize Drill" -msgstr "Bohrergröße ändern" - -#: flatcamGUI/FlatCAMGUI.py:978 flatcamGUI/FlatCAMGUI.py:2682 -msgid "Copy Drill" -msgstr "Bohrer kopieren" - -#: flatcamGUI/FlatCAMGUI.py:980 flatcamGUI/FlatCAMGUI.py:2684 -msgid "Delete Drill" -msgstr "Bohrer löschen" - -#: flatcamGUI/FlatCAMGUI.py:984 flatcamGUI/FlatCAMGUI.py:2688 -msgid "Move Drill" -msgstr "Bohrer bewegen" - -#: flatcamGUI/FlatCAMGUI.py:992 flatcamGUI/FlatCAMGUI.py:2696 -msgid "Add Circle" -msgstr "Kreis hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:994 flatcamGUI/FlatCAMGUI.py:2698 -msgid "Add Arc" -msgstr "Bogen hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:996 flatcamGUI/FlatCAMGUI.py:2700 -msgid "Add Rectangle" -msgstr "Rechteck hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1000 flatcamGUI/FlatCAMGUI.py:2704 -msgid "Add Path" -msgstr "Pfad hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1002 flatcamGUI/FlatCAMGUI.py:2706 -msgid "Add Polygon" -msgstr "Polygon hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1005 flatcamGUI/FlatCAMGUI.py:2709 -msgid "Add Text" -msgstr "Text hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1007 flatcamGUI/FlatCAMGUI.py:2711 -msgid "Add Buffer" -msgstr "Puffer hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1009 flatcamGUI/FlatCAMGUI.py:2713 -msgid "Paint Shape" -msgstr "Malen Form" - -#: flatcamGUI/FlatCAMGUI.py:1011 flatcamGUI/FlatCAMGUI.py:1068 -#: flatcamGUI/FlatCAMGUI.py:2214 flatcamGUI/FlatCAMGUI.py:2259 -#: flatcamGUI/FlatCAMGUI.py:2715 flatcamGUI/FlatCAMGUI.py:2771 -msgid "Eraser" -msgstr "Radiergummi" - -#: flatcamGUI/FlatCAMGUI.py:1015 flatcamGUI/FlatCAMGUI.py:2719 -msgid "Polygon Union" -msgstr "Polygon-Vereinigung" - -#: flatcamGUI/FlatCAMGUI.py:1017 flatcamGUI/FlatCAMGUI.py:2721 -msgid "Polygon Explode" -msgstr "Polygon explodieren" - -#: flatcamGUI/FlatCAMGUI.py:1020 flatcamGUI/FlatCAMGUI.py:2724 -msgid "Polygon Intersection" -msgstr "Polygonschnitt" - -#: flatcamGUI/FlatCAMGUI.py:1022 flatcamGUI/FlatCAMGUI.py:2726 -msgid "Polygon Subtraction" -msgstr "Polygon-Subtraktion" - -#: flatcamGUI/FlatCAMGUI.py:1026 flatcamGUI/FlatCAMGUI.py:2730 -msgid "Cut Path" -msgstr "Pfad ausschneiden" - -#: flatcamGUI/FlatCAMGUI.py:1028 -msgid "Copy Shape(s)" -msgstr "Form kopieren" - -#: flatcamGUI/FlatCAMGUI.py:1031 -msgid "Delete Shape '-'" -msgstr "Form löschen" - -#: flatcamGUI/FlatCAMGUI.py:1033 flatcamGUI/FlatCAMGUI.py:1076 -#: flatcamGUI/FlatCAMGUI.py:2226 flatcamGUI/FlatCAMGUI.py:2263 -#: flatcamGUI/FlatCAMGUI.py:2736 flatcamGUI/FlatCAMGUI.py:2779 -#: flatcamGUI/ObjectUI.py:109 -msgid "Transformations" -msgstr "Transformationen" - -#: flatcamGUI/FlatCAMGUI.py:1036 -msgid "Move Objects " -msgstr "Objekte verschieben " - -#: flatcamGUI/FlatCAMGUI.py:1044 flatcamGUI/FlatCAMGUI.py:2139 -#: flatcamGUI/FlatCAMGUI.py:2748 -msgid "Add Pad" -msgstr "Pad hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1048 flatcamGUI/FlatCAMGUI.py:2140 -#: flatcamGUI/FlatCAMGUI.py:2752 -msgid "Add Track" -msgstr "Track hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1050 flatcamGUI/FlatCAMGUI.py:2139 -#: flatcamGUI/FlatCAMGUI.py:2754 -msgid "Add Region" -msgstr "Region hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1052 flatcamGUI/FlatCAMGUI.py:2245 -#: flatcamGUI/FlatCAMGUI.py:2756 -msgid "Poligonize" -msgstr "Polygonisieren" - -#: flatcamGUI/FlatCAMGUI.py:1055 flatcamGUI/FlatCAMGUI.py:2247 -#: flatcamGUI/FlatCAMGUI.py:2759 -msgid "SemiDisc" -msgstr "Halbscheibe" - -#: flatcamGUI/FlatCAMGUI.py:1057 flatcamGUI/FlatCAMGUI.py:2249 -#: flatcamGUI/FlatCAMGUI.py:2761 -msgid "Disc" -msgstr "Scheibe" - -#: flatcamGUI/FlatCAMGUI.py:1065 flatcamGUI/FlatCAMGUI.py:2257 -#: flatcamGUI/FlatCAMGUI.py:2769 -msgid "Mark Area" -msgstr "Bereich markieren" - -#: flatcamGUI/FlatCAMGUI.py:1079 flatcamGUI/FlatCAMGUI.py:2139 -#: flatcamGUI/FlatCAMGUI.py:2230 flatcamGUI/FlatCAMGUI.py:2293 -#: flatcamGUI/FlatCAMGUI.py:2782 flatcamTools/ToolMove.py:27 -msgid "Move" -msgstr "Bewegung" - -#: flatcamGUI/FlatCAMGUI.py:1087 flatcamGUI/FlatCAMGUI.py:2791 -msgid "Snap to grid" -msgstr "Am Raster ausrichten" - -#: flatcamGUI/FlatCAMGUI.py:1090 flatcamGUI/FlatCAMGUI.py:2794 -msgid "Grid X snapping distance" -msgstr "Raster X Fangdistanz" - -#: flatcamGUI/FlatCAMGUI.py:1095 flatcamGUI/FlatCAMGUI.py:2799 -msgid "Grid Y snapping distance" -msgstr "Raster Y Fangdistanz" - -#: flatcamGUI/FlatCAMGUI.py:1101 flatcamGUI/FlatCAMGUI.py:2805 -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:1108 flatcamGUI/FlatCAMGUI.py:2812 -msgid "Snap to corner" -msgstr "In der Ecke ausrichten" - -#: flatcamGUI/FlatCAMGUI.py:1112 flatcamGUI/FlatCAMGUI.py:2816 -#: flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py:78 -msgid "Max. magnet distance" -msgstr "Max. Magnetabstand" - -#: flatcamGUI/FlatCAMGUI.py:1149 -msgid "Selected" -msgstr "Ausgewählt" - -#: flatcamGUI/FlatCAMGUI.py:1177 flatcamGUI/FlatCAMGUI.py:1185 -msgid "Plot Area" -msgstr "Grundstücksfläche" - -#: flatcamGUI/FlatCAMGUI.py:1212 -msgid "General" -msgstr "Allgemeines" - -#: flatcamGUI/FlatCAMGUI.py:1227 flatcamTools/ToolCopperThieving.py:74 -#: flatcamTools/ToolDblSided.py:64 flatcamTools/ToolExtractDrills.py:61 -#: flatcamTools/ToolInvertGerber.py:72 flatcamTools/ToolOptimal.py:71 -#: flatcamTools/ToolPunchGerber.py:64 -msgid "GERBER" -msgstr "GERBER" - -#: flatcamGUI/FlatCAMGUI.py:1237 flatcamTools/ToolDblSided.py:92 -msgid "EXCELLON" -msgstr "EXCELLON" - -#: flatcamGUI/FlatCAMGUI.py:1247 flatcamTools/ToolDblSided.py:120 -msgid "GEOMETRY" -msgstr "GEOMETRY" - -#: flatcamGUI/FlatCAMGUI.py:1257 -msgid "CNC-JOB" -msgstr "CNC-Auftrag" - -#: flatcamGUI/FlatCAMGUI.py:1266 flatcamGUI/ObjectUI.py:563 -#: flatcamGUI/ObjectUI.py:2132 -msgid "TOOLS" -msgstr "WERKZEUGE" - -#: flatcamGUI/FlatCAMGUI.py:1275 -msgid "TOOLS 2" -msgstr "WERKZEUGE 2" - -#: flatcamGUI/FlatCAMGUI.py:1285 -msgid "UTILITIES" -msgstr "NUTZEN" - -#: flatcamGUI/FlatCAMGUI.py:1302 -#: flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py:192 -msgid "Restore Defaults" -msgstr "Standard wiederherstellen" - -#: flatcamGUI/FlatCAMGUI.py:1305 -msgid "" -"Restore the entire set of default values\n" -"to the initial values loaded after first launch." -msgstr "" -"Stellen Sie den gesamten Satz von Standardwerten wieder her\n" -"auf die nach dem ersten Start geladenen Anfangswerte." - -#: flatcamGUI/FlatCAMGUI.py:1310 -msgid "Open Pref Folder" -msgstr "Öffnen Sie den Einstellungsordner" - -#: flatcamGUI/FlatCAMGUI.py:1313 -msgid "Open the folder where FlatCAM save the preferences files." -msgstr "" -"Öffnen Sie den Ordner, in dem FlatCAM die Voreinstellungsdateien speichert." - -#: flatcamGUI/FlatCAMGUI.py:1317 flatcamGUI/FlatCAMGUI.py:2517 -msgid "Clear GUI Settings" -msgstr "Löschen Sie die GUI-Einstellungen" - -#: flatcamGUI/FlatCAMGUI.py:1321 -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:1332 -msgid "Apply" -msgstr "Anwenden" - -#: flatcamGUI/FlatCAMGUI.py:1335 -msgid "Apply the current preferences without saving to a file." -msgstr "Anwenden ohne zu speichern." - -#: flatcamGUI/FlatCAMGUI.py:1342 -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:1350 -msgid "Will not save the changes and will close the preferences window." -msgstr "Einstellungen werden geschlossen ohne die Änderungen zu speichern." - -#: flatcamGUI/FlatCAMGUI.py:1719 -msgid "SHOW SHORTCUT LIST" -msgstr "Verknüpfungsliste anzeigen" - -#: flatcamGUI/FlatCAMGUI.py:1719 -msgid "Switch to Project Tab" -msgstr "Wechseln Sie zur Registerkarte Projekt" - -#: flatcamGUI/FlatCAMGUI.py:1719 -msgid "Switch to Selected Tab" -msgstr "Wechseln Sie zur ausgewählten Registerkarte" - -#: flatcamGUI/FlatCAMGUI.py:1720 -msgid "Switch to Tool Tab" -msgstr "Wechseln Sie zur Werkzeugregisterkarte" - -#: flatcamGUI/FlatCAMGUI.py:1721 -msgid "New Gerber" -msgstr "Neuer Gerber" - -#: flatcamGUI/FlatCAMGUI.py:1721 -msgid "Edit Object (if selected)" -msgstr "Objekt bearbeiten (falls ausgewählt)" - -#: flatcamGUI/FlatCAMGUI.py:1721 -msgid "Jump to Coordinates" -msgstr "Springe zu den Koordinaten" - -#: flatcamGUI/FlatCAMGUI.py:1722 -msgid "New Excellon" -msgstr "Neuer Excellon" - -#: flatcamGUI/FlatCAMGUI.py:1722 -msgid "Move Obj" -msgstr "Objekt verschieben" - -#: flatcamGUI/FlatCAMGUI.py:1722 -msgid "New Geometry" -msgstr "Neue Geometrie" - -#: flatcamGUI/FlatCAMGUI.py:1722 -msgid "Change Units" -msgstr "Einheiten ändern" - -#: flatcamGUI/FlatCAMGUI.py:1723 -msgid "Open Properties Tool" -msgstr "Öffnen Sie das Eigenschaften-Tool" - -#: flatcamGUI/FlatCAMGUI.py:1723 -msgid "Rotate by 90 degree CW" -msgstr "Um 90 Grad im Uhrzeigersinn drehen" - -#: flatcamGUI/FlatCAMGUI.py:1723 -msgid "Shell Toggle" -msgstr "Shell umschalten" - -#: flatcamGUI/FlatCAMGUI.py:1724 -msgid "" -"Add a Tool (when in Geometry Selected Tab or in Tools NCC or Tools Paint)" -msgstr "" -"Hinzufügen eines Werkzeugs (auf der Registerkarte \"Geometrie ausgewählt\" " -"oder unter \"Werkzeuge\", \"NCC\" oder \"Werkzeuge\", \"Malen\")" - -#: flatcamGUI/FlatCAMGUI.py:1725 -msgid "Flip on X_axis" -msgstr "Auf X-Achse spiegeln" - -#: flatcamGUI/FlatCAMGUI.py:1725 -msgid "Flip on Y_axis" -msgstr "Auf Y-Achse spiegeln" - -#: flatcamGUI/FlatCAMGUI.py:1728 -msgid "Copy Obj" -msgstr "Objekt kopieren" - -#: flatcamGUI/FlatCAMGUI.py:1728 -msgid "Open Tools Database" -msgstr "Werkzeugdatenbank öffnen" - -#: flatcamGUI/FlatCAMGUI.py:1729 -msgid "Open Excellon File" -msgstr "Öffnen Sie die Excellon-Datei" - -#: flatcamGUI/FlatCAMGUI.py:1729 -msgid "Open Gerber File" -msgstr "Öffnen Sie die Gerber-Datei" - -#: flatcamGUI/FlatCAMGUI.py:1729 -msgid "New Project" -msgstr "Neues Projekt" - -#: flatcamGUI/FlatCAMGUI.py:1730 flatcamTools/ToolPDF.py:42 -msgid "PDF Import Tool" -msgstr "PDF-Importwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1730 -msgid "Save Project" -msgstr "Projekt speichern" - -#: flatcamGUI/FlatCAMGUI.py:1730 -msgid "Toggle Plot Area" -msgstr "Zeichenbereich umschalten0" - -#: flatcamGUI/FlatCAMGUI.py:1733 -msgid "Copy Obj_Name" -msgstr "Kopieren Sie den Namen des Objekts" - -#: flatcamGUI/FlatCAMGUI.py:1734 -msgid "Toggle Code Editor" -msgstr "Code-Editor umschalten" - -#: flatcamGUI/FlatCAMGUI.py:1734 -msgid "Toggle the axis" -msgstr "Achse umschalten" - -#: flatcamGUI/FlatCAMGUI.py:1734 flatcamGUI/FlatCAMGUI.py:1933 -#: flatcamGUI/FlatCAMGUI.py:2020 flatcamGUI/FlatCAMGUI.py:2142 -msgid "Distance Minimum Tool" -msgstr "Mindestabstand Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1735 -msgid "Open Preferences Window" -msgstr "Öffnen Sie das Einstellungsfenster" - -#: flatcamGUI/FlatCAMGUI.py:1736 -msgid "Rotate by 90 degree CCW" -msgstr "Um 90 Grad gegen den Uhrzeigersinn drehen" - -#: flatcamGUI/FlatCAMGUI.py:1736 -msgid "Run a Script" -msgstr "Führen Sie ein Skript aus" - -#: flatcamGUI/FlatCAMGUI.py:1736 -msgid "Toggle the workspace" -msgstr "Arbeitsbereich umschalten" - -#: flatcamGUI/FlatCAMGUI.py:1736 -msgid "Skew on X axis" -msgstr "Neigung auf der X-Achse" - -#: flatcamGUI/FlatCAMGUI.py:1737 -msgid "Skew on Y axis" -msgstr "Neigung auf der Y-Achse" - -#: flatcamGUI/FlatCAMGUI.py:1740 -msgid "2-Sided PCB Tool" -msgstr "2-seitiges PCB Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1740 -msgid "Transformations Tool" -msgstr "Transformations-Tool" - -#: flatcamGUI/FlatCAMGUI.py:1742 -msgid "Solder Paste Dispensing Tool" -msgstr "Lotpasten-Dosierwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1743 -msgid "Film PCB Tool" -msgstr "Film PCB Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1743 -msgid "Non-Copper Clearing Tool" -msgstr "Nicht-Kupfer-Räumwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1744 -msgid "Paint Area Tool" -msgstr "Malbereichswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1744 -msgid "Rules Check Tool" -msgstr "Regelprüfwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1745 -msgid "View File Source" -msgstr "Dateiquelle anzeigen" - -#: flatcamGUI/FlatCAMGUI.py:1746 -msgid "Cutout PCB Tool" -msgstr "Ausschnitt PCB Tool" - -#: flatcamGUI/FlatCAMGUI.py:1746 -msgid "Enable all Plots" -msgstr "Alle Zeichnungen aktivieren" - -#: flatcamGUI/FlatCAMGUI.py:1746 -msgid "Disable all Plots" -msgstr "Alle Zeichnungen deaktivieren" - -#: flatcamGUI/FlatCAMGUI.py:1746 -msgid "Disable Non-selected Plots" -msgstr "Nicht ausgewählte Zeichnungen deaktiv" - -#: flatcamGUI/FlatCAMGUI.py:1747 -msgid "Toggle Full Screen" -msgstr "Vollbild umschalten" - -#: flatcamGUI/FlatCAMGUI.py:1750 -msgid "Abort current task (gracefully)" -msgstr "Aktuelle Aufgabe abbrechen (ordnungsgemäß)" - -#: flatcamGUI/FlatCAMGUI.py:1753 -msgid "Save Project As" -msgstr "Projekt speichern als" - -#: flatcamGUI/FlatCAMGUI.py:1754 -msgid "" -"Paste Special. Will convert a Windows path style to the one required in Tcl " -"Shell" -msgstr "" -"Paste Special. Konvertiert einen Windows-Pfadstil in den in Tcl Shell " -"erforderlichen" - -#: flatcamGUI/FlatCAMGUI.py:1757 -msgid "Open Online Manual" -msgstr "Online-Handbuch öffnen" - -#: flatcamGUI/FlatCAMGUI.py:1758 -msgid "Open Online Tutorials" -msgstr "Öffnen Sie Online-Tutorials" - -#: flatcamGUI/FlatCAMGUI.py:1758 -msgid "Refresh Plots" -msgstr "Zeichnungen aktualisieren" - -#: flatcamGUI/FlatCAMGUI.py:1758 flatcamTools/ToolSolderPaste.py:509 -msgid "Delete Object" -msgstr "Objekt löschen" - -#: flatcamGUI/FlatCAMGUI.py:1758 -msgid "Alternate: Delete Tool" -msgstr "Alternative: Werkzeug löschen" - -#: flatcamGUI/FlatCAMGUI.py:1759 -msgid "(left to Key_1)Toggle Notebook Area (Left Side)" -msgstr "(links neben Taste_1) Notebook-Bereich umschalten (linke Seite)" - -#: flatcamGUI/FlatCAMGUI.py:1759 -msgid "En(Dis)able Obj Plot" -msgstr "Objektzeichnung (de)aktivieren" - -#: flatcamGUI/FlatCAMGUI.py:1760 -msgid "Deselects all objects" -msgstr "Hebt die Auswahl aller Objekte auf" - -#: flatcamGUI/FlatCAMGUI.py:1774 -msgid "Editor Shortcut list" -msgstr "Editor-Verknüpfungsliste" - -#: flatcamGUI/FlatCAMGUI.py:1928 -msgid "GEOMETRY EDITOR" -msgstr "GEOMETRIE-EDITOR" - -#: flatcamGUI/FlatCAMGUI.py:1928 -msgid "Draw an Arc" -msgstr "Zeichnen Sie einen Bogen" - -#: flatcamGUI/FlatCAMGUI.py:1928 -msgid "Copy Geo Item" -msgstr "Geo-Objekt kopieren" - -#: flatcamGUI/FlatCAMGUI.py:1929 -msgid "Within Add Arc will toogle the ARC direction: CW or CCW" -msgstr "" -"Innerhalb von Bogen hinzufügen wird die ARC-Richtung getippt: CW oder CCW" - -#: flatcamGUI/FlatCAMGUI.py:1929 -msgid "Polygon Intersection Tool" -msgstr "Werkzeug Polygonschnitt" - -#: flatcamGUI/FlatCAMGUI.py:1930 -msgid "Geo Paint Tool" -msgstr "Geo-Malwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1930 flatcamGUI/FlatCAMGUI.py:2019 -#: flatcamGUI/FlatCAMGUI.py:2139 -msgid "Jump to Location (x, y)" -msgstr "Zum Standort springen (x, y)" - -#: flatcamGUI/FlatCAMGUI.py:1930 -msgid "Toggle Corner Snap" -msgstr "Eckfang umschalten" - -#: flatcamGUI/FlatCAMGUI.py:1930 -msgid "Move Geo Item" -msgstr "Geo-Objekt verschieben" - -#: flatcamGUI/FlatCAMGUI.py:1931 -msgid "Within Add Arc will cycle through the ARC modes" -msgstr "Innerhalb von Bogen hinzufügen werden die ARC-Modi durchlaufen" - -#: flatcamGUI/FlatCAMGUI.py:1931 -msgid "Draw a Polygon" -msgstr "Zeichnen Sie ein Polygon" - -#: flatcamGUI/FlatCAMGUI.py:1931 -msgid "Draw a Circle" -msgstr "Zeichne einen Kreis" - -#: flatcamGUI/FlatCAMGUI.py:1932 -msgid "Draw a Path" -msgstr "Zeichne einen Pfad" - -#: flatcamGUI/FlatCAMGUI.py:1932 -msgid "Draw Rectangle" -msgstr "Rechteck zeichnen" - -#: flatcamGUI/FlatCAMGUI.py:1932 -msgid "Polygon Subtraction Tool" -msgstr "Polygon-Subtraktionswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1932 -msgid "Add Text Tool" -msgstr "Textwerkzeug hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:1933 -msgid "Polygon Union Tool" -msgstr "Polygonverbindungswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1933 -msgid "Flip shape on X axis" -msgstr "Form auf der X-Achse spiegeln" - -#: flatcamGUI/FlatCAMGUI.py:1933 -msgid "Flip shape on Y axis" -msgstr "Form auf der Y-Achse spiegeln" - -#: flatcamGUI/FlatCAMGUI.py:1934 -msgid "Skew shape on X axis" -msgstr "Neigung auf der X-Achse" - -#: flatcamGUI/FlatCAMGUI.py:1934 -msgid "Skew shape on Y axis" -msgstr "Neigung auf der Y-Achse" - -#: flatcamGUI/FlatCAMGUI.py:1934 -msgid "Editor Transformation Tool" -msgstr "Editor-Transformationstool" - -#: flatcamGUI/FlatCAMGUI.py:1935 -msgid "Offset shape on X axis" -msgstr "Versetzte Form auf der X-Achse" - -#: flatcamGUI/FlatCAMGUI.py:1935 -msgid "Offset shape on Y axis" -msgstr "Versetzte Form auf der Y-Achse" - -#: flatcamGUI/FlatCAMGUI.py:1936 flatcamGUI/FlatCAMGUI.py:2022 -#: flatcamGUI/FlatCAMGUI.py:2144 -msgid "Save Object and Exit Editor" -msgstr "Objekt speichern und Editor beenden" - -#: flatcamGUI/FlatCAMGUI.py:1936 -msgid "Polygon Cut Tool" -msgstr "Polygon-Schneidewerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:1937 -msgid "Rotate Geometry" -msgstr "Geometrie drehen" - -#: flatcamGUI/FlatCAMGUI.py:1937 -msgid "Finish drawing for certain tools" -msgstr "Beenden Sie das Zeichnen für bestimmte Werkzeuge" - -#: flatcamGUI/FlatCAMGUI.py:1937 flatcamGUI/FlatCAMGUI.py:2022 -#: flatcamGUI/FlatCAMGUI.py:2142 -msgid "Abort and return to Select" -msgstr "Abbrechen und zurück zu Auswählen" - -#: flatcamGUI/FlatCAMGUI.py:1938 flatcamGUI/FlatCAMGUI.py:2734 -msgid "Delete Shape" -msgstr "Form löschen" - -#: flatcamGUI/FlatCAMGUI.py:2018 -msgid "EXCELLON EDITOR" -msgstr "EXCELLON EDITOR" - -#: flatcamGUI/FlatCAMGUI.py:2018 -msgid "Copy Drill(s)" -msgstr "Bohrer kopieren" - -#: flatcamGUI/FlatCAMGUI.py:2018 flatcamGUI/FlatCAMGUI.py:2268 -msgid "Add Drill" -msgstr "Bohrer hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:2019 -msgid "Move Drill(s)" -msgstr "Bohrer verschieben" - -#: flatcamGUI/FlatCAMGUI.py:2020 -msgid "Add a new Tool" -msgstr "Fügen Sie ein neues Werkzeug hinzu" - -#: flatcamGUI/FlatCAMGUI.py:2021 -msgid "Delete Drill(s)" -msgstr "Bohrer löschen" - -#: flatcamGUI/FlatCAMGUI.py:2021 -msgid "Alternate: Delete Tool(s)" -msgstr "Alternative: Werkzeug (e) löschen" - -#: flatcamGUI/FlatCAMGUI.py:2138 -msgid "GERBER EDITOR" -msgstr "GERBER EDITOR" - -#: flatcamGUI/FlatCAMGUI.py:2138 -msgid "Add Disc" -msgstr "Fügen Sie eine Scheiben hinzu" - -#: flatcamGUI/FlatCAMGUI.py:2138 -msgid "Add SemiDisc" -msgstr "Halbschibe hinzufügen" - -#: flatcamGUI/FlatCAMGUI.py:2140 -msgid "Within Track & Region Tools will cycle in REVERSE the bend modes" -msgstr "" -"Innerhalb von Track- und Region-Werkzeugen werden die Biegemodi umgekehrt" - -#: flatcamGUI/FlatCAMGUI.py:2141 -msgid "Within Track & Region Tools will cycle FORWARD the bend modes" -msgstr "" -"Innerhalb von Track und Region werden mit Tools die Biegemodi vorwärts " -"durchlaufen" - -#: flatcamGUI/FlatCAMGUI.py:2142 -msgid "Alternate: Delete Apertures" -msgstr "Alternative: Löschen Sie die Blenden" - -#: flatcamGUI/FlatCAMGUI.py:2143 -msgid "Eraser Tool" -msgstr "Radiergummi" - -#: flatcamGUI/FlatCAMGUI.py:2144 -#: flatcamGUI/preferences/gerber/GerberEditorPrefGroupUI.py:220 -msgid "Mark Area Tool" -msgstr "Bereich markieren Werkzeug" - -#: flatcamGUI/FlatCAMGUI.py:2144 -msgid "Poligonize Tool" -msgstr "Werkzeug Polygonisieren" - -#: flatcamGUI/FlatCAMGUI.py:2144 -msgid "Transformation Tool" -msgstr "Transformationswerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:2161 -msgid "Toggle Visibility" -msgstr "Sichtbarkeit umschalten" - -#: flatcamGUI/FlatCAMGUI.py:2167 -msgid "New" -msgstr "Neu" - -#: flatcamGUI/FlatCAMGUI.py:2169 flatcamGUI/ObjectUI.py:450 -#: flatcamObjects/FlatCAMGerber.py:239 flatcamObjects/FlatCAMGerber.py:327 -#: flatcamTools/ToolCalibration.py:631 flatcamTools/ToolCalibration.py:648 -#: flatcamTools/ToolCalibration.py:815 flatcamTools/ToolCopperThieving.py:144 -#: flatcamTools/ToolCopperThieving.py:158 -#: flatcamTools/ToolCopperThieving.py:604 flatcamTools/ToolCutOut.py:92 -#: flatcamTools/ToolDblSided.py:226 flatcamTools/ToolFilm.py:69 -#: flatcamTools/ToolFilm.py:102 flatcamTools/ToolFilm.py:549 -#: flatcamTools/ToolFilm.py:557 flatcamTools/ToolImage.py:49 -#: flatcamTools/ToolImage.py:271 flatcamTools/ToolNCC.py:95 -#: flatcamTools/ToolNCC.py:558 flatcamTools/ToolNCC.py:1300 -#: flatcamTools/ToolPaint.py:502 flatcamTools/ToolPaint.py:706 -#: flatcamTools/ToolPanelize.py:118 flatcamTools/ToolPanelize.py:374 -#: flatcamTools/ToolPanelize.py:391 -msgid "Geometry" -msgstr "Geometrie" - -#: flatcamGUI/FlatCAMGUI.py:2173 -#: flatcamGUI/preferences/tools/Tools2PunchGerberPrefGroupUI.py:99 -#: flatcamTools/ToolAlignObjects.py:74 flatcamTools/ToolAlignObjects.py:110 -#: flatcamTools/ToolCalibration.py:197 flatcamTools/ToolCalibration.py:631 -#: flatcamTools/ToolCalibration.py:648 flatcamTools/ToolCalibration.py:807 -#: flatcamTools/ToolCalibration.py:815 flatcamTools/ToolCopperThieving.py:144 -#: flatcamTools/ToolCopperThieving.py:158 -#: flatcamTools/ToolCopperThieving.py:604 flatcamTools/ToolDblSided.py:225 -#: flatcamTools/ToolFilm.py:359 flatcamTools/ToolNCC.py:558 -#: flatcamTools/ToolNCC.py:1300 flatcamTools/ToolPaint.py:502 -#: flatcamTools/ToolPaint.py:706 flatcamTools/ToolPanelize.py:374 -#: flatcamTools/ToolPunchGerber.py:149 flatcamTools/ToolPunchGerber.py:164 -msgid "Excellon" -msgstr "Excellon" - -#: flatcamGUI/FlatCAMGUI.py:2180 -msgid "Grids" -msgstr "Raster" - -#: flatcamGUI/FlatCAMGUI.py:2187 -msgid "Clear Plot" -msgstr "Plot klar löschen" - -#: flatcamGUI/FlatCAMGUI.py:2189 -msgid "Replot" -msgstr "Replotieren" - -#: flatcamGUI/FlatCAMGUI.py:2193 -msgid "Geo Editor" -msgstr "Geo-Editor" - -#: flatcamGUI/FlatCAMGUI.py:2195 -msgid "Path" -msgstr "Pfad" - -#: flatcamGUI/FlatCAMGUI.py:2197 -msgid "Rectangle" -msgstr "Rechteck" - -#: flatcamGUI/FlatCAMGUI.py:2200 -msgid "Circle" -msgstr "Kreis" - -#: flatcamGUI/FlatCAMGUI.py:2204 -msgid "Arc" -msgstr "Bogen" - -#: flatcamGUI/FlatCAMGUI.py:2218 -msgid "Union" -msgstr "Vereinigung" - -#: flatcamGUI/FlatCAMGUI.py:2220 -msgid "Intersection" -msgstr "Überschneidung" - -#: flatcamGUI/FlatCAMGUI.py:2222 -msgid "Subtraction" -msgstr "Subtraktion" - -#: flatcamGUI/FlatCAMGUI.py:2224 flatcamGUI/ObjectUI.py:2221 -#: flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py:56 -msgid "Cut" -msgstr "Schnitt" - -#: flatcamGUI/FlatCAMGUI.py:2235 -msgid "Pad" -msgstr "Pad" - -#: flatcamGUI/FlatCAMGUI.py:2237 -msgid "Pad Array" -msgstr "Pad-Array" - -#: flatcamGUI/FlatCAMGUI.py:2241 -msgid "Track" -msgstr "Track" - -#: flatcamGUI/FlatCAMGUI.py:2243 -msgid "Region" -msgstr "Region" - -#: flatcamGUI/FlatCAMGUI.py:2266 -msgid "Exc Editor" -msgstr "Exc-Editor" - -#: flatcamGUI/FlatCAMGUI.py:2311 -msgid "" -"Relative measurement.\n" -"Reference is last click position" -msgstr "" -"Relative Messung\n" -"Referenz ist Position des letzten Klicks" - -#: flatcamGUI/FlatCAMGUI.py:2317 -msgid "" -"Absolute measurement.\n" -"Reference is (X=0, Y= 0) position" -msgstr "" -"Absolute Messung.\n" -"Referenz ist (X = 0, Y = 0)" - -#: flatcamGUI/FlatCAMGUI.py:2419 -msgid "Lock Toolbars" -msgstr "Symbolleisten sperren" - -#: flatcamGUI/FlatCAMGUI.py:2505 -msgid "FlatCAM Preferences Folder opened." -msgstr "FlatCAM-Einstellungsordner geöffnet." - -#: flatcamGUI/FlatCAMGUI.py:2516 -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:2624 -msgid "&Cutout Tool" -msgstr "Ausschnittwerkzeug" - -#: flatcamGUI/FlatCAMGUI.py:2694 -msgid "Select 'Esc'" -msgstr "Wählen" - -#: flatcamGUI/FlatCAMGUI.py:2732 -msgid "Copy Objects" -msgstr "Objekte kopieren" - -#: flatcamGUI/FlatCAMGUI.py:2740 -msgid "Move Objects" -msgstr "Objekte verschieben" - -#: flatcamGUI/FlatCAMGUI.py:3363 -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:3370 flatcamGUI/FlatCAMGUI.py:3532 -#: flatcamGUI/FlatCAMGUI.py:3577 flatcamGUI/FlatCAMGUI.py:3597 -msgid "Warning" -msgstr "Warnung" - -#: flatcamGUI/FlatCAMGUI.py:3527 -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:3572 -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:3592 -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:3675 flatcamGUI/FlatCAMGUI.py:3890 -msgid "Cancelled. Nothing selected to delete." -msgstr "Abgebrochen. Nichts zum Löschen ausgewählt." - -#: flatcamGUI/FlatCAMGUI.py:3759 flatcamGUI/FlatCAMGUI.py:4006 -msgid "Cancelled. Nothing selected to copy." -msgstr "Abgebrochen. Nichts zum Kopieren ausgewählt." - -#: flatcamGUI/FlatCAMGUI.py:3805 flatcamGUI/FlatCAMGUI.py:4035 -msgid "Cancelled. Nothing selected to move." -msgstr "Abgebrochen. Nichts ausgewählt, um sich zu bewegen." - -#: flatcamGUI/FlatCAMGUI.py:4061 -msgid "New Tool ..." -msgstr "Neues Werkzeug ..." - -#: flatcamGUI/FlatCAMGUI.py:4062 flatcamTools/ToolNCC.py:924 -#: flatcamTools/ToolPaint.py:850 flatcamTools/ToolSolderPaste.py:560 -msgid "Enter a Tool Diameter" -msgstr "Geben Sie einen Werkzeugdurchmesser ein" - -#: flatcamGUI/FlatCAMGUI.py:4074 -msgid "Adding Tool cancelled ..." -msgstr "Tool wird hinzugefügt abgebrochen ..." - -#: flatcamGUI/FlatCAMGUI.py:4088 -msgid "Distance Tool exit..." -msgstr "Entfernungstool beenden ..." - -#: flatcamGUI/FlatCAMGUI.py:4323 flatcamGUI/FlatCAMGUI.py:4330 -msgid "Idle." -msgstr "Untätig." - -#: flatcamGUI/FlatCAMGUI.py:4361 -msgid "Application started ..." -msgstr "Bewerbung gestartet ..." - -#: flatcamGUI/FlatCAMGUI.py:4362 -msgid "Hello!" -msgstr "Hello!" - -#: flatcamGUI/FlatCAMGUI.py:4424 -msgid "Open Project ..." -msgstr "Offenes Projekt ..." - -#: flatcamGUI/FlatCAMGUI.py:4450 -msgid "Exit" -msgstr "Ausgang" - -#: flatcamGUI/GUIElements.py:2513 -#: flatcamGUI/preferences/tools/ToolsFilmPrefGroupUI.py:180 -#: flatcamTools/ToolDblSided.py:173 flatcamTools/ToolDblSided.py:388 -#: flatcamTools/ToolFilm.py:219 -msgid "Reference" -msgstr "Referenz" - -#: flatcamGUI/GUIElements.py:2515 -msgid "" -"The reference can be:\n" -"- Absolute -> the reference point is point (0,0)\n" -"- Relative -> the reference point is the mouse position before Jump" -msgstr "" -"Die Referenz kann sein:\n" -"- Absolut -> Der Bezugspunkt ist Punkt (0,0)\n" -"- Relativ -> Der Referenzpunkt ist die Mausposition vor dem Sprung" - -#: flatcamGUI/GUIElements.py:2520 -msgid "Abs" -msgstr "Abs" - -#: flatcamGUI/GUIElements.py:2521 -msgid "Relative" -msgstr "Relativ" - -#: flatcamGUI/GUIElements.py:2531 -msgid "Location" -msgstr "Ort" - -#: flatcamGUI/GUIElements.py:2533 -msgid "" -"The Location value is a tuple (x,y).\n" -"If the reference is Absolute then the Jump will be at the position (x,y).\n" -"If the reference is Relative then the Jump will be at the (x,y) distance\n" -"from the current mouse location point." -msgstr "" -"Der Standortwert ist ein Tupel (x, y).\n" -"Wenn die Referenz Absolut ist, befindet sich der Sprung an der Position (x, " -"y).\n" -"Wenn die Referenz relativ ist, befindet sich der Sprung in der Entfernung " -"(x, y)\n" -"vom aktuellen Mausstandort aus." - -#: flatcamGUI/GUIElements.py:2573 -msgid "Save Log" -msgstr "Protokoll speichern" - -#: flatcamGUI/GUIElements.py:2592 flatcamTools/ToolShell.py:278 -msgid "Type >help< to get started" -msgstr "Geben Sie> help