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

- added a new FlatCAM Tool: Gerber Invert Tool. It will invert the copper features in a Gerber file: where is copper there will be empty and where is empty it will be copper

Marius Stanciu 6 лет назад
Родитель
Сommit
9fc2ba8ffd

+ 14 - 7
FlatCAMApp.py

@@ -14,7 +14,7 @@ import getopt
 import random
 import random
 import simplejson as json
 import simplejson as json
 import lzma
 import lzma
-import threading
+# import threading
 import shutil
 import shutil
 import stat
 import stat
 
 
@@ -26,7 +26,7 @@ import ctypes
 
 
 from reportlab.graphics import renderPDF
 from reportlab.graphics import renderPDF
 from reportlab.pdfgen import canvas
 from reportlab.pdfgen import canvas
-from reportlab.graphics import renderPM
+# from reportlab.graphics import renderPM
 from reportlab.lib.units import inch, mm
 from reportlab.lib.units import inch, mm
 from reportlab.lib.pagesizes import landscape, portrait
 from reportlab.lib.pagesizes import landscape, portrait
 from svglib.svglib import svg2rlg
 from svglib.svglib import svg2rlg
@@ -39,9 +39,9 @@ from xml.dom.minidom import parseString as parse_xml_string
 from multiprocessing.connection import Listener, Client
 from multiprocessing.connection import Listener, Client
 from multiprocessing import Pool
 from multiprocessing import Pool
 import socket
 import socket
-from array import array
+# from array import array
 
 
-import vispy.scene as scene
+# import vispy.scene as scene
 
 
 # #######################################
 # #######################################
 # #      Imports part of FlatCAM       ##
 # #      Imports part of FlatCAM       ##
@@ -1306,7 +1306,7 @@ class App(QtCore.QObject):
             # Excellon Options
             # Excellon Options
             "excellon_drillz": self.ui.excellon_defaults_form.excellon_opt_group.cutz_entry,
             "excellon_drillz": self.ui.excellon_defaults_form.excellon_opt_group.cutz_entry,
             "excellon_multidepth": self.ui.excellon_defaults_form.excellon_opt_group.mpass_cb,
             "excellon_multidepth": self.ui.excellon_defaults_form.excellon_opt_group.mpass_cb,
-            "excellon_depthperpass":self.ui.excellon_defaults_form.excellon_opt_group.maxdepth_entry,
+            "excellon_depthperpass": self.ui.excellon_defaults_form.excellon_opt_group.maxdepth_entry,
             "excellon_travelz": self.ui.excellon_defaults_form.excellon_opt_group.travelz_entry,
             "excellon_travelz": self.ui.excellon_defaults_form.excellon_opt_group.travelz_entry,
             "excellon_endz": self.ui.excellon_defaults_form.excellon_opt_group.endz_entry,
             "excellon_endz": self.ui.excellon_defaults_form.excellon_opt_group.endz_entry,
             "excellon_feedrate": self.ui.excellon_defaults_form.excellon_opt_group.feedrate_z_entry,
             "excellon_feedrate": self.ui.excellon_defaults_form.excellon_opt_group.feedrate_z_entry,
@@ -2558,6 +2558,7 @@ class App(QtCore.QObject):
         self.edrills_tool = None
         self.edrills_tool = None
         self.align_objects_tool = None
         self.align_objects_tool = None
         self.punch_tool = None
         self.punch_tool = None
+        self.invert_tool = None
 
 
         # always install tools only after the shell is initialized because the self.inform.emit() depends on shell
         # always install tools only after the shell is initialized because the self.inform.emit() depends on shell
         try:
         try:
@@ -2724,6 +2725,8 @@ class App(QtCore.QObject):
         # this holds a widget that is installed in the Plot Area when View Source option is used
         # this holds a widget that is installed in the Plot Area when View Source option is used
         self.source_editor_tab = None
         self.source_editor_tab = None
 
 
+        self.pagesize = dict()
+
         # Storage for shapes, storage that can be used by FlatCAm tools for utility geometry
         # Storage for shapes, storage that can be used by FlatCAm tools for utility geometry
         # VisPy visuals
         # VisPy visuals
         if self.is_legacy is False:
         if self.is_legacy is False:
@@ -3194,6 +3197,9 @@ class App(QtCore.QObject):
         self.punch_tool = ToolPunchGerber(self)
         self.punch_tool = ToolPunchGerber(self)
         self.punch_tool.install(icon=QtGui.QIcon(self.resource_location + '/punch32.png'), pos=self.ui.menutool)
         self.punch_tool.install(icon=QtGui.QIcon(self.resource_location + '/punch32.png'), pos=self.ui.menutool)
 
 
+        self.invert_tool = ToolInvertGerber(self)
+        self.invert_tool.install(icon=QtGui.QIcon(self.resource_location + '/invert32.png'), pos=self.ui.menutool)
+
         self.transform_tool = ToolTransform(self)
         self.transform_tool = ToolTransform(self)
         self.transform_tool.install(icon=QtGui.QIcon(self.resource_location + '/transform.png'),
         self.transform_tool.install(icon=QtGui.QIcon(self.resource_location + '/transform.png'),
                                     pos=self.ui.menuoptions, separator=True)
                                     pos=self.ui.menuoptions, separator=True)
@@ -3338,6 +3344,7 @@ class App(QtCore.QObject):
         self.ui.copperfill_btn.triggered.connect(lambda: self.copper_thieving_tool.run(toggle=True))
         self.ui.copperfill_btn.triggered.connect(lambda: self.copper_thieving_tool.run(toggle=True))
         self.ui.fiducials_btn.triggered.connect(lambda: self.fiducial_tool.run(toggle=True))
         self.ui.fiducials_btn.triggered.connect(lambda: self.fiducial_tool.run(toggle=True))
         self.ui.punch_btn.triggered.connect(lambda: self.punch_tool.run(toggle=True))
         self.ui.punch_btn.triggered.connect(lambda: self.punch_tool.run(toggle=True))
+        self.ui.invert_btn.triggered.connect(lambda: self.invert_tool.run(toggle=True))
 
 
     def object2editor(self):
     def object2editor(self):
         """
         """
@@ -8716,14 +8723,14 @@ class App(QtCore.QObject):
         else:
         else:
             event_pos = (event.xdata, event.ydata)
             event_pos = (event.xdata, event.ydata)
             # Matplotlib has the middle and right buttons mapped in reverse compared with VisPy
             # Matplotlib has the middle and right buttons mapped in reverse compared with VisPy
-            pan_button = 3 if self.defaults["global_pan_button"] == '2'else 2
+            pan_button = 3 if self.defaults["global_pan_button"] == '2' else 2
 
 
         # So it can receive key presses
         # So it can receive key presses
         self.plotcanvas.native.setFocus()
         self.plotcanvas.native.setFocus()
 
 
         self.pos_canvas = self.plotcanvas.translate_coords(event_pos)
         self.pos_canvas = self.plotcanvas.translate_coords(event_pos)
 
 
-        if self.grid_status() == True:
+        if self.grid_status():
             self.pos = self.geo_editor.snap(self.pos_canvas[0], self.pos_canvas[1])
             self.pos = self.geo_editor.snap(self.pos_canvas[0], self.pos_canvas[1])
         else:
         else:
             self.pos = (self.pos_canvas[0], self.pos_canvas[1])
             self.pos = (self.pos_canvas[0], self.pos_canvas[1])

+ 15 - 10
FlatCAMObj.py

@@ -2164,14 +2164,17 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                                 gerber_code += 'D02*\n'
                                 gerber_code += 'D02*\n'
                                 gerber_code += 'G37*\n'
                                 gerber_code += 'G37*\n'
                                 gerber_code += '%LPD*%\n'
                                 gerber_code += '%LPD*%\n'
+        except Exception as e:
+            log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() '0' aperture --> %s" % str(e))
 
 
-            for apid in self.apertures:
-                if apid == '0':
-                    continue
-                else:
-                    gerber_code += 'D%s*\n' % str(apid)
-                    if 'geometry' in self.apertures[apid]:
-                        for geo_elem in self.apertures[apid]['geometry']:
+        for apid in self.apertures:
+            if apid == '0':
+                continue
+            else:
+                gerber_code += 'D%s*\n' % str(apid)
+                if 'geometry' in self.apertures[apid]:
+                    for geo_elem in self.apertures[apid]['geometry']:
+                        try:
                             if 'follow' in geo_elem:
                             if 'follow' in geo_elem:
                                 geo = geo_elem['follow']
                                 geo = geo_elem['follow']
                                 if not geo.is_empty:
                                 if not geo.is_empty:
@@ -2212,7 +2215,10 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                                             prev_coord = coord
                                             prev_coord = coord
 
 
                                         # gerber_code += "D02*\n"
                                         # gerber_code += "D02*\n"
+                        except Exception as e:
+                            log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() 'follow' --> %s" % str(e))
 
 
+                        try:
                             if 'clear' in geo_elem:
                             if 'clear' in geo_elem:
                                 gerber_code += '%LPC*%\n'
                                 gerber_code += '%LPC*%\n'
 
 
@@ -2256,9 +2262,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                                             prev_coord = coord
                                             prev_coord = coord
                                         # gerber_code += "D02*\n"
                                         # gerber_code += "D02*\n"
                                     gerber_code += '%LPD*%\n'
                                     gerber_code += '%LPD*%\n'
-
-        except Exception as e:
-            log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() --> %s" % str(e))
+                        except Exception as e:
+                            log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() 'clear' --> %s" % str(e))
 
 
         if not self.apertures:
         if not self.apertures:
             log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() --> Gerber Object is empty: no apertures.")
             log.debug("FlatCAMObj.FlatCAMGerber.export_gerber() --> Gerber Object is empty: no apertures.")

+ 1 - 0
README.md

@@ -12,6 +12,7 @@ CAD program, and create G-Code for Isolation routing.
 14.02.2020
 14.02.2020
 
 
 - adjusted the UI for Excellon and Geometry objects
 - adjusted the UI for Excellon and Geometry objects
+- added a new FlatCAM Tool: Gerber Invert Tool. It will invert the copper features in a Gerber file: where is copper there will be empty and where is empty it will be copper
 
 
 13.02.2020
 13.02.2020
 
 

+ 2 - 0
flatcamGUI/FlatCAMGUI.py

@@ -928,6 +928,8 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
             QtGui.QIcon(self.app.resource_location + '/calibrate_32.png'), _("Calibration Tool"))
             QtGui.QIcon(self.app.resource_location + '/calibrate_32.png'), _("Calibration Tool"))
         self.punch_btn = self.toolbartools.addAction(
         self.punch_btn = self.toolbartools.addAction(
             QtGui.QIcon(self.app.resource_location + '/punch32.png'), _("Punch Gerber Tool"))
             QtGui.QIcon(self.app.resource_location + '/punch32.png'), _("Punch Gerber Tool"))
+        self.invert_btn = self.toolbartools.addAction(
+            QtGui.QIcon(self.app.resource_location + '/invert32.png'), _("Invert Gerber Tool"))
 
 
         # ########################################################################
         # ########################################################################
         # ########################## Excellon Editor Toolbar# ####################
         # ########################## Excellon Editor Toolbar# ####################

+ 6 - 3
flatcamParsers/ParseGerber.py

@@ -1414,9 +1414,12 @@ class Gerber(Geometry):
             self.follow_geometry = follow_buffer
             self.follow_geometry = follow_buffer
 
 
             # this treats the case when we are storing geometry as solids
             # this treats the case when we are storing geometry as solids
-
-            if len(poly_buffer) == 0 and len(self.solid_geometry) == 0:
-                log.error("Object is not Gerber file or empty. Aborting Object creation.")
+            try:
+                if len(poly_buffer) == 0 and len(self.solid_geometry) == 0:
+                    log.error("Object is not Gerber file or empty. Aborting Object creation.")
+                    return 'fail'
+            except TypeError as e:
+                log.error("Object is not Gerber file or empty. Aborting Object creation. %s" % str(e))
                 return 'fail'
                 return 'fail'
 
 
             log.warning("Joining %d polygons." % len(poly_buffer))
             log.warning("Joining %d polygons." % len(poly_buffer))

+ 274 - 0
flatcamTools/ToolInvertGerber.py

@@ -0,0 +1,274 @@
+# ##########################################################
+# FlatCAM: 2D Post-processing for Manufacturing            #
+# File Author: Marius Adrian Stanciu (c)                   #
+# Date: 2/14/2020                                          #
+# MIT Licence                                              #
+# ##########################################################
+
+from PyQt5 import QtWidgets, QtCore
+
+from FlatCAMTool import FlatCAMTool
+from flatcamGUI.GUIElements import FCButton, FCDoubleSpinner
+
+from shapely.geometry import Polygon, MultiPolygon, MultiLineString, LineString, box
+from shapely.ops import cascaded_union
+
+import traceback
+from copy import deepcopy
+import time
+import logging
+import gettext
+import FlatCAMTranslation as fcTranslate
+import builtins
+
+fcTranslate.apply_language('strings')
+if '_' not in builtins.__dict__:
+    _ = gettext.gettext
+
+log = logging.getLogger('base')
+
+
+class ToolInvertGerber(FlatCAMTool):
+
+    toolName = _("Invert Tool")
+
+    def __init__(self, app):
+        self.app = app
+        self.decimals = self.app.decimals
+
+        FlatCAMTool.__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)
+
+        # Form 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 = QtWidgets.QComboBox()
+        self.gerber_combo.setModel(self.app.collection)
+        self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+        self.gerber_combo.setCurrentIndex(1)
+
+        self.gerber_label = QtWidgets.QLabel('%s:' % _("Gerber Object"))
+        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)
+
+        # Margin
+        self.margin_label = QtWidgets.QLabel('%s:' % _('Margin'))
+        self.margin_label.setToolTip(
+            _("Distance by which to avoid\n"
+              "the edges of the Gerber object.")
+        )
+        self.margin_entry = FCDoubleSpinner()
+        self.margin_entry.set_precision(self.decimals)
+        self.margin_entry.set_range(0.0000, 9999.9999)
+        self.margin_entry.setObjectName(_("Margin"))
+
+        grid0.addWidget(self.margin_label, 3, 0)
+        grid0.addWidget(self.margin_entry, 3, 1)
+
+        separator_line = QtWidgets.QFrame()
+        separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+        separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+        grid0.addWidget(separator_line, 4, 0, 1, 2)
+
+        self.invert_btn = FCButton(_('Invert Gerber'))
+        self.invert_btn.setToolTip(
+            _("Will invert the Gerber object: areas that have copper\n"
+              "will be emty of copper and previous empty area will be\n"
+              "filled with copper.")
+        )
+        self.invert_btn.setStyleSheet("""
+                        QPushButton
+                        {
+                            font-weight: bold;
+                        }
+                        """)
+        grid0.addWidget(self.invert_btn, 5, 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.invert_btn.clicked.connect(self.on_grb_invert)
+        self.reset_button.clicked.connect(self.set_tool_ui)
+
+    def install(self, icon=None, separator=None, **kwargs):
+        FlatCAMTool.install(self, icon, separator, shortcut='', **kwargs)
+
+    def run(self, toggle=True):
+        self.app.report_usage("ToolInvertGerber()")
+        log.debug("ToolInvertGerber() 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])
+
+        FlatCAMTool.run(self)
+        self.set_tool_ui()
+
+        self.app.ui.notebook.setTabText(2, _("Invert Tool"))
+
+    def set_tool_ui(self):
+        self.margin_entry.set_value(0.0)
+
+    def on_grb_invert(self):
+        margin = self.margin_entry.get_value()
+        if round(margin, self.decimals) == 0.0:
+            margin = 1E-10
+
+        grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
+        obj_name = self.gerber_combo.currentText()
+
+        outname = obj_name + "_inverted"
+
+        # 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:
+            self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
+            return
+
+        xmin, ymin, xmax, ymax = grb_obj.bounds()
+
+        grb_box = box(xmin, ymin, xmax, ymax).buffer(margin, resolution=grb_circle_steps, join_style=2)
+
+        try:
+            __ = iter(grb_obj.solid_geometry)
+        except TypeError:
+            grb_obj.solid_geometry = list(grb_obj.solid_geometry)
+
+        new_solid_geometry = deepcopy(grb_box)
+
+        for poly in grb_obj.solid_geometry:
+            new_solid_geometry = new_solid_geometry.difference(poly)
+
+        new_options = dict()
+        for opt in grb_obj.options:
+            new_options[opt] = deepcopy(grb_obj.options[opt])
+
+        new_apertures = dict()
+
+        # for apid, val in grb_obj.apertures.items():
+        #     new_apertures[apid] = dict()
+        #     for key in val:
+        #         if key == 'geometry':
+        #             new_apertures[apid]['geometry'] = list()
+        #             for elem in val['geometry']:
+        #                 geo_elem = dict()
+        #                 if 'follow' in elem:
+        #                     try:
+        #                         geo_elem['clear'] = elem['follow'].buffer(val['size'] / 2.0).exterior
+        #                     except AttributeError:
+        #                         # TODO should test if width or height is bigger
+        #                         geo_elem['clear'] = elem['follow'].buffer(val['width'] / 2.0).exterior
+        #                 if 'clear' in elem:
+        #                     if isinstance(elem['clear'], Polygon):
+        #                         try:
+        #                             geo_elem['solid'] = elem['clear'].buffer(val['size'] / 2.0, grb_circle_steps)
+        #                         except AttributeError:
+        #                             # TODO should test if width or height is bigger
+        #                             geo_elem['solid'] = elem['clear'].buffer(val['width'] / 2.0, grb_circle_steps)
+        #                     else:
+        #                         geo_elem['follow'] = elem['clear']
+        #                 new_apertures[apid]['geometry'].append(deepcopy(geo_elem))
+        #         else:
+        #             new_apertures[apid][key] = deepcopy(val[key])
+
+        if '0' not in new_apertures:
+            new_apertures['0'] = dict()
+            new_apertures['0']['type'] = 'C'
+            new_apertures['0']['size'] = 0.0
+            new_apertures['0']['geometry'] = list()
+
+        try:
+            for poly in new_solid_geometry:
+                new_el = dict()
+                new_el['solid'] = poly
+                new_el['follow'] = poly.exterior
+                new_apertures['0']['geometry'].append(new_el)
+        except TypeError:
+            new_el = dict()
+            new_el['solid'] = new_solid_geometry
+            new_el['follow'] = new_solid_geometry.exterior
+            new_apertures['0']['geometry'].append(new_el)
+
+        for td in new_apertures:
+            print(td, new_apertures[td])
+
+        def init_func(new_obj, app_obj):
+            new_obj.options.update(new_options)
+            new_obj.options['name'] = outname
+            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.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

+ 1 - 0
flatcamTools/ToolPaint.py

@@ -358,6 +358,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         )
         )
         self.paintmargin_entry = FCDoubleSpinner()
         self.paintmargin_entry = FCDoubleSpinner()
         self.paintmargin_entry.set_precision(self.decimals)
         self.paintmargin_entry.set_precision(self.decimals)
+        self.paintmargin_entry.set_range(-9999.9999, 9999.9999)
         self.paintmargin_entry.setObjectName(_("Margin"))
         self.paintmargin_entry.setObjectName(_("Margin"))
 
 
         grid4.addWidget(marginlabel, 2, 0)
         grid4.addWidget(marginlabel, 2, 0)

+ 6 - 4
flatcamTools/ToolPunchGerber.py

@@ -515,6 +515,8 @@ class ToolPunchGerber(FlatCAMTool):
 
 
         punch_method = self.method_punch.get_value()
         punch_method = self.method_punch.get_value()
 
 
+        new_options = deepcopy(grb_obj.options)
+
         if punch_method == 'exc':
         if punch_method == 'exc':
 
 
             # get the Excellon file whose geometry will create the punch holes
             # get the Excellon file whose geometry will create the punch holes
@@ -574,7 +576,7 @@ class ToolPunchGerber(FlatCAMTool):
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
 
 
             def init_func(new_obj, app_obj):
             def init_func(new_obj, app_obj):
-                new_obj.options.update(grb_obj.options)
+                new_obj.options.update(new_options)
                 new_obj.options['name'] = outname
                 new_obj.options['name'] = outname
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)
@@ -688,7 +690,7 @@ class ToolPunchGerber(FlatCAMTool):
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
 
 
             def init_func(new_obj, app_obj):
             def init_func(new_obj, app_obj):
-                new_obj.options.update(grb_obj.options)
+                new_obj.options.update(new_options)
                 new_obj.options['name'] = outname
                 new_obj.options['name'] = outname
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)
@@ -830,7 +832,7 @@ class ToolPunchGerber(FlatCAMTool):
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
 
 
             def init_func(new_obj, app_obj):
             def init_func(new_obj, app_obj):
-                new_obj.options.update(grb_obj.options)
+                new_obj.options.update(new_options)
                 new_obj.options['name'] = outname
                 new_obj.options['name'] = outname
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)
@@ -969,7 +971,7 @@ class ToolPunchGerber(FlatCAMTool):
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
                 new_apertures[str(new_apid)] = deepcopy(ap_val)
 
 
             def init_func(new_obj, app_obj):
             def init_func(new_obj, app_obj):
-                new_obj.options.update(grb_obj.options)
+                new_obj.options.update(new_options)
                 new_obj.options['name'] = outname
                 new_obj.options['name'] = outname
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.fill_color = deepcopy(grb_obj.fill_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)
                 new_obj.outline_color = deepcopy(grb_obj.outline_color)

+ 3 - 3
flatcamTools/ToolSub.py

@@ -254,14 +254,14 @@ class ToolSub(FlatCAMTool):
         FlatCAMTool.run(self)
         FlatCAMTool.run(self)
         self.set_tool_ui()
         self.set_tool_ui()
 
 
+        self.app.ui.notebook.setTabText(2, _("Sub Tool"))
+
+    def set_tool_ui(self):
         self.new_apertures.clear()
         self.new_apertures.clear()
         self.new_tools.clear()
         self.new_tools.clear()
         self.new_solid_geometry = []
         self.new_solid_geometry = []
         self.target_options.clear()
         self.target_options.clear()
 
 
-        self.app.ui.notebook.setTabText(2, _("Sub Tool"))
-
-    def set_tool_ui(self):
         self.tools_frame.show()
         self.tools_frame.show()
         self.close_paths_cb.setChecked(self.app.defaults["tools_sub_close_paths"])
         self.close_paths_cb.setChecked(self.app.defaults["tools_sub_close_paths"])
 
 

+ 2 - 0
flatcamTools/__init__.py

@@ -39,3 +39,5 @@ from flatcamTools.ToolSub import ToolSub
 
 
 from flatcamTools.ToolTransform import ToolTransform
 from flatcamTools.ToolTransform import ToolTransform
 from flatcamTools.ToolPunchGerber import ToolPunchGerber
 from flatcamTools.ToolPunchGerber import ToolPunchGerber
+
+from flatcamTools.ToolInvertGerber import ToolInvertGerber

BIN
share/invert16.png


BIN
share/invert32.png