Marius 5 anni fa
parent
commit
5abb7866d8
100 ha cambiato i file con 2550 aggiunte e 12047 eliminazioni
  1. 0 181
      AppGUI/ColumnarFlowLayout.py
  2. 0 327
      AppGUI/preferences/OptionUI.py
  3. 0 77
      AppGUI/preferences/OptionsGroupUI.py
  4. 0 41
      AppGUI/preferences/PreferencesSectionUI.py
  5. 0 302
      AppGUI/preferences/general/GeneralAppSettingsGroupUI.py
  6. 0 100
      AppGUI/preferences/gerber/GerberOptPrefGroupUI.py
  7. 0 81
      AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py
  8. 0 320
      AppGUI/preferences/tools/ToolsISOPrefGroupUI.py
  9. 0 394
      AppObjects/AppObject.py
  10. 0 440
      AppTools/ToolCorners.py
  11. 0 455
      AppTools/ToolEtchCompensation.py
  12. 0 3018
      AppTools/ToolIsolation.py
  13. 0 361
      AppTools/ToolPDF.py
  14. 0 45
      AppTools/__init__.py
  15. 6 229
      CHANGELOG.md
  16. 2 2
      FlatCAM.py
  17. 628 173
      FlatCAMApp.py
  18. 6 6
      FlatCAMBookmark.py
  19. 63 344
      FlatCAMCommon.py
  20. 18 224
      FlatCAMDB.py
  21. 0 0
      FlatCAMPool.py
  22. 4 4
      FlatCAMPostProc.py
  23. 2 2
      FlatCAMProcess.py
  24. 19 22
      FlatCAMTool.py
  25. 0 4
      FlatCAMTranslation.py
  26. 0 0
      FlatCAMWorker.py
  27. 1 1
      FlatCAMWorkerStack.py
  28. 0 195
      Utils/vispy_example.py
  29. BIN
      assets/resources/axis16.png
  30. BIN
      assets/resources/clear_line16.png
  31. BIN
      assets/resources/contribute256.png
  32. BIN
      assets/resources/corners_32.png
  33. BIN
      assets/resources/dark_resources/axis16.png
  34. BIN
      assets/resources/dark_resources/clear_line16.png
  35. BIN
      assets/resources/dark_resources/contribute256.png
  36. BIN
      assets/resources/dark_resources/corners_32.png
  37. BIN
      assets/resources/dark_resources/edit_file16.png
  38. BIN
      assets/resources/dark_resources/edit_file32.png
  39. BIN
      assets/resources/dark_resources/etch_32.png
  40. BIN
      assets/resources/dark_resources/hud16.png
  41. BIN
      assets/resources/dark_resources/hud_32.png
  42. BIN
      assets/resources/dark_resources/iso_16.png
  43. BIN
      assets/resources/dark_resources/panelize32.png
  44. BIN
      assets/resources/dark_resources/settings18.png
  45. BIN
      assets/resources/dark_resources/shell20.png
  46. BIN
      assets/resources/edit_file16.png
  47. BIN
      assets/resources/edit_file32.png
  48. BIN
      assets/resources/etch_32.png
  49. BIN
      assets/resources/grid32.png
  50. BIN
      assets/resources/grid_lines32.png
  51. BIN
      assets/resources/hud16.png
  52. BIN
      assets/resources/hud_32.png
  53. BIN
      assets/resources/iso_16.png
  54. BIN
      assets/resources/panelize32.png
  55. BIN
      assets/resources/settings18.png
  56. BIN
      assets/resources/shell20.png
  57. 90 515
      camlib.py
  58. 20 42
      defaults.py
  59. 48 29
      flatcamEditors/FlatCAMExcEditor.py
  60. 71 67
      flatcamEditors/FlatCAMGeoEditor.py
  61. 125 119
      flatcamEditors/FlatCAMGrbEditor.py
  62. 4 4
      flatcamEditors/FlatCAMTextEditor.py
  63. 0 0
      flatcamEditors/__init__.py
  64. 105 2449
      flatcamGUI/FlatCAMGUI.py
  65. 14 391
      flatcamGUI/GUIElements.py
  66. 351 151
      flatcamGUI/ObjectUI.py
  67. 14 152
      flatcamGUI/PlotCanvas.py
  68. 9 233
      flatcamGUI/PlotCanvasLegacy.py
  69. 0 1
      flatcamGUI/VisPyCanvas.py
  70. 0 0
      flatcamGUI/VisPyData/data/fonts/opensans-regular.ttf
  71. 0 0
      flatcamGUI/VisPyData/data/freetype/freetype253.dll
  72. 0 0
      flatcamGUI/VisPyData/data/freetype/freetype253_x64.dll
  73. 0 0
      flatcamGUI/VisPyPatches.py
  74. 0 0
      flatcamGUI/VisPyTesselators.py
  75. 1 1
      flatcamGUI/VisPyVisuals.py
  76. 0 0
      flatcamGUI/__init__.py
  77. 19 0
      flatcamGUI/preferences/OptionsGroupUI.py
  78. 218 184
      flatcamGUI/preferences/PreferencesUIManager.py
  79. 2 2
      flatcamGUI/preferences/__init__.py
  80. 37 8
      flatcamGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py
  81. 142 23
      flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py
  82. 3 3
      flatcamGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py
  83. 3 3
      flatcamGUI/preferences/cncjob/CNCJobPreferencesUI.py
  84. 0 0
      flatcamGUI/preferences/cncjob/__init__.py
  85. 5 5
      flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py
  86. 3 3
      flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py
  87. 3 3
      flatcamGUI/preferences/excellon/ExcellonExpPrefGroupUI.py
  88. 86 137
      flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py
  89. 6 6
      flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py
  90. 7 7
      flatcamGUI/preferences/excellon/ExcellonPreferencesUI.py
  91. 0 0
      flatcamGUI/preferences/excellon/__init__.py
  92. 50 32
      flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py
  93. 6 5
      flatcamGUI/preferences/general/GeneralAppPrefGroupUI.py
  94. 303 73
      flatcamGUI/preferences/general/GeneralGUIPrefGroupUI.py
  95. 4 4
      flatcamGUI/preferences/general/GeneralPreferencesUI.py
  96. 0 0
      flatcamGUI/preferences/general/__init__.py
  97. 12 20
      flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py
  98. 3 3
      flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py
  99. 32 18
      flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py
  100. 5 6
      flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py

+ 0 - 181
AppGUI/ColumnarFlowLayout.py

@@ -1,181 +0,0 @@
-# ##########################################################
-# FlatCAM: 2D Post-processing for Manufacturing            #
-# File by:  David Robertson (c)                            #
-# Date:     5/2020                                         #
-# License:  MIT Licence                                    #
-# ##########################################################
-
-import sys
-
-from PyQt5.QtCore import QPoint, QRect, QSize, Qt
-from PyQt5.QtWidgets import QLayout, QSizePolicy
-import math
-
-
-class ColumnarFlowLayout(QLayout):
-    def __init__(self, parent=None, margin=0, spacing=-1):
-        super().__init__(parent)
-
-        if parent is not None:
-            self.setContentsMargins(margin, margin, margin, margin)
-
-        self.setSpacing(spacing)
-        self.itemList = []
-
-    def __del__(self):
-        del_item = self.takeAt(0)
-        while del_item:
-            del_item = self.takeAt(0)
-
-    def addItem(self, item):
-        self.itemList.append(item)
-
-    def count(self):
-        return len(self.itemList)
-
-    def itemAt(self, index):
-        if 0 <= index < len(self.itemList):
-            return self.itemList[index]
-        return None
-
-    def takeAt(self, index):
-        if 0 <= index < len(self.itemList):
-            return self.itemList.pop(index)
-        return None
-
-    def expandingDirections(self):
-        return Qt.Orientations(Qt.Orientation(0))
-
-    def hasHeightForWidth(self):
-        return True
-
-    def heightForWidth(self, width):
-        height = self.doLayout(QRect(0, 0, width, 0), True)
-        return height
-
-    def setGeometry(self, rect):
-        super().setGeometry(rect)
-        self.doLayout(rect, False)
-
-    def sizeHint(self):
-        return self.minimumSize()
-
-    def minimumSize(self):
-        size = QSize()
-
-        for item in self.itemList:
-            size = size.expandedTo(item.minimumSize())
-
-        margin, _, _, _ = self.getContentsMargins()
-
-        size += QSize(2 * margin, 2 * margin)
-        return size
-
-    def doLayout(self, rect: QRect, testOnly: bool) -> int:
-        spacing = self.spacing()
-        x = rect.x()
-        y = rect.y()
-
-        # Determine width of widest item
-        widest = 0
-        for item in self.itemList:
-            widest = max(widest, item.sizeHint().width())
-
-        # Determine how many equal-width columns we can get, and how wide each one should be
-        column_count = math.floor(rect.width() / (widest + spacing))
-        column_count = min(column_count, len(self.itemList))
-        column_count = max(1, column_count)
-        column_width = math.floor((rect.width() - (column_count-1)*spacing - 1) / column_count)
-
-        # Get the heights for all of our items
-        item_heights = {}
-        for item in self.itemList:
-            height = item.heightForWidth(column_width) if item.hasHeightForWidth() else item.sizeHint().height()
-            item_heights[item] = height
-
-        # Prepare our column representation
-        column_contents = []
-        column_heights = []
-        for column_index in range(column_count):
-            column_contents.append([])
-            column_heights.append(0)
-
-        def add_to_column(column: int, item):
-            column_contents[column].append(item)
-            column_heights[column] += (item_heights[item] + spacing)
-
-        def shove_one(from_column: int) -> bool:
-            if len(column_contents[from_column]) >= 1:
-                item = column_contents[from_column].pop(0)
-                column_heights[from_column] -= (item_heights[item] + spacing)
-                add_to_column(from_column-1, item)
-                return True
-            return False
-
-        def shove_cascade_consider(from_column: int) -> bool:
-            changed_item = False
-
-            if len(column_contents[from_column]) > 1:
-                item = column_contents[from_column][0]
-                item_height = item_heights[item]
-                if column_heights[from_column-1] + item_height < max(column_heights):
-                    changed_item = shove_one(from_column) or changed_item
-
-            if from_column+1 < column_count:
-                changed_item = shove_cascade_consider(from_column+1) or changed_item
-
-            return changed_item
-
-        def shove_cascade() -> bool:
-            if column_count < 2:
-                return False
-            changed_item = True
-            while changed_item:
-                changed_item = shove_cascade_consider(1)
-            return changed_item
-
-        def pick_best_shoving_position() -> int:
-            best_pos = 1
-            best_height = sys.maxsize
-            for column_idx in range(1, column_count):
-                if len(column_contents[column_idx]) == 0:
-                    continue
-                item = column_contents[column_idx][0]
-                height_after_shove = column_heights[column_idx-1] + item_heights[item]
-                if height_after_shove < best_height:
-                    best_height = height_after_shove
-                    best_pos = column_idx
-            return best_pos
-
-        # Calculate the best layout
-        column_index = 0
-        for item in self.itemList:
-            item_height = item_heights[item]
-            if column_heights[column_index] != 0 and (column_heights[column_index] + item_height) > max(column_heights):
-                column_index += 1
-                if column_index >= column_count:
-                    # Run out of room, need to shove more stuff in each column
-                    if column_count >= 2:
-                        changed = shove_cascade()
-                        if not changed:
-                            shoving_pos = pick_best_shoving_position()
-                            shove_one(shoving_pos)
-                            shove_cascade()
-                    column_index = column_count-1
-
-            add_to_column(column_index, item)
-
-        shove_cascade()
-
-        # Set geometry according to the layout we have calculated
-        if not testOnly:
-            for column_index, items in enumerate(column_contents):
-                x = column_index * (column_width + spacing)
-                y = 0
-                for item in items:
-                    height = item_heights[item]
-                    item.setGeometry(QRect(x, y, column_width, height))
-                    y += (height + spacing)
-
-        # Return the overall height
-        return max(column_heights)

+ 0 - 327
AppGUI/preferences/OptionUI.py

@@ -1,327 +0,0 @@
-from typing import Union, Sequence, List
-
-from PyQt5 import QtWidgets, QtGui
-from PyQt5.QtCore import QSettings
-
-from AppGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner, FCColorEntry, \
-    FCSliderWithSpinner, FCDoubleSpinner, FloatEntry, FCTextArea
-
-import gettext
-import AppTranslation as fcTranslate
-import builtins
-
-fcTranslate.apply_language('strings')
-if '_' not in builtins.__dict__:
-    _ = gettext.gettext
-
-
-class OptionUI:
-
-    def __init__(self, option: str):
-        self.option = option
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        """
-        Adds the necessary widget to the grid, starting at the supplied row.
-        Returns the number of rows used (normally 1)
-        """
-        raise NotImplementedError()
-
-    def get_field(self):
-        raise NotImplementedError()
-
-
-class BasicOptionUI(OptionUI):
-    """Abstract OptionUI that has a label on the left then some other widget on the right"""
-    def __init__(self, option: str, label_text: str, label_tooltip: Union[str, None] = None,
-                 label_bold: bool = False, label_color: Union[str, None] = None):
-        super().__init__(option=option)
-        self.label_text = label_text
-        self.label_tooltip = label_tooltip
-        self.label_bold = label_bold
-        self.label_color = label_color
-        self.label_widget = self.build_label_widget()
-        self.entry_widget = self.build_entry_widget()
-
-    def build_label_widget(self) -> QtWidgets.QLabel:
-        fmt = "%s:"
-        if self.label_bold:
-            fmt = "<b>%s</b>" % fmt
-        if self.label_color:
-            fmt = "<span style=\"color:%s;\">%s</span>" % (self.label_color, fmt)
-        label_widget = QtWidgets.QLabel(fmt % _(self.label_text))
-        if self.label_tooltip is not None:
-            label_widget.setToolTip(_(self.label_tooltip))
-        return label_widget
-
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        raise NotImplementedError()
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        grid.addWidget(self.label_widget, row, 0)
-        grid.addWidget(self.entry_widget, row, 1)
-        return 1
-
-    def get_field(self):
-        return self.entry_widget
-
-
-class LineEntryOptionUI(BasicOptionUI):
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        return FCEntry()
-
-
-# Not sure why this is needed over DoubleSpinnerOptionUI
-class FloatEntryOptionUI(BasicOptionUI):
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        return FloatEntry()
-
-
-class RadioSetOptionUI(BasicOptionUI):
-
-    def __init__(self, option: str, label_text: str, choices: list, orientation='horizontal', **kwargs):
-        self.choices = choices
-        self.orientation = orientation
-        super().__init__(option=option, label_text=label_text, **kwargs)
-
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        return RadioSet(choices=self.choices, orientation=self.orientation)
-
-
-class TextAreaOptionUI(OptionUI):
-
-    def __init__(self, option: str, label_text: str, label_tooltip: str):
-        super().__init__(option=option)
-        self.label_text = label_text
-        self.label_tooltip = label_tooltip
-        self.label_widget = self.build_label_widget()
-        self.textarea_widget = self.build_textarea_widget()
-
-    def build_label_widget(self):
-        label = QtWidgets.QLabel("%s:" % _(self.label_text))
-        label.setToolTip(_(self.label_tooltip))
-        return label
-
-    def build_textarea_widget(self):
-        textarea = FCTextArea()
-        textarea.setPlaceholderText(_(self.label_tooltip))
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-        if qsettings.contains("textbox_font_size"):
-            tb_fsize = qsettings.value('textbox_font_size', type=int)
-        else:
-            tb_fsize = 10
-        font = QtGui.QFont()
-        font.setPointSize(tb_fsize)
-        textarea.setFont(font)
-
-        return textarea
-
-    def get_field(self):
-        return self.textarea_widget
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        grid.addWidget(self.label_widget, row, 0, 1, 3)
-        grid.addWidget(self.textarea_widget, row+1, 0, 1, 3)
-        return 2
-
-
-class CheckboxOptionUI(OptionUI):
-
-    def __init__(self, option: str, label_text: str, label_tooltip: str):
-        super().__init__(option=option)
-        self.label_text = label_text
-        self.label_tooltip = label_tooltip
-        self.checkbox_widget = self.build_checkbox_widget()
-
-    def build_checkbox_widget(self):
-        checkbox = FCCheckBox('%s' % _(self.label_text))
-        checkbox.setToolTip(_(self.label_tooltip))
-        return checkbox
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        grid.addWidget(self.checkbox_widget, row, 0, 1, 3)
-        return 1
-
-    def get_field(self):
-        return self.checkbox_widget
-
-
-class ComboboxOptionUI(BasicOptionUI):
-
-    def __init__(self, option: str, label_text: str, choices: Sequence, **kwargs):
-        self.choices = choices
-        super().__init__(option=option, label_text=label_text, **kwargs)
-
-    def build_entry_widget(self):
-        combo = FCComboBox()
-        for choice in self.choices:
-            # don't translate the QCombo items as they are used in QSettings and identified by name
-            combo.addItem(choice)
-        return combo
-
-
-class ColorOptionUI(BasicOptionUI):
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        entry = FCColorEntry()
-        return entry
-
-
-class SliderWithSpinnerOptionUI(BasicOptionUI):
-    def __init__(self, option: str, label_text: str, min_value=0, max_value=100, step=1, **kwargs):
-        self.min_value = min_value
-        self.max_value = max_value
-        self.step = step
-        super().__init__(option=option, label_text=label_text, **kwargs)
-
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        entry = FCSliderWithSpinner(min=self.min_value, max=self.max_value, step=self.step)
-        return entry
-
-
-class ColorAlphaSliderOptionUI(SliderWithSpinnerOptionUI):
-    def __init__(self, applies_to: List[str], group, label_text: str, **kwargs):
-        self.applies_to = applies_to
-        self.group = group
-        super().__init__(option="__color_alpha_slider", label_text=label_text, min_value=0, max_value=255, step=1,
-                         **kwargs)
-        self.get_field().valueChanged.connect(self._on_alpha_change)
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        for index, field in enumerate(self._get_target_fields()):
-            field.entry.textChanged.connect(lambda value, i=index: self._on_target_change(target_index=i))
-        return super().add_to_grid(grid, row)
-
-    def _get_target_fields(self):
-        return list(map(lambda n: self.group.option_dict()[n].get_field(), self.applies_to))
-
-    def _on_target_change(self, target_index: int):
-        field = self._get_target_fields()[target_index]
-        color = field.get_value()
-        alpha_part = color[7:]
-        if len(alpha_part) != 2:
-            return
-        alpha = int(alpha_part, 16)
-        if alpha < 0 or alpha > 255 or self.get_field().get_value() == alpha:
-            return
-        self.get_field().set_value(alpha)
-
-    def _on_alpha_change(self):
-        alpha = self.get_field().get_value()
-        for field in self._get_target_fields():
-            old_value = field.get_value()
-            new_value = self._modify_color_alpha(old_value, alpha=alpha)
-            field.set_value(new_value)
-
-    @staticmethod
-    def _modify_color_alpha(color: str, alpha: int):
-        color_without_alpha = color[:7]
-        if alpha > 255:
-            return color_without_alpha + "FF"
-        elif alpha < 0:
-            return color_without_alpha + "00"
-        else:
-            hexalpha = hex(alpha)[2:]
-            if len(hexalpha) == 1:
-                hexalpha = "0" + hexalpha
-            return color_without_alpha + hexalpha
-
-
-class SpinnerOptionUI(BasicOptionUI):
-    def __init__(self, option: str, label_text: str, min_value: int, max_value: int, step: int = 1, **kwargs):
-        self.min_value = min_value
-        self.max_value = max_value
-        self.step = step
-        super().__init__(option=option, label_text=label_text, **kwargs)
-
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        entry = FCSpinner()
-        entry.set_range(self.min_value, self.max_value)
-        entry.set_step(self.step)
-        entry.setWrapping(True)
-        return entry
-
-
-class DoubleSpinnerOptionUI(BasicOptionUI):
-    def __init__(self, option: str, label_text: str, step: float, decimals: int, min_value=None, max_value=None,
-                 suffix=None, **kwargs):
-        self.min_value = min_value
-        self.max_value = max_value
-        self.step = step
-        self.suffix = suffix
-        self.decimals = decimals
-        super().__init__(option=option, label_text=label_text, **kwargs)
-
-    def build_entry_widget(self) -> QtWidgets.QWidget:
-        entry = FCDoubleSpinner(suffix=self.suffix)
-        entry.set_precision(self.decimals)
-        entry.setSingleStep(self.step)
-        if self.min_value is None:
-            self.min_value = entry.minimum()
-        else:
-            entry.setMinimum(self.min_value)
-        if self.max_value is None:
-            self.max_value = entry.maximum()
-        else:
-            entry.setMaximum(self.max_value)
-        return entry
-
-
-class HeadingOptionUI(OptionUI):
-    def __init__(self, label_text: str, label_tooltip: Union[str, None] = None):
-        super().__init__(option="__heading")
-        self.label_text = label_text
-        self.label_tooltip = label_tooltip
-
-    def build_heading_widget(self):
-        heading = QtWidgets.QLabel('<b>%s</b>' % _(self.label_text))
-        heading.setToolTip(_(self.label_tooltip))
-        return heading
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        grid.addWidget(self.build_heading_widget(), row, 0, 1, 2)
-        return 1
-
-    def get_field(self):
-        return None
-
-
-class SeparatorOptionUI(OptionUI):
-
-    def __init__(self):
-        super().__init__(option="__separator")
-
-    @staticmethod
-    def build_separator_widget():
-        separator = QtWidgets.QFrame()
-        separator.setFrameShape(QtWidgets.QFrame.HLine)
-        separator.setFrameShadow(QtWidgets.QFrame.Sunken)
-        return separator
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        grid.addWidget(self.build_separator_widget(), row, 0, 1, 2)
-        return 1
-
-    def get_field(self):
-        return None
-
-
-class FullWidthButtonOptionUI(OptionUI):
-    def __init__(self, option: str, label_text: str, label_tooltip: Union[str, None]):
-        super().__init__(option=option)
-        self.label_text = label_text
-        self.label_tooltip = label_tooltip
-        self.button_widget = self.build_button_widget()
-
-    def build_button_widget(self):
-        button = FCButton(_(self.label_text))
-        if self.label_tooltip is not None:
-            button.setToolTip(_(self.label_tooltip))
-        return button
-
-    def add_to_grid(self, grid: QtWidgets.QGridLayout, row: int) -> int:
-        grid.addWidget(self.button_widget, row, 0, 1, 3)
-        return 1
-
-    def get_field(self):
-        return self.button_widget

+ 0 - 77
AppGUI/preferences/OptionsGroupUI.py

@@ -1,77 +0,0 @@
-# ##########################################################
-# FlatCAM: 2D Post-processing for Manufacturing            #
-# File by:  David Robertson (c)                            #
-# Date:     5/2020                                         #
-# License:  MIT Licence                                    #
-# ##########################################################
-
-from typing import Dict
-
-from PyQt5 import QtWidgets
-from PyQt5.QtCore import QSettings
-
-import gettext
-import AppTranslation as fcTranslate
-import builtins
-
-from AppGUI.preferences.OptionUI import OptionUI
-
-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 OptionsGroupUI(QtWidgets.QGroupBox):
-    app = None
-
-    def __init__(self, title, parent=None):
-        # QtGui.QGroupBox.__init__(self, title, parent=parent)
-        super(OptionsGroupUI, self).__init__()
-        self.setStyleSheet("""
-        QGroupBox
-        {
-            font-size: 16px;
-            font-weight: bold;
-        }
-        """)
-
-        self.layout = QtWidgets.QVBoxLayout()
-        self.setLayout(self.layout)
-
-    def option_dict(self) -> Dict[str, OptionUI]:
-        # FIXME!
-        return {}
-
-
-class OptionsGroupUI2(OptionsGroupUI):
-
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
-
-        self.grid = QtWidgets.QGridLayout()
-        self.layout.addLayout(self.grid)
-        self.grid.setColumnStretch(0, 0)
-        self.grid.setColumnStretch(1, 1)
-
-        self.options = self.build_options()
-
-        row = 0
-        for option in self.options:
-            row += option.add_to_grid(grid=self.grid, row=row)
-
-        self.layout.addStretch()
-
-    def build_options(self) -> [OptionUI]:
-        return []
-
-    def option_dict(self) -> Dict[str, OptionUI]:
-        result = {}
-        for optionui in self.options:
-            result[optionui.option] = optionui
-        return result

+ 0 - 41
AppGUI/preferences/PreferencesSectionUI.py

@@ -1,41 +0,0 @@
-from typing import Dict
-from PyQt5 import QtWidgets, QtCore
-
-from AppGUI.ColumnarFlowLayout import ColumnarFlowLayout
-from AppGUI.preferences.OptionUI import OptionUI
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
-
-
-class PreferencesSectionUI(QtWidgets.QWidget):
-
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
-        self.layout = ColumnarFlowLayout()  # QtWidgets.QHBoxLayout()
-        self.setLayout(self.layout)
-
-        self.groups = self.build_groups()
-        for group in self.groups:
-            group.setMinimumWidth(250)
-            self.layout.addWidget(group)
-
-    def build_groups(self) -> [OptionsGroupUI]:
-        return []
-
-    def option_dict(self) -> Dict[str, OptionUI]:
-        result = {}
-        for group in self.groups:
-            groupoptions = group.option_dict()
-            result.update(groupoptions)
-        return result
-
-    def build_tab(self):
-        scroll_area = QtWidgets.QScrollArea()
-        scroll_area.setWidget(self)
-        scroll_area.setWidgetResizable(True)
-        return scroll_area
-
-    def get_tab_id(self) -> str:
-        raise NotImplementedError
-
-    def get_tab_label(self) -> str:
-        raise NotImplementedError

+ 0 - 302
AppGUI/preferences/general/GeneralAppSettingsGroupUI.py

@@ -1,302 +0,0 @@
-
-from PyQt5 import QtCore
-from PyQt5.QtCore import QSettings
-from AppGUI.GUIElements import OptionalInputSection
-from AppGUI.preferences import settings
-from AppGUI.preferences.OptionUI import *
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI2
-
-import gettext
-import AppTranslation as fcTranslate
-import builtins
-fcTranslate.apply_language('strings')
-if '_' not in builtins.__dict__:
-    _ = gettext.gettext
-
-
-class GeneralAppSettingsGroupUI(OptionsGroupUI2):
-    def __init__(self, decimals=4, **kwargs):
-        self.decimals = decimals
-        self.pagesize = {}
-        self.pagesize.update(
-            {
-                'A0': (841, 1189),
-                'A1': (594, 841),
-                'A2': (420, 594),
-                'A3': (297, 420),
-                'A4': (210, 297),
-                'A5': (148, 210),
-                'A6': (105, 148),
-                'A7': (74, 105),
-                'A8': (52, 74),
-                'A9': (37, 52),
-                'A10': (26, 37),
-
-                'B0': (1000, 1414),
-                'B1': (707, 1000),
-                'B2': (500, 707),
-                'B3': (353, 500),
-                'B4': (250, 353),
-                'B5': (176, 250),
-                'B6': (125, 176),
-                'B7': (88, 125),
-                'B8': (62, 88),
-                'B9': (44, 62),
-                'B10': (31, 44),
-
-                'C0': (917, 1297),
-                'C1': (648, 917),
-                'C2': (458, 648),
-                'C3': (324, 458),
-                'C4': (229, 324),
-                'C5': (162, 229),
-                'C6': (114, 162),
-                'C7': (81, 114),
-                'C8': (57, 81),
-                'C9': (40, 57),
-                'C10': (28, 40),
-
-                # American paper sizes
-                'LETTER': (8.5, 11),
-                'LEGAL': (8.5, 14),
-                'ELEVENSEVENTEEN': (11, 17),
-
-                # From https://en.wikipedia.org/wiki/Paper_size
-                'JUNIOR_LEGAL': (5, 8),
-                'HALF_LETTER': (5.5, 8),
-                'GOV_LETTER': (8, 10.5),
-                'GOV_LEGAL': (8.5, 13),
-                'LEDGER': (17, 11),
-            }
-        )
-        super().__init__(**kwargs)
-
-        self.setTitle(str(_("App Settings")))
-
-        qsettings = QSettings("Open Source", "FlatCAM")
-
-        self.notebook_font_size_field = self.option_dict()["notebook_font_size"].get_field()
-        if qsettings.contains("notebook_font_size"):
-            self.notebook_font_size_field.set_value(qsettings.value('notebook_font_size', type=int))
-        else:
-            self.notebook_font_size_field.set_value(12)
-
-        self.axis_font_size_field = self.option_dict()["axis_font_size"].get_field()
-        if qsettings.contains("axis_font_size"):
-            self.axis_font_size_field.set_value(qsettings.value('axis_font_size', type=int))
-        else:
-            self.axis_font_size_field.set_value(8)
-
-        self.textbox_font_size_field = self.option_dict()["textbox_font_size"].get_field()
-        if qsettings.contains("textbox_font_size"):
-            self.textbox_font_size_field.set_value(settings.value('textbox_font_size', type=int))
-        else:
-            self.textbox_font_size_field.set_value(10)
-
-        self.workspace_enabled_field = self.option_dict()["global_workspace"].get_field()
-        self.workspace_type_field = self.option_dict()["global_workspaceT"].get_field()
-        self.workspace_type_label = self.option_dict()["global_workspaceT"].label_widget
-        self.workspace_orientation_field = self.option_dict()["global_workspace_orientation"].get_field()
-        self.workspace_orientation_label = self.option_dict()["global_workspace_orientation"].label_widget
-        self.wks = OptionalInputSection(self.workspace_enabled_field, [self.workspace_type_label, self.workspace_type_field, self.workspace_orientation_label, self.workspace_orientation_field])
-
-        self.mouse_cursor_color_enabled_field = self.option_dict()["global_cursor_color_enabled"].get_field()
-        self.mouse_cursor_color_field = self.option_dict()["global_cursor_color"].get_field()
-        self.mouse_cursor_color_label = self.option_dict()["global_cursor_color"].label_widget
-        self.mois = OptionalInputSection(self.mouse_cursor_color_enabled_field, [self.mouse_cursor_color_label, self.mouse_cursor_color_field])
-        self.mouse_cursor_color_enabled_field.stateChanged.connect(self.on_mouse_cursor_color_enable)
-        self.mouse_cursor_color_field.entry.editingFinished.connect(self.on_mouse_cursor_entry)
-
-    def build_options(self) -> [OptionUI]:
-        return [
-            HeadingOptionUI(label_text="Grid Settings", label_tooltip=None),
-            DoubleSpinnerOptionUI(
-                option="global_gridx",
-                label_text="X value",
-                label_tooltip="This is the Grid snap value on X axis.",
-                step=0.1,
-                decimals=self.decimals
-            ),
-            DoubleSpinnerOptionUI(
-                option="global_gridy",
-                label_text='Y value',
-                label_tooltip="This is the Grid snap value on Y axis.",
-                step=0.1,
-                decimals=self.decimals
-            ),
-            DoubleSpinnerOptionUI(
-                option="global_snap_max",
-                label_text="Snap Max",
-                label_tooltip="Max. magnet distance",
-                step=0.1,
-                decimals=self.decimals
-            ),
-            SeparatorOptionUI(),
-
-            HeadingOptionUI(label_text="Workspace Settings", label_tooltip=None),
-            CheckboxOptionUI(
-                option="global_workspace",
-                label_text="Active",
-                label_tooltip="Draw a delimiting rectangle on canvas.\n"
-                              "The purpose is to illustrate the limits for our work."
-            ),
-            ComboboxOptionUI(
-                option="global_workspaceT",
-                label_text="Size",
-                label_tooltip="Select the type of rectangle to be used on canvas,\nas valid workspace.",
-                choices=list(self.pagesize.keys())
-            ),
-            RadioSetOptionUI(
-                option="global_workspace_orientation",
-                label_text="Orientation",
-                label_tooltip="Can be:\n- Portrait\n- Landscape",
-                choices=[
-                    {'label': _('Portrait'), 'value': 'p'},
-                    {'label': _('Landscape'), 'value': 'l'},
-                ]
-            ),
-            # FIXME enabling OptionalInputSection ??
-            SeparatorOptionUI(),
-
-            HeadingOptionUI(label_text="Font Size", label_tooltip=None),
-            SpinnerOptionUI(
-                option="notebook_font_size",
-                label_text="Notebook",
-                label_tooltip="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.",
-                min_value=8, max_value=40, step=1
-            ),
-            SpinnerOptionUI(
-                option="axis_font_size",
-                label_text="Axis",
-                label_tooltip="This sets the font size for canvas axis.",
-                min_value=8, max_value=40, step=1
-            ),
-            SpinnerOptionUI(
-                option="textbox_font_size",
-                label_text="Textbox",
-                label_tooltip="This sets the font size for the Textbox GUI\n"
-                              "elements that are used in the application.",
-                min_value=8, max_value=40, step=1
-            ),
-            SeparatorOptionUI(),
-
-            HeadingOptionUI(label_text="Mouse Settings", label_tooltip=None),
-            RadioSetOptionUI(
-                option="global_cursor_type",
-                label_text="Cursor Shape",
-                label_tooltip="Choose a mouse cursor shape.\n"
-                              "- Small -> with a customizable size.\n"
-                              "- Big -> Infinite lines",
-                choices=[
-                    {"label": _("Small"), "value": "small"},
-                    {"label": _("Big"), "value": "big"}
-                ]
-            ),
-            SpinnerOptionUI(
-                option="global_cursor_size",
-                label_text="Cursor Size",
-                label_tooltip="Set the size of the mouse cursor, in pixels.",
-                min_value=10, max_value=70, step=1
-            ),
-            SpinnerOptionUI(
-                option="global_cursor_width",
-                label_text="Cursor Width",
-                label_tooltip="Set the line width of the mouse cursor, in pixels.",
-                min_value=1, max_value=10, step=1
-            ),
-            CheckboxOptionUI(
-                option="global_cursor_color_enabled",
-                label_text="Cursor Color",
-                label_tooltip="Check this box to color mouse cursor."
-            ),
-            ColorOptionUI(
-                option="global_cursor_color",
-                label_text="Cursor Color",
-                label_tooltip="Set the color of the mouse cursor."
-            ),
-            # FIXME enabling of cursor color
-            RadioSetOptionUI(
-                option="global_pan_button",
-                label_text="Pan Button",
-                label_tooltip="Select the mouse button to use for panning:\n"
-                              "- MMB --> Middle Mouse Button\n"
-                              "- RMB --> Right Mouse Button",
-                choices=[{'label': _('MMB'), 'value': '3'},
-                         {'label': _('RMB'), 'value': '2'}]
-            ),
-            RadioSetOptionUI(
-                option="global_mselect_key",
-                label_text="Multiple Selection",
-                label_tooltip="Select the key used for multiple selection.",
-                choices=[{'label': _('CTRL'),  'value': 'Control'},
-                         {'label': _('SHIFT'), 'value': 'Shift'}]
-            ),
-            SeparatorOptionUI(),
-
-            CheckboxOptionUI(
-                option="global_delete_confirmation",
-                label_text="Delete object confirmation",
-                label_tooltip="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."
-            ),
-            CheckboxOptionUI(
-                option="global_open_style",
-                label_text='"Open" behavior',
-                label_tooltip="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."
-            ),
-            CheckboxOptionUI(
-                option="global_toggle_tooltips",
-                label_text="Enable ToolTips",
-                label_tooltip="Check this box if you want to have toolTips displayed\n"
-                              "when hovering with mouse over items throughout the App."
-            ),
-            CheckboxOptionUI(
-                option="global_machinist_setting",
-                label_text="Allow Machinist Unsafe Settings",
-                label_tooltip="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"
-                              "<<WARNING>>: Don't change this unless you know what you are doing !!!"
-            ),
-            SpinnerOptionUI(
-                option="global_bookmarks_limit",
-                label_text="Bookmarks limit",
-                label_tooltip="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.",
-                min_value=0, max_value=9999, step=1
-            ),
-            ComboboxOptionUI(
-                option="global_activity_icon",
-                label_text="Activity Icon",
-                label_tooltip="Select the GIF that show activity when FlatCAM is active.",
-                choices=['Ball black', 'Ball green', 'Arrow green', 'Eclipse green']
-            )
-
-        ]
-
-    def on_mouse_cursor_color_enable(self, val):
-        if val:
-            self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
-        else:
-            theme_settings = QtCore.QSettings("Open Source", "FlatCAM")
-            if theme_settings.contains("theme"):
-                theme = theme_settings.value('theme', type=str)
-            else:
-                theme = 'white'
-
-            if theme == 'white':
-                self.app.cursor_color_3D = 'black'
-            else:
-                self.app.cursor_color_3D = 'gray'
-
-    def on_mouse_cursor_entry(self):
-        self.app.defaults['global_cursor_color'] = self.mouse_cursor_color_field.get_value()
-        self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]

+ 0 - 100
AppGUI/preferences/gerber/GerberOptPrefGroupUI.py

@@ -1,100 +0,0 @@
-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("<b>%s:</b>" % _("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('<b>%s:</b>' % _('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()

+ 0 - 81
AppGUI/preferences/tools/ToolsCornersPrefGroupUI.py

@@ -1,81 +0,0 @@
-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('<b>%s:</b>' % _('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()

+ 0 - 320
AppGUI/preferences/tools/ToolsISOPrefGroupUI.py

@@ -1,320 +0,0 @@
-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("<b>%s:</b>" % _("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('<b><font color="green">%s:</font></b>' % _('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"
-              "- 'Area Selection' -> Isolate polygons within a selection area.\n"
-              "- 'Polygon 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()

+ 0 - 394
AppObjects/AppObject.py

@@ -1,394 +0,0 @@
-# ###########################################################
-# 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}: <span style="color:{color};">{name}</span>'.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}: <span style="color:{color};">{name}</span>'.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}: <span style="color:{color};">{name}</span>'.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}: <span style="color:{color};">{name}</span>'.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}: <span style="color:{color};">{name}</span>'.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}: <span style="color:{color};">{name}</span>'.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()

+ 0 - 440
AppTools/ToolCorners.py

@@ -1,440 +0,0 @@
-# ##########################################################
-# 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('<b>%s:</b>' % _("GERBER"))
-        self.object_label.setToolTip(
-            _("The Gerber object 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('<b>%s:</b>' % _('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('<b>%s:</b>' % _('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."))

+ 0 - 455
AppTools/ToolEtchCompensation.py

@@ -1,455 +0,0 @@
-# ##########################################################
-# 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('<b>%s:</b>' % _("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)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 3, 0, 1, 2)
-
-        self.util_label = QtWidgets.QLabel("<b>%s:</b>" % _("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)
-
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 9, 0, 1, 2)
-
-        self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("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", _("Alkaline baths")])
-
-        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

+ 0 - 3018
AppTools/ToolIsolation.py

@@ -1,3018 +0,0 @@
-# ##########################################################
-# 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, OptionalInputSection, 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('<b>%s</b>:' % _("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('<b>%s</b>' % _('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"
-              "Isolation routing 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 routing 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('<b>%s</b>' % _("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(
-            "<b>%s: <font color='#0000FF'>%s %d</font></b>" % (_('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('<b>%s</b>' % _("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 = OptionalInputSection(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"
-              "- 'Area Selection' -> Isolate polygons within a selection area.\n"
-              "- 'Polygon 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('<span style="color:green;"><b>%s</b></span>' % _('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.type_excobj_radio.hide()
-            self.exc_obj_combo.hide()
-
-            self.select_combo.setCurrentIndex(0)
-            self.select_combo.hide()
-            self.select_label.hide()
-        else:
-            self.level.setText('<span style="color:red;"><b>%s</b></span>' % _('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(
-                "<b>%s: <font color='#0000FF'>%s</font></b>" % (_('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(
-                    "<b>%s: <font color='#0000FF'>%s %d</font></b>" % (_('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(
-                    "<b>%s: <font color='#0000FF'>%s</font></b>" % (_('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.app.inform.emit('[WARNING_NOTCL] %s' % _("Click on a polygon to isolate it."))
-            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
-
-        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:
-            prog_plot = self.app.defaults["tools_iso_plotting"]
-
-            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, prog_plot=prog_plot)
-                    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)
-
-            # clean the progressive plotted shapes if it was used
-
-            if prog_plot == 'progressive':
-                self.temp_shapes.clear(update=True)
-
-        # Switch notebook to Selected page
-        self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
-
-    def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, plot=True):
-        """
-        Isolate the provided Gerber object using "rest machining" strategy
-
-        :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
-
-        # decide to use "progressive" or "normal" plotting
-        prog_plot = self.app.defaults["tools_iso_plotting"]
-
-        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, prog_plot=prog_plot,
-                                                                      prog_plot_handler=self.plot_temp_shapes)
-
-                    # ############################################################
-                    # ########## 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
-
-        # clean the progressive plotted shapes if it was used
-        if self.app.defaults["tools_iso_plotting"] == 'progressive':
-            self.temp_shapes.clear(update=True)
-
-        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 not-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
-        prog_plot = self.app.defaults["tools_iso_plotting"]
-
-        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, prog_plot=prog_plot)
-                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
-
-        # clean the progressive plotted shapes if it was used
-        if prog_plot == 'progressive':
-            self.temp_shapes.clear(update=True)
-
-        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("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f&nbsp;" % (curr_pos[0], curr_pos[1]))
-        # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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,
-                          prog_plot=False):
-        """
-        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
-        :param prog_plot:       Type of plotting: "normal" or "progressive"
-        :type prog_plot:        str
-        :return:                The buffered geometry
-        :rtype:                 MultiPolygon or Polygon
-        """
-
-        if follow:
-            geom = self.grb_obj.isolation_geometry(offset, geometry=geometry, follow=follow, prog_plot=prog_plot)
-            return geom
-        else:
-            try:
-                geom = self.grb_obj.isolation_geometry(offset, geometry=geometry, iso_type=env_iso_type,
-                                                       passes=nr_passes, prog_plot=prog_plot)
-            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,
-                               prog_plot="normal", prog_plot_handler=None):
-        """
-        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
-        :param prog_plot:           kind of plotting: "progressive" or "normal"
-        :type prog_plot:            str
-        :param prog_plot_handler:   method used to plot shapes if plot_prog is "proggressive"
-        :type prog_plot_handler:
-        :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:
-                    temp_geo = geo.buffer(iso_offset)
-                    good_pass_iso.append(temp_geo)
-                    if prog_plot == 'progressive':
-                        prog_plot_handler(temp_geo)
-
-            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 = []

+ 0 - 361
AppTools/ToolPDF.py

@@ -1,361 +0,0 @@
-# ##########################################################
-# 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()

+ 0 - 45
AppTools/__init__.py

@@ -1,45 +0,0 @@
-
-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

+ 6 - 229
CHANGELOG.md

@@ -7,229 +7,6 @@ CHANGELOG for FlatCAM beta
 
 
 =================================================
 =================================================
 
 
-2.06.2020
-
-- Tcl Shell - added a button to delete the content of the active line
-- Tcl Command Isolate - fixed to work in the new configuration
-- Tcl Command Follow - fixed to work in the new configuration
-- Etch Compensation Tool - added a new etchant: alkaline baths
-- fixed spacing in the status toolbar icons
-- updated the translation files to the latest changes
-- modified behavior of object comboboxes in Paint, NCC and CutOut Tools: now if an object is selected in Project Tab and is of the supported kind in the Tool, it will be auto-selected
-- fixed some more strings
-- updated the Google-translations for the German, Spanish, French
-- updated the Romanian translation
-- replaced the icon for the Editor in Toolbar (both for the normal icons and for icons in dark theme)
-
-1.06.2020
-
-- made the Distance Tool display the angle in values between 0 and 359.9999 degrees
-- changed some strings
-- fixed the warning that old preferences found even for new installation
-- in Paint Tool fixed the message to select a polygon when using the Selection: Single Polygon being overwritten by the "Grid disabled" message
-- more changes in strings throughout the app
-- made some minor changes in the GUI of the FlatCAM Tools
-- in Tools Database made sure that each new tool added has a unique name
-- in AppTool made some methods to be class methods
-- reverted the class methods in AppTool
-- added a button for Transformations Tool in the lower side (common) of the Object UI
-- some other UI changes
-- after using Isolation Tool it will switch automatically to the Geometry UI
-- in Preferences replaced some widgets with a new one that combine a Slider with a Spinner (from David Robertson)
-- in Preferences replaced the widgets that sets colors with a compound one (from David Robertson)
-- made Progressive plotting work in Isolation Tool
-- fix an issue with progressive plotted shapes not being deleted on the end of the job
-- some fixed due of recent changes and some strings changed
-- added a validator for the FCColorEntry GUI element such that only the valid chars are accepted
-- changed the status bar label to have an icon instead of text
-- added a label in status bar that will toggle the Preferences tab
-- made some changes such that that the label in status bar for toggling the Preferences Tab will be updated in various cases of closing the tab
-- changed colors for the status bar labels and added some of the new icons in the gray version
-- remade visibility as threaded - it seems that I can't really squeeze more performance from this
-
-31.05.2020
-
-- structural changes in Preferences from David Robertson
-- made last filter selected for open file to be used next time when opening files (for Excellon, GCode and Gerber files, for now)
-
-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
 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).
 - 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).
@@ -285,7 +62,7 @@ CHANGELOG for FlatCAM beta
 2.05.2020
 2.05.2020
 
 
 - changed the icons for the grid snap in the status bar
 - changed the icons for the grid snap in the status bar
-- moved some of the methods from FlatCAMApp.App to flatcamGUI.MainGUI class
+- moved some of the methods from FlatCAMApp.App to flatcamGUI.FlatCAMGUI class
 - fixed bug in Gerber Editor in which the units conversion wasn't calculated correct
 - 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 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
 - fixed bug in Gerber Editor that kept all the apertures (including the geometry) of a previously edited object that was not saved after edit
@@ -1553,7 +1330,7 @@ CHANGELOG for FlatCAM beta
 - optimized Rules Check Tool so it runs faster when doing Copper 2 Copper rule
 - optimized Rules Check Tool so it runs faster when doing Copper 2 Copper rule
 - small GUI changes in Optimal Tool and in Film Tool
 - small GUI changes in Optimal Tool and in Film Tool
 - some PEP8 corrections
 - some PEP8 corrections
-- some code annotations to make it easier to navigate in the MainGUI.py
+- some code annotations to make it easier to navigate in the FlatCAMGUI.py
 - fixed exit FullScreen with Escape key
 - 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
 - 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()
 - disabled a log.debug in ObjectColection.get_by_name()
@@ -2992,7 +2769,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
 - 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
 - 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().
 - fixed tools shortcuts not working anymore due of the new toggle parameter for the .run().
-- finished preparing for internationalization for the files: FlatCAMEditor, MainGUI
+- finished preparing for internationalization for the files: FlatCAMEditor, FlatCAMGUI
 - finished preparing for internationalization for the files: FlatCAMObj, ObjectUI
 - finished preparing for internationalization for the files: FlatCAMObj, ObjectUI
 - sorted the languages in the Preferences combobox
 - sorted the languages in the Preferences combobox
 
 
@@ -3314,7 +3091,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 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.
 - fixed FlatCAM crash when trying to make drills GCode out of a file that have only slots.
 - changed the messages for Units Conversion
 - changed the messages for Units Conversion
-- all key shortcuts work across the entire application; moved all the shortcuts definitions in MainGUI.keyPressEvent()
+- all key shortcuts work across the entire application; moved all the shortcuts definitions in FlatCAMGUI.keyPressEvent()
 - renamed the theme to layout because it is really a layout change
 - renamed the theme to layout because it is really a layout change
 - added plot kind for CNC Job in the App Preferences
 - added plot kind for CNC Job in the App Preferences
 - combined the geocutout and cutout_any TCL commands - work in progress
 - combined the geocutout and cutout_any TCL commands - work in progress
@@ -3835,7 +3612,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 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.
 - fixed a G01 usage (should be G1) in Marlin preprocessor.
-- changed the position of the Tool Dia entry in the Object UI and in MainGUI
+- changed the position of the Tool Dia entry in the Object UI and in FlatCAMGUI
 - fixed issues in the installer
 - fixed issues in the installer
 
 
 30.10.2018
 30.10.2018
@@ -4395,7 +4172,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 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
 - 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
 - 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 app_obj.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 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.
 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
 - 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
 - fixed the issue that the "Defaults" button was having no effect when clicked and Options Combo was in Project Options

+ 2 - 2
FlatCAM.py

@@ -3,8 +3,8 @@ import os
 
 
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings, Qt
 from PyQt5.QtCore import QSettings, Qt
-from App_Main import App
-from AppGUI import VisPyPatches
+from FlatCAMApp import App
+from flatcamGUI import VisPyPatches
 
 
 from multiprocessing import freeze_support
 from multiprocessing import freeze_support
 # import copyreg
 # import copyreg

File diff suppressed because it is too large
+ 628 - 173
FlatCAMApp.py


+ 6 - 6
Bookmark.py → FlatCAMBookmark.py

@@ -1,5 +1,5 @@
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5 import QtGui, QtCore, QtWidgets
-from AppGUI.GUIElements import FCTable, FCEntry, FCButton, FCFileSaveDialog
+from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCFileSaveDialog
 
 
 import sys
 import sys
 import webbrowser
 import webbrowser
@@ -7,7 +7,7 @@ import webbrowser
 from copy import deepcopy
 from copy import deepcopy
 from datetime import datetime
 from datetime import datetime
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -287,12 +287,12 @@ class BookmarkManager(QtWidgets.QWidget):
         date = date.replace(' ', '_')
         date = date.replace(' ', '_')
 
 
         filter__ = "Text File (*.TXT);;All Files (*.*)"
         filter__ = "Text File (*.TXT);;All Files (*.*)"
-        filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export Bookmarks"),
-                                                           directory='{l_save}/{n}_{date}'.format(
+        filename, _f = FCFileSaveDialog.get_saved_filename(caption=_("Export FlatCAM Bookmarks"),
+                                                           directory='{l_save}/FlatCAM_{n}_{date}'.format(
                                                                 l_save=str(self.app.get_last_save_folder()),
                                                                 l_save=str(self.app.get_last_save_folder()),
                                                                 n=_("Bookmarks"),
                                                                 n=_("Bookmarks"),
                                                                 date=date),
                                                                 date=date),
-                                                           ext_filter=filter__)
+                                                           filter=filter__)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -334,7 +334,7 @@ class BookmarkManager(QtWidgets.QWidget):
         self.app.log.debug("on_import_bookmarks()")
         self.app.log.debug("on_import_bookmarks()")
 
 
         filter_ = "Text File (*.txt);;All Files (*.*)"
         filter_ = "Text File (*.txt);;All Files (*.*)"
-        filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import Bookmarks"), filter=filter_)
+        filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import FlatCAM Bookmarks"), filter=filter_)
 
 
         filename = str(filename)
         filename = str(filename)
 
 

+ 63 - 344
Common.py → FlatCAMCommon.py

@@ -12,18 +12,15 @@
 # ##########################################################
 # ##########################################################
 from PyQt5 import QtCore
 from PyQt5 import QtCore
 
 
-from shapely.geometry import Polygon, Point, LineString
-from shapely.ops import unary_union
+from shapely.geometry import Polygon, MultiPolygon
 
 
-from AppGUI.VisPyVisuals import ShapeCollection
-from AppTool import AppTool
-
-from copy import deepcopy
+from flatcamGUI.VisPyVisuals import ShapeCollection
+from FlatCAMTool import FlatCAMTool
 
 
 import numpy as np
 import numpy as np
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -32,9 +29,7 @@ if '_' not in builtins.__dict__:
 
 
 
 
 class GracefulException(Exception):
 class GracefulException(Exception):
-    """
-    Graceful Exception raised when the user is requesting to cancel the current threaded task
-    """
+    # Graceful Exception raised when the user is requesting to cancel the current threaded task
     def __init__(self):
     def __init__(self):
         super().__init__()
         super().__init__()
 
 
@@ -109,11 +104,8 @@ def color_variant(hex_color, bright_factor=1):
     Takes a color in HEX format #FF00FF and produces a lighter or darker variant
     Takes a color in HEX format #FF00FF and produces a lighter or darker variant
 
 
     :param hex_color:           color to change
     :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
+    :param bright_factor:   factor to change the color brightness [0 ... 1]
+    :return:                    modified color
     """
     """
 
 
     if len(hex_color) != 7:
     if len(hex_color) != 7:
@@ -138,9 +130,7 @@ def color_variant(hex_color, bright_factor=1):
 
 
 
 
 class ExclusionAreas(QtCore.QObject):
 class ExclusionAreas(QtCore.QObject):
-    """
-    Functionality for adding Exclusion Areas for the Excellon and Geometry FlatCAM Objects
-    """
+
     e_shape_modified = QtCore.pyqtSignal()
     e_shape_modified = QtCore.pyqtSignal()
 
 
     def __init__(self, app):
     def __init__(self, app):
@@ -156,7 +146,7 @@ class ExclusionAreas(QtCore.QObject):
             except AttributeError:
             except AttributeError:
                 self.exclusion_shapes = None
                 self.exclusion_shapes = None
         else:
         else:
-            from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
             self.exclusion_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name="exclusion")
             self.exclusion_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name="exclusion")
 
 
         # Event signals disconnect id holders
         # Event signals disconnect id holders
@@ -177,8 +167,8 @@ class ExclusionAreas(QtCore.QObject):
         {
         {
             "obj_type":   string ("excellon" or "geometry")   <- self.obj_type
             "obj_type":   string ("excellon" or "geometry")   <- self.obj_type
             "shape":      Shapely polygon
             "shape":      Shapely polygon
-            "strategy":   string ("over" or "around")         <- self.strategy_button
-            "overz":      float                               <- self.over_z_button
+            "strategy":   string ("over" or "around")         <- self.strategy
+            "overz":      float                               <- self.over_z
         }
         }
         '''
         '''
         self.exclusion_areas_storage = []
         self.exclusion_areas_storage = []
@@ -188,9 +178,9 @@ class ExclusionAreas(QtCore.QObject):
         self.solid_geometry = []
         self.solid_geometry = []
         self.obj_type = None
         self.obj_type = None
 
 
-        self.shape_type_button = None
-        self.over_z_button = None
-        self.strategy_button = None
+        self.shape_type = 'square'  # TODO use the self.app.defaults when made general (not in Geo object Pref UI)
+        self.over_z = 0.1
+        self.strategy = None
         self.cnc_button = None
         self.cnc_button = None
 
 
     def on_add_area_click(self, shape_button, overz_button, strategy_radio, cnc_button, solid_geo, obj_type):
     def on_add_area_click(self, shape_button, overz_button, strategy_radio, cnc_button, solid_geo, obj_type):
@@ -198,22 +188,21 @@ class ExclusionAreas(QtCore.QObject):
 
 
         :param shape_button:    a FCButton that has the value for the shape
         :param shape_button:    a FCButton that has the value for the shape
         :param overz_button:    a FCDoubleSpinner that holds the Over Z value
         :param overz_button:    a FCDoubleSpinner that holds the Over Z value
-        :param strategy_radio:  a RadioSet button with the strategy_button value
+        :param strategy_radio:  a RadioSet button with the strategy value
         :param cnc_button:      a FCButton in Object UI that when clicked the CNCJob is created
         :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
                                 We have a reference here so we can change the color signifying that exclusion areas are
                                 available.
                                 available.
         :param solid_geo:       reference to the object solid geometry for which we add exclusion areas
         :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
+        :param obj_type:        Type of FlatCAM object that called this method
+        :type obj_type:         String: "excellon" or "geometry"
+        :return:
         """
         """
         self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
         self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
         self.app.call_source = 'geometry'
         self.app.call_source = 'geometry'
 
 
-        self.shape_type_button = shape_button
-
-        self.over_z_button = overz_button
-        self.strategy_button = strategy_radio
+        self.shape_type = shape_button.get_value()
+        self.over_z = overz_button.get_value()
+        self.strategy = strategy_radio.get_value()
         self.cnc_button = cnc_button
         self.cnc_button = cnc_button
 
 
         self.solid_geometry = solid_geo
         self.solid_geometry = solid_geo
@@ -234,14 +223,6 @@ class ExclusionAreas(QtCore.QObject):
 
 
     # To be called after clicking on the plot.
     # To be called after clicking on the plot.
     def on_mouse_release(self, event):
     def on_mouse_release(self, event):
-        """
-        Called on mouse click release.
-
-        :param event:   Mouse event
-        :type event:
-        :return:        None
-        :rtype:
-        """
         if self.app.is_legacy is False:
         if self.app.is_legacy is False:
             event_pos = event.pos
             event_pos = event.pos
             # event_is_dragging = event.is_dragging
             # event_is_dragging = event.is_dragging
@@ -259,11 +240,11 @@ class ExclusionAreas(QtCore.QObject):
 
 
         x1, y1 = curr_pos[0], curr_pos[1]
         x1, y1 = curr_pos[0], curr_pos[1]
 
 
-        # shape_type_button = self.ui.area_shape_radio.get_value()
+        # shape_type = self.ui.area_shape_radio.get_value()
 
 
         # do clear area only for left mouse clicks
         # do clear area only for left mouse clicks
         if event.button == 1:
         if event.button == 1:
-            if self.shape_type_button.get_value() == "square":
+            if self.shape_type == "square":
                 if self.first_click is False:
                 if self.first_click is False:
                     self.first_click = True
                     self.first_click = True
                     self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area."))
                     self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the area."))
@@ -287,14 +268,14 @@ class ExclusionAreas(QtCore.QObject):
                     # {
                     # {
                     #     "obj_type":   string("excellon" or "geometry") < - self.obj_type
                     #     "obj_type":   string("excellon" or "geometry") < - self.obj_type
                     #     "shape":      Shapely polygon
                     #     "shape":      Shapely polygon
-                    #     "strategy_button":   string("over" or "around") < - self.strategy_button
-                    #     "overz":      float < - self.over_z_button
+                    #     "strategy":   string("over" or "around") < - self.strategy
+                    #     "overz":      float < - self.over_z
                     # }
                     # }
                     new_el = {
                     new_el = {
                         "obj_type":     self.obj_type,
                         "obj_type":     self.obj_type,
                         "shape":        new_rectangle,
                         "shape":        new_rectangle,
-                        "strategy":     self.strategy_button.get_value(),
-                        "overz":        self.over_z_button.get_value()
+                        "strategy":     self.strategy,
+                        "overz":        self.over_z
                     }
                     }
                     self.exclusion_areas_storage.append(new_el)
                     self.exclusion_areas_storage.append(new_el)
 
 
@@ -306,7 +287,7 @@ class ExclusionAreas(QtCore.QObject):
                         face_color = "#FF7400BF"
                         face_color = "#FF7400BF"
 
 
                     # add a temporary shape on canvas
                     # add a temporary shape on canvas
-                    AppTool.draw_tool_selection_shape(
+                    FlatCAMTool.draw_tool_selection_shape(
                         self, old_coords=(x0, y0), coords=(x1, y1),
                         self, old_coords=(x0, y0), coords=(x1, y1),
                         color=color,
                         color=color,
                         face_color=face_color,
                         face_color=face_color,
@@ -324,7 +305,7 @@ class ExclusionAreas(QtCore.QObject):
                 return ""
                 return ""
         elif event.button == right_button and self.mouse_is_dragging is False:
         elif event.button == right_button and self.mouse_is_dragging is False:
 
 
-            shape_type = self.shape_type_button.get_value()
+            shape_type = self.shape_type
 
 
             if shape_type == "square":
             if shape_type == "square":
                 self.first_click = False
                 self.first_click = False
@@ -341,23 +322,21 @@ class ExclusionAreas(QtCore.QObject):
 
 
                     # we need to add a Polygon and a Polygon can be made only from at least 3 points
                     # we need to add a Polygon and a Polygon can be made only from at least 3 points
                     if len(self.points) > 2:
                     if len(self.points) > 2:
-                        AppTool.delete_moving_selection_shape(self)
+                        FlatCAMTool.delete_moving_selection_shape(self)
                         pol = Polygon(self.points)
                         pol = Polygon(self.points)
                         # do not add invalid polygons even if they are drawn by utility geometry
                         # do not add invalid polygons even if they are drawn by utility geometry
                         if pol.is_valid:
                         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
-                            }
-                            """
+                            # {
+                            #     "obj_type":   string("excellon" or "geometry") < - self.obj_type
+                            #     "shape":      Shapely polygon
+                            #     "strategy":   string("over" or "around") < - self.strategy
+                            #     "overz":      float < - self.over_z
+                            # }
                             new_el = {
                             new_el = {
                                 "obj_type": self.obj_type,
                                 "obj_type": self.obj_type,
                                 "shape": pol,
                                 "shape": pol,
-                                "strategy": self.strategy_button.get_value(),
-                                "overz": self.over_z_button.get_value()
+                                "strategy": self.strategy,
+                                "overz": self.over_z
                             }
                             }
                             self.exclusion_areas_storage.append(new_el)
                             self.exclusion_areas_storage.append(new_el)
 
 
@@ -368,7 +347,7 @@ class ExclusionAreas(QtCore.QObject):
                                 color = "#098a8f"
                                 color = "#098a8f"
                                 face_color = "#FF7400BF"
                                 face_color = "#FF7400BF"
 
 
-                            AppTool.draw_selection_shape_polygon(
+                            FlatCAMTool.draw_selection_shape_polygon(
                                 self, points=self.points,
                                 self, points=self.points,
                                 color=color,
                                 color=color,
                                 face_color=face_color,
                                 face_color=face_color,
@@ -380,7 +359,7 @@ class ExclusionAreas(QtCore.QObject):
                     self.poly_drawn = False
                     self.poly_drawn = False
                     return
                     return
 
 
-            # AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
+            # FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
 
 
             if self.app.is_legacy is False:
             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_release', self.on_mouse_release)
@@ -403,11 +382,11 @@ class ExclusionAreas(QtCore.QObject):
             if len(self.exclusion_areas_storage) == 0:
             if len(self.exclusion_areas_storage) == 0:
                 return
                 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 ..."))
+            self.app.inform.emit(
+                "[success] %s" % _("Exclusion areas added. Checking overlap with the object geometry ..."))
+
             for el in self.exclusion_areas_storage:
             for el in self.exclusion_areas_storage:
-                if el["shape"].intersects(unary_union(self.solid_geometry)):
+                if el["shape"].intersects(MultiPolygon(self.solid_geometry)):
                     self.on_clear_area_click()
                     self.on_clear_area_click()
                     self.app.inform.emit(
                     self.app.inform.emit(
                         "[ERROR_NOTCL] %s" % _("Failed. Exclusion areas intersects the object geometry ..."))
                         "[ERROR_NOTCL] %s" % _("Failed. Exclusion areas intersects the object geometry ..."))
@@ -427,15 +406,10 @@ class ExclusionAreas(QtCore.QObject):
             )
             )
 
 
             self.e_shape_modified.emit()
             self.e_shape_modified.emit()
+            for k in self.exclusion_areas_storage:
+                print(k)
 
 
     def area_disconnect(self):
     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:
         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_release', self.on_mouse_release)
             self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
             self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
@@ -454,22 +428,15 @@ class ExclusionAreas(QtCore.QObject):
         self.poly_drawn = False
         self.poly_drawn = False
         self.exclusion_areas_storage = []
         self.exclusion_areas_storage = []
 
 
-        AppTool.delete_moving_selection_shape(self)
-        # AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
+        FlatCAMTool.delete_moving_selection_shape(self)
+        # FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
 
 
         self.app.call_source = "app"
         self.app.call_source = "app"
         self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted."))
         self.app.inform.emit("[WARNING_NOTCL] %s" % _("Cancelled. Area exclusion drawing was interrupted."))
 
 
+    # called on mouse move
     def on_mouse_move(self, event):
     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()
+        shape_type = self.shape_type
 
 
         if self.app.is_legacy is False:
         if self.app.is_legacy is False:
             event_pos = event.pos
             event_pos = event.pos
@@ -499,20 +466,15 @@ class ExclusionAreas(QtCore.QObject):
                                          size=self.app.defaults["global_cursor_size"])
                                          size=self.app.defaults["global_cursor_size"])
 
 
         # update the positions on status bar
         # update the positions on status bar
+        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                           "<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
         if self.cursor_pos is None:
         if self.cursor_pos is None:
             self.cursor_pos = (0, 0)
             self.cursor_pos = (0, 0)
 
 
         self.app.dx = curr_pos[0] - float(self.cursor_pos[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.dy = curr_pos[1] - float(self.cursor_pos[1])
-        self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f&nbsp;" % (curr_pos[0], curr_pos[1]))
-        # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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)
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
 
 
         if self.obj_type == 'excellon':
         if self.obj_type == 'excellon':
             color = "#FF7400"
             color = "#FF7400"
@@ -531,20 +493,14 @@ class ExclusionAreas(QtCore.QObject):
                                                      face_color=face_color,
                                                      face_color=face_color,
                                                      coords=(curr_pos[0], curr_pos[1]))
                                                      coords=(curr_pos[0], curr_pos[1]))
         else:
         else:
-            AppTool.delete_moving_selection_shape(self)
-            AppTool.draw_moving_selection_shape_poly(
+            FlatCAMTool.delete_moving_selection_shape(self)
+            FlatCAMTool.draw_moving_selection_shape_poly(
                 self, points=self.points,
                 self, points=self.points,
                 color=color,
                 color=color,
                 face_color=face_color,
                 face_color=face_color,
                 data=(curr_pos[0], curr_pos[1]))
                 data=(curr_pos[0], curr_pos[1]))
 
 
     def on_clear_area_click(self):
     def on_clear_area_click(self):
-        """
-        Slot for clicking the button for Deleting all the Exclusion areas.
-
-        :return:    None
-        :rtype:
-        """
         self.clear_shapes()
         self.clear_shapes()
 
 
         # restore the default StyleSheet
         # restore the default StyleSheet
@@ -559,28 +515,21 @@ class ExclusionAreas(QtCore.QObject):
         self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object."))
         self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object."))
 
 
     def clear_shapes(self):
     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()
         self.exclusion_areas_storage.clear()
-        AppTool.delete_moving_selection_shape(self)
+        FlatCAMTool.delete_moving_selection_shape(self)
         self.app.delete_selection_shape()
         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."))
+        FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
+        self.app.inform.emit('[success] %s' % _("All exclusion zones deleted."))
 
 
     def delete_sel_shapes(self, idxs):
     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
+        :param idxs: list of indexes in self.exclusion_areas_storage list to be deleted
+        :return:
         """
         """
 
 
         # delete all plotted shapes
         # delete all plotted shapes
-        AppTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
+        FlatCAMTool.delete_tool_selection_shape(self, shapes_storage=self.exclusion_shapes)
 
 
         # delete shapes
         # delete shapes
         for idx in sorted(idxs, reverse=True):
         for idx in sorted(idxs, reverse=True):
@@ -618,234 +567,4 @@ class ExclusionAreas(QtCore.QObject):
                                             """)
                                             """)
             self.cnc_button.setToolTip('%s' % _("Generate the CNC Job object."))
             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
+            self.app.inform.emit('[success] %s' % _("All exclusion zones deleted."))

+ 18 - 224
AppDatabase.py → FlatCAMDB.py

@@ -1,5 +1,5 @@
 from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5 import QtGui, QtCore, QtWidgets
-from AppGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
+from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox, FCSpinner, \
     FCTree, RadioSet, FCFileSaveDialog
     FCTree, RadioSet, FCFileSaveDialog
 from camlib import to_dict
 from camlib import to_dict
 
 
@@ -8,10 +8,8 @@ import json
 
 
 from copy import deepcopy
 from copy import deepcopy
 from datetime import datetime
 from datetime import datetime
-import math
-
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -119,7 +117,7 @@ class ToolsDB(QtWidgets.QWidget):
         )
         )
         self.buttons_box.addWidget(import_db_btn)
         self.buttons_box.addWidget(import_db_btn)
 
 
-        self.add_tool_from_db = FCButton(_("Transfer the Tool"))
+        self.add_tool_from_db = FCButton(_("Add Tool from Tools DB"))
         self.add_tool_from_db.setToolTip(
         self.add_tool_from_db.setToolTip(
             _("Add a new tool in the Tools Table of the\n"
             _("Add a new tool in the Tools Table of the\n"
               "active Geometry object after selecting a tool\n"
               "active Geometry object after selecting a tool\n"
@@ -315,7 +313,7 @@ class ToolsDB(QtWidgets.QWidget):
             self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
             self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
             return
             return
 
 
-        self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
+        self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
 
 
         self.build_db_ui()
         self.build_db_ui()
 
 
@@ -657,7 +655,7 @@ class ToolsDB(QtWidgets.QWidget):
                                                                l_save=str(self.app.get_last_save_folder()),
                                                                l_save=str(self.app.get_last_save_folder()),
                                                                n=_("Tools_Database"),
                                                                n=_("Tools_Database"),
                                                                date=date),
                                                                date=date),
-                                                           ext_filter=filter__)
+                                                           filter=filter__)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -726,7 +724,7 @@ class ToolsDB(QtWidgets.QWidget):
                 self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
                 self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
                 return
                 return
 
 
-            self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
+            self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
             self.build_db_ui()
             self.build_db_ui()
             self.callback_on_edited()
             self.callback_on_edited()
 
 
@@ -1032,7 +1030,6 @@ class ToolsDB2(QtWidgets.QWidget):
         self.advanced_box.setTitle(_("Advanced Geo Parameters"))
         self.advanced_box.setTitle(_("Advanced Geo Parameters"))
         self.advanced_box.setFixedWidth(250)
         self.advanced_box.setFixedWidth(250)
 
 
-        # NCC TOOL BOX
         self.ncc_box = QtWidgets.QGroupBox()
         self.ncc_box = QtWidgets.QGroupBox()
         self.ncc_box.setStyleSheet("""
         self.ncc_box.setStyleSheet("""
                         QGroupBox
                         QGroupBox
@@ -1045,7 +1042,6 @@ class ToolsDB2(QtWidgets.QWidget):
         self.ncc_box.setTitle(_("NCC Parameters"))
         self.ncc_box.setTitle(_("NCC Parameters"))
         self.ncc_box.setFixedWidth(250)
         self.ncc_box.setFixedWidth(250)
 
 
-        # PAINT TOOL BOX
         self.paint_box = QtWidgets.QGroupBox()
         self.paint_box = QtWidgets.QGroupBox()
         self.paint_box.setStyleSheet("""
         self.paint_box.setStyleSheet("""
                         QGroupBox
                         QGroupBox
@@ -1058,24 +1054,10 @@ class ToolsDB2(QtWidgets.QWidget):
         self.paint_box.setTitle(_("Paint Parameters"))
         self.paint_box.setTitle(_("Paint Parameters"))
         self.paint_box.setFixedWidth(250)
         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.basic_box.setLayout(self.basic_vlay)
         self.advanced_box.setLayout(self.advanced_vlay)
         self.advanced_box.setLayout(self.advanced_vlay)
         self.ncc_box.setLayout(self.ncc_vlay)
         self.ncc_box.setLayout(self.ncc_vlay)
         self.paint_box.setLayout(self.paint_vlay)
         self.paint_box.setLayout(self.paint_vlay)
-        self.iso_box.setLayout(self.iso_vlay)
 
 
         geo_vlay = QtWidgets.QVBoxLayout()
         geo_vlay = QtWidgets.QVBoxLayout()
         geo_vlay.addWidget(self.basic_box)
         geo_vlay.addWidget(self.basic_box)
@@ -1085,7 +1067,6 @@ class ToolsDB2(QtWidgets.QWidget):
         tools_vlay = QtWidgets.QVBoxLayout()
         tools_vlay = QtWidgets.QVBoxLayout()
         tools_vlay.addWidget(self.ncc_box)
         tools_vlay.addWidget(self.ncc_box)
         tools_vlay.addWidget(self.paint_box)
         tools_vlay.addWidget(self.paint_box)
-        tools_vlay.addWidget(self.iso_box)
         tools_vlay.addStretch()
         tools_vlay.addStretch()
 
 
         param_hlay.addLayout(geo_vlay)
         param_hlay.addLayout(geo_vlay)
@@ -1497,7 +1478,7 @@ class ToolsDB2(QtWidgets.QWidget):
 
 
         self.ncc_method_combo = FCComboBox()
         self.ncc_method_combo = FCComboBox()
         self.ncc_method_combo.addItems(
         self.ncc_method_combo.addItems(
-            [_("Standard"), _("Seed"), _("Lines"), _("Combo")]
+            [_("Standard"), _("Seed"), _("Lines")]
         )
         )
         self.ncc_method_combo.setObjectName("gdb_n_method")
         self.ncc_method_combo.setObjectName("gdb_n_method")
 
 
@@ -1640,101 +1621,6 @@ class ToolsDB2(QtWidgets.QWidget):
         self.grid3.addWidget(self.pathconnect_cb, 10, 0)
         self.grid3.addWidget(self.pathconnect_cb, 10, 0)
         self.grid3.addWidget(self.paintcontour_cb, 10, 1)
         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
         # GUI for the lower part of the window
@@ -1792,19 +1678,12 @@ class ToolsDB2(QtWidgets.QWidget):
         )
         )
         self.buttons_box.addWidget(self.save_db_btn)
         self.buttons_box.addWidget(self.save_db_btn)
 
 
-        self.add_tool_from_db = FCButton(_("Transfer the Tool"))
+        self.add_tool_from_db = FCButton(_("Add Tool from Tools DB"))
         self.add_tool_from_db.setToolTip(
         self.add_tool_from_db.setToolTip(
-            _("Insert a new tool in the Tools Table of the\n"
-              "object/application tool after selecting a tool\n"
+            _("Add a new tool in the Tools Table of the\n"
+              "active Geometry object after selecting a tool\n"
               "in the Tools Database.")
               "in the Tools Database.")
         )
         )
-        self.add_tool_from_db.setStyleSheet("""
-                                            QPushButton
-                                            {
-                                                font-weight: bold;
-                                                color: green;
-                                            }
-                                            """)
         self.add_tool_from_db.hide()
         self.add_tool_from_db.hide()
 
 
         self.cancel_tool_from_db = FCButton(_("Cancel"))
         self.cancel_tool_from_db = FCButton(_("Cancel"))
@@ -1814,7 +1693,7 @@ class ToolsDB2(QtWidgets.QWidget):
         tree_layout.addLayout(hlay)
         tree_layout.addLayout(hlay)
         hlay.addWidget(self.add_tool_from_db)
         hlay.addWidget(self.add_tool_from_db)
         hlay.addWidget(self.cancel_tool_from_db)
         hlay.addWidget(self.cancel_tool_from_db)
-        # hlay.addStretch()
+        hlay.addStretch()
 
 
         # ##############################################################################
         # ##############################################################################
         # ##############################################################################
         # ##############################################################################
@@ -1864,13 +1743,6 @@ class ToolsDB2(QtWidgets.QWidget):
             "tools_paintmethod":        self.paintmethod_combo,
             "tools_paintmethod":        self.paintmethod_combo,
             "tools_pathconnect":        self.pathconnect_cb,
             "tools_pathconnect":        self.pathconnect_cb,
             "tools_paintcontour":       self.paintcontour_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 = {
         self.name2option = {
@@ -1915,13 +1787,6 @@ class ToolsDB2(QtWidgets.QWidget):
             'gdb_p_method':         "tools_paintmethod",
             'gdb_p_method':         "tools_paintmethod",
             'gdb_p_connect':        "tools_pathconnect",
             'gdb_p_connect':        "tools_pathconnect",
             'gdb_p_contour':        "tools_paintcontour",
             '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
         self.current_toolid = None
@@ -2022,7 +1887,7 @@ class ToolsDB2(QtWidgets.QWidget):
         self.blockSignals(False)
         self.blockSignals(False)
 
 
     def setup_db_ui(self):
     def setup_db_ui(self):
-        filename = self.app.data_path + '\geo_tools_db.FlatDB'
+        filename = self.app.data_path + '/geo_tools_db.FlatDB'
 
 
         # load the database tools from the file
         # load the database tools from the file
         try:
         try:
@@ -2041,7 +1906,7 @@ class ToolsDB2(QtWidgets.QWidget):
             self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
             self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
             return
             return
 
 
-        self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
+        self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
 
 
         self.build_db_ui()
         self.build_db_ui()
 
 
@@ -2074,23 +1939,21 @@ class ToolsDB2(QtWidgets.QWidget):
             if self.db_tool_dict:
             if self.db_tool_dict:
                 self.storage_to_form(self.db_tool_dict['1'])
                 self.storage_to_form(self.db_tool_dict['1'])
 
 
-                # Enable AppGUI
+                # Enable GUI
                 self.basic_box.setEnabled(True)
                 self.basic_box.setEnabled(True)
                 self.advanced_box.setEnabled(True)
                 self.advanced_box.setEnabled(True)
                 self.ncc_box.setEnabled(True)
                 self.ncc_box.setEnabled(True)
                 self.paint_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.setCurrentItem(self.tree_widget.topLevelItem(0))
                 # self.tree_widget.setFocus()
                 # self.tree_widget.setFocus()
 
 
             else:
             else:
-                # Disable AppGUI
+                # Disable GUI
                 self.basic_box.setEnabled(False)
                 self.basic_box.setEnabled(False)
                 self.advanced_box.setEnabled(False)
                 self.advanced_box.setEnabled(False)
                 self.ncc_box.setEnabled(False)
                 self.ncc_box.setEnabled(False)
                 self.paint_box.setEnabled(False)
                 self.paint_box.setEnabled(False)
-                self.iso_box.setEnabled(False)
         else:
         else:
             self.storage_to_form(self.db_tool_dict[str(self.current_toolid)])
             self.storage_to_form(self.db_tool_dict[str(self.current_toolid)])
 
 
@@ -2143,27 +2006,10 @@ class ToolsDB2(QtWidgets.QWidget):
             "tools_paintmethod":        self.app.defaults["tools_paintmethod"],
             "tools_paintmethod":        self.app.defaults["tools_paintmethod"],
             "tools_pathconnect":        self.app.defaults["tools_pathconnect"],
             "tools_pathconnect":        self.app.defaults["tools_pathconnect"],
             "tools_paintcontour":       self.app.defaults["tools_paintcontour"],
             "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"],
         })
         })
 
 
-        temp = []
-        for k, v in self.db_tool_dict.items():
-            if "new_tool_" in v['name']:
-                temp.append(float(v['name'].rpartition('_')[2]))
-
-        if temp:
-            new_name = "new_tool_%d" % int(max(temp) + 1)
-        else:
-            new_name = "new_tool_1"
-
         dict_elem = {}
         dict_elem = {}
-        dict_elem['name'] = new_name
+        dict_elem['name'] = 'new_tool'
         if type(self.app.defaults["geometry_cnctooldia"]) == float:
         if type(self.app.defaults["geometry_cnctooldia"]) == float:
             dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
             dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
         else:
         else:
@@ -2271,7 +2117,7 @@ class ToolsDB2(QtWidgets.QWidget):
                                                                 l_save=str(self.app.get_last_save_folder()),
                                                                 l_save=str(self.app.get_last_save_folder()),
                                                                 n=_("Tools_Database"),
                                                                 n=_("Tools_Database"),
                                                                 date=date),
                                                                 date=date),
-                                                           ext_filter=filter__)
+                                                           filter=filter__)
 
 
         filename = str(filename)
         filename = str(filename)
 
 
@@ -2340,7 +2186,7 @@ class ToolsDB2(QtWidgets.QWidget):
                 self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
                 self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
                 return
                 return
 
 
-            self.app.inform.emit('[success] %s: %s' % (_("Loaded Tools DB from"), filename))
+            self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
             self.build_db_ui()
             self.build_db_ui()
             self.update_storage()
             self.update_storage()
 
 
@@ -2372,18 +2218,6 @@ class ToolsDB2(QtWidgets.QWidget):
         self.app.tools_db_changed_flag = False
         self.app.tools_db_changed_flag = False
         self.on_save_tools_db()
         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):
     def ui_connect(self):
         # make sure that we don't make multiple connections to the widgets
         # make sure that we don't make multiple connections to the widgets
         self.ui_disconnect()
         self.ui_disconnect()
@@ -2413,40 +2247,12 @@ class ToolsDB2(QtWidgets.QWidget):
             if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner):
             if isinstance(wdg, FCSpinner) or isinstance(wdg, FCDoubleSpinner):
                 wdg.valueChanged.connect(self.update_storage)
                 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):
     def ui_disconnect(self):
         try:
         try:
             self.name_entry.editingFinished.disconnect(self.update_tree_name)
             self.name_entry.editingFinished.disconnect(self.update_tree_name)
         except (TypeError, AttributeError):
         except (TypeError, AttributeError):
             pass
             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:
         for key in self.form_fields:
             wdg = self.form_fields[key]
             wdg = self.form_fields[key]
 
 
@@ -2592,18 +2398,6 @@ class ToolsDB2(QtWidgets.QWidget):
             elif wdg_name == "gdb_p_contour":
             elif wdg_name == "gdb_p_contour":
                 self.db_tool_dict[tool_id]['data']['tools_paintcontour'] = val
                 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()
         self.callback_app()
 
 
     def on_tool_requested_from_app(self):
     def on_tool_requested_from_app(self):

+ 0 - 0
AppPool.py → FlatCAMPool.py


+ 4 - 4
AppPreProcessor.py → FlatCAMPostProc.py

@@ -19,10 +19,10 @@ log = logging.getLogger('base')
 preprocessors = {}
 preprocessors = {}
 
 
 
 
-class ABCPreProcRegister(ABCMeta):
+class ABCPostProcRegister(ABCMeta):
     # handles preprocessors registration on instantiation
     # handles preprocessors registration on instantiation
     def __new__(cls, clsname, bases, attrs):
     def __new__(cls, clsname, bases, attrs):
-        newclass = super(ABCPreProcRegister, cls).__new__(cls, clsname, bases, attrs)
+        newclass = super(ABCPostProcRegister, cls).__new__(cls, clsname, bases, attrs)
         if object not in bases:
         if object not in bases:
             if newclass.__name__ in preprocessors:
             if newclass.__name__ in preprocessors:
                 log.warning('Preprocessor %s has been overriden' % newclass.__name__)
                 log.warning('Preprocessor %s has been overriden' % newclass.__name__)
@@ -30,7 +30,7 @@ class ABCPreProcRegister(ABCMeta):
         return newclass
         return newclass
 
 
 
 
-class PreProc(object, metaclass=ABCPreProcRegister):
+class FlatCAMPostProc(object, metaclass=ABCPostProcRegister):
     @abstractmethod
     @abstractmethod
     def start_code(self, p):
     def start_code(self, p):
         pass
         pass
@@ -76,7 +76,7 @@ class PreProc(object, metaclass=ABCPreProcRegister):
         pass
         pass
 
 
 
 
-class AppPreProcTools(object, metaclass=ABCPreProcRegister):
+class FlatCAMPostProc_Tools(object, metaclass=ABCPostProcRegister):
     @abstractmethod
     @abstractmethod
     def start_code(self, p):
     def start_code(self, p):
         pass
         pass

+ 2 - 2
AppProcess.py → FlatCAMProcess.py

@@ -6,12 +6,12 @@
 # MIT Licence                                              #
 # MIT Licence                                              #
 # ##########################################################
 # ##########################################################
 
 
-from AppGUI.GUIElements import FlatCAMActivityView
+from flatcamGUI.FlatCAMGUI import FlatCAMActivityView
 from PyQt5 import QtCore
 from PyQt5 import QtCore
 import weakref
 import weakref
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')

+ 19 - 22
AppTool.py → FlatCAMTool.py

@@ -6,12 +6,13 @@
 # MIT Licence                                              #
 # MIT Licence                                              #
 # ########################################################## ##
 # ########################################################## ##
 
 
-from PyQt5 import QtCore, QtWidgets
+from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets
+from PyQt5.QtCore import Qt
 
 
 from shapely.geometry import Polygon, LineString
 from shapely.geometry import Polygon, LineString
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -19,23 +20,22 @@ if '_' not in builtins.__dict__:
     _ = gettext.gettext
     _ = gettext.gettext
 
 
 
 
-class AppTool(QtWidgets.QWidget):
+class FlatCAMTool(QtWidgets.QWidget):
 
 
     toolName = "FlatCAM Generic Tool"
     toolName = "FlatCAM Generic Tool"
 
 
     def __init__(self, app, parent=None):
     def __init__(self, app, parent=None):
         """
         """
 
 
-        :param app:         The application this tool will run in.
-        :type app:          App_Main.App
-        :param parent:      Qt Parent
-        :return:            AppTool
+        :param app: The application this tool will run in.
+        :type app: App
+        :param parent: Qt Parent
+        :return: FlatCAMTool
         """
         """
-        QtWidgets.QWidget.__init__(self, parent)
-
         self.app = app
         self.app = app
-        self.decimals = self.app.decimals
+        self.decimals = app.decimals
 
 
+        QtWidgets.QWidget.__init__(self, parent)
         # self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
         # self.setSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
 
 
         self.layout = QtWidgets.QVBoxLayout()
         self.layout = QtWidgets.QVBoxLayout()
@@ -87,10 +87,10 @@ class AppTool(QtWidgets.QWidget):
 
 
         if self.app.tool_tab_locked is True:
         if self.app.tool_tab_locked is True:
             return
             return
-        # Remove anything else in the AppGUI
+        # Remove anything else in the GUI
         self.app.ui.tool_scroll_area.takeWidget()
         self.app.ui.tool_scroll_area.takeWidget()
 
 
-        # Put ourself in the AppGUI
+        # Put ourself in the GUI
         self.app.ui.tool_scroll_area.setWidget(self)
         self.app.ui.tool_scroll_area.setWidget(self)
 
 
         # Switch notebook to tool page
         # Switch notebook to tool page
@@ -277,25 +277,22 @@ class AppTool(QtWidgets.QWidget):
 
 
     def confirmation_message(self, accepted, minval, maxval):
     def confirmation_message(self, accepted, minval, maxval):
         if accepted is False:
         if accepted is False:
-            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
-                                                                                  self.decimals,
-                                                                                  minval,
-                                                                                  self.decimals,
-                                                                                  maxval), False)
+            self.app.inform.emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' %
+                                 (_("Edited value is out of range"), self.decimals, minval, self.decimals, maxval))
         else:
         else:
-            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+            self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
 
 
     def confirmation_message_int(self, accepted, minval, maxval):
     def confirmation_message_int(self, accepted, minval, maxval):
         if accepted is False:
         if accepted is False:
-            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
-                                            (_("Edited value is out of range"), minval, maxval), False)
+            self.app.inform.emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                 (_("Edited value is out of range"), minval, maxval))
         else:
         else:
-            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+            self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
 
 
     def sizeHint(self):
     def sizeHint(self):
         """
         """
         I've overloaded this just in case I will need to make changes in the future to enforce dimensions
         I've overloaded this just in case I will need to make changes in the future to enforce dimensions
         :return:
         :return:
         """
         """
-        default_hint_size = super(AppTool, self).sizeHint()
+        default_hint_size = super(FlatCAMTool, self).sizeHint()
         return QtCore.QSize(default_hint_size.width(), default_hint_size.height())
         return QtCore.QSize(default_hint_size.width(), default_hint_size.height())

+ 0 - 4
AppTranslation.py → FlatCAMTranslation.py

@@ -106,8 +106,6 @@ def on_language_apply_click(app, restart=False):
                                   (_("Are you sure do you want to change the current language to"), name.capitalize()))
                                   (_("Are you sure do you want to change the current language to"), name.capitalize()))
         msgbox.setWindowTitle(_("Apply Language ..."))
         msgbox.setWindowTitle(_("Apply Language ..."))
         msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/language32.png'))
         msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/language32.png'))
-        msgbox.setIcon(QtWidgets.QMessageBox.Question)
-
         bt_yes = msgbox.addButton(_("Yes"), QtWidgets.QMessageBox.YesRole)
         bt_yes = msgbox.addButton(_("Yes"), QtWidgets.QMessageBox.YesRole)
         bt_no = msgbox.addButton(_("No"), QtWidgets.QMessageBox.NoRole)
         bt_no = msgbox.addButton(_("No"), QtWidgets.QMessageBox.NoRole)
 
 
@@ -205,8 +203,6 @@ def restart_program(app, ask=None):
                          "Do you want to Save the project?"))
                          "Do you want to Save the project?"))
         msgbox.setWindowTitle(_("Save changes"))
         msgbox.setWindowTitle(_("Save changes"))
         msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/save_as.png'))
         msgbox.setWindowIcon(QtGui.QIcon(resource_loc + '/save_as.png'))
-        msgbox.setIcon(QtWidgets.QMessageBox.Question)
-
         bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
         bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
         bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
         bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
 
 

+ 0 - 0
AppWorker.py → FlatCAMWorker.py


+ 1 - 1
AppWorkerStack.py → FlatCAMWorkerStack.py

@@ -1,5 +1,5 @@
 from PyQt5 import QtCore
 from PyQt5 import QtCore
-from AppWorker import Worker
+from FlatCAMWorker import Worker
 import multiprocessing
 import multiprocessing
 
 
 
 

+ 0 - 195
Utils/vispy_example.py

@@ -1,195 +0,0 @@
-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
-        
-        # <VisPyCanvas>
-        self.vispy_canvas.create_native()
-        self.vispy_canvas.native.setParent(self.my_app.ui)
-
-        # <QtCore.QObject>
-        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_())

BIN
assets/resources/axis16.png


BIN
assets/resources/clear_line16.png


BIN
assets/resources/contribute256.png


BIN
assets/resources/corners_32.png


BIN
assets/resources/dark_resources/axis16.png


BIN
assets/resources/dark_resources/clear_line16.png


BIN
assets/resources/dark_resources/contribute256.png


BIN
assets/resources/dark_resources/corners_32.png


BIN
assets/resources/dark_resources/edit_file16.png


BIN
assets/resources/dark_resources/edit_file32.png


BIN
assets/resources/dark_resources/etch_32.png


BIN
assets/resources/dark_resources/hud16.png


BIN
assets/resources/dark_resources/hud_32.png


BIN
assets/resources/dark_resources/iso_16.png


BIN
assets/resources/dark_resources/panelize32.png


BIN
assets/resources/dark_resources/settings18.png


BIN
assets/resources/dark_resources/shell20.png


BIN
assets/resources/edit_file16.png


BIN
assets/resources/edit_file32.png


BIN
assets/resources/etch_32.png


BIN
assets/resources/grid32.png


BIN
assets/resources/grid_lines32.png


BIN
assets/resources/hud16.png


BIN
assets/resources/hud_32.png


BIN
assets/resources/iso_16.png


BIN
assets/resources/panelize32.png


BIN
assets/resources/settings18.png


BIN
assets/resources/shell20.png


File diff suppressed because it is too large
+ 90 - 515
camlib.py


+ 20 - 42
defaults.py

@@ -2,16 +2,16 @@ import os
 import stat
 import stat
 import sys
 import sys
 from copy import deepcopy
 from copy import deepcopy
-from Common import LoudDict
+from FlatCAMCommon import LoudDict
 from camlib import to_dict, CNCjob, Geometry
 from camlib import to_dict, CNCjob, Geometry
 import simplejson
 import simplejson
 import logging
 import logging
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
-from AppParsers.ParseExcellon import Excellon
-from AppParsers.ParseGerber import Gerber
+from flatcamParsers.ParseExcellon import Excellon
+from flatcamParsers.ParseGerber import Gerber
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
 if '_' not in builtins.__dict__:
 if '_' not in builtins.__dict__:
@@ -43,7 +43,6 @@ class FlatCAMDefaults:
 
 
         # General
         # General
         "global_graphic_engine": '3D',
         "global_graphic_engine": '3D',
-        "global_hud": True,
         "global_app_level": 'b',
         "global_app_level": 'b',
         "global_portable": False,
         "global_portable": False,
         "global_language": 'English',
         "global_language": 'English',
@@ -172,6 +171,12 @@ class FlatCAMDefaults:
                                "All Files (*.*)",
                                "All Files (*.*)",
 
 
         # Gerber Options
         # 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_noncoppermargin": 0.1,
         "gerber_noncopperrounded": False,
         "gerber_noncopperrounded": False,
         "gerber_bboxmargin": 0.1,
         "gerber_bboxmargin": 0.1,
@@ -182,6 +187,11 @@ class FlatCAMDefaults:
         "gerber_aperture_scale_factor": 1.0,
         "gerber_aperture_scale_factor": 1.0,
         "gerber_aperture_buffer_factor": 0.0,
         "gerber_aperture_buffer_factor": 0.0,
         "gerber_follow": False,
         "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_buffering": "full",
         "gerber_simplification": False,
         "gerber_simplification": False,
         "gerber_simp_tolerance": 0.0005,
         "gerber_simp_tolerance": 0.0005,
@@ -212,7 +222,6 @@ class FlatCAMDefaults:
         # Excellon General
         # Excellon General
         "excellon_plot": True,
         "excellon_plot": True,
         "excellon_solid": True,
         "excellon_solid": True,
-        "excellon_multicolored": False,
         "excellon_format_upper_in": 2,
         "excellon_format_upper_in": 2,
         "excellon_format_lower_in": 4,
         "excellon_format_lower_in": 4,
         "excellon_format_upper_mm": 3,
         "excellon_format_upper_mm": 3,
@@ -301,7 +310,6 @@ class FlatCAMDefaults:
 
 
         # Geometry General
         # Geometry General
         "geometry_plot": True,
         "geometry_plot": True,
-        "geometry_multicolored": False,
         "geometry_circle_steps": 64,
         "geometry_circle_steps": 64,
         "geometry_cnctooldia": "2.4",
         "geometry_cnctooldia": "2.4",
         "geometry_plot_line": "#FF0000",
         "geometry_plot_line": "#FF0000",
@@ -382,28 +390,6 @@ class FlatCAMDefaults:
         "cncjob_annotation_fontsize": 9,
         "cncjob_annotation_fontsize": 9,
         "cncjob_annotation_fontcolor": '#990000',
         "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
         # NCC Tool
         "tools_ncctools": "1.0, 0.5",
         "tools_ncctools": "1.0, 0.5",
         "tools_nccorder": 'rev',
         "tools_nccorder": 'rev',
@@ -418,13 +404,13 @@ class FlatCAMDefaults:
         "tools_ncc_offset_value": 0.0000,
         "tools_ncc_offset_value": 0.0000,
         "tools_nccref": _('Itself'),
         "tools_nccref": _('Itself'),
         "tools_ncc_area_shape": "square",
         "tools_ncc_area_shape": "square",
+        "tools_ncc_plotting": 'normal',
         "tools_nccmilling_type": 'cl',
         "tools_nccmilling_type": 'cl',
         "tools_ncctool_type": 'C1',
         "tools_ncctool_type": 'C1',
         "tools_ncccutz": -0.05,
         "tools_ncccutz": -0.05,
         "tools_ncctipdia": 0.1,
         "tools_ncctipdia": 0.1,
         "tools_ncctipangle": 30,
         "tools_ncctipangle": 30,
         "tools_nccnewdia": 0.1,
         "tools_nccnewdia": 0.1,
-        "tools_ncc_plotting": 'normal',
 
 
         # Cutout Tool
         # Cutout Tool
         "tools_cutouttooldia": 2.4,
         "tools_cutouttooldia": 2.4,
@@ -443,7 +429,7 @@ class FlatCAMDefaults:
         "tools_paintoverlap": 20,
         "tools_paintoverlap": 20,
         "tools_paintmargin": 0.0,
         "tools_paintmargin": 0.0,
         "tools_paintmethod": _("Seed"),
         "tools_paintmethod": _("Seed"),
-        "tools_selectmethod": _("All"),
+        "tools_selectmethod": _("All Polygons"),
         "tools_paint_area_shape": "square",
         "tools_paint_area_shape": "square",
         "tools_pathconnect": True,
         "tools_pathconnect": True,
         "tools_paintcontour": True,
         "tools_paintcontour": True,
@@ -538,12 +524,6 @@ class FlatCAMDefaults:
         # Distance Tool
         # Distance Tool
         "tools_dist_snap_center": False,
         "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 ###############################################################
         # ################################ TOOLS 2 ###############################################################
         # ########################################################################################################
         # ########################################################################################################
@@ -697,15 +677,13 @@ class FlatCAMDefaults:
     }
     }
 
 
     @classmethod
     @classmethod
-    def save_factory_defaults(cls, file_path: str, version: float):
+    def save_factory_defaults(cls, file_path: str):
         """Writes the factory defaults to a file at the given path, overwriting any existing file."""
         """Writes the factory defaults to a file at the given path, overwriting any existing file."""
         # Delete any existing factory defaults file
         # Delete any existing factory defaults file
         if os.path.isfile(file_path):
         if os.path.isfile(file_path):
             os.chmod(file_path, stat.S_IRWXO | stat.S_IWRITE | stat.S_IWGRP)
             os.chmod(file_path, stat.S_IRWXO | stat.S_IWRITE | stat.S_IWGRP)
             os.remove(file_path)
             os.remove(file_path)
 
 
-        cls.factory_defaults['version'] = version
-
         try:
         try:
             # recreate a new factory defaults file and save the factory defaults data into it
             # recreate a new factory defaults file and save the factory defaults data into it
             f_f_def_s = open(file_path, "w")
             f_f_def_s = open(file_path, "w")
@@ -786,8 +764,8 @@ class FlatCAMDefaults:
         if defaults is None:
         if defaults is None:
             return
             return
 
 
-        # Perform migration if necessary but only if the defaults dict is not empty
-        if self.__is_old_defaults(defaults) and defaults:
+        # Perform migration if necessary
+        if self.__is_old_defaults(defaults):
             self.old_defaults_found = True
             self.old_defaults_found = True
             defaults = self.__migrate_old_defaults(defaults=defaults)
             defaults = self.__migrate_old_defaults(defaults=defaults)
         else:
         else:

+ 48 - 29
AppEditors/FlatCAMExcEditor.py → flatcamEditors/FlatCAMExcEditor.py

@@ -9,9 +9,9 @@ from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtCore import Qt, QSettings
 from PyQt5.QtCore import Qt, QSettings
 
 
 from camlib import distance, arc, FlatCAMRTreeStorage
 from camlib import distance, arc, FlatCAMRTreeStorage
-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 flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, RadioSet, FCSpinner
+from flatcamEditors.FlatCAMGeoEditor import FCShapeTool, DrawTool, DrawToolShape, DrawToolUtilityShape, FlatCAMGeoEditor
+from flatcamParsers.ParseExcellon import Excellon
 
 
 from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point
 from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon, Point
 import shapely.affinity as affinity
 import shapely.affinity as affinity
@@ -26,7 +26,7 @@ import logging
 from copy import deepcopy
 from copy import deepcopy
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -2123,7 +2123,7 @@ class FlatCAMExcEditor(QtCore.QObject):
             else:
             else:
                 self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
                 self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
         else:
         else:
-            from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
             self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_exc_editor')
             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')
             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
         # store the status of the editor so the Delete at object level will not work until the edit is finished
         self.editor_active = False
         self.editor_active = False
-        log.debug("Initialization of the Excellon Editor is finished ...")
+        log.debug("Initialization of the FlatCAM Excellon Editor is finished ...")
 
 
     def pool_recreated(self, pool):
     def pool_recreated(self, pool):
         self.shapes.pool = pool
         self.shapes.pool = pool
@@ -2312,7 +2312,7 @@ class FlatCAMExcEditor(QtCore.QObject):
                 tool_dia = float('%.*f' % (self.decimals, v['C']))
                 tool_dia = float('%.*f' % (self.decimals, v['C']))
                 self.tool2tooldia[int(k)] = tool_dia
                 self.tool2tooldia[int(k)] = tool_dia
 
 
-        # Init AppGUI
+        # Init GUI
         self.addtool_entry.set_value(float(self.app.defaults['excellon_editor_newdia']))
         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_array_size_entry.set_value(int(self.app.defaults['excellon_editor_array_size']))
         self.drill_axis_radio.set_value(self.app.defaults['excellon_editor_lin_dir'])
         self.drill_axis_radio.set_value(self.app.defaults['excellon_editor_lin_dir'])
@@ -2819,8 +2819,10 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.tool_shape.enabled = True
         self.tool_shape.enabled = True
         # self.app.app_cursor.enabled = True
         # self.app.app_cursor.enabled = True
 
 
-        self.app.ui.corner_snap_btn.setVisible(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.snap_magnet.setVisible(True)
+        self.app.ui.corner_snap_btn.setVisible(True)
 
 
         self.app.ui.exc_editor_menu.setDisabled(False)
         self.app.ui.exc_editor_menu.setDisabled(False)
         self.app.ui.exc_editor_menu.menuAction().setVisible(True)
         self.app.ui.exc_editor_menu.menuAction().setVisible(True)
@@ -2830,11 +2832,12 @@ class FlatCAMExcEditor(QtCore.QObject):
 
 
         self.app.ui.exc_edit_toolbar.setDisabled(False)
         self.app.ui.exc_edit_toolbar.setDisabled(False)
         self.app.ui.exc_edit_toolbar.setVisible(True)
         self.app.ui.exc_edit_toolbar.setVisible(True)
-        # self.app.ui.status_toolbar.setDisabled(False)
+        # self.app.ui.snap_toolbar.setDisabled(False)
 
 
         # start with GRID toolbar activated
         # start with GRID toolbar activated
         if self.app.ui.grid_snap_btn.isChecked() is False:
         if self.app.ui.grid_snap_btn.isChecked() is False:
             self.app.ui.grid_snap_btn.trigger()
             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.popmenu_disable.setVisible(False)
         self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
         self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
@@ -2866,8 +2869,30 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.clear()
         self.clear()
         self.app.ui.exc_edit_toolbar.setDisabled(True)
         self.app.ui.exc_edit_toolbar.setDisabled(True)
 
 
-        self.app.ui.corner_snap_btn.setVisible(False)
-        self.app.ui.snap_magnet.setVisible(False)
+        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)
 
 
         # set the Editor Toolbar visibility to what was before entering in the Editor
         # 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 \
         self.app.ui.exc_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
@@ -3043,7 +3068,7 @@ class FlatCAMExcEditor(QtCore.QObject):
 
 
         self.set_ui()
         self.set_ui()
 
 
-        # now that we hava data, create the AppGUI interface and add it to the Tool Tab
+        # now that we hava data, create the GUI interface and add it to the Tool Tab
         self.build_ui(first_run=True)
         self.build_ui(first_run=True)
 
 
         # we activate this after the initial build as we don't need to see the tool been populated
         # we activate this after the initial build as we don't need to see the tool been populated
@@ -3336,17 +3361,15 @@ class FlatCAMExcEditor(QtCore.QObject):
         with self.app.proc_container.new(_("Creating Excellon.")):
         with self.app.proc_container.new(_("Creating Excellon.")):
 
 
             try:
             try:
-                edited_obj = self.app.app_obj.new_object("excellon", outname, obj_init)
+                edited_obj = self.app.new_object("excellon", outname, obj_init)
                 edited_obj.source_file = self.app.export_excellon(obj_name=edited_obj.options['name'],
                 edited_obj.source_file = self.app.export_excellon(obj_name=edited_obj.options['name'],
                                                                   local_use=edited_obj,
                                                                   local_use=edited_obj,
                                                                   filename=None,
                                                                   filename=None,
                                                                   use_thread=False)
                                                                   use_thread=False)
             except Exception as e:
             except Exception as e:
-                self.deactivate()
                 log.error("Error on Edited object creation: %s" % str(e))
                 log.error("Error on Edited object creation: %s" % str(e))
                 return
                 return
 
 
-            self.deactivate()
             self.app.inform.emit('[success] %s' % _("Excellon editing finished."))
             self.app.inform.emit('[success] %s' % _("Excellon editing finished."))
 
 
     def on_tool_select(self, tool):
     def on_tool_select(self, tool):
@@ -3440,8 +3463,8 @@ class FlatCAMExcEditor(QtCore.QObject):
             self.pos = (self.pos[0], self.pos[1])
             self.pos = (self.pos[0], self.pos[1])
 
 
         if event.button == 1:
         if event.button == 1:
-            # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-            #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
+            self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
 
 
             # Selection with left mouse button
             # Selection with left mouse button
             if self.active_tool is not None and event.button == 1:
             if self.active_tool is not None and event.button == 1:
@@ -3778,22 +3801,18 @@ class FlatCAMExcEditor(QtCore.QObject):
         self.snap_x = x
         self.snap_x = x
         self.snap_y = y
         self.snap_y = y
 
 
+        # update the position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                           "<b>Y</b>: %.4f" % (x, y))
+
         if self.pos is None:
         if self.pos is None:
             self.pos = (0, 0)
             self.pos = (0, 0)
         self.app.dx = x - self.pos[0]
         self.app.dx = x - self.pos[0]
         self.app.dy = y - self.pos[1]
         self.app.dy = y - self.pos[1]
 
 
-        # # update the position label in the infobar since the APP mouse event handlers are disconnected
-        self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f&nbsp;" % (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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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)
+        # update the reference position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
 
 
         # ## Utility geometry (animated)
         # ## Utility geometry (animated)
         self.update_utility_geometry(data=(x, y))
         self.update_utility_geometry(data=(x, y))
@@ -4026,7 +4045,7 @@ class FlatCAMExcEditor(QtCore.QObject):
 
 
     def select_tool(self, toolname):
     def select_tool(self, toolname):
         """
         """
-        Selects a drawing tool. Impacts the object and AppGUI.
+        Selects a drawing tool. Impacts the object and GUI.
 
 
         :param toolname: Name of the tool.
         :param toolname: Name of the tool.
         :return: None
         :return: None

+ 71 - 67
AppEditors/FlatCAMGeoEditor.py → flatcamEditors/FlatCAMGeoEditor.py

@@ -15,10 +15,11 @@ from PyQt5 import QtGui, QtCore, QtWidgets
 from PyQt5.QtCore import Qt, QSettings
 from PyQt5.QtCore import Qt, QSettings
 
 
 from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
 from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStorage
-from AppTool import AppTool
-from AppGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
-    FCDoubleSpinner, FCButton, FCInputDialog, FCTree
-from AppParsers.ParseFont import *
+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 shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
 from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
 from shapely.ops import cascaded_union, unary_union, linemerge
 from shapely.ops import cascaded_union, unary_union, linemerge
@@ -33,7 +34,7 @@ from rtree import index as rtindex
 from copy import deepcopy
 from copy import deepcopy
 # from vispy.io import read_png
 # from vispy.io import read_png
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -41,7 +42,7 @@ if '_' not in builtins.__dict__:
     _ = gettext.gettext
     _ = gettext.gettext
 
 
 
 
-class BufferSelectionTool(AppTool):
+class BufferSelectionTool(FlatCAMTool):
     """
     """
     Simple input for buffer distance.
     Simple input for buffer distance.
     """
     """
@@ -49,7 +50,7 @@ class BufferSelectionTool(AppTool):
     toolName = "Buffer Selection"
     toolName = "Buffer Selection"
 
 
     def __init__(self, app, draw_app):
     def __init__(self, app, draw_app):
-        AppTool.__init__(self, app)
+        FlatCAMTool.__init__(self, app)
 
 
         self.draw_app = draw_app
         self.draw_app = draw_app
         self.decimals = app.decimals
         self.decimals = app.decimals
@@ -117,12 +118,12 @@ class BufferSelectionTool(AppTool):
         self.buffer_int_button.clicked.connect(self.on_buffer_int)
         self.buffer_int_button.clicked.connect(self.on_buffer_int)
         self.buffer_ext_button.clicked.connect(self.on_buffer_ext)
         self.buffer_ext_button.clicked.connect(self.on_buffer_ext)
 
 
-        # Init AppGUI
+        # Init GUI
         self.buffer_distance_entry.set_value(0.01)
         self.buffer_distance_entry.set_value(0.01)
 
 
     def run(self):
     def run(self):
         self.app.defaults.report_usage("Geo Editor ToolBuffer()")
         self.app.defaults.report_usage("Geo Editor ToolBuffer()")
-        AppTool.run(self)
+        FlatCAMTool.run(self)
 
 
         # if the splitter us hidden, display it
         # if the splitter us hidden, display it
         if self.app.ui.splitter.sizes()[0] == 0:
         if self.app.ui.splitter.sizes()[0] == 0:
@@ -186,7 +187,7 @@ class BufferSelectionTool(AppTool):
         self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
         self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
 
 
 
 
-class TextInputTool(AppTool):
+class TextInputTool(FlatCAMTool):
     """
     """
     Simple input for buffer distance.
     Simple input for buffer distance.
     """
     """
@@ -194,7 +195,7 @@ class TextInputTool(AppTool):
     toolName = "Text Input Tool"
     toolName = "Text Input Tool"
 
 
     def __init__(self, app):
     def __init__(self, app):
-        AppTool.__init__(self, app)
+        FlatCAMTool.__init__(self, app)
 
 
         self.app = app
         self.app = app
         self.text_path = []
         self.text_path = []
@@ -339,7 +340,7 @@ class TextInputTool(AppTool):
 
 
     def run(self):
     def run(self):
         self.app.defaults.report_usage("Geo Editor TextInputTool()")
         self.app.defaults.report_usage("Geo Editor TextInputTool()")
-        AppTool.run(self)
+        FlatCAMTool.run(self)
 
 
         # if the splitter us hidden, display it
         # if the splitter us hidden, display it
         if self.app.ui.splitter.sizes()[0] == 0:
         if self.app.ui.splitter.sizes()[0] == 0:
@@ -404,7 +405,7 @@ class TextInputTool(AppTool):
         self.app.ui.notebook.setTabText(2, _("Tool"))
         self.app.ui.notebook.setTabText(2, _("Tool"))
 
 
 
 
-class PaintOptionsTool(AppTool):
+class PaintOptionsTool(FlatCAMTool):
     """
     """
     Inputs to specify how to paint the selected polygons.
     Inputs to specify how to paint the selected polygons.
     """
     """
@@ -412,7 +413,7 @@ class PaintOptionsTool(AppTool):
     toolName = "Paint Tool"
     toolName = "Paint Tool"
 
 
     def __init__(self, app, fcdraw):
     def __init__(self, app, fcdraw):
-        AppTool.__init__(self, app)
+        FlatCAMTool.__init__(self, app)
 
 
         self.app = app
         self.app = app
         self.fcdraw = fcdraw
         self.fcdraw = fcdraw
@@ -537,7 +538,7 @@ class PaintOptionsTool(AppTool):
 
 
     def run(self):
     def run(self):
         self.app.defaults.report_usage("Geo Editor ToolPaint()")
         self.app.defaults.report_usage("Geo Editor ToolPaint()")
-        AppTool.run(self)
+        FlatCAMTool.run(self)
 
 
         # if the splitter us hidden, display it
         # if the splitter us hidden, display it
         if self.app.ui.splitter.sizes()[0] == 0:
         if self.app.ui.splitter.sizes()[0] == 0:
@@ -546,7 +547,7 @@ class PaintOptionsTool(AppTool):
         self.app.ui.notebook.setTabText(2, _("Paint Tool"))
         self.app.ui.notebook.setTabText(2, _("Paint Tool"))
 
 
     def set_tool_ui(self):
     def set_tool_ui(self):
-        # Init AppGUI
+        # Init GUI
         if self.app.defaults["tools_painttooldia"]:
         if self.app.defaults["tools_painttooldia"]:
             self.painttooldia_entry.set_value(self.app.defaults["tools_painttooldia"])
             self.painttooldia_entry.set_value(self.app.defaults["tools_painttooldia"])
         else:
         else:
@@ -598,7 +599,7 @@ class PaintOptionsTool(AppTool):
         self.app.ui.splitter.setSizes([0, 1])
         self.app.ui.splitter.setSizes([0, 1])
 
 
 
 
-class TransformEditorTool(AppTool):
+class TransformEditorTool(FlatCAMTool):
     """
     """
     Inputs to specify how to paint the selected polygons.
     Inputs to specify how to paint the selected polygons.
     """
     """
@@ -611,7 +612,7 @@ class TransformEditorTool(AppTool):
     offsetName = _("Offset")
     offsetName = _("Offset")
 
 
     def __init__(self, app, draw_app):
     def __init__(self, app, draw_app):
-        AppTool.__init__(self, app)
+        FlatCAMTool.__init__(self, app)
 
 
         self.app = app
         self.app = app
         self.draw_app = draw_app
         self.draw_app = draw_app
@@ -980,7 +981,7 @@ class TransformEditorTool(AppTool):
 
 
     def run(self):
     def run(self):
         self.app.defaults.report_usage("Geo Editor Transform Tool()")
         self.app.defaults.report_usage("Geo Editor Transform Tool()")
-        AppTool.run(self)
+        FlatCAMTool.run(self)
         self.set_tool_ui()
         self.set_tool_ui()
 
 
         # if the splitter us hidden, display it
         # if the splitter us hidden, display it
@@ -990,7 +991,7 @@ class TransformEditorTool(AppTool):
         self.app.ui.notebook.setTabText(2, _("Transform Tool"))
         self.app.ui.notebook.setTabText(2, _("Transform Tool"))
 
 
     def install(self, icon=None, separator=None, **kwargs):
     def install(self, icon=None, separator=None, **kwargs):
-        AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
+        FlatCAMTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
 
 
     def set_tool_ui(self):
     def set_tool_ui(self):
         # Initialize form
         # Initialize form
@@ -3382,7 +3383,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
             self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
             self.shapes = self.app.plotcanvas.new_shape_collection(layers=1)
             self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
             self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1)
         else:
         else:
-            from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
             self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_geo_editor')
             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')
             self.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_geo_editor')
 
 
@@ -3466,32 +3467,22 @@ class FlatCAMGeoEditor(QtCore.QObject):
             :return:
             :return:
             """
             """
             try:
             try:
-                text_value = entry.text()
-                if ',' in text_value:
-                    text_value = text_value.replace(',', '.')
-                self.options[opt] = float(text_value)
+                self.options[opt] = float(entry.text())
             except Exception as e:
             except Exception as e:
-                entry.set_value(self.app.defaults[opt])
                 log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e))
                 log.debug("FlatCAMGeoEditor.__init__().entry2option() --> %s" % str(e))
                 return
                 return
 
 
-        def grid_changed(goption, gentry):
+        def gridx_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:
             :return:
             """
             """
-            if goption not in ['global_gridx', 'global_gridy']:
-                return
-
             entry2option(opt=goption, entry=gentry)
             entry2option(opt=goption, entry=gentry)
             # if the grid link is checked copy the value in the GridX field to GridY
             # if the grid link is checked copy the value in the GridX field to GridY
             try:
             try:
-                text_value = gentry.text()
-                if ',' in text_value:
-                    text_value = text_value.replace(',', '.')
-                val = float(text_value)
+                val = float(gentry.get_value())
             except ValueError:
             except ValueError:
                 return
                 return
 
 
@@ -3500,7 +3491,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
 
         self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator())
         self.app.ui.grid_gap_x_entry.setValidator(QtGui.QDoubleValidator())
         self.app.ui.grid_gap_x_entry.textChanged.connect(
         self.app.ui.grid_gap_x_entry.textChanged.connect(
-            lambda: grid_changed("global_gridx", self.app.ui.grid_gap_x_entry))
+            lambda: gridx_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.setValidator(QtGui.QDoubleValidator())
         self.app.ui.grid_gap_y_entry.textChanged.connect(
         self.app.ui.grid_gap_y_entry.textChanged.connect(
@@ -3551,7 +3542,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
         # store the status of the editor so the Delete at object level will not work until the edit is finished
         self.editor_active = False
         self.editor_active = False
-        log.debug("Initialization of the Geometry Editor is finished ...")
+        log.debug("Initialization of the FlatCAM Geometry Editor is finished ...")
 
 
     def pool_recreated(self, pool):
     def pool_recreated(self, pool):
         self.shapes.pool = pool
         self.shapes.pool = pool
@@ -3568,14 +3559,14 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
 
         # Remove anything else in the GUI Selected Tab
         # Remove anything else in the GUI Selected Tab
         self.app.ui.selected_scroll_area.takeWidget()
         self.app.ui.selected_scroll_area.takeWidget()
-        # Put ourselves in the AppGUI Selected Tab
+        # Put ourselves in the GUI Selected Tab
         self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget)
         self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget)
         # Switch notebook to Selected page
         # Switch notebook to Selected page
         self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
         self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
 
 
     def build_ui(self):
     def build_ui(self):
         """
         """
-        Build the AppGUI in the Selected Tab for this editor
+        Build the GUI in the Selected Tab for this editor
 
 
         :return:
         :return:
         """
         """
@@ -3653,8 +3644,10 @@ class FlatCAMGeoEditor(QtCore.QObject):
         self.tool_shape.enabled = True
         self.tool_shape.enabled = True
         self.app.app_cursor.enabled = True
         self.app.app_cursor.enabled = True
 
 
-        self.app.ui.corner_snap_btn.setVisible(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.snap_magnet.setVisible(True)
+        self.app.ui.corner_snap_btn.setVisible(True)
 
 
         self.app.ui.geo_editor_menu.setDisabled(False)
         self.app.ui.geo_editor_menu.setDisabled(False)
         self.app.ui.geo_editor_menu.menuAction().setVisible(True)
         self.app.ui.geo_editor_menu.menuAction().setVisible(True)
@@ -3665,7 +3658,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         self.app.ui.geo_edit_toolbar.setDisabled(False)
         self.app.ui.geo_edit_toolbar.setDisabled(False)
         self.app.ui.geo_edit_toolbar.setVisible(True)
         self.app.ui.geo_edit_toolbar.setVisible(True)
 
 
-        self.app.ui.status_toolbar.setDisabled(False)
+        self.app.ui.snap_toolbar.setDisabled(False)
 
 
         self.app.ui.popmenu_disable.setVisible(False)
         self.app.ui.popmenu_disable.setVisible(False)
         self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
         self.app.ui.cmenu_newmenu.menuAction().setVisible(False)
@@ -3682,7 +3675,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
 
         self.item_selected.connect(self.on_geo_elem_selected)
         self.item_selected.connect(self.on_geo_elem_selected)
 
 
-        # ## AppGUI Events
+        # ## GUI Events
         self.tw.itemSelectionChanged.connect(self.on_tree_selection_change)
         self.tw.itemSelectionChanged.connect(self.on_tree_selection_change)
         # self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
         # self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
         # self.tw.customContextMenuRequested.connect(self.on_menu_request)
         # self.tw.customContextMenuRequested.connect(self.on_menu_request)
@@ -3710,8 +3703,27 @@ class FlatCAMGeoEditor(QtCore.QObject):
         self.app.ui.geo_edit_toolbar.setDisabled(True)
         self.app.ui.geo_edit_toolbar.setDisabled(True)
 
 
         settings = QSettings("Open Source", "FlatCAM")
         settings = QSettings("Open Source", "FlatCAM")
-        self.app.ui.corner_snap_btn.setVisible(False)
-        self.app.ui.snap_magnet.setVisible(False)
+        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)
 
 
         # set the Editor Toolbar visibility to what was before entering in the Editor
         # 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 \
         self.app.ui.geo_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
@@ -3745,7 +3757,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
             pass
             pass
 
 
         try:
         try:
-            # ## AppGUI Events
+            # ## GUI Events
             self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change)
             self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change)
             # self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
             # self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
             # self.tw.customContextMenuRequested.connect(self.on_menu_request)
             # self.tw.customContextMenuRequested.connect(self.on_menu_request)
@@ -4088,6 +4100,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
         # start with GRID toolbar activated
         # start with GRID toolbar activated
         if self.app.ui.grid_snap_btn.isChecked() is False:
         if self.app.ui.grid_snap_btn.isChecked() is False:
             self.app.ui.grid_snap_btn.trigger()
             self.app.ui.grid_snap_btn.trigger()
+            self.app.ui.on_grid_snap_triggered(state=True)
 
 
     def on_buffer_tool(self):
     def on_buffer_tool(self):
         buff_tool = BufferSelectionTool(self.app, self)
         buff_tool = BufferSelectionTool(self.app, self)
@@ -4135,11 +4148,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
 
         # make sure that the cursor shape is enabled/disabled, too
         # make sure that the cursor shape is enabled/disabled, too
         if self.options['grid_snap'] is True:
         if self.options['grid_snap'] is True:
-            self.app.inform[str, bool].emit(_("Grid Snap enabled."), False)
             self.app.app_cursor.enabled = True
             self.app.app_cursor.enabled = True
         else:
         else:
             self.app.app_cursor.enabled = False
             self.app.app_cursor.enabled = False
-            self.app.inform[str, bool].emit(_("Grid Snap disabled."), False)
 
 
     def on_canvas_click(self, event):
     def on_canvas_click(self, event):
         """
         """
@@ -4162,8 +4173,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
             self.pos = (self.pos[0], self.pos[1])
             self.pos = (self.pos[0], self.pos[1])
 
 
         if event.button == 1:
         if event.button == 1:
-            # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-            #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
+            self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
 
 
             modifiers = QtWidgets.QApplication.keyboardModifiers()
             modifiers = QtWidgets.QApplication.keyboardModifiers()
             # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
             # If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
@@ -4250,23 +4261,18 @@ class FlatCAMGeoEditor(QtCore.QObject):
         self.snap_y = y
         self.snap_y = y
         self.app.mouse = [x, 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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
+                                           "<b>Y</b>: %.4f" % (x, y))
+
         if self.pos is None:
         if self.pos is None:
             self.pos = (0, 0)
             self.pos = (0, 0)
         self.app.dx = x - self.pos[0]
         self.app.dx = x - self.pos[0]
         self.app.dy = y - self.pos[1]
         self.app.dy = y - self.pos[1]
 
 
-        # # update the position label in the infobar since the APP mouse event handlers are disconnected
-        self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f&nbsp;" % (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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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)
+        # update the reference position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
 
 
         if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
         if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
             pass
             pass
@@ -4659,7 +4665,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
 
 
     def select_tool(self, toolname):
     def select_tool(self, toolname):
         """
         """
-        Selects a drawing tool. Impacts the object and AppGUI.
+        Selects a drawing tool. Impacts the object and GUI.
 
 
         :param toolname: Name of the tool.
         :param toolname: Name of the tool.
         :return: None
         :return: None
@@ -4744,8 +4750,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
         Transfers the geometry tool shape buffer to the selected geometry
         Transfers the geometry tool shape buffer to the selected geometry
         object. The geometry already in the object are removed.
         object. The geometry already in the object are removed.
 
 
-        :param fcgeometry:  GeometryObject
-        :return:            None
+        :param fcgeometry: GeometryObject
+        :return: None
         """
         """
         if self.multigeo_tool:
         if self.multigeo_tool:
             fcgeometry.tools[self.multigeo_tool]['solid_geometry'] = []
             fcgeometry.tools[self.multigeo_tool]['solid_geometry'] = []
@@ -4770,8 +4776,6 @@ class FlatCAMGeoEditor(QtCore.QObject):
                 new_geo = linemerge(new_geo)
                 new_geo = linemerge(new_geo)
             fcgeometry.solid_geometry.append(new_geo)
             fcgeometry.solid_geometry.append(new_geo)
 
 
-        self.deactivate()
-
     def update_options(self, obj):
     def update_options(self, obj):
         if self.paint_tooldia:
         if self.paint_tooldia:
             obj.options['cnctooldia'] = deepcopy(str(self.paint_tooldia))
             obj.options['cnctooldia'] = deepcopy(str(self.paint_tooldia))

+ 125 - 119
AppEditors/FlatCAMGrbEditor.py → flatcamEditors/FlatCAMGrbEditor.py

@@ -14,13 +14,15 @@ import shapely.affinity as affinity
 
 
 from vispy.geometry import Rect
 from vispy.geometry import Rect
 
 
+import threading
+import time
 from copy import copy, deepcopy
 from copy import copy, deepcopy
 import logging
 import logging
 
 
 from camlib import distance, arc, three_point_circle
 from camlib import distance, arc, three_point_circle
-from AppGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \
+from flatcamGUI.GUIElements import FCEntry, FCComboBox, FCTable, FCDoubleSpinner, FCSpinner, RadioSet, \
     EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
     EvalEntry2, FCInputDialog, FCButton, OptionalInputSection, FCCheckBox
-from AppTool import AppTool
+from FlatCAMTool import FlatCAMTool
 
 
 import numpy as np
 import numpy as np
 from numpy.linalg import norm as numpy_norm
 from numpy.linalg import norm as numpy_norm
@@ -30,7 +32,7 @@ import math
 # import pngcanvas
 # import pngcanvas
 import traceback
 import traceback
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -1084,6 +1086,15 @@ class FCRegion(FCShapeTool):
 
 
         self.draw_app.app.inform.emit('[success] %s' % _("Done."))
         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):
     def on_key(self, key):
         # Jump to coords
         # Jump to coords
         if key == QtCore.Qt.Key_J or key == 'J':
         if key == QtCore.Qt.Key_J or key == 'J':
@@ -1149,36 +1160,16 @@ class FCRegion(FCShapeTool):
 
 
             return msg
             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(FCShapeTool):
+class FCTrack(FCRegion):
     """
     """
     Resulting type: Polygon
     Resulting type: Polygon
     """
     """
     def __init__(self, draw_app):
     def __init__(self, draw_app):
-        DrawTool.__init__(self, draw_app)
+        FCRegion.__init__(self, draw_app)
         self.name = 'track'
         self.name = 'track'
         self.draw_app = draw_app
         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:
         try:
             QtGui.QGuiApplication.restoreOverrideCursor()
             QtGui.QGuiApplication.restoreOverrideCursor()
         except Exception as e:
         except Exception as e:
@@ -1192,23 +1183,52 @@ class FCTrack(FCShapeTool):
 
 
         self.draw_app.app.inform.emit(_('Track Mode 1: 45 degrees ...'))
         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):
     def click(self, point):
         self.draw_app.in_action = True
         self.draw_app.in_action = True
-
-        if not self.points:
-            self.points.append(point)
-        elif point != self.points[-1]:
+        try:
+            if point != self.points[-1]:
+                self.points.append(point)
+        except IndexError:
             self.points.append(point)
             self.points.append(point)
-        else:
-            return
 
 
         new_geo_el = {}
         new_geo_el = {}
 
 
         if len(self.temp_points) == 1:
         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['solid'] = Point(self.temp_points).buffer(self.buf_val,
+                                                                 resolution=int(self.steps_per_circle / 4))
             new_geo_el['follow'] = Point(self.temp_points)
             new_geo_el['follow'] = Point(self.temp_points)
         else:
         else:
-            new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
+            new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val,
+                                                                      resolution=int(self.steps_per_circle / 4))
             new_geo_el['follow'] = LineString(self.temp_points)
             new_geo_el['follow'] = LineString(self.temp_points)
 
 
         self.draw_app.add_gerber_shape(DrawToolShape(new_geo_el),
         self.draw_app.add_gerber_shape(DrawToolShape(new_geo_el),
@@ -1221,25 +1241,23 @@ class FCTrack(FCShapeTool):
 
 
         return ""
         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):
     def utility_geometry(self, data=None):
         self.update_grid_info()
         self.update_grid_info()
         new_geo_el = {}
         new_geo_el = {}
 
 
-        if not self.points:
-            new_geo_el['solid'] = Point(data).buffer(self.buf_val, int(self.steps_per_circle))
+        if len(self.points) == 0:
+            new_geo_el['solid'] = Point(data).buffer(self.buf_val,
+                                                     resolution=int(self.steps_per_circle / 4))
+
             return DrawToolUtilityShape(new_geo_el)
             return DrawToolUtilityShape(new_geo_el)
-        else:
+        elif len(self.points) > 0:
+
+            self.temp_points = [self.points[-1]]
             old_x = self.points[-1][0]
             old_x = self.points[-1][0]
             old_y = self.points[-1][1]
             old_y = self.points[-1][1]
             x = data[0]
             x = data[0]
             y = data[1]
             y = data[1]
 
 
-            self.temp_points = [self.points[-1]]
-
             mx = abs(round((x - old_x) / self.gridx_size))
             mx = abs(round((x - old_x) / self.gridx_size))
             my = abs(round((y - old_y) / self.gridy_size))
             my = abs(round((y - old_y) / self.gridy_size))
 
 
@@ -1287,30 +1305,14 @@ class FCTrack(FCShapeTool):
 
 
             self.temp_points.append(data)
             self.temp_points.append(data)
             if len(self.temp_points) == 1:
             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['solid'] = Point(self.temp_points).buffer(self.buf_val,
+                                                                     resolution=int(self.steps_per_circle / 4))
                 return DrawToolUtilityShape(new_geo_el)
                 return DrawToolUtilityShape(new_geo_el)
 
 
-            new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val, int(self.steps_per_circle))
+            new_geo_el['solid'] = LineString(self.temp_points).buffer(self.buf_val,
+                                                                      resolution=int(self.steps_per_circle / 4))
             return DrawToolUtilityShape(new_geo_el)
             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):
     def on_key(self, key):
         if key == 'Backspace' or key == QtCore.Qt.Key_Backspace:
         if key == 'Backspace' or key == QtCore.Qt.Key_Backspace:
             if len(self.points) > 0:
             if len(self.points) > 0:
@@ -1403,15 +1405,6 @@ class FCTrack(FCShapeTool):
 
 
             return msg
             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):
 class FCDisc(FCShapeTool):
     """
     """
@@ -2962,7 +2955,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         # this var will store the state of the toolbar before starting the editor
         # this var will store the state of the toolbar before starting the editor
         self.toolbar_old_state = False
         self.toolbar_old_state = False
 
 
-        # Init AppGUI
+        # Init GUI
         self.apdim_lbl.hide()
         self.apdim_lbl.hide()
         self.apdim_entry.hide()
         self.apdim_entry.hide()
         self.gerber_obj = None
         self.gerber_obj = None
@@ -2974,7 +2967,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
             self.tool_shape = self.canvas.new_shape_collection(layers=1)
             self.tool_shape = self.canvas.new_shape_collection(layers=1)
             self.ma_annotation = self.canvas.new_text_group()
             self.ma_annotation = self.canvas.new_text_group()
         else:
         else:
-            from AppGUI.PlotCanvasLegacy import ShapeCollectionLegacy
+            from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy
             self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_grb_editor')
             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.tool_shape = ShapeCollectionLegacy(obj=self, app=self.app, name='tool_shapes_grb_editor')
             self.ma_annotation = ShapeCollectionLegacy(
             self.ma_annotation = ShapeCollectionLegacy(
@@ -3117,7 +3110,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.complete = True
         self.complete = True
 
 
         self.set_ui()
         self.set_ui()
-        log.debug("Initialization of the Gerber Editor is finished ...")
+        log.debug("Initialization of the FlatCAM Gerber Editor is finished ...")
 
 
     def pool_recreated(self, pool):
     def pool_recreated(self, pool):
         self.shapes.pool = pool
         self.shapes.pool = pool
@@ -3146,7 +3139,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
             tt_aperture = self.sorted_apcode[i]
             tt_aperture = self.sorted_apcode[i]
             self.tid2apcode[i + 1] = tt_aperture
             self.tid2apcode[i + 1] = tt_aperture
 
 
-        # Init AppGUI
+        # Init GUI
 
 
         self.buffer_distance_entry.set_value(self.app.defaults["gerber_editor_buff_f"])
         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"])
         self.scale_factor_entry.set_value(self.app.defaults["gerber_editor_scale_f"])
@@ -3435,7 +3428,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
             else:
             else:
                 # deleted_tool_dia = float(self.apertures_table.item(self.apertures_table.currentRow(), 1).text())
                 # deleted_tool_dia = float(self.apertures_table.item(self.apertures_table.currentRow(), 1).text())
                 if len(self.apertures_table.selectionModel().selectedRows()) == 0:
                 if len(self.apertures_table.selectionModel().selectedRows()) == 0:
-                    self.app.inform.emit('[WARNING_NOTCL] %s' % _(" Select an aperture in Aperture Table"))
+                    self.app.inform.emit('[WARNING_NOTCL]%s' % _(" Select an aperture in Aperture Table"))
                     return
                     return
 
 
                 deleted_apcode_list = []
                 deleted_apcode_list = []
@@ -3692,8 +3685,10 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.shapes.enabled = True
         self.shapes.enabled = True
         self.tool_shape.enabled = True
         self.tool_shape.enabled = True
 
 
-        self.app.ui.corner_snap_btn.setVisible(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.snap_magnet.setVisible(True)
+        self.app.ui.corner_snap_btn.setVisible(True)
 
 
         self.app.ui.grb_editor_menu.setDisabled(False)
         self.app.ui.grb_editor_menu.setDisabled(False)
         self.app.ui.grb_editor_menu.menuAction().setVisible(True)
         self.app.ui.grb_editor_menu.menuAction().setVisible(True)
@@ -3703,11 +3698,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
 
         self.app.ui.grb_edit_toolbar.setDisabled(False)
         self.app.ui.grb_edit_toolbar.setDisabled(False)
         self.app.ui.grb_edit_toolbar.setVisible(True)
         self.app.ui.grb_edit_toolbar.setVisible(True)
-        # self.app.ui.status_toolbar.setDisabled(False)
+        # self.app.ui.snap_toolbar.setDisabled(False)
 
 
         # start with GRID toolbar activated
         # start with GRID toolbar activated
         if self.app.ui.grid_snap_btn.isChecked() is False:
         if self.app.ui.grid_snap_btn.isChecked() is False:
             self.app.ui.grid_snap_btn.trigger()
             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
         # adjust the visibility of some of the canvas context menu
         self.app.ui.popmenu_edit.setVisible(False)
         self.app.ui.popmenu_edit.setVisible(False)
@@ -3740,8 +3736,29 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.app.ui.grb_edit_toolbar.setDisabled(True)
         self.app.ui.grb_edit_toolbar.setDisabled(True)
 
 
         settings = QSettings("Open Source", "FlatCAM")
         settings = QSettings("Open Source", "FlatCAM")
-        self.app.ui.corner_snap_btn.setVisible(False)
-        self.app.ui.snap_magnet.setVisible(False)
+        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)
 
 
         # set the Editor Toolbar visibility to what was before entering in the Editor
         # 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 \
         self.app.ui.grb_edit_toolbar.setVisible(False) if self.toolbar_old_state is False \
@@ -4193,7 +4210,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
 
     def on_multiprocessing_finished(self):
     def on_multiprocessing_finished(self):
         self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
         self.app.proc_container.update_view_text(' %s' % _("Setting up the UI"))
-        self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the AppGUI"))
+        self.app.inform.emit('[success] %s.' % _("Adding geometry finished. Preparing the GUI"))
         self.set_ui()
         self.set_ui()
         self.build_ui(first_run=True)
         self.build_ui(first_run=True)
         self.plot_all()
         self.plot_all()
@@ -4228,7 +4245,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
             new_grb_name = self.edited_obj_name + "_edit"
             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.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
     @staticmethod
     def update_options(obj):
     def update_options(obj):
@@ -4250,10 +4266,9 @@ class FlatCAMGrbEditor(QtCore.QObject):
         """
         """
         Creates a new Gerber object for the edited Gerber. Thread-safe.
         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
-        :type aperture_storage:     dict
+        :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
         :return: None
         :return: None
         """
         """
 
 
@@ -4345,27 +4360,26 @@ class FlatCAMGrbEditor(QtCore.QObject):
                 self.app.inform.emit('[ERROR_NOTCL] %s' %
                 self.app.inform.emit('[ERROR_NOTCL] %s' %
                                      _("There are no Aperture definitions in the file. Aborting Gerber creation."))
                                      _("There are no Aperture definitions in the file. Aborting Gerber creation."))
             except Exception:
             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()
                 msg += traceback.format_exc()
                 app_obj.inform.emit(msg)
                 app_obj.inform.emit(msg)
                 raise
                 raise
-
             grb_obj.source_file = self.app.export_gerber(obj_name=out_name, filename=None,
             grb_obj.source_file = self.app.export_gerber(obj_name=out_name, filename=None,
                                                          local_use=grb_obj, use_thread=False)
                                                          local_use=grb_obj, use_thread=False)
 
 
         with self.app.proc_container.new(_("Creating Gerber.")):
         with self.app.proc_container.new(_("Creating Gerber.")):
             try:
             try:
-                self.app.app_obj.new_object("gerber", outname, obj_init)
+                self.app.new_object("gerber", outname, obj_init)
             except Exception as e:
             except Exception as e:
                 log.error("Error on Edited object creation: %s" % str(e))
                 log.error("Error on Edited object creation: %s" % str(e))
                 # make sure to clean the previous results
                 # make sure to clean the previous results
                 self.results = []
                 self.results = []
                 return
                 return
 
 
+            self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished."))
             # make sure to clean the previous results
             # make sure to clean the previous results
             self.results = []
             self.results = []
-            self.deactivate_grb_editor()
-            self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished."))
 
 
     def on_tool_select(self, tool):
     def on_tool_select(self, tool):
         """
         """
@@ -4523,8 +4537,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
             self.pos = (self.pos[0], self.pos[1])
             self.pos = (self.pos[0], self.pos[1])
 
 
         if event.button == 1:
         if event.button == 1:
-            # self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-            #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
+            self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
+                                                   "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (0, 0))
 
 
             # Selection with left mouse button
             # Selection with left mouse button
             if self.active_tool is not None:
             if self.active_tool is not None:
@@ -4536,7 +4550,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
                         self.app.defaults["global_point_clipboard_format"] %
                         self.app.defaults["global_point_clipboard_format"] %
                         (self.decimals, self.pos[0], self.decimals, self.pos[1])
                         (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
                     return
 
 
                 # Dispatch event to active_tool
                 # Dispatch event to active_tool
@@ -4547,7 +4562,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
                     if self.current_storage is not None:
                     if self.current_storage is not None:
                         self.on_grb_shape_complete(self.current_storage)
                         self.on_grb_shape_complete(self.current_storage)
                         self.build_ui()
                         self.build_ui()
-
                     # MS: always return to the Select Tool if modifier key is not pressed
                     # MS: always return to the Select Tool if modifier key is not pressed
                     # else return to the current tool
                     # else return to the current tool
                     key_modifier = QtWidgets.QApplication.keyboardModifiers()
                     key_modifier = QtWidgets.QApplication.keyboardModifiers()
@@ -4555,7 +4569,6 @@ class FlatCAMGrbEditor(QtCore.QObject):
                         modifier_to_use = Qt.ControlModifier
                         modifier_to_use = Qt.ControlModifier
                     else:
                     else:
                         modifier_to_use = Qt.ShiftModifier
                         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
                     # 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.
                     # in the selected list, we removed it. Therefore first click selects, second deselects.
                     if key_modifier == modifier_to_use:
                     if key_modifier == modifier_to_use:
@@ -4616,14 +4629,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
                         # if right click on canvas and the active tool need to be finished (like Path or Polygon)
                         # if right click on canvas and the active tool need to be finished (like Path or Polygon)
                         # right mouse click will finish the action
                         # right mouse click will finish the action
                         if isinstance(self.active_tool, FCShapeTool):
                         if isinstance(self.active_tool, FCShapeTool):
-                            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()
+                            self.active_tool.click(self.app.geo_editor.snap(self.x, self.y))
+                            self.active_tool.make()
                             if self.active_tool.complete:
                             if self.active_tool.complete:
                                 self.on_grb_shape_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
                                 # MS: always return to the Select Tool if modifier key is not pressed
                                 # else return to the current tool but not for FCTrack
                                 # else return to the current tool but not for FCTrack
@@ -4763,23 +4774,18 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
 
         self.app.mouse = [x, 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("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   " 
+                                           "<b>Y</b>: %.4f" % (x, y))
+
         if self.pos is None:
         if self.pos is None:
             self.pos = (0, 0)
             self.pos = (0, 0)
         self.app.dx = x - self.pos[0]
         self.app.dx = x - self.pos[0]
         self.app.dy = y - self.pos[1]
         self.app.dy = y - self.pos[1]
 
 
-        # # update the position label in the infobar since the APP mouse event handlers are disconnected
-        self.app.ui.position_label.setText("&nbsp;<b>X</b>: %.4f&nbsp;&nbsp;   "
-                                           "<b>Y</b>: %.4f&nbsp;" % (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("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: "
-        #                                        "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (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)
+        # update the reference position label in the infobar since the APP mouse event handlers are disconnected
+        self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp;  <b>Dy</b>: " 
+                                               "%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (self.app.dx, self.app.dy))
 
 
         self.update_utility_geometry(data=(x, y))
         self.update_utility_geometry(data=(x, y))
 
 
@@ -5026,7 +5032,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
 
 
     def select_tool(self, toolname):
     def select_tool(self, toolname):
         """
         """
-        Selects a drawing tool. Impacts the object and AppGUI.
+        Selects a drawing tool. Impacts the object and GUI.
 
 
         :param toolname: Name of the tool.
         :param toolname: Name of the tool.
         :return: None
         :return: None
@@ -5292,7 +5298,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
         self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
         self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
 
 
 
 
-class TransformEditorTool(AppTool):
+class TransformEditorTool(FlatCAMTool):
     """
     """
     Inputs to specify how to paint the selected polygons.
     Inputs to specify how to paint the selected polygons.
     """
     """
@@ -5305,7 +5311,7 @@ class TransformEditorTool(AppTool):
     offsetName = _("Offset")
     offsetName = _("Offset")
 
 
     def __init__(self, app, draw_app):
     def __init__(self, app, draw_app):
-        AppTool.__init__(self, app)
+        FlatCAMTool.__init__(self, app)
 
 
         self.app = app
         self.app = app
         self.draw_app = draw_app
         self.draw_app = draw_app
@@ -5691,13 +5697,13 @@ class TransformEditorTool(AppTool):
             except AttributeError:
             except AttributeError:
                 pass
                 pass
 
 
-        AppTool.run(self)
+        FlatCAMTool.run(self)
         self.set_tool_ui()
         self.set_tool_ui()
 
 
         self.app.ui.notebook.setTabText(2, _("Transform Tool"))
         self.app.ui.notebook.setTabText(2, _("Transform Tool"))
 
 
     def install(self, icon=None, separator=None, **kwargs):
     def install(self, icon=None, separator=None, **kwargs):
-        AppTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
+        FlatCAMTool.install(self, icon, separator, shortcut='Alt+T', **kwargs)
 
 
     def set_tool_ui(self):
     def set_tool_ui(self):
         # Initialize form
         # Initialize form

+ 4 - 4
AppEditors/FlatCAMTextEditor.py → flatcamEditors/FlatCAMTextEditor.py

@@ -5,7 +5,7 @@
 # MIT Licence                                              #
 # MIT Licence                                              #
 # ##########################################################
 # ##########################################################
 
 
-from AppGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
+from flatcamGUI.GUIElements import FCFileSaveDialog, FCEntry, FCTextAreaExtended, FCTextAreaLineNumber
 from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
 from PyQt5 import QtPrintSupport, QtWidgets, QtCore, QtGui
 
 
 from reportlab.platypus import SimpleDocTemplate, Paragraph
 from reportlab.platypus import SimpleDocTemplate, Paragraph
@@ -15,7 +15,7 @@ from reportlab.lib.units import inch, mm
 # from io import StringIO
 # from io import StringIO
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -214,10 +214,10 @@ class TextEditor(QtWidgets.QWidget):
             filename = str(FCFileSaveDialog.get_saved_filename(
             filename = str(FCFileSaveDialog.get_saved_filename(
                 caption=_("Export Code ..."),
                 caption=_("Export Code ..."),
                 directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name),
                 directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name),
-                ext_filter=_filter_
+                filter=_filter_
             )[0])
             )[0])
         except TypeError:
         except TypeError:
-            filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), ext_filter=_filter_)[0])
+            filename = str(FCFileSaveDialog.get_saved_filename(caption=_("Export Code ..."), filter=_filter_)[0])
 
 
         if filename == "":
         if filename == "":
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))
             self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled."))

+ 0 - 0
AppEditors/__init__.py → flatcamEditors/__init__.py


File diff suppressed because it is too large
+ 105 - 2449
flatcamGUI/FlatCAMGUI.py


+ 14 - 391
AppGUI/GUIElements.py → flatcamGUI/GUIElements.py

@@ -23,7 +23,7 @@ import html
 import sys
 import sys
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 log = logging.getLogger('base')
 log = logging.getLogger('base')
@@ -570,13 +570,9 @@ class FCEntry3(FCEntry):
 
 
 
 
 class EvalEntry(QtWidgets.QLineEdit):
 class EvalEntry(QtWidgets.QLineEdit):
-    def __init__(self, border_color=None, parent=None):
+    def __init__(self, parent=None):
         super(EvalEntry, self).__init__(parent)
         super(EvalEntry, self).__init__(parent)
         self.readyToEdit = True
         self.readyToEdit = True
-
-        if border_color:
-            self.setStyleSheet("QLineEdit {border: 1px solid %s;}" % border_color)
-
         self.editingFinished.connect(self.on_edit_finished)
         self.editingFinished.connect(self.on_edit_finished)
 
 
     def on_edit_finished(self):
     def on_edit_finished(self):
@@ -603,6 +599,7 @@ class EvalEntry(QtWidgets.QLineEdit):
 
 
     def get_value(self):
     def get_value(self):
         raw = str(self.text()).strip(' ')
         raw = str(self.text()).strip(' ')
+        evaled = 0.0
         try:
         try:
             evaled = eval(raw)
             evaled = eval(raw)
         except Exception as e:
         except Exception as e:
@@ -642,7 +639,7 @@ class EvalEntry2(QtWidgets.QLineEdit):
 
 
     def get_value(self):
     def get_value(self):
         raw = str(self.text()).strip(' ')
         raw = str(self.text()).strip(' ')
-
+        evaled = 0.0
         try:
         try:
             evaled = eval(raw)
             evaled = eval(raw)
         except Exception as e:
         except Exception as e:
@@ -659,132 +656,6 @@ class EvalEntry2(QtWidgets.QLineEdit):
         return QtCore.QSize(EDIT_SIZE_HINT, default_hint_size.height())
         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 FCColorEntry(QtWidgets.QFrame):
-
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
-
-        self.entry = FCEntry()
-        regex = QtCore.QRegExp("[#A-Fa-f0-9]*")
-        validator = QtGui.QRegExpValidator(regex, self.entry)
-        self.entry.setValidator(validator)
-
-        self.button = QtWidgets.QPushButton()
-        self.button.setFixedSize(15, 15)
-        self.button.setStyleSheet("border-color: dimgray;")
-
-        self.layout = QtWidgets.QHBoxLayout()
-        self.layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-        self.layout.setContentsMargins(0, 0, 0, 0)
-        self.layout.addWidget(self.entry)
-        self.layout.addWidget(self.button)
-        self.setLayout(self.layout)
-
-        self.entry.editingFinished.connect(self._sync_button_color)
-        self.button.clicked.connect(self._on_button_clicked)
-
-        self.editingFinished = self.entry.editingFinished
-
-    def get_value(self) -> str:
-        return self.entry.get_value()
-
-    def set_value(self, value: str):
-        self.entry.set_value(value)
-        self._sync_button_color()
-
-    def _sync_button_color(self):
-        value = self.get_value()
-        self.button.setStyleSheet("background-color:%s;" % self._extract_color(value))
-
-    def _on_button_clicked(self):
-        value = self.entry.get_value()
-        current_color = QtGui.QColor(self._extract_color(value))
-
-        color_dialog = QtWidgets.QColorDialog()
-        selected_color = color_dialog.getColor(initial=current_color, options=QtWidgets.QColorDialog.ShowAlphaChannel)
-
-        if selected_color.isValid() is False:
-            return
-
-        new_value = str(selected_color.name()) + self._extract_alpha(value)
-        self.set_value(new_value)
-
-    def _extract_color(self, value: str) -> str:
-        return value[:7]
-
-    def _extract_alpha(self, value: str) -> str:
-        return value[7:9]
-
-
-class FCSliderWithSpinner(QtWidgets.QFrame):
-
-    def __init__(self, min=0, max=100, step=1, **kwargs):
-        super().__init__(**kwargs)
-
-        self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
-        self.slider.setMinimum(min)
-        self.slider.setMaximum(max)
-        self.slider.setSingleStep(step)
-
-        self.spinner = FCSpinner()
-        self.spinner.set_range(min, max)
-        self.spinner.set_step(step)
-        self.spinner.setMinimumWidth(70)
-
-        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
-        self.spinner.setSizePolicy(sizePolicy)
-
-        self.layout = QtWidgets.QHBoxLayout()
-        self.layout.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
-        self.layout.setContentsMargins(0, 0, 0, 0)
-        self.layout.addWidget(self.slider)
-        self.layout.addWidget(self.spinner)
-        self.setLayout(self.layout)
-
-        self.slider.valueChanged.connect(self._on_slider)
-        self.spinner.valueChanged.connect(self._on_spinner)
-
-        self.valueChanged = self.spinner.valueChanged
-
-    def get_value(self) -> int:
-        return self.spinner.get_value()
-
-    def set_value(self, value: int):
-        self.spinner.set_value(value)
-
-    def _on_spinner(self):
-        spinner_value = self.spinner.value()
-        self.slider.setValue(spinner_value)
-
-    def _on_slider(self):
-        slider_value = self.slider.value()
-        self.spinner.set_value(slider_value)
-
-
 class FCSpinner(QtWidgets.QSpinBox):
 class FCSpinner(QtWidgets.QSpinBox):
 
 
     returnPressed = QtCore.pyqtSignal()
     returnPressed = QtCore.pyqtSignal()
@@ -813,8 +684,6 @@ class FCSpinner(QtWidgets.QSpinBox):
             self.setAlignment(align_val)
             self.setAlignment(align_val)
 
 
         self.prev_readyToEdit = True
         self.prev_readyToEdit = True
-        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred)
-        self.setSizePolicy(sizePolicy)
 
 
     def eventFilter(self, object, event):
     def eventFilter(self, object, event):
         if event.type() == QtCore.QEvent.MouseButtonPress and self.prev_readyToEdit is True:
         if event.type() == QtCore.QEvent.MouseButtonPress and self.prev_readyToEdit is True:
@@ -947,8 +816,6 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
             self.setAlignment(align_val)
             self.setAlignment(align_val)
 
 
         self.prev_readyToEdit = True
         self.prev_readyToEdit = True
-        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Preferred)
-        self.setSizePolicy(sizePolicy)
 
 
     def on_edit_finished(self):
     def on_edit_finished(self):
         self.clearFocus()
         self.clearFocus()
@@ -2231,24 +2098,6 @@ class FCDetachableTab2(FCDetachableTab):
     def __init__(self, protect=None, protect_by_name=None, parent=None):
     def __init__(self, protect=None, protect_by_name=None, parent=None):
         super(FCDetachableTab2, self).__init__(protect=protect, protect_by_name=protect_by_name, parent=parent)
         super(FCDetachableTab2, self).__init__(protect=protect, protect_by_name=protect_by_name, parent=parent)
 
 
-        try:
-            self.tabBar.onCloseTabSignal.disconnect()
-        except TypeError:
-            pass
-
-        self.tabBar.onCloseTabSignal.connect(self.on_closetab_middle_button)
-
-    def on_closetab_middle_button(self, current_index):
-        """
-
-        :param current_index:
-        :return:
-        """
-
-        # if tab is protected don't delete it
-        if self.tabBar.tabButton(current_index, QtWidgets.QTabBar.RightSide) is not None:
-            self.closeTab(current_index)
-
     def closeTab(self, currentIndex):
     def closeTab(self, currentIndex):
         """
         """
         Slot connected to the tabCloseRequested signal
         Slot connected to the tabCloseRequested signal
@@ -2347,14 +2196,11 @@ class OptionalHideInputSection:
         """
         """
         Associates the a checkbox with a set of inputs.
         Associates the a checkbox with a set of inputs.
 
 
-        :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
+        :param cb: Checkbox that enables the optional inputs.
+        :param optinputs: List of widgets that are optional.
+        :param logic: When True the logic is normal, when False the logic is in reverse
+        It means that for logic=True, when the checkbox is checked the widgets are Enabled, and
+        for logic=False, when the checkbox is checked the widgets are Disabled
         :return:
         :return:
         """
         """
         assert isinstance(cb, FCCheckBox), \
         assert isinstance(cb, FCCheckBox), \
@@ -2625,8 +2471,7 @@ class SpinBoxDelegate(QtWidgets.QItemDelegate):
     def updateEditorGeometry(self, editor, option, index):
     def updateEditorGeometry(self, editor, option, index):
         editor.setGeometry(option.rect)
         editor.setGeometry(option.rect)
 
 
-    @staticmethod
-    def setDecimals(spinbox, digits):
+    def setDecimals(self, spinbox, digits):
         spinbox.setDecimals(digits)
         spinbox.setDecimals(digits)
 
 
 
 
@@ -2674,7 +2519,7 @@ class DialogBoxRadio(QtWidgets.QDialog):
         :param title: string with the window title
         :param title: string with the window title
         :param label: string with the message inside the dialog box
         :param label: string with the message inside the dialog box
         """
         """
-        super(DialogBoxRadio, self).__init__(parent=parent)
+        super(DialogBoxRadio, self).__init__()
         if initial_text is None:
         if initial_text is None:
             self.location = str((0, 0))
             self.location = str((0, 0))
         else:
         else:
@@ -2917,12 +2762,9 @@ class MyCompleter(QCompleter):
     insertText = QtCore.pyqtSignal(str)
     insertText = QtCore.pyqtSignal(str)
 
 
     def __init__(self, parent=None):
     def __init__(self, parent=None):
-        QCompleter.__init__(self, parent=parent)
+        QCompleter.__init__(self)
         self.setCompletionMode(QCompleter.PopupCompletion)
         self.setCompletionMode(QCompleter.PopupCompletion)
         self.highlighted.connect(self.setHighlighted)
         self.highlighted.connect(self.setHighlighted)
-
-        self.lastSelected = ''
-
         # self.popup().installEventFilter(self)
         # self.popup().installEventFilter(self)
 
 
     # def eventFilter(self, obj, event):
     # def eventFilter(self, obj, event):
@@ -3078,9 +2920,9 @@ class FCFileSaveDialog(QtWidgets.QFileDialog):
         super(FCFileSaveDialog, self).__init__(*args)
         super(FCFileSaveDialog, self).__init__(*args)
 
 
     @staticmethod
     @staticmethod
-    def get_saved_filename(parent=None, caption='', directory='', ext_filter='', initialFilter=''):
+    def get_saved_filename(parent=None, caption='', directory='', filter='', initialFilter=''):
         filename, _filter = QtWidgets.QFileDialog.getSaveFileName(parent=parent, caption=caption,
         filename, _filter = QtWidgets.QFileDialog.getSaveFileName(parent=parent, caption=caption,
-                                                                  directory=directory, filter=ext_filter,
+                                                                  directory=directory, filter=filter,
                                                                   initialFilter=initialFilter)
                                                                   initialFilter=initialFilter)
 
 
         filename = str(filename)
         filename = str(filename)
@@ -3096,225 +2938,6 @@ class FCFileSaveDialog(QtWidgets.QFileDialog):
             return filename, _filter
             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):
 def rreplace(s, old, new, occurrence):
     """
     """
     Credits go here:
     Credits go here:

+ 351 - 151
AppGUI/ObjectUI.py → flatcamGUI/ObjectUI.py

@@ -11,11 +11,11 @@
 # Date: 3/10/2019                                          #
 # Date: 3/10/2019                                          #
 # ##########################################################
 # ##########################################################
 
 
-from AppGUI.GUIElements import *
+from flatcamGUI.GUIElements import *
 import sys
 import sys
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -35,7 +35,7 @@ class ObjectUI(QtWidgets.QWidget):
     put UI elements in ObjectUI.custom_box (QtWidgets.QLayout).
     put UI elements in ObjectUI.custom_box (QtWidgets.QLayout).
     """
     """
 
 
-    def __init__(self, app, icon_file='assets/resources/flatcam_icon32.png', title=_('App Object'),
+    def __init__(self, app, icon_file='assets/resources/flatcam_icon32.png', title=_('FlatCAM Object'),
                  parent=None, common=True):
                  parent=None, common=True):
         QtWidgets.QWidget.__init__(self, parent=parent)
         QtWidgets.QWidget.__init__(self, parent=parent)
 
 
@@ -114,7 +114,7 @@ class ObjectUI(QtWidgets.QWidget):
             self.common_grid.addWidget(self.transform_label, 2, 0, 1, 2)
             self.common_grid.addWidget(self.transform_label, 2, 0, 1, 2)
 
 
             # ### Scale ####
             # ### Scale ####
-            self.scale_entry = NumericalEvalEntry(border_color='#0069A9')
+            self.scale_entry = FCEntry()
             self.scale_entry.set_value(1.0)
             self.scale_entry.set_value(1.0)
             self.scale_entry.setToolTip(
             self.scale_entry.setToolTip(
                 _("Factor by which to multiply\n"
                 _("Factor by which to multiply\n"
@@ -132,7 +132,7 @@ class ObjectUI(QtWidgets.QWidget):
             self.common_grid.addWidget(self.scale_button, 3, 1)
             self.common_grid.addWidget(self.scale_button, 3, 1)
 
 
             # ### Offset ####
             # ### Offset ####
-            self.offsetvector_entry = NumericalEvalTupleEntry(border_color='#0069A9')
+            self.offsetvector_entry = EvalEntry2()
             self.offsetvector_entry.setText("(0.0, 0.0)")
             self.offsetvector_entry.setText("(0.0, 0.0)")
             self.offsetvector_entry.setToolTip(
             self.offsetvector_entry.setToolTip(
                 _("Amount by which to move the object\n"
                 _("Amount by which to move the object\n"
@@ -149,30 +149,21 @@ class ObjectUI(QtWidgets.QWidget):
             self.common_grid.addWidget(self.offsetvector_entry, 4, 0)
             self.common_grid.addWidget(self.offsetvector_entry, 4, 0)
             self.common_grid.addWidget(self.offset_button, 4, 1)
             self.common_grid.addWidget(self.offset_button, 4, 1)
 
 
-            self.transformations_button = QtWidgets.QPushButton(_('Transformations'))
-            self.transformations_button.setToolTip(
-                _("Geometrical transformations of the current object.")
-            )
-            self.common_grid.addWidget(self.transformations_button, 5, 0, 1, 2)
-
         layout.addStretch()
         layout.addStretch()
     
     
     def confirmation_message(self, accepted, minval, maxval):
     def confirmation_message(self, accepted, minval, maxval):
         if accepted is False:
         if accepted is False:
-            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
-                                                                                  self.decimals,
-                                                                                  minval,
-                                                                                  self.decimals,
-                                                                                  maxval), False)
+            self.app.inform.emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' %
+                                 (_("Edited value is out of range"), self.decimals, minval, self.decimals, maxval))
         else:
         else:
-            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+            self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
 
 
     def confirmation_message_int(self, accepted, minval, maxval):
     def confirmation_message_int(self, accepted, minval, maxval):
         if accepted is False:
         if accepted is False:
-            self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
-                                 (_("Edited value is out of range"), minval, maxval), False)
+            self.app.inform.emit('[WARNING_NOTCL] %s: [%d, %d]' %
+                                 (_("Edited value is out of range"), minval, maxval))
         else:
         else:
-            self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
+            self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
 
 
 
 
 class GerberObjectUI(ObjectUI):
 class GerberObjectUI(ObjectUI):
@@ -214,37 +205,27 @@ class GerberObjectUI(ObjectUI):
         self.multicolored_cb.setMinimumWidth(55)
         self.multicolored_cb.setMinimumWidth(55)
         grid0.addWidget(self.multicolored_cb, 0, 2)
         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
         # ## Object name
         self.name_hlay = QtWidgets.QHBoxLayout()
         self.name_hlay = QtWidgets.QHBoxLayout()
-        grid0.addLayout(self.name_hlay, 1, 0, 1, 3)
-
+        self.custom_box.addLayout(self.name_hlay)
         name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
         name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
         self.name_entry = FCEntry()
         self.name_entry = FCEntry()
         self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
         self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
         self.name_hlay.addWidget(name_label)
         self.name_hlay.addWidget(name_label)
         self.name_hlay.addWidget(self.name_entry)
         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()
         hlay_plot = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(hlay_plot)
         self.custom_box.addLayout(hlay_plot)
 
 
         # ### Gerber Apertures ####
         # ### Gerber Apertures ####
-        self.apertures_table_label = QtWidgets.QLabel('%s:' % _('Apertures'))
+        self.apertures_table_label = QtWidgets.QLabel('<b>%s:</b>' % _('Apertures'))
         self.apertures_table_label.setToolTip(
         self.apertures_table_label.setToolTip(
             _("Apertures Table for the Gerber Object.")
             _("Apertures Table for the Gerber Object.")
         )
         )
@@ -301,7 +282,254 @@ class GerberObjectUI(ObjectUI):
         # start with apertures table hidden
         # start with apertures table hidden
         self.apertures_table.setVisible(False)
         self.apertures_table.setVisible(False)
 
 
-        # Buffer Geometry
+        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("<b>%s</b>" % _("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('<b>%s:</b>' % _('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('<b>%s:</b>' % _('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)
+
         self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry'))
         self.create_buffer_button = QtWidgets.QPushButton(_('Buffer Solid Geometry'))
         self.create_buffer_button.setToolTip(
         self.create_buffer_button.setToolTip(
             _("This button is shown only when the Gerber file\n"
             _("This button is shown only when the Gerber file\n"
@@ -309,12 +537,19 @@ class GerberObjectUI(ObjectUI):
               "Clicking this will create the buffered geometry\n"
               "Clicking this will create the buffered geometry\n"
               "required for isolation.")
               "required for isolation.")
         )
         )
-        self.custom_box.addWidget(self.create_buffer_button)
+        grid1.addWidget(self.create_buffer_button, 13, 0, 1, 3)
 
 
-        separator_line = QtWidgets.QFrame()
-        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
-        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        self.custom_box.addWidget(separator_line)
+        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)
 
 
         # ###########################################
         # ###########################################
         # ########## NEW GRID #######################
         # ########## NEW GRID #######################
@@ -328,21 +563,14 @@ class GerberObjectUI(ObjectUI):
         self.tool_lbl = QtWidgets.QLabel('<b>%s</b>' % _("TOOLS"))
         self.tool_lbl = QtWidgets.QLabel('<b>%s</b>' % _("TOOLS"))
         grid2.addWidget(self.tool_lbl, 0, 0, 1, 2)
         grid2.addWidget(self.tool_lbl, 0, 0, 1, 2)
 
 
-        # Isolation Tool - will create isolation paths around the copper features
-        self.iso_button = QtWidgets.QPushButton(_('Isolation Routing'))
-        self.iso_button.setToolTip(
+        # ## Clear non-copper regions
+        self.clearcopper_label = QtWidgets.QLabel("%s" % _("Clear N-copper"))
+        self.clearcopper_label.setToolTip(
             _("Create a Geometry object with\n"
             _("Create a Geometry object with\n"
-              "toolpaths to cut around polygons.")
+              "toolpaths to cut all non-copper regions.")
         )
         )
-        self.iso_button.setStyleSheet("""
-                                      QPushButton
-                                      {
-                                          font-weight: bold;
-                                      }
-                                      """)
-        grid2.addWidget(self.iso_button, 1, 0, 1, 2)
+        self.clearcopper_label.setMinimumWidth(90)
 
 
-        # ## Clear non-copper regions
         self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
         self.generate_ncc_button = QtWidgets.QPushButton(_('NCC Tool'))
         self.generate_ncc_button.setToolTip(
         self.generate_ncc_button.setToolTip(
             _("Create the Geometry Object\n"
             _("Create the Geometry Object\n"
@@ -354,9 +582,17 @@ class GerberObjectUI(ObjectUI):
                             font-weight: bold;
                             font-weight: bold;
                         }
                         }
                         """)
                         """)
-        grid2.addWidget(self.generate_ncc_button, 2, 0, 1, 2)
+        grid2.addWidget(self.clearcopper_label, 1, 0)
+        grid2.addWidget(self.generate_ncc_button, 1, 1)
 
 
         # ## Board cutout
         # ## 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 = QtWidgets.QPushButton(_('Cutout Tool'))
         self.generate_cutout_button.setToolTip(
         self.generate_cutout_button.setToolTip(
             _("Generate the geometry for\n"
             _("Generate the geometry for\n"
@@ -368,12 +604,13 @@ class GerberObjectUI(ObjectUI):
                             font-weight: bold;
                             font-weight: bold;
                         }
                         }
                         """)
                         """)
-        grid2.addWidget(self.generate_cutout_button, 3, 0, 1, 2)
+        grid2.addWidget(self.board_cutout_label, 2, 0)
+        grid2.addWidget(self.generate_cutout_button, 2, 1)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid2.addWidget(separator_line, 4, 0, 1, 2)
+        grid2.addWidget(separator_line, 3, 0, 1, 2)
 
 
         # ## Non-copper regions
         # ## Non-copper regions
         self.noncopper_label = QtWidgets.QLabel("<b>%s</b>" % _("Non-copper regions"))
         self.noncopper_label = QtWidgets.QLabel("<b>%s</b>" % _("Non-copper regions"))
@@ -385,7 +622,7 @@ class GerberObjectUI(ObjectUI):
               "copper from a specified region.")
               "copper from a specified region.")
         )
         )
 
 
-        grid2.addWidget(self.noncopper_label, 5, 0, 1, 2)
+        grid2.addWidget(self.noncopper_label, 4, 0, 1, 2)
 
 
         # Margin
         # Margin
         bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
         bmlabel = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
@@ -401,8 +638,8 @@ class GerberObjectUI(ObjectUI):
         self.noncopper_margin_entry.set_precision(self.decimals)
         self.noncopper_margin_entry.set_precision(self.decimals)
         self.noncopper_margin_entry.setSingleStep(0.1)
         self.noncopper_margin_entry.setSingleStep(0.1)
 
 
-        grid2.addWidget(bmlabel, 6, 0)
-        grid2.addWidget(self.noncopper_margin_entry, 6, 1)
+        grid2.addWidget(bmlabel, 5, 0)
+        grid2.addWidget(self.noncopper_margin_entry, 5, 1)
 
 
         # Rounded corners
         # Rounded corners
         self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
         self.noncopper_rounded_cb = FCCheckBox(label=_("Rounded Geo"))
@@ -412,13 +649,13 @@ class GerberObjectUI(ObjectUI):
         self.noncopper_rounded_cb.setMinimumWidth(90)
         self.noncopper_rounded_cb.setMinimumWidth(90)
 
 
         self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
         self.generate_noncopper_button = QtWidgets.QPushButton(_('Generate Geo'))
-        grid2.addWidget(self.noncopper_rounded_cb, 7, 0)
-        grid2.addWidget(self.generate_noncopper_button, 7, 1)
+        grid2.addWidget(self.noncopper_rounded_cb, 6, 0)
+        grid2.addWidget(self.generate_noncopper_button, 6, 1)
 
 
         separator_line1 = QtWidgets.QFrame()
         separator_line1 = QtWidgets.QFrame()
         separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line1.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line1.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid2.addWidget(separator_line1, 8, 0, 1, 2)
+        grid2.addWidget(separator_line1, 7, 0, 1, 2)
 
 
         # ## Bounding box
         # ## Bounding box
         self.boundingbox_label = QtWidgets.QLabel('<b>%s</b>' % _('Bounding Box'))
         self.boundingbox_label = QtWidgets.QLabel('<b>%s</b>' % _('Bounding Box'))
@@ -427,7 +664,7 @@ class GerberObjectUI(ObjectUI):
               "Square shape.")
               "Square shape.")
         )
         )
 
 
-        grid2.addWidget(self.boundingbox_label, 9, 0, 1, 2)
+        grid2.addWidget(self.boundingbox_label, 8, 0, 1, 2)
 
 
         bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
         bbmargin = QtWidgets.QLabel('%s:' % _('Boundary Margin'))
         bbmargin.setToolTip(
         bbmargin.setToolTip(
@@ -440,8 +677,8 @@ class GerberObjectUI(ObjectUI):
         self.bbmargin_entry.set_precision(self.decimals)
         self.bbmargin_entry.set_precision(self.decimals)
         self.bbmargin_entry.setSingleStep(0.1)
         self.bbmargin_entry.setSingleStep(0.1)
 
 
-        grid2.addWidget(bbmargin, 10, 0)
-        grid2.addWidget(self.bbmargin_entry, 10, 1)
+        grid2.addWidget(bbmargin, 9, 0)
+        grid2.addWidget(self.bbmargin_entry, 9, 1)
 
 
         self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo"))
         self.bbrounded_cb = FCCheckBox(label=_("Rounded Geo"))
         self.bbrounded_cb.setToolTip(
         self.bbrounded_cb.setToolTip(
@@ -456,8 +693,8 @@ class GerberObjectUI(ObjectUI):
         self.generate_bb_button.setToolTip(
         self.generate_bb_button.setToolTip(
             _("Generate the Geometry object.")
             _("Generate the Geometry object.")
         )
         )
-        grid2.addWidget(self.bbrounded_cb, 11, 0)
-        grid2.addWidget(self.generate_bb_button, 11, 1)
+        grid2.addWidget(self.bbrounded_cb, 10, 0)
+        grid2.addWidget(self.generate_bb_button, 10, 1)
 
 
 
 
 class ExcellonObjectUI(ObjectUI):
 class ExcellonObjectUI(ObjectUI):
@@ -486,38 +723,22 @@ class ExcellonObjectUI(ObjectUI):
                           parent=parent,
                           parent=parent,
                           app=self.app)
                           app=self.app)
 
 
-        # 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)
+        # ### Plot options ####
+        hlay_plot = QtWidgets.QHBoxLayout()
+        self.custom_box.addLayout(hlay_plot)
 
 
         self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
         self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("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 = FCCheckBox(label=_('Solid'))
         self.solid_cb.setToolTip(
         self.solid_cb.setToolTip(
             _("Solid circles.")
             _("Solid circles.")
         )
         )
-        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)
+        hlay_plot.addWidget(self.plot_options_label)
+        hlay_plot.addStretch()
+        hlay_plot.addWidget(self.solid_cb)
 
 
         # ## Object name
         # ## Object name
         self.name_hlay = QtWidgets.QHBoxLayout()
         self.name_hlay = QtWidgets.QHBoxLayout()
-        grid_h.addLayout(self.name_hlay, 1, 0, 1, 3)
-
+        self.custom_box.addLayout(self.name_hlay)
         name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
         name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
         self.name_entry = FCEntry()
         self.name_entry = FCEntry()
         self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
         self.name_entry.setFocusPolicy(QtCore.Qt.StrongFocus)
@@ -1304,28 +1525,12 @@ class GeometryObjectUI(ObjectUI):
         )
         )
 
 
         # Plot options
         # 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("<b>%s:</b>" % _("Plot Options"))
         self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
-        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)
+        self.custom_box.addWidget(self.plot_options_label)
 
 
         # ## Object name
         # ## Object name
         self.name_hlay = QtWidgets.QHBoxLayout()
         self.name_hlay = QtWidgets.QHBoxLayout()
-        grid_header.addLayout(self.name_hlay, 1, 0, 1, 3)
+        self.custom_box.addLayout(self.name_hlay)
 
 
         name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
         name_label = QtWidgets.QLabel("<b>%s:</b>" % _("Name"))
         self.name_entry = FCEntry()
         self.name_entry = FCEntry()
@@ -1489,24 +1694,19 @@ class GeometryObjectUI(ObjectUI):
         grid1.addWidget(self.addtool_entry_lbl, 3, 0)
         grid1.addWidget(self.addtool_entry_lbl, 3, 0)
         grid1.addWidget(self.addtool_entry, 3, 1)
         grid1.addWidget(self.addtool_entry, 3, 1)
 
 
-        bhlay = QtWidgets.QHBoxLayout()
-
         self.addtool_btn = QtWidgets.QPushButton(_('Add'))
         self.addtool_btn = QtWidgets.QPushButton(_('Add'))
         self.addtool_btn.setToolTip(
         self.addtool_btn.setToolTip(
             _("Add a new tool to the Tool Table\n"
             _("Add a new tool to the Tool Table\n"
-              "with the diameter specified above.")
+              "with the specified diameter.")
         )
         )
+        grid1.addWidget(self.addtool_btn, 4, 0, 1, 2)
 
 
         self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add from DB'))
         self.addtool_from_db_btn = QtWidgets.QPushButton(_('Add from DB'))
         self.addtool_from_db_btn.setToolTip(
         self.addtool_from_db_btn.setToolTip(
             _("Add a new tool to the Tool Table\n"
             _("Add a new tool to the Tool Table\n"
               "from the Tool DataBase.")
               "from the Tool DataBase.")
         )
         )
-
-        bhlay.addWidget(self.addtool_btn)
-        bhlay.addWidget(self.addtool_from_db_btn)
-
-        grid1.addLayout(bhlay, 5, 0, 1, 2)
+        grid1.addWidget(self.addtool_from_db_btn, 7, 0, 1, 2)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -1554,10 +1754,10 @@ class GeometryObjectUI(ObjectUI):
         # ################# GRID LAYOUT 3   ###############################
         # ################# GRID LAYOUT 3   ###############################
         # #################################################################
         # #################################################################
 
 
-        self.grid3 = QtWidgets.QGridLayout()
-        self.grid3.setColumnStretch(0, 0)
-        self.grid3.setColumnStretch(1, 1)
-        self.geo_param_box.addLayout(self.grid3)
+        grid3 = QtWidgets.QGridLayout()
+        grid3.setColumnStretch(0, 0)
+        grid3.setColumnStretch(1, 1)
+        self.geo_param_box.addLayout(grid3)
 
 
         # ### Tools Data ## ##
         # ### Tools Data ## ##
         self.tool_data_label = QtWidgets.QLabel(
         self.tool_data_label = QtWidgets.QLabel(
@@ -1568,7 +1768,7 @@ class GeometryObjectUI(ObjectUI):
                 "Each tool store it's own set of such data."
                 "Each tool store it's own set of such data."
             )
             )
         )
         )
-        self.grid3.addWidget(self.tool_data_label, 0, 0, 1, 2)
+        grid3.addWidget(self.tool_data_label, 0, 0, 1, 2)
 
 
         # Tip Dia
         # Tip Dia
         self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
         self.tipdialabel = QtWidgets.QLabel('%s:' % _('V-Tip Dia'))
@@ -1582,8 +1782,8 @@ class GeometryObjectUI(ObjectUI):
         self.tipdia_entry.set_range(0.00001, 9999.9999)
         self.tipdia_entry.set_range(0.00001, 9999.9999)
         self.tipdia_entry.setSingleStep(0.1)
         self.tipdia_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.tipdialabel, 1, 0)
-        self.grid3.addWidget(self.tipdia_entry, 1, 1)
+        grid3.addWidget(self.tipdialabel, 1, 0)
+        grid3.addWidget(self.tipdia_entry, 1, 1)
 
 
         # Tip Angle
         # Tip Angle
         self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
         self.tipanglelabel = QtWidgets.QLabel('%s:' % _('V-Tip Angle'))
@@ -1598,8 +1798,8 @@ class GeometryObjectUI(ObjectUI):
         self.tipangle_entry.set_range(1.0, 180.0)
         self.tipangle_entry.set_range(1.0, 180.0)
         self.tipangle_entry.setSingleStep(1)
         self.tipangle_entry.setSingleStep(1)
 
 
-        self.grid3.addWidget(self.tipanglelabel, 2, 0)
-        self.grid3.addWidget(self.tipangle_entry, 2, 1)
+        grid3.addWidget(self.tipanglelabel, 2, 0)
+        grid3.addWidget(self.tipangle_entry, 2, 1)
 
 
         # Cut Z
         # Cut Z
         self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
         self.cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
@@ -1619,8 +1819,8 @@ class GeometryObjectUI(ObjectUI):
 
 
         self.cutz_entry.setSingleStep(0.1)
         self.cutz_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.cutzlabel, 3, 0)
-        self.grid3.addWidget(self.cutz_entry, 3, 1)
+        grid3.addWidget(self.cutzlabel, 3, 0)
+        grid3.addWidget(self.cutz_entry, 3, 1)
 
 
         # Multi-pass
         # Multi-pass
         self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
         self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
@@ -1645,8 +1845,8 @@ class GeometryObjectUI(ObjectUI):
         )
         )
         self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
         self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
 
 
-        self.grid3.addWidget(self.mpass_cb, 4, 0)
-        self.grid3.addWidget(self.maxdepth_entry, 4, 1)
+        grid3.addWidget(self.mpass_cb, 4, 0)
+        grid3.addWidget(self.maxdepth_entry, 4, 1)
 
 
         # Travel Z
         # Travel Z
         self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
         self.travelzlabel = QtWidgets.QLabel('%s:' % _('Travel Z'))
@@ -1664,8 +1864,8 @@ class GeometryObjectUI(ObjectUI):
 
 
         self.travelz_entry.setSingleStep(0.1)
         self.travelz_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.travelzlabel, 5, 0)
-        self.grid3.addWidget(self.travelz_entry, 5, 1)
+        grid3.addWidget(self.travelzlabel, 5, 0)
+        grid3.addWidget(self.travelz_entry, 5, 1)
 
 
         # Feedrate X-Y
         # Feedrate X-Y
         self.frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y'))
         self.frlabel = QtWidgets.QLabel('%s:' % _('Feedrate X-Y'))
@@ -1678,8 +1878,8 @@ class GeometryObjectUI(ObjectUI):
         self.cncfeedrate_entry.set_range(0, 99999.9999)
         self.cncfeedrate_entry.set_range(0, 99999.9999)
         self.cncfeedrate_entry.setSingleStep(0.1)
         self.cncfeedrate_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.frlabel, 10, 0)
-        self.grid3.addWidget(self.cncfeedrate_entry, 10, 1)
+        grid3.addWidget(self.frlabel, 10, 0)
+        grid3.addWidget(self.cncfeedrate_entry, 10, 1)
 
 
         # Feedrate Z (Plunge)
         # Feedrate Z (Plunge)
         self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
         self.frzlabel = QtWidgets.QLabel('%s:' % _('Feedrate Z'))
@@ -1693,8 +1893,8 @@ class GeometryObjectUI(ObjectUI):
         self.feedrate_z_entry.set_range(0, 99999.9999)
         self.feedrate_z_entry.set_range(0, 99999.9999)
         self.feedrate_z_entry.setSingleStep(0.1)
         self.feedrate_z_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.frzlabel, 11, 0)
-        self.grid3.addWidget(self.feedrate_z_entry, 11, 1)
+        grid3.addWidget(self.frzlabel, 11, 0)
+        grid3.addWidget(self.feedrate_z_entry, 11, 1)
 
 
         # Feedrate rapids
         # Feedrate rapids
         self.fr_rapidlabel = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
         self.fr_rapidlabel = QtWidgets.QLabel('%s:' % _('Feedrate Rapids'))
@@ -1710,8 +1910,8 @@ class GeometryObjectUI(ObjectUI):
         self.feedrate_rapid_entry.set_range(0, 99999.9999)
         self.feedrate_rapid_entry.set_range(0, 99999.9999)
         self.feedrate_rapid_entry.setSingleStep(0.1)
         self.feedrate_rapid_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.fr_rapidlabel, 12, 0)
-        self.grid3.addWidget(self.feedrate_rapid_entry, 12, 1)
+        grid3.addWidget(self.fr_rapidlabel, 12, 0)
+        grid3.addWidget(self.feedrate_rapid_entry, 12, 1)
         # default values is to hide
         # default values is to hide
         self.fr_rapidlabel.hide()
         self.fr_rapidlabel.hide()
         self.feedrate_rapid_entry.hide()
         self.feedrate_rapid_entry.hide()
@@ -1736,8 +1936,8 @@ class GeometryObjectUI(ObjectUI):
               "meet with last cut, we generate an\n"
               "meet with last cut, we generate an\n"
               "extended cut over the first cut section.")
               "extended cut over the first cut section.")
         )
         )
-        self.grid3.addWidget(self.extracut_cb, 13, 0)
-        self.grid3.addWidget(self.e_cut_entry, 13, 1)
+        grid3.addWidget(self.extracut_cb, 13, 0)
+        grid3.addWidget(self.e_cut_entry, 13, 1)
 
 
         # Spindlespeed
         # Spindlespeed
         self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
         self.spindle_label = QtWidgets.QLabel('%s:' % _('Spindle speed'))
@@ -1752,8 +1952,8 @@ class GeometryObjectUI(ObjectUI):
         self.cncspindlespeed_entry.set_range(0, 1000000)
         self.cncspindlespeed_entry.set_range(0, 1000000)
         self.cncspindlespeed_entry.set_step(100)
         self.cncspindlespeed_entry.set_step(100)
 
 
-        self.grid3.addWidget(self.spindle_label, 14, 0)
-        self.grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
+        grid3.addWidget(self.spindle_label, 14, 0)
+        grid3.addWidget(self.cncspindlespeed_entry, 14, 1)
 
 
         # Dwell
         # Dwell
         self.dwell_cb = FCCheckBox('%s:' % _('Dwell'))
         self.dwell_cb = FCCheckBox('%s:' % _('Dwell'))
@@ -1773,8 +1973,8 @@ class GeometryObjectUI(ObjectUI):
         )
         )
         self.ois_dwell_geo = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
         self.ois_dwell_geo = OptionalInputSection(self.dwell_cb, [self.dwelltime_entry])
 
 
-        self.grid3.addWidget(self.dwell_cb, 15, 0)
-        self.grid3.addWidget(self.dwelltime_entry, 15, 1)
+        grid3.addWidget(self.dwell_cb, 15, 0)
+        grid3.addWidget(self.dwelltime_entry, 15, 1)
 
 
         # Probe depth
         # Probe depth
         self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
         self.pdepth_label = QtWidgets.QLabel('%s:' % _("Probe Z depth"))
@@ -1787,8 +1987,8 @@ class GeometryObjectUI(ObjectUI):
         self.pdepth_entry.set_range(-9999.9999, 9999.9999)
         self.pdepth_entry.set_range(-9999.9999, 9999.9999)
         self.pdepth_entry.setSingleStep(0.1)
         self.pdepth_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.pdepth_label, 17, 0)
-        self.grid3.addWidget(self.pdepth_entry, 17, 1)
+        grid3.addWidget(self.pdepth_label, 17, 0)
+        grid3.addWidget(self.pdepth_entry, 17, 1)
 
 
         self.pdepth_label.hide()
         self.pdepth_label.hide()
         self.pdepth_entry.setVisible(False)
         self.pdepth_entry.setVisible(False)
@@ -1803,8 +2003,8 @@ class GeometryObjectUI(ObjectUI):
         self.feedrate_probe_entry.set_range(0.0, 9999.9999)
         self.feedrate_probe_entry.set_range(0.0, 9999.9999)
         self.feedrate_probe_entry.setSingleStep(0.1)
         self.feedrate_probe_entry.setSingleStep(0.1)
 
 
-        self.grid3.addWidget(self.feedrate_probe_label, 18, 0)
-        self.grid3.addWidget(self.feedrate_probe_entry, 18, 1)
+        grid3.addWidget(self.feedrate_probe_label, 18, 0)
+        grid3.addWidget(self.feedrate_probe_entry, 18, 1)
 
 
         self.feedrate_probe_label.hide()
         self.feedrate_probe_label.hide()
         self.feedrate_probe_entry.setVisible(False)
         self.feedrate_probe_entry.setVisible(False)

+ 14 - 152
AppGUI/PlotCanvas.py → flatcamGUI/PlotCanvas.py

@@ -8,21 +8,13 @@
 from PyQt5 import QtCore
 from PyQt5 import QtCore
 
 
 import logging
 import logging
-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
+from flatcamGUI.VisPyCanvas import VisPyCanvas, Color
+from flatcamGUI.VisPyVisuals import ShapeGroup, ShapeCollection, TextCollection, TextGroup, Cursor
+from vispy.scene.visuals import InfiniteLine, Line
 
 
 import numpy as np
 import numpy as np
 from vispy.geometry import Rect
 from vispy.geometry import Rect
 
 
-fcTranslate.apply_language('strings')
-if '_' not in builtins.__dict__:
-    _ = gettext.gettext
-
 log = logging.getLogger('base')
 log = logging.getLogger('base')
 
 
 
 
@@ -62,12 +54,8 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
 
         if theme == 'white':
         if theme == 'white':
             self.line_color = (0.3, 0.0, 0.0, 1.0)
             self.line_color = (0.3, 0.0, 0.0, 1.0)
-            self.rect_hud_color = Color('#0000FF10')
-            self.text_hud_color = 'black'
         else:
         else:
             self.line_color = (0.4, 0.4, 0.4, 1.0)
             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,
         # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
         # which might decrease performance
         # which might decrease performance
@@ -141,6 +129,11 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
         self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 0.8), vertical=False,
         self.h_line = InfiniteLine(pos=0, color=(0.70, 0.3, 0.3, 0.8), vertical=False,
                                    parent=self.view.scene)
                                    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
         self.line_parent = None
         if self.fcapp.defaults["global_cursor_color_enabled"]:
         if self.fcapp.defaults["global_cursor_color_enabled"]:
             c_color = Color(self.fcapp.defaults["global_cursor_color"]).rgba
             c_color = Color(self.fcapp.defaults["global_cursor_color"]).rgba
@@ -153,61 +146,13 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
         self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False,
         self.cursor_h_line = InfiniteLine(pos=None, color=c_color, vertical=False,
                                           parent=self.line_parent)
                                           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_collections = []
 
 
         self.shape_collection = self.new_shape_collection()
         self.shape_collection = self.new_shape_collection()
         self.fcapp.pool_recreated.connect(self.on_pool_recreated)
         self.fcapp.pool_recreated.connect(self.on_pool_recreated)
         self.text_collection = self.new_text_collection()
         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.text_collection.enabled = True
 
 
         self.c = None
         self.c = None
@@ -218,76 +163,6 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
 
         self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
         self.graph_event_connect('mouse_wheel', self.on_mouse_scroll)
 
 
-    def on_toggle_axis(self, signal=None, state=None):
-        if state is None:
-            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: orange;
-                                                          }
-                                                          """)
-            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: mediumpurple;
-                                                  }
-                                                  """)
-            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):
     def draw_workspace(self, workspace_size):
         """
         """
         Draw a rectangular shape on canvas to specify our valid workspace.
         Draw a rectangular shape on canvas to specify our valid workspace.
@@ -308,30 +183,17 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
 
         a = np.array([(0, 0), (dims[0], 0), (dims[0], dims[1]), (0, dims[1])])
         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
-        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: olivedrab;
-                        }
-                        """)
+        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
 
 
     def delete_workspace(self):
     def delete_workspace(self):
         try:
         try:
             self.workspace_line.parent = None
             self.workspace_line.parent = None
         except Exception:
         except Exception:
             pass
             pass
-        self.fcapp.ui.wplace_label.setStyleSheet("")
 
 
     # redraw the workspace lines on the plot by re adding them to the parent view.scene
     # redraw the workspace lines on the plot by re adding them to the parent view.scene
     def restore_workspace(self):
     def restore_workspace(self):

+ 9 - 233
AppGUI/PlotCanvasLegacy.py → flatcamGUI/PlotCanvasLegacy.py

@@ -19,10 +19,8 @@ from shapely.geometry import Polygon, LineString, LinearRing
 from copy import deepcopy
 from copy import deepcopy
 import logging
 import logging
 
 
-import numpy as np
-
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 # Prevent conflict with Qt5 and above.
 # Prevent conflict with Qt5 and above.
@@ -31,13 +29,13 @@ mpl_use("Qt5Agg")
 from matplotlib.figure import Figure
 from matplotlib.figure import Figure
 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
 from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
 from matplotlib.lines import Line2D
 from matplotlib.lines import Line2D
-from matplotlib.offsetbox import AnchoredText
 # from matplotlib.widgets import Cursor
 # from matplotlib.widgets import Cursor
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
 if '_' not in builtins.__dict__:
 if '_' not in builtins.__dict__:
     _ = gettext.gettext
     _ = gettext.gettext
 
 
+
 log = logging.getLogger('base')
 log = logging.getLogger('base')
 
 
 
 
@@ -47,9 +45,9 @@ class CanvasCache(QtCore.QObject):
     Case story #1:
     Case story #1:
 
 
     1) No objects in the project.
     1) No objects in the project.
-    2) Object is created (app_obj.new_object() emits object_created(obj)).
+    2) Object is created (new_object() emits object_created(obj)).
        on_object_created() adds (i) object to collection and emits
        on_object_created() adds (i) object to collection and emits
-       (ii) app_obj.new_object_available() then calls (iii) object.plot()
+       (ii) new_object_available() then calls (iii) object.plot()
     3) object.plot() creates axes if necessary on
     3) object.plot() creates axes if necessary on
        app.collection.figure. Then plots on it.
        app.collection.figure. Then plots on it.
     4) Plots on a cache-size canvas (in background).
     4) Plots on a cache-size canvas (in background).
@@ -115,7 +113,7 @@ class CanvasCache(QtCore.QObject):
 
 
         # Continue to update the cache.
         # Continue to update the cache.
 
 
-    # def on_app_obj.new_object_available(self):
+    # def on_new_object_available(self):
     #
     #
     #     log.debug("A new object is available. Should plot it!")
     #     log.debug("A new object is available. Should plot it!")
 
 
@@ -149,13 +147,9 @@ class PlotCanvasLegacy(QtCore.QObject):
         if self.app.defaults['global_theme'] == 'white':
         if self.app.defaults['global_theme'] == 'white':
             theme_color = '#FFFFFF'
             theme_color = '#FFFFFF'
             tick_color = '#000000'
             tick_color = '#000000'
-            self.rect_hud_color = '#0000FF10'
-            self.text_hud_color = '#000000'
         else:
         else:
             theme_color = '#000000'
             theme_color = '#000000'
             tick_color = '#FFFFFF'
             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,
         # workspace lines; I didn't use the rectangle because I didn't want to add another VisPy Node,
         # which might decrease performance
         # which might decrease performance
@@ -301,163 +295,14 @@ class PlotCanvasLegacy(QtCore.QObject):
         # signal is the mouse is dragging
         # signal is the mouse is dragging
         self.is_dragging = False
         self.is_dragging = False
 
 
-        self.mouse_press_pos = None
-
         # signal if there is a doubleclick
         # signal if there is a doubleclick
         self.is_dblclk = False
         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
         # 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
         # all CNC have a limited workspace
         if self.app.defaults['global_workspace'] is True:
         if self.app.defaults['global_workspace'] is True:
             self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
             self.draw_workspace(workspace_size=self.app.defaults["global_workspaceT"])
 
 
-        if self.app.defaults['global_hud'] is True:
-            self.on_toggle_hud(state=True)
-
-        # Axis Display
-        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: orange;
-                                                            }
-                                                            """)
-                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: mediumpurple;
-                                                }
-                                                """)
-            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):
     def draw_workspace(self, workspace_size):
         """
         """
         Draw a rectangular shape on canvas to specify our valid workspace.
         Draw a rectangular shape on canvas to specify our valid workspace.
@@ -484,23 +329,12 @@ class PlotCanvasLegacy(QtCore.QObject):
             self.axes.add_line(self.workspace_line)
             self.axes.add_line(self.workspace_line)
             self.canvas.draw()
             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: olivedrab;
-                        }
-                        """)
-
     def delete_workspace(self):
     def delete_workspace(self):
         try:
         try:
             self.axes.lines.remove(self.workspace_line)
             self.axes.lines.remove(self.workspace_line)
             self.canvas.draw()
             self.canvas.draw()
         except Exception:
         except Exception:
             pass
             pass
-        self.fcapp.ui.wplace_label.setStyleSheet("")
 
 
     def graph_event_connect(self, event_name, callback):
     def graph_event_connect(self, event_name, callback):
         """
         """
@@ -589,7 +423,7 @@ class PlotCanvasLegacy(QtCore.QObject):
 
 
             if self.big_cursor is False:
             if self.big_cursor is False:
                 try:
                 try:
-                    x, y = self.snap(x_pos, y_pos)
+                    x, y = self.app.geo_editor.snap(x_pos, y_pos)
 
 
                     # Pointer (snapped)
                     # Pointer (snapped)
                     # The size of the cursor is multiplied by 1.65 because that value made the cursor similar with the
                     # The size of the cursor is multiplied by 1.65 because that value made the cursor similar with the
@@ -622,7 +456,7 @@ class PlotCanvasLegacy(QtCore.QObject):
                     pass
                     pass
                 self.canvas.draw_idle()
                 self.canvas.draw_idle()
 
 
-            self.canvas.blit(self.axes.bbox)
+        self.canvas.blit(self.axes.bbox)
 
 
     def clear_cursor(self, state):
     def clear_cursor(self, state):
         if state is True:
         if state is True:
@@ -947,7 +781,6 @@ class PlotCanvasLegacy(QtCore.QObject):
     def on_mouse_press(self, event):
     def on_mouse_press(self, event):
 
 
         self.is_dragging = True
         self.is_dragging = True
-        self.mouse_press_pos = (event.x, event.y)
 
 
         # Check for middle mouse button press
         # Check for middle mouse button press
         if self.app.defaults["global_pan_button"] == '2':
         if self.app.defaults["global_pan_button"] == '2':
@@ -973,11 +806,7 @@ class PlotCanvasLegacy(QtCore.QObject):
 
 
     def on_mouse_release(self, event):
     def on_mouse_release(self, event):
 
 
-        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
+        self.is_dragging = False
 
 
         # Check for middle mouse button release to complete pan procedure
         # Check for middle mouse button release to complete pan procedure
         # Check for middle mouse button press
         # Check for middle mouse button press
@@ -1029,7 +858,7 @@ class PlotCanvasLegacy(QtCore.QObject):
             self.canvas.draw_idle()
             self.canvas.draw_idle()
 
 
             # #### Temporary place-holder for cached update #####
             # #### 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:
         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)
             self.draw_cursor(x_pos=x, y_pos=y, color=self.app.cursor_color_3D)
@@ -1081,59 +910,6 @@ class PlotCanvasLegacy(QtCore.QObject):
 
 
         return width / xpx, height / ypx
         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):
 class FakeCursor(QtCore.QObject):
     """
     """

+ 0 - 1
AppGUI/VisPyCanvas.py → flatcamGUI/VisPyCanvas.py

@@ -13,7 +13,6 @@ import numpy as np
 
 
 import vispy.scene as scene
 import vispy.scene as scene
 from vispy.scene.cameras.base_camera import BaseCamera
 from vispy.scene.cameras.base_camera import BaseCamera
-# from vispy.scene.widgets import Widget as VisPyWidget
 from vispy.color import Color
 from vispy.color import Color
 
 
 import time
 import time

+ 0 - 0
AppGUI/VisPyData/data/fonts/opensans-regular.ttf → flatcamGUI/VisPyData/data/fonts/opensans-regular.ttf


+ 0 - 0
AppGUI/VisPyData/data/freetype/freetype253.dll → flatcamGUI/VisPyData/data/freetype/freetype253.dll


+ 0 - 0
AppGUI/VisPyData/data/freetype/freetype253_x64.dll → flatcamGUI/VisPyData/data/freetype/freetype253_x64.dll


+ 0 - 0
AppGUI/VisPyPatches.py → flatcamGUI/VisPyPatches.py


+ 0 - 0
AppGUI/VisPyTesselators.py → flatcamGUI/VisPyTesselators.py


+ 1 - 1
AppGUI/VisPyVisuals.py → flatcamGUI/VisPyVisuals.py

@@ -13,7 +13,7 @@ from vispy.color import Color
 from shapely.geometry import Polygon, LineString, LinearRing
 from shapely.geometry import Polygon, LineString, LinearRing
 import threading
 import threading
 import numpy as np
 import numpy as np
-from AppGUI.VisPyTesselators import GLUTess
+from flatcamGUI.VisPyTesselators import GLUTess
 
 
 
 
 class FlatCAMLineVisual(LineVisual):
 class FlatCAMLineVisual(LineVisual):

+ 0 - 0
AppGUI/__init__.py → flatcamGUI/__init__.py


+ 19 - 0
flatcamGUI/preferences/OptionsGroupUI.py

@@ -0,0 +1,19 @@
+from PyQt5 import QtWidgets
+
+
+class OptionsGroupUI(QtWidgets.QGroupBox):
+    app = None
+
+    def __init__(self, title, parent=None):
+        # QtGui.QGroupBox.__init__(self, title, parent=parent)
+        super(OptionsGroupUI, self).__init__()
+        self.setStyleSheet("""
+        QGroupBox
+        {
+            font-size: 16px;
+            font-weight: bold;
+        }
+        """)
+
+        self.layout = QtWidgets.QVBoxLayout()
+        self.setLayout(self.layout)

+ 218 - 184
AppGUI/preferences/PreferencesUIManager.py → flatcamGUI/preferences/PreferencesUIManager.py

@@ -5,7 +5,7 @@ from defaults import FlatCAMDefaults
 import logging
 import logging
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -29,7 +29,7 @@ class PreferencesUIManager:
 
 
         :param defaults:    a dictionary storage where all the application settings are stored
         :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 data_path:   a path to the file where all the preferences are stored for persistence
-        :param ui:          reference to the MainGUI class which constructs the UI
+        :param ui:          reference to the FlatCAMGUI class which constructs the UI
         :param inform:      a pyqtSignal used to display information's in the StatusBar of the GUI
         :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
         self.preferences_changed_flag = False
 
 
         # when adding entries here read the comments in the  method found below named:
         # when adding entries here read the comments in the  method found below named:
-        # def app_obj.new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
+        # def new_object(self, kind, name, initialize, active=True, fit=True, plot=True)
         self.defaults_form_fields = {
         self.defaults_form_fields = {
             # General App
             # General App
             "decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry,
             "decimals_inch": self.ui.general_defaults_form.general_app_group.precision_inch_entry,
@@ -123,10 +123,16 @@ class PreferencesUIManager:
             "gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio,
             "gerber_def_zeros": self.ui.gerber_defaults_form.gerber_gen_group.gerber_zeros_radio,
             "gerber_clean_apertures": self.ui.gerber_defaults_form.gerber_gen_group.gerber_clean_cb,
             "gerber_clean_apertures": self.ui.gerber_defaults_form.gerber_gen_group.gerber_clean_cb,
             "gerber_extra_buffering": self.ui.gerber_defaults_form.gerber_gen_group.gerber_extra_buffering,
             "gerber_extra_buffering": self.ui.gerber_defaults_form.gerber_gen_group.gerber_extra_buffering,
-            "gerber_plot_fill": self.ui.gerber_defaults_form.gerber_gen_group.fill_color_entry,
-            "gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.line_color_entry,
+            "gerber_plot_fill": self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry,
+            "gerber_plot_line": self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry,
 
 
             # Gerber Options
             # 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_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_noncopperrounded": self.ui.gerber_defaults_form.gerber_opt_group.noncopper_rounded_cb,
             "gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry,
             "gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry,
@@ -137,6 +143,12 @@ class PreferencesUIManager:
             # "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry,
             # "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_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_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_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_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,
             "gerber_simp_tolerance": self.ui.gerber_defaults_form.gerber_adv_opt_group.simplification_tol_spinner,
@@ -168,7 +180,6 @@ class PreferencesUIManager:
             # Excellon General
             # Excellon General
             "excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb,
             "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_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":
             "excellon_format_upper_in":
                 self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry,
                 self.ui.excellon_defaults_form.excellon_gen_group.excellon_format_upper_in_entry,
             "excellon_format_lower_in":
             "excellon_format_lower_in":
@@ -210,31 +221,31 @@ class PreferencesUIManager:
             "excellon_gcode_type": self.ui.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio,
             "excellon_gcode_type": self.ui.excellon_defaults_form.excellon_opt_group.excellon_gcode_type_radio,
 
 
             # Excellon Advanced Options
             # 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 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
-            "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_dir": self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_dir_radio,
             "excellon_editor_circ_angle":
             "excellon_editor_circ_angle":
                 self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_angle_entry,
                 self.ui.excellon_defaults_form.excellon_editor_group.drill_circular_angle_entry,
@@ -259,117 +270,94 @@ class PreferencesUIManager:
                 self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_angle_entry,
                 self.ui.excellon_defaults_form.excellon_editor_group.slot_array_circular_angle_entry,
 
 
             # Geometry General
             # Geometry General
-            "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_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 Options
             # 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 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_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
-            "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 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_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_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,
 
 
             # CNC Job Options
             # 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
             # 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,
             "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
             # 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_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,
+            "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,
 
 
             # CutOut Tool
             # CutOut Tool
             "tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
             "tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry,
@@ -479,12 +467,6 @@ class PreferencesUIManager:
             "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
             "tools_solderpaste_pp": self.ui.tools_defaults_form.tools_solderpaste_group.pp_combo,
             "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
             "tools_sub_close_paths": self.ui.tools_defaults_form.tools_sub_group.close_paths_cb,
 
 
-            # 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 ####################################################
             # ########################################## TOOLS 2 ####################################################
             # #######################################################################################################
             # #######################################################################################################
@@ -668,7 +650,7 @@ class PreferencesUIManager:
 
 
     def show_preferences_gui(self):
     def show_preferences_gui(self):
         """
         """
-        Called to initialize and show the Preferences AppGUI
+        Called to initialize and show the Preferences GUI
 
 
         :return: None
         :return: None
         """
         """
@@ -737,7 +719,7 @@ class PreferencesUIManager:
         self.ui.fa_scroll_area.setWidget(fa_form)
         self.ui.fa_scroll_area.setWidget(fa_form)
         fa_form.show()
         fa_form.show()
 
 
-        # Initialize the color box's color in Preferences -> Global -> Colors
+        # Initialize the color box's color in Preferences -> Global -> Colo
         self.__init_color_pickers()
         self.__init_color_pickers()
 
 
         # Button handlers
         # Button handlers
@@ -750,90 +732,167 @@ class PreferencesUIManager:
 
 
     def __init_color_pickers(self):
     def __init_color_pickers(self):
         # Init Gerber Plot Colors
         # Init Gerber Plot Colors
-        self.ui.gerber_defaults_form.gerber_gen_group.fill_color_entry.set_value(self.defaults['gerber_plot_fill'])
-        self.ui.gerber_defaults_form.gerber_gen_group.line_color_entry.set_value(self.defaults['gerber_plot_line'])
-
-        self.ui.gerber_defaults_form.gerber_gen_group.gerber_alpha_entry.set_value(
-            int(self.defaults['gerber_plot_fill'][7:9], 16))    # alpha
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_entry.set_value(self.defaults['gerber_plot_fill'])
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['gerber_plot_fill'])[:7])
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_spinner.set_value(
+            int(self.defaults['gerber_plot_fill'][7:9], 16))
+        self.ui.gerber_defaults_form.gerber_gen_group.pf_color_alpha_slider.setValue(
+            int(self.defaults['gerber_plot_fill'][7:9], 16))
+
+        self.ui.gerber_defaults_form.gerber_gen_group.pl_color_entry.set_value(self.defaults['gerber_plot_line'])
+        self.ui.gerber_defaults_form.gerber_gen_group.pl_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['gerber_plot_line'])[:7])
 
 
         # Init Excellon Plot Colors
         # Init Excellon Plot Colors
         self.ui.excellon_defaults_form.excellon_gen_group.fill_color_entry.set_value(
         self.ui.excellon_defaults_form.excellon_gen_group.fill_color_entry.set_value(
             self.defaults['excellon_plot_fill'])
             self.defaults['excellon_plot_fill'])
+        self.ui.excellon_defaults_form.excellon_gen_group.fill_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['excellon_plot_fill'])[:7])
+        self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_spinner.set_value(
+            int(self.defaults['excellon_plot_fill'][7:9], 16))
+        self.ui.excellon_defaults_form.excellon_gen_group.color_alpha_slider.setValue(
+            int(self.defaults['excellon_plot_fill'][7:9], 16))
+
         self.ui.excellon_defaults_form.excellon_gen_group.line_color_entry.set_value(
         self.ui.excellon_defaults_form.excellon_gen_group.line_color_entry.set_value(
             self.defaults['excellon_plot_line'])
             self.defaults['excellon_plot_line'])
-
-        self.ui.excellon_defaults_form.excellon_gen_group.excellon_alpha_entry.set_value(
-            int(self.defaults['excellon_plot_fill'][7:9], 16))
+        self.ui.excellon_defaults_form.excellon_gen_group.line_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['excellon_plot_line'])[:7])
 
 
         # Init Geometry Plot Colors
         # Init Geometry Plot Colors
         self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry.set_value(
         self.ui.geometry_defaults_form.geometry_gen_group.line_color_entry.set_value(
             self.defaults['geometry_plot_line'])
             self.defaults['geometry_plot_line'])
+        self.ui.geometry_defaults_form.geometry_gen_group.line_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['geometry_plot_line'])[:7])
 
 
         # Init CNCJob Travel Line Colors
         # Init CNCJob Travel Line Colors
         self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry.set_value(
         self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_entry.set_value(
             self.defaults['cncjob_travel_fill'])
             self.defaults['cncjob_travel_fill'])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tfill_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_travel_fill'])[:7])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_spinner.set_value(
+            int(self.defaults['cncjob_travel_fill'][7:9], 16))
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tcolor_alpha_slider.setValue(
+            int(self.defaults['cncjob_travel_fill'][7:9], 16))
+
         self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry.set_value(
         self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_entry.set_value(
             self.defaults['cncjob_travel_line'])
             self.defaults['cncjob_travel_line'])
-
-        self.ui.cncjob_defaults_form.cncjob_gen_group.cncjob_alpha_entry.set_value(
-            int(self.defaults['cncjob_travel_fill'][7:9], 16))      # alpha
+        self.ui.cncjob_defaults_form.cncjob_gen_group.tline_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_travel_line'])[:7])
 
 
         # Init CNCJob Plot Colors
         # Init CNCJob Plot Colors
         self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry.set_value(
         self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_entry.set_value(
             self.defaults['cncjob_plot_fill'])
             self.defaults['cncjob_plot_fill'])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.fill_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_plot_fill'])[:7])
 
 
         self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry.set_value(
         self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_entry.set_value(
             self.defaults['cncjob_plot_line'])
             self.defaults['cncjob_plot_line'])
+        self.ui.cncjob_defaults_form.cncjob_gen_group.line_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_plot_line'])[:7])
 
 
         # Init Left-Right Selection colors
         # Init Left-Right Selection colors
         self.ui.general_defaults_form.general_gui_group.sf_color_entry.set_value(self.defaults['global_sel_fill'])
         self.ui.general_defaults_form.general_gui_group.sf_color_entry.set_value(self.defaults['global_sel_fill'])
-        self.ui.general_defaults_form.general_gui_group.sl_color_entry.set_value(self.defaults['global_sel_line'])
-
-        self.ui.general_defaults_form.general_gui_group.left_right_alpha_entry.set_value(
+        self.ui.general_defaults_form.general_gui_group.sf_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_sel_fill'])[:7])
+        self.ui.general_defaults_form.general_gui_group.sf_color_alpha_spinner.set_value(
+            int(self.defaults['global_sel_fill'][7:9], 16))
+        self.ui.general_defaults_form.general_gui_group.sf_color_alpha_slider.setValue(
             int(self.defaults['global_sel_fill'][7:9], 16))
             int(self.defaults['global_sel_fill'][7:9], 16))
 
 
+        self.ui.general_defaults_form.general_gui_group.sl_color_entry.set_value(self.defaults['global_sel_line'])
+        self.ui.general_defaults_form.general_gui_group.sl_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_sel_line'])[:7])
+
         # Init Right-Left Selection colors
         # Init Right-Left Selection colors
         self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(
         self.ui.general_defaults_form.general_gui_group.alt_sf_color_entry.set_value(
             self.defaults['global_alt_sel_fill'])
             self.defaults['global_alt_sel_fill'])
+        self.ui.general_defaults_form.general_gui_group.alt_sf_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_alt_sel_fill'])[:7])
+        self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_spinner.set_value(
+            int(self.defaults['global_sel_fill'][7:9], 16))
+        self.ui.general_defaults_form.general_gui_group.alt_sf_color_alpha_slider.setValue(
+            int(self.defaults['global_sel_fill'][7:9], 16))
+
         self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(
         self.ui.general_defaults_form.general_gui_group.alt_sl_color_entry.set_value(
             self.defaults['global_alt_sel_line'])
             self.defaults['global_alt_sel_line'])
-
-        self.ui.general_defaults_form.general_gui_group.right_left_alpha_entry.set_value(
-            int(self.defaults['global_sel_fill'][7:9], 16))
+        self.ui.general_defaults_form.general_gui_group.alt_sl_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_alt_sel_line'])[:7])
 
 
         # Init Draw color and Selection Draw Color
         # Init Draw color and Selection Draw Color
         self.ui.general_defaults_form.general_gui_group.draw_color_entry.set_value(
         self.ui.general_defaults_form.general_gui_group.draw_color_entry.set_value(
             self.defaults['global_draw_color'])
             self.defaults['global_draw_color'])
+        self.ui.general_defaults_form.general_gui_group.draw_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_draw_color'])[:7])
 
 
         self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(
         self.ui.general_defaults_form.general_gui_group.sel_draw_color_entry.set_value(
             self.defaults['global_sel_draw_color'])
             self.defaults['global_sel_draw_color'])
+        self.ui.general_defaults_form.general_gui_group.sel_draw_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_sel_draw_color'])[:7])
 
 
         # Init Project Items color
         # Init Project Items color
         self.ui.general_defaults_form.general_gui_group.proj_color_entry.set_value(
         self.ui.general_defaults_form.general_gui_group.proj_color_entry.set_value(
             self.defaults['global_proj_item_color'])
             self.defaults['global_proj_item_color'])
+        self.ui.general_defaults_form.general_gui_group.proj_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_proj_item_color'])[:7])
 
 
         # Init Project Disabled Items color
         # Init Project Disabled Items color
         self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.set_value(
         self.ui.general_defaults_form.general_gui_group.proj_color_dis_entry.set_value(
             self.defaults['global_proj_item_dis_color'])
             self.defaults['global_proj_item_dis_color'])
+        self.ui.general_defaults_form.general_gui_group.proj_color_dis_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_proj_item_dis_color'])[:7])
 
 
-        # Init Mouse Cursor color
+        # Init Project Disabled Items color
         self.ui.general_defaults_form.general_app_set_group.mouse_cursor_entry.set_value(
         self.ui.general_defaults_form.general_app_set_group.mouse_cursor_entry.set_value(
             self.defaults['global_cursor_color'])
             self.defaults['global_cursor_color'])
+        self.ui.general_defaults_form.general_app_set_group.mouse_cursor_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['global_cursor_color'])[:7])
 
 
         # Init the Annotation CNC Job color
         # Init the Annotation CNC Job color
         self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.set_value(
         self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_entry.set_value(
             self.defaults['cncjob_annotation_fontcolor'])
             self.defaults['cncjob_annotation_fontcolor'])
+        self.ui.cncjob_defaults_form.cncjob_adv_opt_group.annotation_fontcolor_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['cncjob_annotation_fontcolor'])[:7])
 
 
         # Init the Tool Film color
         # Init the Tool Film color
         self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(
         self.ui.tools_defaults_form.tools_film_group.film_color_entry.set_value(
             self.defaults['tools_film_color'])
             self.defaults['tools_film_color'])
+        self.ui.tools_defaults_form.tools_film_group.film_color_button.setStyleSheet(
+            "background-color:%s;"
+            "border-color: dimgray" % str(self.defaults['tools_film_color'])[:7]
+        )
 
 
         # Init the Tool QRCode colors
         # Init the Tool QRCode colors
         self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value(
         self.ui.tools2_defaults_form.tools2_qrcode_group.fill_color_entry.set_value(
             self.defaults['tools_qrcode_fill_color'])
             self.defaults['tools_qrcode_fill_color'])
+        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'])[:7])
 
 
         self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value(
         self.ui.tools2_defaults_form.tools2_qrcode_group.back_color_entry.set_value(
             self.defaults['tools_qrcode_back_color'])
             self.defaults['tools_qrcode_back_color'])
+        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'])[:7])
 
 
     def on_save_button(self, save_to_file=True):
     def on_save_button(self, save_to_file=True):
         log.debug("on_save_button() --> Applying preferences to file.")
         log.debug("on_save_button() --> Applying preferences to file.")
@@ -862,17 +921,12 @@ class PreferencesUIManager:
             theme = 'white'
             theme = 'white'
 
 
         should_restart = False
         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:
+        val = self.ui.general_defaults_form.general_gui_group.theme_radio.get_value()
+        if val != theme:
             msgbox = QtWidgets.QMessageBox()
             msgbox = QtWidgets.QMessageBox()
             msgbox.setText(_("Are you sure you want to continue?"))
             msgbox.setText(_("Are you sure you want to continue?"))
-            msgbox.setWindowTitle(_("Application will restart"))
+            msgbox.setWindowTitle(_("Application restart"))
             msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/warning.png'))
             msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/warning.png'))
-            msgbox.setIcon(QtWidgets.QMessageBox.Question)
 
 
             bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
             bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
             msgbox.addButton(_('Cancel'), QtWidgets.QMessageBox.NoRole)
             msgbox.addButton(_('Cancel'), QtWidgets.QMessageBox.NoRole)
@@ -881,22 +935,15 @@ class PreferencesUIManager:
             msgbox.exec_()
             msgbox.exec_()
             response = msgbox.clickedButton()
             response = msgbox.clickedButton()
 
 
-            if theme_new_val != theme:
-                if response == bt_yes:
-                    theme_settings.setValue('theme', theme_new_val)
+            if response == bt_yes:
+                theme_settings.setValue('theme', val)
 
 
-                    # This will write the setting to the platform specific storage.
-                    del theme_settings
+                # 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)
+                should_restart = True
             else:
             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)
+                self.ui.general_defaults_form.general_gui_group.theme_radio.set_value(theme)
 
 
         if save_to_file or should_restart is True:
         if save_to_file or should_restart is True:
             self.save_defaults(silent=False)
             self.save_defaults(silent=False)
@@ -920,10 +967,6 @@ class PreferencesUIManager:
         tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value()
         tb_fsize = self.ui.general_defaults_form.general_app_set_group.textbox_font_size_spinner.get_value()
         settgs.setValue('textbox_font_size', tb_fsize)
         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(
         settgs.setValue(
             'machinist',
             'machinist',
             1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0
             1 if self.ui.general_defaults_form.general_app_set_group.machinist_cb.get_value() else 0
@@ -948,14 +991,10 @@ class PreferencesUIManager:
         self.preferences_changed_flag = False
         self.preferences_changed_flag = False
         self.ignore_tab_close_event = True
         self.ignore_tab_close_event = True
 
 
-        # restore stylesheet to default for the statusBar icon
-        self.ui.pref_status_label.setStyleSheet("")
-
         try:
         try:
             self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.disconnect()
             self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.disconnect()
         except (TypeError, AttributeError):
         except (TypeError, AttributeError):
             pass
             pass
-
         self.defaults_write_form(source_dict=self.defaults.current_defaults)
         self.defaults_write_form(source_dict=self.defaults.current_defaults)
         self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.connect(
         self.ui.general_defaults_form.general_app_group.units_radio.activated_custom.connect(
             lambda: self.ui.app.on_toggle_units(no_pref=False))
             lambda: self.ui.app.on_toggle_units(no_pref=False))
@@ -996,7 +1035,6 @@ class PreferencesUIManager:
         :return:            None
         :return:            None
         """
         """
         self.defaults.report_usage("save_defaults")
         self.defaults.report_usage("save_defaults")
-        log.debug("App.PreferencesUIManager.save_defaults()")
 
 
         if data_path is None:
         if data_path is None:
             data_path = self.data_path
             data_path = self.data_path
@@ -1033,7 +1071,7 @@ class PreferencesUIManager:
         if self.ui.toolbarfile.isVisible():
         if self.ui.toolbarfile.isVisible():
             tb_status += 1
             tb_status += 1
 
 
-        if self.ui.toolbaredit.isVisible():
+        if self.ui.toolbargeo.isVisible():
             tb_status += 2
             tb_status += 2
 
 
         if self.ui.toolbarview.isVisible():
         if self.ui.toolbarview.isVisible():
@@ -1051,7 +1089,7 @@ class PreferencesUIManager:
         if self.ui.grb_edit_toolbar.isVisible():
         if self.ui.grb_edit_toolbar.isVisible():
             tb_status += 64
             tb_status += 64
 
 
-        if self.ui.status_toolbar.isVisible():
+        if self.ui.snap_toolbar.isVisible():
             tb_status += 128
             tb_status += 128
 
 
         if self.ui.toolbarshell.isVisible():
         if self.ui.toolbarshell.isVisible():
@@ -1080,9 +1118,6 @@ class PreferencesUIManager:
         if self.ignore_tab_close_event:
         if self.ignore_tab_close_event:
             return
             return
 
 
-        # restore stylesheet to default for the statusBar icon
-        self.ui.pref_status_label.setStyleSheet("")
-
         # disconnect
         # disconnect
         for idx in range(self.ui.pref_tab_area.count()):
         for idx in range(self.ui.pref_tab_area.count()):
             for tb in self.ui.pref_tab_area.widget(idx).findChildren(QtCore.QObject):
             for tb in self.ui.pref_tab_area.widget(idx).findChildren(QtCore.QObject):
@@ -1118,7 +1153,6 @@ class PreferencesUIManager:
                              "Do you want to save the Preferences?"))
                              "Do you want to save the Preferences?"))
             msgbox.setWindowTitle(_("Save Preferences"))
             msgbox.setWindowTitle(_("Save Preferences"))
             msgbox.setWindowIcon(QtGui.QIcon(self.ui.app.resource_location + '/save_as.png'))
             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)
             bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.YesRole)
             msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)
             msgbox.addButton(_('No'), QtWidgets.QMessageBox.NoRole)

+ 2 - 2
AppGUI/preferences/__init__.py → flatcamGUI/preferences/__init__.py

@@ -1,6 +1,6 @@
-from AppGUI.GUIElements import *
+from flatcamGUI.GUIElements import *
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 
 

+ 37 - 8
AppGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py → flatcamGUI/preferences/cncjob/CNCJobAdvOptPrefGroupUI.py

@@ -1,10 +1,10 @@
-from PyQt5 import QtWidgets, QtGui
+from PyQt5 import QtWidgets, QtGui, QtCore
 from PyQt5.QtCore import QSettings, Qt
 from PyQt5.QtCore import QSettings, Qt
 
 
-from AppGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCColorEntry
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCTextArea, FCCheckBox, FCComboBox, FCSpinner, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -158,16 +158,28 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
         self.annotation_color_label.setToolTip(
         self.annotation_color_label.setToolTip(
             _("Set the font color for the annotation texts.")
             _("Set the font color for the annotation texts.")
         )
         )
-        self.annotation_fontcolor_entry = FCColorEntry()
-
+        self.annotation_fontcolor_entry = FCEntry()
+        self.annotation_fontcolor_button = QtWidgets.QPushButton()
+        self.annotation_fontcolor_button.setFixedSize(15, 15)
+
+        self.form_box_child = QtWidgets.QHBoxLayout()
+        self.form_box_child.setContentsMargins(0, 0, 0, 0)
+        self.form_box_child.addWidget(self.annotation_fontcolor_entry)
+        self.form_box_child.addWidget(self.annotation_fontcolor_button, alignment=Qt.AlignRight)
+        self.form_box_child.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
+
+        color_widget = QtWidgets.QWidget()
+        color_widget.setLayout(self.form_box_child)
         grid0.addWidget(self.annotation_color_label, 3, 0)
         grid0.addWidget(self.annotation_color_label, 3, 0)
-        grid0.addWidget(self.annotation_fontcolor_entry, 3, 1)
-
+        grid0.addWidget(color_widget, 3, 1)
         grid0.addWidget(QtWidgets.QLabel(''), 3, 2)
         grid0.addWidget(QtWidgets.QLabel(''), 3, 2)
+
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         self.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
         self.tc_variable_combo.currentIndexChanged[str].connect(self.on_cnc_custom_parameters)
+
         self.annotation_fontcolor_entry.editingFinished.connect(self.on_annotation_fontcolor_entry)
         self.annotation_fontcolor_entry.editingFinished.connect(self.on_annotation_fontcolor_entry)
+        self.annotation_fontcolor_button.clicked.connect(self.on_annotation_fontcolor_button)
 
 
     def on_cnc_custom_parameters(self, signal_text):
     def on_cnc_custom_parameters(self, signal_text):
         if signal_text == 'Parameters':
         if signal_text == 'Parameters':
@@ -177,3 +189,20 @@ class CNCJobAdvOptPrefGroupUI(OptionsGroupUI):
 
 
     def on_annotation_fontcolor_entry(self):
     def on_annotation_fontcolor_entry(self):
         self.app.defaults['cncjob_annotation_fontcolor'] = self.annotation_fontcolor_entry.get_value()
         self.app.defaults['cncjob_annotation_fontcolor'] = self.annotation_fontcolor_entry.get_value()
+        self.annotation_fontcolor_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_annotation_fontcolor']))
+
+    def on_annotation_fontcolor_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_annotation_fontcolor'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        annotation_color = c_dialog.getColor(initial=current_color)
+
+        if annotation_color.isValid() is False:
+            return
+
+        self.annotation_fontcolor_button.setStyleSheet("background-color:%s" % str(annotation_color.name()))
+
+        new_val_sel = str(annotation_color.name())
+        self.annotation_fontcolor_entry.set_value(new_val_sel)
+        self.app.defaults['cncjob_annotation_fontcolor'] = new_val_sel

+ 142 - 23
AppGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py → flatcamGUI/preferences/cncjob/CNCJobGenPrefGroupUI.py

@@ -1,10 +1,10 @@
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCSliderWithSpinner, FCColorEntry
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCCheckBox, RadioSet, FCSpinner, FCDoubleSpinner, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -170,10 +170,17 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
         self.tline_color_label.setToolTip(
         self.tline_color_label.setToolTip(
             _("Set the travel line color for plotted objects.")
             _("Set the travel line color for plotted objects.")
         )
         )
-        self.tline_color_entry = FCColorEntry()
+        self.tline_color_entry = FCEntry()
+        self.tline_color_button = QtWidgets.QPushButton()
+        self.tline_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.tline_color_entry)
+        self.form_box_child_2.addWidget(self.tline_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.tline_color_label, 14, 0)
         grid0.addWidget(self.tline_color_label, 14, 0)
-        grid0.addWidget(self.tline_color_entry, 14, 1)
+        grid0.addLayout(self.form_box_child_2, 14, 1)
 
 
         # Plot Fill Color
         # Plot Fill Color
         self.tfill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
         self.tfill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@@ -182,20 +189,38 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
               "First 6 digits are the color and the last 2\n"
               "First 6 digits are the color and the last 2\n"
               "digits are for alpha (transparency) level.")
               "digits are for alpha (transparency) level.")
         )
         )
-        self.tfill_color_entry = FCColorEntry()
+        self.tfill_color_entry = FCEntry()
+        self.tfill_color_button = QtWidgets.QPushButton()
+        self.tfill_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.tfill_color_entry)
+        self.form_box_child_1.addWidget(self.tfill_color_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.tfill_color_label, 15, 0)
         grid0.addWidget(self.tfill_color_label, 15, 0)
-        grid0.addWidget(self.tfill_color_entry, 15, 1)
+        grid0.addLayout(self.form_box_child_1, 15, 1)
 
 
         # Plot Fill Transparency Level
         # Plot Fill Transparency Level
-        self.cncjob_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.cncjob_alpha_label.setToolTip(
+        self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.alpha_label.setToolTip(
             _("Set the fill transparency for plotted objects.")
             _("Set the fill transparency for plotted objects.")
         )
         )
-        self.cncjob_alpha_entry = FCSliderWithSpinner(0, 255, 1)
+        self.tcolor_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.tcolor_alpha_slider.setMinimum(0)
+        self.tcolor_alpha_slider.setMaximum(255)
+        self.tcolor_alpha_slider.setSingleStep(1)
+
+        self.tcolor_alpha_spinner = FCSpinner()
+        self.tcolor_alpha_spinner.setMinimumWidth(70)
+        self.tcolor_alpha_spinner.set_range(0, 255)
 
 
-        grid0.addWidget(self.cncjob_alpha_label, 16, 0)
-        grid0.addWidget(self.cncjob_alpha_entry, 16, 1)
+        self.form_box_child_3 = QtWidgets.QHBoxLayout()
+        self.form_box_child_3.addWidget(self.tcolor_alpha_slider)
+        self.form_box_child_3.addWidget(self.tcolor_alpha_spinner)
+
+        grid0.addWidget(self.alpha_label, 16, 0)
+        grid0.addLayout(self.form_box_child_3, 16, 1)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -203,7 +228,7 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(separator_line, 17, 0, 1, 2)
         grid0.addWidget(separator_line, 17, 0, 1, 2)
 
 
         # CNCJob Object Color
         # CNCJob Object Color
-        self.cnc_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Object Color'))
+        self.cnc_color_label = QtWidgets.QLabel('<b>%s</b>' % _('CNCJob Object Color'))
         grid0.addWidget(self.cnc_color_label, 18, 0, 1, 2)
         grid0.addWidget(self.cnc_color_label, 18, 0, 1, 2)
 
 
         # Plot Line Color
         # Plot Line Color
@@ -211,10 +236,17 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
         self.line_color_label.setToolTip(
         self.line_color_label.setToolTip(
             _("Set the color for plotted objects.")
             _("Set the color for plotted objects.")
         )
         )
-        self.line_color_entry = FCColorEntry()
+        self.line_color_entry = FCEntry()
+        self.line_color_button = QtWidgets.QPushButton()
+        self.line_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.line_color_entry)
+        self.form_box_child_2.addWidget(self.line_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.line_color_label, 19, 0)
         grid0.addWidget(self.line_color_label, 19, 0)
-        grid0.addWidget(self.line_color_entry, 19, 1)
+        grid0.addLayout(self.form_box_child_2, 19, 1)
 
 
         # Plot Fill Color
         # Plot Fill Color
         self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
         self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@@ -223,21 +255,32 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
               "First 6 digits are the color and the last 2\n"
               "First 6 digits are the color and the last 2\n"
               "digits are for alpha (transparency) level.")
               "digits are for alpha (transparency) level.")
         )
         )
-        self.fill_color_entry = FCColorEntry()
+        self.fill_color_entry = FCEntry()
+        self.fill_color_button = QtWidgets.QPushButton()
+        self.fill_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.fill_color_entry)
+        self.form_box_child_1.addWidget(self.fill_color_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.fill_color_label, 20, 0)
         grid0.addWidget(self.fill_color_label, 20, 0)
-        grid0.addWidget(self.fill_color_entry, 20, 1)
+        grid0.addLayout(self.form_box_child_1, 20, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         # Setting plot colors signals
         # Setting plot colors signals
         self.tline_color_entry.editingFinished.connect(self.on_tline_color_entry)
         self.tline_color_entry.editingFinished.connect(self.on_tline_color_entry)
+        self.tline_color_button.clicked.connect(self.on_tline_color_button)
         self.tfill_color_entry.editingFinished.connect(self.on_tfill_color_entry)
         self.tfill_color_entry.editingFinished.connect(self.on_tfill_color_entry)
-
-        self.cncjob_alpha_entry.valueChanged.connect(self.on_cncjob_alpha_changed)  # alpha
+        self.tfill_color_button.clicked.connect(self.on_tfill_color_button)
+        self.tcolor_alpha_spinner.valueChanged.connect(self.on_tcolor_spinner)
+        self.tcolor_alpha_slider.valueChanged.connect(self.on_tcolor_slider)
 
 
         self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
         self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
+        self.line_color_button.clicked.connect(self.on_line_color_button)
         self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
         self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
+        self.fill_color_button.clicked.connect(self.on_fill_color_button)
 
 
     # ------------------------------------------------------
     # ------------------------------------------------------
     # Setting travel colors handlers
     # Setting travel colors handlers
@@ -245,12 +288,27 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
     def on_tfill_color_entry(self):
     def on_tfill_color_entry(self):
         self.app.defaults['cncjob_travel_fill'] = self.tfill_color_entry.get_value()[:7] + \
         self.app.defaults['cncjob_travel_fill'] = self.tfill_color_entry.get_value()[:7] + \
                                                   self.app.defaults['cncjob_travel_fill'][7:9]
                                                   self.app.defaults['cncjob_travel_fill'][7:9]
+        self.tfill_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_travel_fill'])[:7])
 
 
-    def on_tline_color_entry(self):
-        self.app.defaults['cncjob_travel_line'] = self.tline_color_entry.get_value()[:7] + \
-                                                  self.app.defaults['cncjob_travel_line'][7:9]
+    def on_tfill_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_travel_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.tfill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
 
 
-    def on_cncjob_alpha_changed(self, spinner_value):
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_travel_fill'][7:9])
+        self.tfill_color_entry.set_value(new_val)
+        self.app.defaults['cncjob_travel_fill'] = new_val
+
+    def on_tcolor_spinner(self):
+        spinner_value = self.tcolor_alpha_spinner.value()
+        self.tcolor_alpha_slider.setValue(spinner_value)
         self.app.defaults['cncjob_travel_fill'] = \
         self.app.defaults['cncjob_travel_fill'] = \
             self.app.defaults['cncjob_travel_fill'][:7] + \
             self.app.defaults['cncjob_travel_fill'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
@@ -258,13 +316,74 @@ class CNCJobGenPrefGroupUI(OptionsGroupUI):
             self.app.defaults['cncjob_travel_line'][:7] + \
             self.app.defaults['cncjob_travel_line'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
 
 
+    def on_tcolor_slider(self):
+        slider_value = self.tcolor_alpha_slider.value()
+        self.tcolor_alpha_spinner.setValue(slider_value)
+
+    def on_tline_color_entry(self):
+        self.app.defaults['cncjob_travel_line'] = self.tline_color_entry.get_value()[:7] + \
+                                                  self.app.defaults['cncjob_travel_line'][7:9]
+        self.tline_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_travel_line'])[:7])
+
+    def on_tline_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_travel_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.tline_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_travel_line'][7:9])
+        self.tline_color_entry.set_value(new_val_line)
+        self.app.defaults['cncjob_travel_line'] = new_val_line
+
     # ------------------------------------------------------
     # ------------------------------------------------------
     # Setting plot colors handlers
     # Setting plot colors handlers
     # ------------------------------------------------------
     # ------------------------------------------------------
     def on_fill_color_entry(self):
     def on_fill_color_entry(self):
         self.app.defaults['cncjob_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
         self.app.defaults['cncjob_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
                                                   self.app.defaults['cncjob_plot_fill'][7:9]
                                                   self.app.defaults['cncjob_plot_fill'][7:9]
+        self.fill_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_plot_fill'])[:7])
+
+    def on_fill_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_plot_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['cncjob_plot_fill'][7:9])
+        self.fill_color_entry.set_value(new_val)
+        self.app.defaults['cncjob_plot_fill'] = new_val
 
 
     def on_line_color_entry(self):
     def on_line_color_entry(self):
         self.app.defaults['cncjob_plot_line'] = self.line_color_entry.get_value()[:7] + \
         self.app.defaults['cncjob_plot_line'] = self.line_color_entry.get_value()[:7] + \
                                                   self.app.defaults['cncjob_plot_line'][7:9]
                                                   self.app.defaults['cncjob_plot_line'][7:9]
+        self.line_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['cncjob_plot_line'])[:7])
+
+    def on_line_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['cncjob_plot_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['cncjob_plot_line'][7:9])
+        self.line_color_entry.set_value(new_val_line)
+        self.app.defaults['cncjob_plot_line'] = new_val_line

+ 3 - 3
AppGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py → flatcamGUI/preferences/cncjob/CNCJobOptPrefGroupUI.py

@@ -1,11 +1,11 @@
 from PyQt5 import QtWidgets, QtGui
 from PyQt5 import QtWidgets, QtGui
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCTextArea
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCTextArea
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')

+ 3 - 3
AppGUI/preferences/cncjob/CNCJobPreferencesUI.py → flatcamGUI/preferences/cncjob/CNCJobPreferencesUI.py

@@ -1,8 +1,8 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 
 
-from AppGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI
-from AppGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI
-from AppGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI
+from flatcamGUI.preferences.cncjob.CNCJobAdvOptPrefGroupUI import CNCJobAdvOptPrefGroupUI
+from flatcamGUI.preferences.cncjob.CNCJobOptPrefGroupUI import CNCJobOptPrefGroupUI
+from flatcamGUI.preferences.cncjob.CNCJobGenPrefGroupUI import CNCJobGenPrefGroupUI
 
 
 
 
 class CNCJobPreferencesUI(QtWidgets.QWidget):
 class CNCJobPreferencesUI(QtWidgets.QWidget):

+ 0 - 0
AppGUI/preferences/cncjob/__init__.py → flatcamGUI/preferences/cncjob/__init__.py


+ 5 - 5
AppGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py → flatcamGUI/preferences/excellon/ExcellonAdvOptPrefGroupUI.py

@@ -1,10 +1,10 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCDoubleSpinner, RadioSet, FCCheckBox, NumericalEvalTupleEntry, NumericalEvalEntry
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCEntry, FloatEntry, RadioSet, FCCheckBox
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -60,7 +60,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
         toolchange_xy_label.setToolTip(
         toolchange_xy_label.setToolTip(
             _("Toolchange X,Y position.")
             _("Toolchange X,Y position.")
         )
         )
-        self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
+        self.toolchangexy_entry = FCEntry()
 
 
         grid1.addWidget(toolchange_xy_label, 1, 0)
         grid1.addWidget(toolchange_xy_label, 1, 0)
         grid1.addWidget(self.toolchangexy_entry, 1, 1)
         grid1.addWidget(self.toolchangexy_entry, 1, 1)
@@ -71,7 +71,7 @@ class ExcellonAdvOptPrefGroupUI(OptionsGroupUI):
             _("Height of the tool just after start.\n"
             _("Height of the tool just after start.\n"
               "Delete the value if you don't need this feature.")
               "Delete the value if you don't need this feature.")
         )
         )
-        self.estartz_entry = NumericalEvalEntry(border_color='#0069A9')
+        self.estartz_entry = FloatEntry()
 
 
         grid1.addWidget(startzlabel, 2, 0)
         grid1.addWidget(startzlabel, 2, 0)
         grid1.addWidget(self.estartz_entry, 2, 1)
         grid1.addWidget(self.estartz_entry, 2, 1)

+ 3 - 3
AppGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py → flatcamGUI/preferences/excellon/ExcellonEditorPrefGroupUI.py

@@ -1,11 +1,11 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')

+ 3 - 3
AppGUI/preferences/excellon/ExcellonExpPrefGroupUI.py → flatcamGUI/preferences/excellon/ExcellonExpPrefGroupUI.py

@@ -1,10 +1,10 @@
 from PyQt5 import QtWidgets, QtCore
 from PyQt5 import QtWidgets, QtCore
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import RadioSet, FCSpinner
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import RadioSet, FCSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')

+ 86 - 137
AppGUI/preferences/excellon/ExcellonGenPrefGroupUI.py → flatcamGUI/preferences/excellon/ExcellonGenPrefGroupUI.py

@@ -3,10 +3,10 @@ import platform
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry, FCSliderWithSpinner, FCColorEntry
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, RadioSet, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -36,31 +36,22 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
         grid1 = QtWidgets.QGridLayout()
         grid1 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid1)
         self.layout.addLayout(grid1)
 
 
-        # Plot CB
         self.plot_cb = FCCheckBox(label=_('Plot'))
         self.plot_cb = FCCheckBox(label=_('Plot'))
         self.plot_cb.setToolTip(
         self.plot_cb.setToolTip(
             "Plot (show) this object."
             "Plot (show) this object."
         )
         )
         grid1.addWidget(self.plot_cb, 0, 0)
         grid1.addWidget(self.plot_cb, 0, 0)
 
 
-        # Solid CB
         self.solid_cb = FCCheckBox(label=_('Solid'))
         self.solid_cb = FCCheckBox(label=_('Solid'))
         self.solid_cb.setToolTip(
         self.solid_cb.setToolTip(
             "Plot as solid circles."
             "Plot as solid circles."
         )
         )
         grid1.addWidget(self.solid_cb, 0, 1)
         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 = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid1.addWidget(separator_line, 1, 0, 1, 3)
+        grid1.addWidget(separator_line, 1, 0, 1, 2)
 
 
         grid2 = QtWidgets.QGridLayout()
         grid2 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid2)
         self.layout.addLayout(grid2)
@@ -264,7 +255,7 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
         grid2.addWidget(separator_line, 11, 0, 1, 2)
         grid2.addWidget(separator_line, 11, 0, 1, 2)
 
 
         # Excellon Object Color
         # Excellon Object Color
-        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Object Color'))
+        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Excellon Object Color'))
         grid2.addWidget(self.gerber_color_label, 12, 0, 1, 2)
         grid2.addWidget(self.gerber_color_label, 12, 0, 1, 2)
 
 
         # Plot Line Color
         # Plot Line Color
@@ -272,10 +263,17 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
         self.line_color_label.setToolTip(
         self.line_color_label.setToolTip(
             _("Set the line color for plotted objects.")
             _("Set the line color for plotted objects.")
         )
         )
-        self.line_color_entry = FCColorEntry()
+        self.line_color_entry = FCEntry()
+        self.line_color_button = QtWidgets.QPushButton()
+        self.line_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.line_color_entry)
+        self.form_box_child_2.addWidget(self.line_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid2.addWidget(self.line_color_label, 13, 0)
         grid2.addWidget(self.line_color_label, 13, 0)
-        grid2.addWidget(self.line_color_entry, 13, 1)
+        grid2.addLayout(self.form_box_child_2, 13, 1)
 
 
         # Plot Fill Color
         # Plot Fill Color
         self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
         self.fill_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@@ -284,20 +282,38 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
               "First 6 digits are the color and the last 2\n"
               "First 6 digits are the color and the last 2\n"
               "digits are for alpha (transparency) level.")
               "digits are for alpha (transparency) level.")
         )
         )
-        self.fill_color_entry = FCColorEntry()
+        self.fill_color_entry = FCEntry()
+        self.fill_color_button = QtWidgets.QPushButton()
+        self.fill_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.fill_color_entry)
+        self.form_box_child_1.addWidget(self.fill_color_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid2.addWidget(self.fill_color_label, 14, 0)
         grid2.addWidget(self.fill_color_label, 14, 0)
-        grid2.addWidget(self.fill_color_entry, 14, 1)
+        grid2.addLayout(self.form_box_child_1, 14, 1)
 
 
         # Plot Fill Transparency Level
         # Plot Fill Transparency Level
-        self.excellon_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.excellon_alpha_label.setToolTip(
+        self.alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.alpha_label.setToolTip(
             _("Set the fill transparency for plotted objects.")
             _("Set the fill transparency for plotted objects.")
         )
         )
-        self.excellon_alpha_entry = FCSliderWithSpinner(0, 255, 1)
+        self.color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.color_alpha_slider.setMinimum(0)
+        self.color_alpha_slider.setMaximum(255)
+        self.color_alpha_slider.setSingleStep(1)
 
 
-        grid2.addWidget(self.excellon_alpha_label, 15, 0)
-        grid2.addWidget(self.excellon_alpha_entry, 15, 1)
+        self.color_alpha_spinner = FCSpinner()
+        self.color_alpha_spinner.setMinimumWidth(70)
+        self.color_alpha_spinner.set_range(0, 255)
+
+        self.form_box_child_3 = QtWidgets.QHBoxLayout()
+        self.form_box_child_3.addWidget(self.color_alpha_slider)
+        self.form_box_child_3.addWidget(self.color_alpha_spinner)
+
+        grid2.addWidget(self.alpha_label, 15, 0)
+        grid2.addLayout(self.form_box_child_3, 15, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()
 
 
@@ -317,18 +333,14 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
 
 
         # Setting plot colors signals
         # Setting plot colors signals
         self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
         self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
+        self.line_color_button.clicked.connect(self.on_line_color_button)
         self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
         self.fill_color_entry.editingFinished.connect(self.on_fill_color_entry)
-
-        self.excellon_alpha_entry.valueChanged.connect(self.on_excellon_alpha_changed)  # alpha
+        self.fill_color_button.clicked.connect(self.on_fill_color_button)
+        self.color_alpha_spinner.valueChanged.connect(self.on_color_spinner)
+        self.color_alpha_slider.valueChanged.connect(self.on_color_slider)
 
 
         # Load the defaults values into the Excellon Format and Excellon Zeros fields
         # Load the defaults values into the Excellon Format and Excellon Zeros fields
         self.excellon_defaults_button.clicked.connect(self.on_excellon_defaults_button)
         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):
     def optimization_selection(self):
         if self.excellon_optimization_radio.get_value() == 'M':
         if self.excellon_optimization_radio.get_value() == 'M':
@@ -342,12 +354,26 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
     def on_fill_color_entry(self):
     def on_fill_color_entry(self):
         self.app.defaults['excellon_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
         self.app.defaults['excellon_plot_fill'] = self.fill_color_entry.get_value()[:7] + \
             self.app.defaults['excellon_plot_fill'][7:9]
             self.app.defaults['excellon_plot_fill'][7:9]
+        self.fill_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_fill'])[:7])
 
 
-    def on_line_color_entry(self):
-        self.app.defaults['excellon_plot_line'] = self.line_color_entry.get_value()[:7] + \
-                                                self.app.defaults['excellon_plot_line'][7:9]
+    def on_fill_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['excellon_plot_fill'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
+
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.fill_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
 
 
-    def on_excellon_alpha_changed(self, spinner_value):
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['excellon_plot_fill'][7:9])
+        self.fill_color_entry.set_value(new_val)
+        self.app.defaults['excellon_plot_fill'] = new_val
+
+    def on_color_spinner(self):
+        spinner_value = self.color_alpha_spinner.value()
+        self.color_alpha_slider.setValue(spinner_value)
         self.app.defaults['excellon_plot_fill'] = \
         self.app.defaults['excellon_plot_fill'] = \
             self.app.defaults['excellon_plot_fill'][:7] + \
             self.app.defaults['excellon_plot_fill'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
@@ -355,6 +381,31 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
             self.app.defaults['excellon_plot_line'][:7] + \
             self.app.defaults['excellon_plot_line'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
 
 
+    def on_color_slider(self):
+        slider_value = self.color_alpha_slider.value()
+        self.color_alpha_spinner.setValue(slider_value)
+
+    def on_line_color_entry(self):
+        self.app.defaults['excellon_plot_line'] = self.line_color_entry.get_value()[:7] + \
+                                                self.app.defaults['excellon_plot_line'][7:9]
+        self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['excellon_plot_line'])[:7])
+
+    def on_line_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['excellon_plot_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['excellon_plot_line'][7:9])
+        self.line_color_entry.set_value(new_val_line)
+        self.app.defaults['excellon_plot_line'] = new_val_line
+
     def on_excellon_defaults_button(self):
     def on_excellon_defaults_button(self):
         self.app.preferencesUiManager.defaults_form_fields["excellon_format_lower_in"].set_value('4')
         self.app.preferencesUiManager.defaults_form_fields["excellon_format_lower_in"].set_value('4')
         self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_in"].set_value('2')
         self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_in"].set_value('2')
@@ -362,105 +413,3 @@ class ExcellonGenPrefGroupUI(OptionsGroupUI):
         self.app.preferencesUiManager.defaults_form_fields["excellon_format_upper_mm"].set_value('3')
         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_zeros"].set_value('L')
         self.app.preferencesUiManager.defaults_form_fields["excellon_units"].set_value('INCH')
         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()

+ 6 - 6
AppGUI/preferences/excellon/ExcellonOptPrefGroupUI.py → flatcamGUI/preferences/excellon/ExcellonOptPrefGroupUI.py

@@ -1,12 +1,12 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import Qt, QSettings
 from PyQt5.QtCore import Qt, QSettings
 
 
-from AppGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \
-    FCComboBox, NumericalEvalTupleEntry
-from AppGUI.preferences import machinist_setting
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCEntry, FCSpinner, OptionalInputSection, \
+    FCComboBox
+from flatcamGUI.preferences import machinist_setting
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -198,7 +198,7 @@ class ExcellonOptPrefGroupUI(OptionsGroupUI):
               "If no value is entered then there is no move\n"
               "If no value is entered then there is no move\n"
               "on X,Y plane at the end of the job.")
               "on X,Y plane at the end of the job.")
         )
         )
-        self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
+        self.endxy_entry = FCEntry()
 
 
         grid2.addWidget(endmove_xy_label, 9, 0)
         grid2.addWidget(endmove_xy_label, 9, 0)
         grid2.addWidget(self.endxy_entry, 9, 1)
         grid2.addWidget(self.endxy_entry, 9, 1)

+ 7 - 7
AppGUI/preferences/excellon/ExcellonPreferencesUI.py → flatcamGUI/preferences/excellon/ExcellonPreferencesUI.py

@@ -1,14 +1,14 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-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
+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
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -31,7 +31,7 @@ class ExcellonPreferencesUI(QtWidgets.QWidget):
         self.decimals = decimals
         self.decimals = decimals
 
 
         self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals)
         self.excellon_gen_group = ExcellonGenPrefGroupUI(decimals=self.decimals)
-        self.excellon_gen_group.setMinimumWidth(240)
+        self.excellon_gen_group.setMinimumWidth(220)
         self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals)
         self.excellon_opt_group = ExcellonOptPrefGroupUI(decimals=self.decimals)
         self.excellon_opt_group.setMinimumWidth(290)
         self.excellon_opt_group.setMinimumWidth(290)
         self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals)
         self.excellon_exp_group = ExcellonExpPrefGroupUI(decimals=self.decimals)

+ 0 - 0
AppGUI/preferences/excellon/__init__.py → flatcamGUI/preferences/excellon/__init__.py


+ 50 - 32
AppGUI/preferences/general/GeneralAPPSetGroupUI.py → flatcamGUI/preferences/general/GeneralAPPSetGroupUI.py

@@ -1,13 +1,13 @@
-from PyQt5 import QtCore, QtWidgets
+from PyQt5 import QtCore, QtWidgets, QtGui
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \
-    FCColorEntry
-from AppGUI.preferences import settings
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, RadioSet, OptionalInputSection, FCSpinner, \
+    FCEntry
+from flatcamGUI.preferences import settings
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -177,6 +177,14 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
                                               {'label': _('Landscape'), 'value': 'l'},
                                               {'label': _('Landscape'), 'value': 'l'},
                                               ], stretch=False)
                                               ], 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_label, 8, 0)
         grid0.addWidget(self.wk_orientation_radio, 8, 1)
         grid0.addWidget(self.wk_orientation_radio, 8, 1)
 
 
@@ -193,7 +201,7 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
         self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook'))
         self.notebook_font_size_label = QtWidgets.QLabel('%s:' % _('Notebook'))
         self.notebook_font_size_label.setToolTip(
         self.notebook_font_size_label.setToolTip(
             _("This sets the font size for the elements found in the Notebook.\n"
             _("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"
+              "The notebook is the collapsible area in the left side of the GUI,\n"
               "and include the Project, Selected and Tool tabs.")
               "and include the Project, Selected and Tool tabs.")
         )
         )
 
 
@@ -232,8 +240,8 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
         # TextBox Font Size
         # TextBox Font Size
         self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox'))
         self.textbox_font_size_label = QtWidgets.QLabel('%s:' % _('Textbox'))
         self.textbox_font_size_label.setToolTip(
         self.textbox_font_size_label.setToolTip(
-            _("This sets the font size for the Textbox AppGUI\n"
-              "elements that are used in the application.")
+            _("This sets the font size for the Textbox GUI\n"
+              "elements that are used in FlatCAM.")
         )
         )
 
 
         self.textbox_font_size_spinner = FCSpinner()
         self.textbox_font_size_spinner = FCSpinner()
@@ -249,29 +257,10 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
         grid0.addWidget(self.textbox_font_size_label, 13, 0)
         grid0.addWidget(self.textbox_font_size_label, 13, 0)
         grid0.addWidget(self.textbox_font_size_spinner, 13, 1)
         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 = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
         separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
-        grid0.addWidget(separator_line, 16, 0, 1, 2)
+        grid0.addWidget(separator_line, 14, 0, 1, 2)
 
 
         # -----------------------------------------------------------
         # -----------------------------------------------------------
         # -------------- MOUSE SETTINGS -----------------------------
         # -------------- MOUSE SETTINGS -----------------------------
@@ -334,16 +323,24 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
         self.mouse_color_label.setToolTip(
         self.mouse_color_label.setToolTip(
             _("Set the color of the mouse cursor.")
             _("Set the color of the mouse cursor.")
         )
         )
-        self.mouse_cursor_entry = FCColorEntry()
+        self.mouse_cursor_entry = FCEntry()
+        self.mouse_cursor_button = QtWidgets.QPushButton()
+        self.mouse_cursor_button.setFixedSize(15, 15)
+
+        self.form_box_child_1 = QtWidgets.QHBoxLayout()
+        self.form_box_child_1.addWidget(self.mouse_cursor_entry)
+        self.form_box_child_1.addWidget(self.mouse_cursor_button)
+        self.form_box_child_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.mouse_color_label, 26, 0)
         grid0.addWidget(self.mouse_color_label, 26, 0)
-        grid0.addWidget(self.mouse_cursor_entry, 26, 1)
+        grid0.addLayout(self.form_box_child_1, 26, 1)
 
 
         self.mois = OptionalInputSection(
         self.mois = OptionalInputSection(
             self.mouse_cursor_color_cb,
             self.mouse_cursor_color_cb,
             [
             [
                 self.mouse_color_label,
                 self.mouse_color_label,
-                self.mouse_cursor_entry
+                self.mouse_cursor_entry,
+                self.mouse_cursor_button
             ]
             ]
         )
         )
         # Select mouse pan button
         # Select mouse pan button
@@ -443,7 +440,9 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         self.mouse_cursor_color_cb.stateChanged.connect(self.on_mouse_cursor_color_enable)
         self.mouse_cursor_color_cb.stateChanged.connect(self.on_mouse_cursor_color_enable)
+
         self.mouse_cursor_entry.editingFinished.connect(self.on_mouse_cursor_entry)
         self.mouse_cursor_entry.editingFinished.connect(self.on_mouse_cursor_entry)
+        self.mouse_cursor_button.clicked.connect(self.on_mouse_cursor_button)
 
 
     def on_mouse_cursor_color_enable(self, val):
     def on_mouse_cursor_color_enable(self, val):
         if val:
         if val:
@@ -462,4 +461,23 @@ class GeneralAPPSetGroupUI(OptionsGroupUI):
 
 
     def on_mouse_cursor_entry(self):
     def on_mouse_cursor_entry(self):
         self.app.defaults['global_cursor_color'] = self.mouse_cursor_entry.get_value()
         self.app.defaults['global_cursor_color'] = self.mouse_cursor_entry.get_value()
+        self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_cursor_color']))
+
+        self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
+
+    def on_mouse_cursor_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_cursor_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        proj_color = c_dialog.getColor(initial=current_color)
+
+        if proj_color.isValid() is False:
+            return
+
+        self.mouse_cursor_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
+
+        new_val_sel = str(proj_color.name())
+        self.mouse_cursor_entry.set_value(new_val_sel)
+        self.app.defaults['global_cursor_color'] = new_val_sel
+
         self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]
         self.app.cursor_color_3D = self.app.defaults["global_cursor_color"]

+ 6 - 5
AppGUI/preferences/general/GeneralAppPrefGroupUI.py → flatcamGUI/preferences/general/GeneralAppPrefGroupUI.py

@@ -3,12 +3,12 @@ import sys
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \
+from flatcamGUI.GUIElements import RadioSet, FCSpinner, FCCheckBox, FCComboBox, FCButton, OptionalInputSection, \
     FCDoubleSpinner
     FCDoubleSpinner
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -380,11 +380,12 @@ class GeneralAppPrefGroupUI(OptionsGroupUI):
 
 
     def on_toggle_shell_from_settings(self, state):
     def on_toggle_shell_from_settings(self, state):
         """
         """
-        Toggle shell ui: if is visible close it, if it is closed then open it
-
+        Toggle shell: if is visible close it, if it is closed then open it
         :return: None
         :return: None
         """
         """
 
 
+        self.app.defaults.report_usage("on_toggle_shell_from_settings()")
+
         if state is True:
         if state is True:
             if not self.app.ui.shell_dock.isVisible():
             if not self.app.ui.shell_dock.isVisible():
                 self.app.ui.shell_dock.show()
                 self.app.ui.shell_dock.show()

+ 303 - 73
AppGUI/preferences/general/GeneralGUIPrefGroupUI.py → flatcamGUI/preferences/general/GeneralGUIPrefGroupUI.py

@@ -1,11 +1,11 @@
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5.QtCore import QSettings, Qt
 from PyQt5.QtCore import QSettings, Qt
 
 
-from AppGUI.GUIElements import RadioSet, FCCheckBox, FCComboBox, FCSliderWithSpinner, FCColorEntry
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import RadioSet, FCCheckBox, FCButton, FCComboBox, FCEntry, FCSpinner
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -35,7 +35,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         # Theme selection
         # Theme selection
         self.theme_label = QtWidgets.QLabel('%s:' % _('Theme'))
         self.theme_label = QtWidgets.QLabel('%s:' % _('Theme'))
         self.theme_label.setToolTip(
         self.theme_label.setToolTip(
-            _("Select a theme for the application.\n"
+            _("Select a theme for FlatCAM.\n"
               "It will theme the plot area.")
               "It will theme the plot area.")
         )
         )
 
 
@@ -72,7 +72,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         # Layout selection
         # Layout selection
         self.layout_label = QtWidgets.QLabel('%s:' % _('Layout'))
         self.layout_label = QtWidgets.QLabel('%s:' % _('Layout'))
         self.layout_label.setToolTip(
         self.layout_label.setToolTip(
-            _("Select a layout for the application.\n"
+            _("Select an layout for FlatCAM.\n"
               "It is applied immediately.")
               "It is applied immediately.")
         )
         )
         self.layout_combo = FCComboBox()
         self.layout_combo = FCComboBox()
@@ -94,7 +94,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         # Style selection
         # Style selection
         self.style_label = QtWidgets.QLabel('%s:' % _('Style'))
         self.style_label = QtWidgets.QLabel('%s:' % _('Style'))
         self.style_label.setToolTip(
         self.style_label.setToolTip(
-            _("Select a style for the application.\n"
+            _("Select an style for FlatCAM.\n"
               "It will be applied at the next app start.")
               "It will be applied at the next app start.")
         )
         )
         self.style_combo = FCComboBox()
         self.style_combo = FCComboBox()
@@ -110,7 +110,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         # Enable High DPI Support
         # Enable High DPI Support
         self.hdpi_cb = FCCheckBox('%s' % _('Activate HDPI Support'))
         self.hdpi_cb = FCCheckBox('%s' % _('Activate HDPI Support'))
         self.hdpi_cb.setToolTip(
         self.hdpi_cb.setToolTip(
-            _("Enable High DPI support for the application.\n"
+            _("Enable High DPI support for FlatCAM.\n"
               "It will be applied at the next app start.")
               "It will be applied at the next app start.")
         )
         )
 
 
@@ -126,7 +126,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         # Enable Hover box
         # Enable Hover box
         self.hover_cb = FCCheckBox('%s' % _('Display Hover Shape'))
         self.hover_cb = FCCheckBox('%s' % _('Display Hover Shape'))
         self.hover_cb.setToolTip(
         self.hover_cb.setToolTip(
-            _("Enable display of a hover shape for the application objects.\n"
+            _("Enable display of a hover shape for FlatCAM objects.\n"
               "It is displayed whenever the mouse cursor is hovering\n"
               "It is displayed whenever the mouse cursor is hovering\n"
               "over any kind of not-selected object.")
               "over any kind of not-selected object.")
         )
         )
@@ -135,7 +135,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         # Enable Selection box
         # Enable Selection box
         self.selection_cb = FCCheckBox('%s' % _('Display Selection Shape'))
         self.selection_cb = FCCheckBox('%s' % _('Display Selection Shape'))
         self.selection_cb.setToolTip(
         self.selection_cb.setToolTip(
-            _("Enable the display of a selection shape for the application objects.\n"
+            _("Enable the display of a selection shape for FlatCAM objects.\n"
               "It is displayed whenever the mouse selects an object\n"
               "It is displayed whenever the mouse selects an object\n"
               "either by clicking or dragging mouse from left to right or\n"
               "either by clicking or dragging mouse from left to right or\n"
               "right to left.")
               "right to left.")
@@ -155,10 +155,17 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.sl_color_label.setToolTip(
         self.sl_color_label.setToolTip(
             _("Set the line color for the 'left to right' selection box.")
             _("Set the line color for the 'left to right' selection box.")
         )
         )
-        self.sl_color_entry = FCColorEntry()
+        self.sl_color_entry = FCEntry()
+        self.sl_color_button = QtWidgets.QPushButton()
+        self.sl_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_4 = QtWidgets.QHBoxLayout()
+        self.form_box_child_4.addWidget(self.sl_color_entry)
+        self.form_box_child_4.addWidget(self.sl_color_button)
+        self.form_box_child_4.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.sl_color_label, 16, 0)
         grid0.addWidget(self.sl_color_label, 16, 0)
-        grid0.addWidget(self.sl_color_entry, 16, 1)
+        grid0.addLayout(self.form_box_child_4, 16, 1)
 
 
         self.sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
         self.sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
         self.sf_color_label.setToolTip(
         self.sf_color_label.setToolTip(
@@ -167,20 +174,38 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
               "First 6 digits are the color and the last 2\n"
               "First 6 digits are the color and the last 2\n"
               "digits are for alpha (transparency) level.")
               "digits are for alpha (transparency) level.")
         )
         )
-        self.sf_color_entry = FCColorEntry()
+        self.sf_color_entry = FCEntry()
+        self.sf_color_button = QtWidgets.QPushButton()
+        self.sf_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_5 = QtWidgets.QHBoxLayout()
+        self.form_box_child_5.addWidget(self.sf_color_entry)
+        self.form_box_child_5.addWidget(self.sf_color_button)
+        self.form_box_child_5.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.sf_color_label, 17, 0)
         grid0.addWidget(self.sf_color_label, 17, 0)
-        grid0.addWidget(self.sf_color_entry, 17, 1)
+        grid0.addLayout(self.form_box_child_5, 17, 1)
 
 
         # Plot Selection (left - right) Fill Transparency Level
         # Plot Selection (left - right) Fill Transparency Level
-        self.left_right_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.left_right_alpha_label.setToolTip(
+        self.sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.sf_alpha_label.setToolTip(
             _("Set the fill transparency for the 'left to right' selection box.")
             _("Set the fill transparency for the 'left to right' selection box.")
         )
         )
-        self.left_right_alpha_entry = FCSliderWithSpinner(0, 255, 1)
+        self.sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.sf_color_alpha_slider.setMinimum(0)
+        self.sf_color_alpha_slider.setMaximum(255)
+        self.sf_color_alpha_slider.setSingleStep(1)
+
+        self.sf_color_alpha_spinner = FCSpinner()
+        self.sf_color_alpha_spinner.setMinimumWidth(70)
+        self.sf_color_alpha_spinner.set_range(0, 255)
 
 
-        grid0.addWidget(self.left_right_alpha_label, 18, 0)
-        grid0.addWidget(self.left_right_alpha_entry, 18, 1)
+        self.form_box_child_6 = QtWidgets.QHBoxLayout()
+        self.form_box_child_6.addWidget(self.sf_color_alpha_slider)
+        self.form_box_child_6.addWidget(self.sf_color_alpha_spinner)
+
+        grid0.addWidget(self.sf_alpha_label, 18, 0)
+        grid0.addLayout(self.form_box_child_6, 18, 1)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -196,10 +221,17 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.alt_sl_color_label.setToolTip(
         self.alt_sl_color_label.setToolTip(
             _("Set the line color for the 'right to left' selection box.")
             _("Set the line color for the 'right to left' selection box.")
         )
         )
-        self.alt_sl_color_entry = FCColorEntry()
+        self.alt_sl_color_entry = FCEntry()
+        self.alt_sl_color_button = QtWidgets.QPushButton()
+        self.alt_sl_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_7 = QtWidgets.QHBoxLayout()
+        self.form_box_child_7.addWidget(self.alt_sl_color_entry)
+        self.form_box_child_7.addWidget(self.alt_sl_color_button)
+        self.form_box_child_7.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.alt_sl_color_label, 21, 0)
         grid0.addWidget(self.alt_sl_color_label, 21, 0)
-        grid0.addWidget(self.alt_sl_color_entry, 21, 1)
+        grid0.addLayout(self.form_box_child_7, 21, 1)
 
 
         # Plot Selection (right - left) Fill Color
         # Plot Selection (right - left) Fill Color
         self.alt_sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
         self.alt_sf_color_label = QtWidgets.QLabel('%s:' % _('Fill'))
@@ -209,20 +241,38 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
               "First 6 digits are the color and the last 2\n"
               "First 6 digits are the color and the last 2\n"
               "digits are for alpha (transparency) level.")
               "digits are for alpha (transparency) level.")
         )
         )
-        self.alt_sf_color_entry = FCColorEntry()
+        self.alt_sf_color_entry = FCEntry()
+        self.alt_sf_color_button = QtWidgets.QPushButton()
+        self.alt_sf_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_8 = QtWidgets.QHBoxLayout()
+        self.form_box_child_8.addWidget(self.alt_sf_color_entry)
+        self.form_box_child_8.addWidget(self.alt_sf_color_button)
+        self.form_box_child_8.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.alt_sf_color_label, 22, 0)
         grid0.addWidget(self.alt_sf_color_label, 22, 0)
-        grid0.addWidget(self.alt_sf_color_entry, 22, 1)
+        grid0.addLayout(self.form_box_child_8, 22, 1)
 
 
         # Plot Selection (right - left) Fill Transparency Level
         # Plot Selection (right - left) Fill Transparency Level
-        self.right_left_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
-        self.right_left_alpha_label.setToolTip(
+        self.alt_sf_alpha_label = QtWidgets.QLabel('%s:' % _('Alpha'))
+        self.alt_sf_alpha_label.setToolTip(
             _("Set the fill transparency for selection 'right to left' box.")
             _("Set the fill transparency for selection 'right to left' box.")
         )
         )
-        self.right_left_alpha_entry = FCSliderWithSpinner(0, 255, 1)
+        self.alt_sf_color_alpha_slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
+        self.alt_sf_color_alpha_slider.setMinimum(0)
+        self.alt_sf_color_alpha_slider.setMaximum(255)
+        self.alt_sf_color_alpha_slider.setSingleStep(1)
+
+        self.alt_sf_color_alpha_spinner = FCSpinner()
+        self.alt_sf_color_alpha_spinner.setMinimumWidth(70)
+        self.alt_sf_color_alpha_spinner.set_range(0, 255)
+
+        self.form_box_child_9 = QtWidgets.QHBoxLayout()
+        self.form_box_child_9.addWidget(self.alt_sf_color_alpha_slider)
+        self.form_box_child_9.addWidget(self.alt_sf_color_alpha_spinner)
 
 
-        grid0.addWidget(self.right_left_alpha_label, 23, 0)
-        grid0.addWidget(self.right_left_alpha_entry, 23, 1)
+        grid0.addWidget(self.alt_sf_alpha_label, 23, 0)
+        grid0.addLayout(self.form_box_child_9, 23, 1)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -241,20 +291,34 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.alt_sf_color_label.setToolTip(
         self.alt_sf_color_label.setToolTip(
             _("Set the color for the shape.")
             _("Set the color for the shape.")
         )
         )
-        self.draw_color_entry = FCColorEntry()
+        self.draw_color_entry = FCEntry()
+        self.draw_color_button = QtWidgets.QPushButton()
+        self.draw_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_10 = QtWidgets.QHBoxLayout()
+        self.form_box_child_10.addWidget(self.draw_color_entry)
+        self.form_box_child_10.addWidget(self.draw_color_button)
+        self.form_box_child_10.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.draw_color_label, 26, 0)
         grid0.addWidget(self.draw_color_label, 26, 0)
-        grid0.addWidget(self.draw_color_entry, 26, 1)
+        grid0.addLayout(self.form_box_child_10, 26, 1)
 
 
         # Editor Draw Selection Color
         # Editor Draw Selection Color
         self.sel_draw_color_label = QtWidgets.QLabel('%s:' % _('Selection'))
         self.sel_draw_color_label = QtWidgets.QLabel('%s:' % _('Selection'))
         self.sel_draw_color_label.setToolTip(
         self.sel_draw_color_label.setToolTip(
             _("Set the color of the shape when selected.")
             _("Set the color of the shape when selected.")
         )
         )
-        self.sel_draw_color_entry = FCColorEntry()
+        self.sel_draw_color_entry = FCEntry()
+        self.sel_draw_color_button = QtWidgets.QPushButton()
+        self.sel_draw_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_11 = QtWidgets.QHBoxLayout()
+        self.form_box_child_11.addWidget(self.sel_draw_color_entry)
+        self.form_box_child_11.addWidget(self.sel_draw_color_button)
+        self.form_box_child_11.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.sel_draw_color_label, 27, 0)
         grid0.addWidget(self.sel_draw_color_label, 27, 0)
-        grid0.addWidget(self.sel_draw_color_entry, 27, 1)
+        grid0.addLayout(self.form_box_child_11, 27, 1)
 
 
         separator_line = QtWidgets.QFrame()
         separator_line = QtWidgets.QFrame()
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
         separator_line.setFrameShape(QtWidgets.QFrame.HLine)
@@ -273,20 +337,34 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.proj_color_label.setToolTip(
         self.proj_color_label.setToolTip(
             _("Set the color of the items in Project Tab Tree.")
             _("Set the color of the items in Project Tab Tree.")
         )
         )
-        self.proj_color_entry = FCColorEntry()
+        self.proj_color_entry = FCEntry()
+        self.proj_color_button = QtWidgets.QPushButton()
+        self.proj_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_12 = QtWidgets.QHBoxLayout()
+        self.form_box_child_12.addWidget(self.proj_color_entry)
+        self.form_box_child_12.addWidget(self.proj_color_button)
+        self.form_box_child_12.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.proj_color_label, 30, 0)
         grid0.addWidget(self.proj_color_label, 30, 0)
-        grid0.addWidget(self.proj_color_entry, 30, 1)
+        grid0.addLayout(self.form_box_child_12, 30, 1)
 
 
         self.proj_color_dis_label = QtWidgets.QLabel('%s:' % _('Disabled'))
         self.proj_color_dis_label = QtWidgets.QLabel('%s:' % _('Disabled'))
         self.proj_color_dis_label.setToolTip(
         self.proj_color_dis_label.setToolTip(
             _("Set the color of the items in Project Tab Tree,\n"
             _("Set the color of the items in Project Tab Tree,\n"
               "for the case when the items are disabled.")
               "for the case when the items are disabled.")
         )
         )
-        self.proj_color_dis_entry = FCColorEntry()
+        self.proj_color_dis_entry = FCEntry()
+        self.proj_color_dis_button = QtWidgets.QPushButton()
+        self.proj_color_dis_button.setFixedSize(15, 15)
+
+        self.form_box_child_13 = QtWidgets.QHBoxLayout()
+        self.form_box_child_13.addWidget(self.proj_color_dis_entry)
+        self.form_box_child_13.addWidget(self.proj_color_dis_button)
+        self.form_box_child_13.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.proj_color_dis_label, 31, 0)
         grid0.addWidget(self.proj_color_dis_label, 31, 0)
-        grid0.addWidget(self.proj_color_dis_entry, 31, 1)
+        grid0.addLayout(self.form_box_child_13, 31, 1)
 
 
         # Project autohide CB
         # Project autohide CB
         self.project_autohide_cb = FCCheckBox(label=_('Project AutoHide'))
         self.project_autohide_cb = FCCheckBox(label=_('Project AutoHide'))
@@ -309,22 +387,32 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
 
 
         # Setting selection (left - right) colors signals
         # Setting selection (left - right) colors signals
         self.sf_color_entry.editingFinished.connect(self.on_sf_color_entry)
         self.sf_color_entry.editingFinished.connect(self.on_sf_color_entry)
+        self.sf_color_button.clicked.connect(self.on_sf_color_button)
+        self.sf_color_alpha_spinner.valueChanged.connect(self.on_sf_color_spinner)
+        self.sf_color_alpha_slider.valueChanged.connect(self.on_sf_color_slider)
         self.sl_color_entry.editingFinished.connect(self.on_sl_color_entry)
         self.sl_color_entry.editingFinished.connect(self.on_sl_color_entry)
-
-        self.left_right_alpha_entry.valueChanged.connect(self.on_left_right_alpha_changed)  # alpha
+        self.sl_color_button.clicked.connect(self.on_sl_color_button)
 
 
         # Setting selection (right - left) colors signals
         # Setting selection (right - left) colors signals
         self.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry)
         self.alt_sf_color_entry.editingFinished.connect(self.on_alt_sf_color_entry)
+        self.alt_sf_color_button.clicked.connect(self.on_alt_sf_color_button)
+        self.alt_sf_color_alpha_spinner.valueChanged.connect(self.on_alt_sf_color_spinner)
+        self.alt_sf_color_alpha_slider.valueChanged.connect(self.on_alt_sf_color_slider)
         self.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry)
         self.alt_sl_color_entry.editingFinished.connect(self.on_alt_sl_color_entry)
-
-        self.right_left_alpha_entry.valueChanged.connect(self.on_right_left_alpha_changed)  # alpha
+        self.alt_sl_color_button.clicked.connect(self.on_alt_sl_color_button)
 
 
         # Setting Editor Draw colors signals
         # Setting Editor Draw colors signals
         self.draw_color_entry.editingFinished.connect(self.on_draw_color_entry)
         self.draw_color_entry.editingFinished.connect(self.on_draw_color_entry)
+        self.draw_color_button.clicked.connect(self.on_draw_color_button)
+
         self.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry)
         self.sel_draw_color_entry.editingFinished.connect(self.on_sel_draw_color_entry)
+        self.sel_draw_color_button.clicked.connect(self.on_sel_draw_color_button)
 
 
         self.proj_color_entry.editingFinished.connect(self.on_proj_color_entry)
         self.proj_color_entry.editingFinished.connect(self.on_proj_color_entry)
+        self.proj_color_button.clicked.connect(self.on_proj_color_button)
+
         self.proj_color_dis_entry.editingFinished.connect(self.on_proj_color_dis_entry)
         self.proj_color_dis_entry.editingFinished.connect(self.on_proj_color_dis_entry)
+        self.proj_color_dis_button.clicked.connect(self.on_proj_color_dis_button)
 
 
         self.layout_combo.activated.connect(self.on_layout)
         self.layout_combo.activated.connect(self.on_layout)
 
 
@@ -349,64 +437,191 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
     # Setting selection colors (left - right) handlers
     # Setting selection colors (left - right) handlers
     def on_sf_color_entry(self):
     def on_sf_color_entry(self):
         self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][7:9]
         self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][7:9]
+        self.sf_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_fill'])[:7])
 
 
-    def on_sl_color_entry(self):
-        self.app.defaults['global_sel_line'] = self.sl_color_entry.get_value()[:7] + \
-            self.app.defaults['global_sel_line'][7:9]
+    def on_sf_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_sel_fill'][:7])
 
 
-    def on_left_right_alpha_changed(self, spinner_value):
-        """
-        Change the alpha level for the color of the selection box when selection is done left to right.
-        Called on valueChanged of a FCSliderWithSpinner.
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
 
 
-        :param spinner_value:   passed value within [0, 255]
-        :type spinner_value:    int
-        :return:                None
-        :rtype:
-        """
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
 
 
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_sel_fill'][7:9])
+        self.sf_color_entry.set_value(new_val)
+        self.app.defaults['global_sel_fill'] = new_val
+
+    def on_sf_color_spinner(self):
+        spinner_value = self.sf_color_alpha_spinner.value()
+        self.sf_color_alpha_slider.setValue(spinner_value)
         self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][:7] + \
         self.app.defaults['global_sel_fill'] = self.app.defaults['global_sel_fill'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
         self.app.defaults['global_sel_line'] = self.app.defaults['global_sel_line'][:7] + \
         self.app.defaults['global_sel_line'] = self.app.defaults['global_sel_line'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
 
 
+    def on_sf_color_slider(self):
+        slider_value = self.sf_color_alpha_slider.value()
+        self.sf_color_alpha_spinner.setValue(slider_value)
+
+    def on_sl_color_entry(self):
+        self.app.defaults['global_sel_line'] = self.sl_color_entry.get_value()[:7] + \
+            self.app.defaults['global_sel_line'][7:9]
+        self.sl_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_sel_line'])[:7])
+
+    def on_sl_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_sel_line'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_sel_line'][7:9])
+        self.sl_color_entry.set_value(new_val_line)
+        self.app.defaults['global_sel_line'] = new_val_line
+
     # Setting selection colors (right - left) handlers
     # Setting selection colors (right - left) handlers
     def on_alt_sf_color_entry(self):
     def on_alt_sf_color_entry(self):
         self.app.defaults['global_alt_sel_fill'] = self.alt_sf_color_entry.get_value()[:7] + \
         self.app.defaults['global_alt_sel_fill'] = self.alt_sf_color_entry.get_value()[:7] + \
                                                    self.app.defaults['global_alt_sel_fill'][7:9]
                                                    self.app.defaults['global_alt_sel_fill'][7:9]
+        self.alt_sf_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_alt_sel_fill'])[:7]
+        )
 
 
-    def on_alt_sl_color_entry(self):
-        self.app.defaults['global_alt_sel_line'] = self.alt_sl_color_entry.get_value()[:7] + \
-                                                   self.app.defaults['global_alt_sel_line'][7:9]
+    def on_alt_sf_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_alt_sel_fill'][:7])
 
 
-    def on_right_left_alpha_changed(self, spinner_value):
-        """
-        Change the alpha level for the color of the selection box when selection is done right to left.
-        Called on valueChanged of a FCSliderWithSpinner.
+        c_dialog = QtWidgets.QColorDialog()
+        plot_fill_color = c_dialog.getColor(initial=current_color)
 
 
-        :param spinner_value:   passed value within [0, 255]
-        :type spinner_value:    int
-        :return:                None
-        :rtype:
-        """
+        if plot_fill_color.isValid() is False:
+            return
+
+        self.alt_sf_color_button.setStyleSheet("background-color:%s" % str(plot_fill_color.name()))
+
+        new_val = str(plot_fill_color.name()) + str(self.app.defaults['global_alt_sel_fill'][7:9])
+        self.alt_sf_color_entry.set_value(new_val)
+        self.app.defaults['global_alt_sel_fill'] = new_val
 
 
+    def on_alt_sf_color_spinner(self):
+        spinner_value = self.alt_sf_color_alpha_spinner.value()
+        self.alt_sf_color_alpha_slider.setValue(spinner_value)
         self.app.defaults['global_alt_sel_fill'] = self.app.defaults['global_alt_sel_fill'][:7] + \
         self.app.defaults['global_alt_sel_fill'] = self.app.defaults['global_alt_sel_fill'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
         self.app.defaults['global_alt_sel_line'] = self.app.defaults['global_alt_sel_line'][:7] + \
         self.app.defaults['global_alt_sel_line'] = self.app.defaults['global_alt_sel_line'][:7] + \
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
             (hex(spinner_value)[2:] if int(hex(spinner_value)[2:], 16) > 0 else '00')
 
 
+    def on_alt_sf_color_slider(self):
+        slider_value = self.alt_sf_color_alpha_slider.value()
+        self.alt_sf_color_alpha_spinner.setValue(slider_value)
+
+    def on_alt_sl_color_entry(self):
+        self.app.defaults['global_alt_sel_line'] = self.alt_sl_color_entry.get_value()[:7] + \
+                                                   self.app.defaults['global_alt_sel_line'][7:9]
+        self.alt_sl_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_alt_sel_line'])[:7]
+        )
+
+    def on_alt_sl_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_alt_sel_line'][:7])
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.alt_sl_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['global_alt_sel_line'][7:9])
+        self.alt_sl_color_entry.set_value(new_val_line)
+        self.app.defaults['global_alt_sel_line'] = new_val_line
+
     # Setting Editor colors
     # Setting Editor colors
     def on_draw_color_entry(self):
     def on_draw_color_entry(self):
         self.app.defaults['global_draw_color'] = self.draw_color_entry.get_value()
         self.app.defaults['global_draw_color'] = self.draw_color_entry.get_value()
+        self.draw_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['global_draw_color']))
+
+    def on_draw_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_draw_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        draw_color = c_dialog.getColor(initial=current_color)
+
+        if draw_color.isValid() is False:
+            return
+
+        self.draw_color_button.setStyleSheet("background-color:%s" % str(draw_color.name()))
+
+        new_val = str(draw_color.name())
+        self.draw_color_entry.set_value(new_val)
+        self.app.defaults['global_draw_color'] = new_val
 
 
     def on_sel_draw_color_entry(self):
     def on_sel_draw_color_entry(self):
         self.app.defaults['global_sel_draw_color'] = self.sel_draw_color_entry.get_value()
         self.app.defaults['global_sel_draw_color'] = self.sel_draw_color_entry.get_value()
+        self.sel_draw_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_sel_draw_color']))
+
+    def on_sel_draw_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_sel_draw_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        sel_draw_color = c_dialog.getColor(initial=current_color)
+
+        if sel_draw_color.isValid() is False:
+            return
+
+        self.sel_draw_color_button.setStyleSheet("background-color:%s" % str(sel_draw_color.name()))
+
+        new_val_sel = str(sel_draw_color.name())
+        self.sel_draw_color_entry.set_value(new_val_sel)
+        self.app.defaults['global_sel_draw_color'] = new_val_sel
 
 
     def on_proj_color_entry(self):
     def on_proj_color_entry(self):
         self.app.defaults['global_proj_item_color'] = self.proj_color_entry.get_value()
         self.app.defaults['global_proj_item_color'] = self.proj_color_entry.get_value()
+        self.proj_color_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_proj_item_color']))
+
+    def on_proj_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_proj_item_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        proj_color = c_dialog.getColor(initial=current_color)
+
+        if proj_color.isValid() is False:
+            return
+
+        self.proj_color_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
+
+        new_val_sel = str(proj_color.name())
+        self.proj_color_entry.set_value(new_val_sel)
+        self.app.defaults['global_proj_item_color'] = new_val_sel
 
 
     def on_proj_color_dis_entry(self):
     def on_proj_color_dis_entry(self):
         self.app.defaults['global_proj_item_dis_color'] = self.proj_color_dis_entry.get_value()
         self.app.defaults['global_proj_item_dis_color'] = self.proj_color_dis_entry.get_value()
+        self.proj_color_dis_button.setStyleSheet(
+            "background-color:%s" % str(self.app.defaults['global_proj_item_dis_color']))
+
+    def on_proj_color_dis_button(self):
+        current_color = QtGui.QColor(self.app.defaults['global_proj_item_dis_color'])
+
+        c_dialog = QtWidgets.QColorDialog()
+        proj_color = c_dialog.getColor(initial=current_color)
+
+        if proj_color.isValid() is False:
+            return
+
+        self.proj_color_dis_button.setStyleSheet("background-color:%s" % str(proj_color.name()))
+
+        new_val_sel = str(proj_color.name())
+        self.proj_color_dis_entry.set_value(new_val_sel)
+        self.app.defaults['global_proj_item_dis_color'] = new_val_sel
 
 
     def on_layout(self, index=None, lay=None):
     def on_layout(self, index=None, lay=None):
         """
         """
@@ -432,13 +647,14 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         # first remove the toolbars:
         # first remove the toolbars:
         try:
         try:
             self.app.ui.removeToolBar(self.app.ui.toolbarfile)
             self.app.ui.removeToolBar(self.app.ui.toolbarfile)
-            self.app.ui.removeToolBar(self.app.ui.toolbaredit)
+            self.app.ui.removeToolBar(self.app.ui.toolbargeo)
             self.app.ui.removeToolBar(self.app.ui.toolbarview)
             self.app.ui.removeToolBar(self.app.ui.toolbarview)
             self.app.ui.removeToolBar(self.app.ui.toolbarshell)
             self.app.ui.removeToolBar(self.app.ui.toolbarshell)
             self.app.ui.removeToolBar(self.app.ui.toolbartools)
             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.exc_edit_toolbar)
             self.app.ui.removeToolBar(self.app.ui.geo_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.grb_edit_toolbar)
+            self.app.ui.removeToolBar(self.app.ui.snap_toolbar)
             self.app.ui.removeToolBar(self.app.ui.toolbarshell)
             self.app.ui.removeToolBar(self.app.ui.toolbarshell)
         except Exception:
         except Exception:
             pass
             pass
@@ -449,9 +665,9 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
             self.app.ui.toolbarfile.setObjectName('File_TB')
             self.app.ui.toolbarfile.setObjectName('File_TB')
             self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
             self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbarfile)
 
 
-            self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
-            self.app.ui.toolbaredit.setObjectName('Edit_TB')
-            self.app.ui.addToolBar(Qt.LeftToolBarArea, self.app.ui.toolbaredit)
+            self.app.ui.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.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
             self.app.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar')
             self.app.ui.toolbarshell.setObjectName('Shell_TB')
             self.app.ui.toolbarshell.setObjectName('Shell_TB')
@@ -481,15 +697,22 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
             self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
             self.app.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB')
             self.app.ui.addToolBar(Qt.RightToolBarArea, self.app.ui.exc_edit_toolbar)
             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:
         else:
             # ## TOOLBAR INSTALLATION # ##
             # ## TOOLBAR INSTALLATION # ##
             self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
             self.app.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar')
             self.app.ui.toolbarfile.setObjectName('File_TB')
             self.app.ui.toolbarfile.setObjectName('File_TB')
             self.app.ui.addToolBar(self.app.ui.toolbarfile)
             self.app.ui.addToolBar(self.app.ui.toolbarfile)
 
 
-            self.app.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar')
-            self.app.ui.toolbaredit.setObjectName('Edit_TB')
-            self.app.ui.addToolBar(self.app.ui.toolbaredit)
+            self.app.ui.toolbargeo = QtWidgets.QToolBar('Edit Toolbar')
+            self.app.ui.toolbargeo.setObjectName('Edit_TB')
+            self.app.ui.addToolBar(self.app.ui.toolbargeo)
 
 
             self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
             self.app.ui.toolbarview = QtWidgets.QToolBar('View Toolbar')
             self.app.ui.toolbarview.setObjectName('View_TB')
             self.app.ui.toolbarview.setObjectName('View_TB')
@@ -520,9 +743,18 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
             self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
             self.app.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB')
             self.app.ui.addToolBar(self.app.ui.grb_edit_toolbar)
             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':
         if current_layout == 'minimal':
             self.app.ui.toolbarview.setVisible(False)
             self.app.ui.toolbarview.setVisible(False)
             self.app.ui.toolbarshell.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.geo_edit_toolbar.setVisible(False)
             self.app.ui.grb_edit_toolbar.setVisible(False)
             self.app.ui.grb_edit_toolbar.setVisible(False)
             self.app.ui.exc_edit_toolbar.setVisible(False)
             self.app.ui.exc_edit_toolbar.setVisible(False)
@@ -535,9 +767,7 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
         self.app.connect_toolbar_signals()
         self.app.connect_toolbar_signals()
 
 
         self.app.ui.grid_snap_btn.setChecked(True)
         self.app.ui.grid_snap_btn.setChecked(True)
-
-        self.app.ui.corner_snap_btn.setVisible(False)
-        self.app.ui.snap_magnet.setVisible(False)
+        self.app.ui.on_grid_snap_triggered(state=True)
 
 
         self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
         self.app.ui.grid_gap_x_entry.setText(str(self.app.defaults["global_gridx"]))
         self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))
         self.app.ui.grid_gap_y_entry.setText(str(self.app.defaults["global_gridy"]))

+ 4 - 4
AppGUI/preferences/general/GeneralPreferencesUI.py → flatcamGUI/preferences/general/GeneralPreferencesUI.py

@@ -1,12 +1,12 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI
-from AppGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI
-from AppGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI
+from flatcamGUI.preferences.general.GeneralAppPrefGroupUI import GeneralAppPrefGroupUI
+from flatcamGUI.preferences.general.GeneralAPPSetGroupUI import GeneralAPPSetGroupUI
+from flatcamGUI.preferences.general.GeneralGUIPrefGroupUI import GeneralGUIPrefGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')

+ 0 - 0
AppGUI/preferences/general/__init__.py → flatcamGUI/preferences/general/__init__.py


+ 12 - 20
AppGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py → flatcamGUI/preferences/geometry/GeometryAdvOptPrefGroupUI.py

@@ -1,12 +1,11 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel, NumericalEvalTupleEntry, \
-    NumericalEvalEntry
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCEntry, FloatEntry, FCDoubleSpinner, FCCheckBox, RadioSet, FCLabel
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -47,9 +46,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         toolchange_xy_label.setToolTip(
         toolchange_xy_label.setToolTip(
             _("Toolchange X,Y position.")
             _("Toolchange X,Y position.")
         )
         )
-        self.toolchangexy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
-
         grid1.addWidget(toolchange_xy_label, 1, 0)
         grid1.addWidget(toolchange_xy_label, 1, 0)
+        self.toolchangexy_entry = FCEntry()
         grid1.addWidget(self.toolchangexy_entry, 1, 1)
         grid1.addWidget(self.toolchangexy_entry, 1, 1)
 
 
         # Start move Z
         # Start move Z
@@ -58,9 +56,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
             _("Height of the tool just after starting the work.\n"
             _("Height of the tool just after starting the work.\n"
               "Delete the value if you don't need this feature.")
               "Delete the value if you don't need this feature.")
         )
         )
-        self.gstartz_entry = NumericalEvalEntry(border_color='#0069A9')
-
         grid1.addWidget(startzlabel, 2, 0)
         grid1.addWidget(startzlabel, 2, 0)
+        self.gstartz_entry = FloatEntry()
         grid1.addWidget(self.gstartz_entry, 2, 1)
         grid1.addWidget(self.gstartz_entry, 2, 1)
 
 
         # Feedrate rapids
         # Feedrate rapids
@@ -189,11 +186,6 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         grid1.addWidget(segy_label, 11, 0)
         grid1.addWidget(segy_label, 11, 0)
         grid1.addWidget(self.segy_entry, 11, 1)
         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 ----------
         # --- Area Exclusion ----------
         # -----------------------------
         # -----------------------------
@@ -203,10 +195,10 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
               "Those parameters are available only for\n"
               "Those parameters are available only for\n"
               "Advanced App. Level.")
               "Advanced App. Level.")
         )
         )
-        grid1.addWidget(self.adv_label, 13, 0, 1, 2)
+        grid1.addWidget(self.adv_label, 12, 0, 1, 2)
 
 
         # Exclusion Area CB
         # Exclusion Area CB
-        self.exclusion_cb = FCCheckBox('%s' % _("Exclusion areas"))
+        self.exclusion_cb = FCCheckBox('%s:' % _("Exclusion areas"))
         self.exclusion_cb.setToolTip(
         self.exclusion_cb.setToolTip(
             _(
             _(
                 "Include exclusion areas.\n"
                 "Include exclusion areas.\n"
@@ -214,7 +206,7 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
                 "is forbidden."
                 "is forbidden."
             )
             )
         )
         )
-        grid1.addWidget(self.exclusion_cb, 14, 0, 1, 2)
+        grid1.addWidget(self.exclusion_cb, 13, 0, 1, 2)
 
 
         # Area Selection shape
         # Area Selection shape
         self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
         self.area_shape_label = QtWidgets.QLabel('%s:' % _("Shape"))
@@ -225,8 +217,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
         self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'},
                                           {'label': _("Polygon"), 'value': 'polygon'}])
                                           {'label': _("Polygon"), 'value': 'polygon'}])
 
 
-        grid1.addWidget(self.area_shape_label, 15, 0)
-        grid1.addWidget(self.area_shape_radio, 15, 1)
+        grid1.addWidget(self.area_shape_label, 14, 0)
+        grid1.addWidget(self.area_shape_radio, 14, 1)
 
 
         # Chose Strategy
         # Chose Strategy
         self.strategy_label = FCLabel('%s:' % _("Strategy"))
         self.strategy_label = FCLabel('%s:' % _("Strategy"))
@@ -237,8 +229,8 @@ class GeometryAdvOptPrefGroupUI(OptionsGroupUI):
         self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
         self.strategy_radio = RadioSet([{'label': _('Over'), 'value': 'over'},
                                         {'label': _('Around'), 'value': 'around'}])
                                         {'label': _('Around'), 'value': 'around'}])
 
 
-        grid1.addWidget(self.strategy_label, 16, 0)
-        grid1.addWidget(self.strategy_radio, 16, 1)
+        grid1.addWidget(self.strategy_label, 15, 0)
+        grid1.addWidget(self.strategy_radio, 15, 1)
 
 
         # Over Z
         # Over Z
         self.over_z_label = FCLabel('%s:' % _("Over Z"))
         self.over_z_label = FCLabel('%s:' % _("Over Z"))

+ 3 - 3
AppGUI/preferences/geometry/GeometryEditorPrefGroupUI.py → flatcamGUI/preferences/geometry/GeometryEditorPrefGroupUI.py

@@ -1,11 +1,11 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCSpinner, RadioSet
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCSpinner, RadioSet
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')

+ 32 - 18
AppGUI/preferences/geometry/GeometryGenPrefGroupUI.py → flatcamGUI/preferences/geometry/GeometryGenPrefGroupUI.py

@@ -1,11 +1,11 @@
-from PyQt5 import QtWidgets
+from PyQt5 import QtWidgets, QtCore, QtGui
 from PyQt5.QtCore import QSettings
 from PyQt5.QtCore import QSettings
 
 
-from AppGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry, FCColorEntry
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCCheckBox, FCSpinner, FCEntry
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -31,22 +31,12 @@ class GeometryGenPrefGroupUI(OptionsGroupUI):
         self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
         self.plot_options_label = QtWidgets.QLabel("<b>%s:</b>" % _("Plot Options"))
         self.layout.addWidget(self.plot_options_label)
         self.layout.addWidget(self.plot_options_label)
 
 
-        plot_hlay = QtWidgets.QHBoxLayout()
-        self.layout.addLayout(plot_hlay)
-
         # Plot CB
         # Plot CB
         self.plot_cb = FCCheckBox(label=_('Plot'))
         self.plot_cb = FCCheckBox(label=_('Plot'))
         self.plot_cb.setToolTip(
         self.plot_cb.setToolTip(
             _("Plot (show) this object.")
             _("Plot (show) this object.")
         )
         )
-        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)
+        self.layout.addWidget(self.plot_cb)
 
 
         grid0 = QtWidgets.QGridLayout()
         grid0 = QtWidgets.QGridLayout()
         self.layout.addLayout(grid0)
         self.layout.addLayout(grid0)
@@ -87,7 +77,7 @@ class GeometryGenPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(separator_line, 9, 0, 1, 2)
         grid0.addWidget(separator_line, 9, 0, 1, 2)
 
 
         # Geometry Object Color
         # Geometry Object Color
-        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Object Color'))
+        self.gerber_color_label = QtWidgets.QLabel('<b>%s</b>' % _('Geometry Object Color'))
         grid0.addWidget(self.gerber_color_label, 10, 0, 1, 2)
         grid0.addWidget(self.gerber_color_label, 10, 0, 1, 2)
 
 
         # Plot Line Color
         # Plot Line Color
@@ -95,15 +85,39 @@ class GeometryGenPrefGroupUI(OptionsGroupUI):
         self.line_color_label.setToolTip(
         self.line_color_label.setToolTip(
             _("Set the line color for plotted objects.")
             _("Set the line color for plotted objects.")
         )
         )
-        self.line_color_entry = FCColorEntry()
+        self.line_color_entry = FCEntry()
+        self.line_color_button = QtWidgets.QPushButton()
+        self.line_color_button.setFixedSize(15, 15)
+
+        self.form_box_child_2 = QtWidgets.QHBoxLayout()
+        self.form_box_child_2.addWidget(self.line_color_entry)
+        self.form_box_child_2.addWidget(self.line_color_button)
+        self.form_box_child_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
 
 
         grid0.addWidget(self.line_color_label, 11, 0)
         grid0.addWidget(self.line_color_label, 11, 0)
-        grid0.addWidget(self.line_color_entry, 11, 1)
+        grid0.addLayout(self.form_box_child_2, 11, 1)
 
 
         self.layout.addStretch()
         self.layout.addStretch()
 
 
         # Setting plot colors signals
         # Setting plot colors signals
         self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
         self.line_color_entry.editingFinished.connect(self.on_line_color_entry)
+        self.line_color_button.clicked.connect(self.on_line_color_button)
 
 
     def on_line_color_entry(self):
     def on_line_color_entry(self):
         self.app.defaults['geometry_plot_line'] = self.line_color_entry.get_value()[:7] + 'FF'
         self.app.defaults['geometry_plot_line'] = self.line_color_entry.get_value()[:7] + 'FF'
+        self.line_color_button.setStyleSheet("background-color:%s" % str(self.app.defaults['geometry_plot_line'])[:7])
+
+    def on_line_color_button(self):
+        current_color = QtGui.QColor(self.app.defaults['geometry_plot_line'][:7])
+        # print(current_color)
+
+        c_dialog = QtWidgets.QColorDialog()
+        plot_line_color = c_dialog.getColor(initial=current_color)
+
+        if plot_line_color.isValid() is False:
+            return
+
+        self.line_color_button.setStyleSheet("background-color:%s" % str(plot_line_color.name()))
+
+        new_val_line = str(plot_line_color.name()) + str(self.app.defaults['geometry_plot_line'][7:9])
+        self.line_color_entry.set_value(new_val_line)

+ 5 - 6
AppGUI/preferences/geometry/GeometryOptPrefGroupUI.py → flatcamGUI/preferences/geometry/GeometryOptPrefGroupUI.py

@@ -1,13 +1,12 @@
 from PyQt5 import QtWidgets
 from PyQt5 import QtWidgets
 from PyQt5.QtCore import Qt, QSettings
 from PyQt5.QtCore import Qt, QSettings
 
 
-from AppGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCSpinner, FCComboBox, \
-    NumericalEvalTupleEntry
-from AppGUI.preferences import machinist_setting
-from AppGUI.preferences.OptionsGroupUI import OptionsGroupUI
+from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCEntry, FCSpinner, FCComboBox
+from flatcamGUI.preferences import machinist_setting
+from flatcamGUI.preferences.OptionsGroupUI import OptionsGroupUI
 
 
 import gettext
 import gettext
-import AppTranslation as fcTranslate
+import FlatCAMTranslation as fcTranslate
 import builtins
 import builtins
 
 
 fcTranslate.apply_language('strings')
 fcTranslate.apply_language('strings')
@@ -177,7 +176,7 @@ class GeometryOptPrefGroupUI(OptionsGroupUI):
               "If no value is entered then there is no move\n"
               "If no value is entered then there is no move\n"
               "on X,Y plane at the end of the job.")
               "on X,Y plane at the end of the job.")
         )
         )
-        self.endxy_entry = NumericalEvalTupleEntry(border_color='#0069A9')
+        self.endxy_entry = FCEntry()
 
 
         grid1.addWidget(endmove_xy_label, 7, 0)
         grid1.addWidget(endmove_xy_label, 7, 0)
         grid1.addWidget(self.endxy_entry, 7, 1)
         grid1.addWidget(self.endxy_entry, 7, 1)

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