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

- fixed the Gerber object UI layout
- added ability to mark individual apertures in Gerber file using the Gerber Aperture Table

Marius Stanciu 7 лет назад
Родитель
Сommit
d43de2ea77
6 измененных файлов с 273 добавлено и 104 удалено
  1. 8 0
      FlatCAMApp.py
  2. 120 75
      FlatCAMObj.py
  3. 60 24
      ObjectUI.py
  4. 3 1
      PlotCanvas.py
  5. 5 0
      README.md
  6. 77 4
      camlib.py

+ 8 - 0
FlatCAMApp.py

@@ -4936,6 +4936,14 @@ class App(QtCore.QObject):
         # Clear pool
         # Clear pool
         self.clear_pool()
         self.clear_pool()
 
 
+        #delete shapes left drawn from mark shape_collections, if any
+        for obj in self.collection.get_list():
+            try:
+                obj.mark_shapes.enabled = False
+                obj.mark_shapes.clear(update=True)
+            except:
+                pass
+
         # tcl needs to be reinitialized, otherwise  old shell variables etc  remains
         # tcl needs to be reinitialized, otherwise  old shell variables etc  remains
         self.init_tcl()
         self.init_tcl()
 
 

+ 120 - 75
FlatCAMObj.py

@@ -70,6 +70,8 @@ class FlatCAMObj(QtCore.QObject):
         # self.shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene)
         # self.shapes = ShapeCollection(parent=self.app.plotcanvas.vispy_canvas.view.scene)
         self.shapes = self.app.plotcanvas.new_shape_group()
         self.shapes = self.app.plotcanvas.new_shape_group()
 
 
+        self.mark_shapes = self.app.plotcanvas.new_shape_collection(layers=2)
+
         self.item = None  # Link with project view item
         self.item = None  # Link with project view item
 
 
         self.muted_ui = False
         self.muted_ui = False
@@ -311,6 +313,13 @@ class FlatCAMObj(QtCore.QObject):
             key = self.shapes.add(tolerance=self.drawing_tolerance, **kwargs)
             key = self.shapes.add(tolerance=self.drawing_tolerance, **kwargs)
         return key
         return key
 
 
+    def add_mark_shape(self, **kwargs):
+        if self.deleted:
+            raise ObjectDeleted()
+        else:
+            key = self.mark_shapes.add(tolerance=self.drawing_tolerance, **kwargs)
+        return key
+
     @property
     @property
     def visible(self):
     def visible(self):
         return self.shapes.visible
         return self.shapes.visible
@@ -570,17 +579,17 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
                 ap_size_item = QtWidgets.QTableWidgetItem('')
                 ap_size_item = QtWidgets.QTableWidgetItem('')
             ap_size_item.setFlags(QtCore.Qt.ItemIsEnabled)
             ap_size_item.setFlags(QtCore.Qt.ItemIsEnabled)
 
 
-            plot_item = FCCheckBox()
-            plot_item.setLayoutDirection(QtCore.Qt.RightToLeft)
-            if self.ui.plot_cb.isChecked():
-                plot_item.setChecked(True)
+            mark_item = FCCheckBox()
+            mark_item.setLayoutDirection(QtCore.Qt.RightToLeft)
+            # if self.ui.aperture_table_visibility_cb.isChecked():
+            #     mark_item.setChecked(True)
 
 
             self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
             self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
             self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
             self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
             self.ui.apertures_table.setItem(self.apertures_row, 3, ap_size_item)   # Aperture Dimensions
             self.ui.apertures_table.setItem(self.apertures_row, 3, ap_size_item)   # Aperture Dimensions
             self.ui.apertures_table.setItem(self.apertures_row, 4, ap_dim_item)   # Aperture Dimensions
             self.ui.apertures_table.setItem(self.apertures_row, 4, ap_dim_item)   # Aperture Dimensions
 
 
-            self.ui.apertures_table.setCellWidget(self.apertures_row, 5, plot_item)
+            self.ui.apertures_table.setCellWidget(self.apertures_row, 5, mark_item)
 
 
             self.apertures_row += 1
             self.apertures_row += 1
 
 
@@ -596,14 +605,14 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             ap_type_item = QtWidgets.QTableWidgetItem('AM')
             ap_type_item = QtWidgets.QTableWidgetItem('AM')
             ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
             ap_type_item.setFlags(QtCore.Qt.ItemIsEnabled)
 
 
-            plot_item = FCCheckBox()
-            plot_item.setLayoutDirection(QtCore.Qt.RightToLeft)
-            if self.ui.plot_cb.isChecked():
-                plot_item.setChecked(True)
+            mark_item = FCCheckBox()
+            mark_item.setLayoutDirection(QtCore.Qt.RightToLeft)
+            # if self.ui.aperture_table_visibility_cb.isChecked():
+            #     mark_item.setChecked(True)
 
 
             self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
             self.ui.apertures_table.setItem(self.apertures_row, 1, ap_code_item)  # Aperture Code
             self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
             self.ui.apertures_table.setItem(self.apertures_row, 2, ap_type_item)  # Aperture Type
-            self.ui.apertures_table.setCellWidget(self.apertures_row, 5, plot_item)
+            self.ui.apertures_table.setCellWidget(self.apertures_row, 5, mark_item)
 
 
             self.apertures_row += 1
             self.apertures_row += 1
 
 
@@ -634,7 +643,40 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         self.ui.apertures_table.setSortingEnabled(False)
         self.ui.apertures_table.setSortingEnabled(False)
         self.ui.apertures_table.setMinimumHeight(self.ui.apertures_table.getHeight())
         self.ui.apertures_table.setMinimumHeight(self.ui.apertures_table.getHeight())
 
 
-        # self.ui_connect()
+        self.ui_connect()
+
+    def ui_connect(self):
+        for row in range(self.ui.apertures_table.rowCount()):
+            self.ui.apertures_table.cellWidget(row, 5).clicked.connect(self.on_mark_cb_click_table)
+
+    def ui_disconnect(self):
+        for row in range(self.ui.apertures_table.rowCount()):
+            try:
+                self.ui.apertures_table.cellWidget(row, 5).clicked.disconnect()
+            except:
+                pass
+
+    def on_mark_cb_click_table(self):
+        self.ui_disconnect()
+        cw = self.sender()
+        cw_index = self.ui.apertures_table.indexAt(cw.pos())
+        cw_row = cw_index.row()
+        check_row = 0
+
+        self.mark_shapes.clear(update=True)
+        for aperture in self.apertures:
+            # find the apertures_table row associated with the aperture
+            for row in range(self.ui.apertures_table.rowCount()):
+                if int(self.ui.apertures_table.item(row, 1).text()) == int(aperture):
+                    check_row = row
+                    break
+
+            if self.ui.apertures_table.cellWidget(check_row, 5).isChecked():
+                self.plot_apertures(color = '#2d4606bf', marked_aperture=aperture, visible=True)
+
+        self.mark_shapes.redraw()
+
+        self.ui_connect()
 
 
     def on_generatenoncopper_button_click(self, *args):
     def on_generatenoncopper_button_click(self, *args):
         self.app.report_usage("gerber_on_generatenoncopper_button")
         self.app.report_usage("gerber_on_generatenoncopper_button")
@@ -895,6 +937,7 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
         if self.muted_ui:
         if self.muted_ui:
             return
             return
         self.read_form_item('plot')
         self.read_form_item('plot')
+        self.plot()
 
 
     def on_solid_cb_click(self, *args):
     def on_solid_cb_click(self, *args):
         if self.muted_ui:
         if self.muted_ui:
@@ -916,8 +959,19 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
     def on_aperture_table_visibility_change(self):
     def on_aperture_table_visibility_change(self):
         if self.ui.aperture_table_visibility_cb.isChecked():
         if self.ui.aperture_table_visibility_cb.isChecked():
             self.ui.apertures_table.setVisible(True)
             self.ui.apertures_table.setVisible(True)
+            self.ui.scale_aperture_label.setVisible(True)
+            self.ui.scale_aperture_entry.setVisible(True)
+            self.ui.scale_aperture_button.setVisible(True)
         else:
         else:
             self.ui.apertures_table.setVisible(False)
             self.ui.apertures_table.setVisible(False)
+            self.ui.scale_aperture_label.setVisible(False)
+            self.ui.scale_aperture_entry.setVisible(False)
+            self.ui.scale_aperture_button.setVisible(False)
+
+            # on hide disable all mark plots
+            for row in range(self.ui.apertures_table.rowCount()):
+                self.ui.apertures_table.cellWidget(row, 5).set_value(False)
+            self.mark_shapes.clear(update=True)
 
 
     def convert_units(self, units):
     def convert_units(self, units):
         """
         """
@@ -1005,70 +1059,60 @@ class FlatCAMGerber(FlatCAMObj, Gerber):
             self.shapes.clear(update=True)
             self.shapes.clear(update=True)
 
 
     # experimental plot() when the solid_geometry is stored in the self.apertures
     # experimental plot() when the solid_geometry is stored in the self.apertures
-    # def plot_apertures(self, **kwargs):
-    #     """
-    #
-    #     :param kwargs: color and face_color
-    #     :return:
-    #     """
-    #
-    #     FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> FlatCAMGerber.plot()")
-    #
-    #     # Does all the required setup and returns False
-    #     # if the 'ptint' option is set to False.
-    #     if not FlatCAMObj.plot(self):
-    #         return
-    #
-    #     if 'color' in kwargs:
-    #         color = kwargs['color']
-    #     else:
-    #         color = self.app.defaults['global_plot_line']
-    #     if 'face_color' in kwargs:
-    #         face_color = kwargs['face_color']
-    #     else:
-    #         face_color = self.app.defaults['global_plot_fill']
-    #
-    #     geometry = {}
-    #     for ap in self.apertures:
-    #         geometry[ap] = self.apertures[ap]['solid_geometry']
-    #         try:
-    #             _ = iter(geometry[ap])
-    #         except TypeError:
-    #             geometry[ap] = [geometry[ap]]
-    #
-    #     def random_color():
-    #         color = np.random.rand(4)
-    #         color[3] = 1
-    #         return color
-    #
-    #     try:
-    #         if self.options["solid"]:
-    #             for geo in geometry:
-    #                 for g in geometry[geo]:
-    #                     if type(g) == Polygon or type(g) == LineString:
-    #                         self.add_shape(shape=g, color=color,
-    #                                        face_color=random_color() if self.options['multicolored']
-    #                                        else face_color, visible=self.options['plot'])
-    #                     else:
-    #                         for el in g:
-    #                             self.add_shape(shape=el, color=color,
-    #                                            face_color=random_color() if self.options['multicolored']
-    #                                            else face_color, visible=self.options['plot'])
-    #         else:
-    #             for geo in geometry:
-    #                 for g in geometry[geo]:
-    #                     if type(g) == Polygon or type(g) == LineString:
-    #                         self.add_shape(shape=g,
-    #                                        color=random_color() if self.options['multicolored'] else 'black',
-    #                                        visible=self.options['plot'])
-    #                     else:
-    #                         for el in g:
-    #                             self.add_shape(shape=el,
-    #                                            color=random_color() if self.options['multicolored'] else 'black',
-    #                                            visible=self.options['plot'])
-    #         self.shapes.redraw()
-    #     except (ObjectDeleted, AttributeError):
-    #         self.shapes.clear(update=True)
+    def plot_apertures(self, **kwargs):
+        """
+
+        :param kwargs: color and face_color
+        :return:
+        """
+
+        FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> FlatCAMGerber.plot_apertures()")
+
+        # Does all the required setup and returns False
+        # if the 'ptint' option is set to False.
+        if not FlatCAMObj.plot(self):
+            return
+
+        # for marking apertures, line color and fill color are the same
+        if 'color' in kwargs:
+            color = kwargs['color']
+        else:
+            color = self.app.defaults['global_plot_fill']
+
+        if 'marked_aperture' not in kwargs:
+            return
+        else:
+            aperture_to_plot_mark = kwargs['marked_aperture']
+            if aperture_to_plot_mark is None:
+                return
+
+        if 'visible' not in kwargs:
+            visibility = True
+        else:
+            visibility = kwargs['visible']
+
+        geometry = {}
+        for ap in self.apertures:
+            geometry[int(ap)] = self.apertures[ap]['solid_geometry']
+            try:
+                _ = iter(geometry[int(ap)])
+            except TypeError:
+                geometry[int(ap)] = [geometry[int(ap)]]
+
+        try:
+            if aperture_to_plot_mark in self.apertures:
+                for geo in geometry[int(aperture_to_plot_mark)]:
+                    if type(geo) == Polygon or type(geo) == LineString:
+                        self.add_mark_shape(shape=geo, color=color,
+                                       face_color=color, visible=visibility)
+                    else:
+                        for el in geo:
+                            self.add_mark_shape(shape=el, color=color,
+                                           face_color=color, visible=visibility)
+
+            self.mark_shapes.redraw()
+        except (ObjectDeleted, AttributeError):
+            self.mark_shapes.clear(update=True)
 
 
     def serialize(self):
     def serialize(self):
         return {
         return {
@@ -4383,6 +4427,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
                 self.tools[tool]['solid_geometry'] = scale_recursion(self.tools[tool]['solid_geometry'])
                 self.tools[tool]['solid_geometry'] = scale_recursion(self.tools[tool]['solid_geometry'])
         else:
         else:
             self.solid_geometry=scale_recursion(self.solid_geometry)
             self.solid_geometry=scale_recursion(self.solid_geometry)
+
         self.app.inform.emit("[success]Geometry Scale done.")
         self.app.inform.emit("[success]Geometry Scale done.")
 
 
     def offset(self, vect):
     def offset(self, vect):

+ 60 - 24
ObjectUI.py

@@ -127,6 +127,8 @@ class GerberObjectUI(ObjectUI):
         self.custom_box.addLayout(grid0)
         self.custom_box.addLayout(grid0)
 
 
         self.plot_options_label = QtWidgets.QLabel("<b>Plot Options:</b>")
         self.plot_options_label = QtWidgets.QLabel("<b>Plot Options:</b>")
+        self.plot_options_label.setFixedWidth(90)
+
         grid0.addWidget(self.plot_options_label, 0, 0)
         grid0.addWidget(self.plot_options_label, 0, 0)
 
 
         # Solid CB
         # Solid CB
@@ -158,10 +160,12 @@ class GerberObjectUI(ObjectUI):
         self.custom_box.addLayout(hlay_plot)
         self.custom_box.addLayout(hlay_plot)
 
 
         #### Gerber Apertures ####
         #### Gerber Apertures ####
-        self.apertures_table_label = QtWidgets.QLabel('<b>Apertures Table</b>')
+        self.apertures_table_label = QtWidgets.QLabel('<b>Apertures</b>')
         self.apertures_table_label.setToolTip(
         self.apertures_table_label.setToolTip(
-            "Apertures in this Gerber object."
+            "Apertures Table containining this Gerber object apertures."
         )
         )
+        self.apertures_table_label.setFixedWidth(90)
+
         hlay_plot.addWidget(self.apertures_table_label)
         hlay_plot.addWidget(self.apertures_table_label)
 
 
         # Aperture Table Visibility CB
         # Aperture Table Visibility CB
@@ -181,7 +185,7 @@ class GerberObjectUI(ObjectUI):
         self.custom_box.addWidget(self.apertures_table)
         self.custom_box.addWidget(self.apertures_table)
 
 
         self.apertures_table.setColumnCount(6)
         self.apertures_table.setColumnCount(6)
-        self.apertures_table.setHorizontalHeaderLabels(['#', 'Code', 'Type', 'Size', 'Dim', 'P'])
+        self.apertures_table.setHorizontalHeaderLabels(['#', 'Code', 'Type', 'Size', 'Dim', 'M'])
         self.apertures_table.setSortingEnabled(False)
         self.apertures_table.setSortingEnabled(False)
 
 
         self.apertures_table.horizontalHeaderItem(0).setToolTip(
         self.apertures_table.horizontalHeaderItem(0).setToolTip(
@@ -197,16 +201,40 @@ class GerberObjectUI(ObjectUI):
             " - (width, height) for R, O type.\n"
             " - (width, height) for R, O type.\n"
             " - (dia, nVertices) for P type")
             " - (dia, nVertices) for P type")
         self.apertures_table.horizontalHeaderItem(5).setToolTip(
         self.apertures_table.horizontalHeaderItem(5).setToolTip(
-            "Toggle display of the aperture instances.")
+            "Mark the aperture instances on canvas.")
+        # self.apertures_table.setColumnHidden(5, True)
+
+        #### Aperture Scale ####
+        self.scale_aperture_grid = QtWidgets.QGridLayout()
+        self.custom_box.addLayout(self.scale_aperture_grid)
+
+        # Factor
+        self.scale_aperture_label = QtWidgets.QLabel('<b>Factor:</b>')
+        self.scale_aperture_label.setToolTip(
+            "Change the size of the selected apertures.\n"
+            "Factor by which to multiply\n"
+            "geometric features of this object."
+        )
+        self.scale_aperture_label.setFixedWidth(90)
+        self.scale_aperture_grid.addWidget(self.scale_aperture_label, 0, 0)
+
+        self.scale_aperture_entry = FloatEntry2()
+        self.scale_aperture_entry.set_value(1.0)
+        self.scale_aperture_grid.addWidget(self.scale_aperture_entry, 0, 1)
+
+        # Scale Button
+        self.scale_aperture_button = QtWidgets.QPushButton('Scale')
+        self.scale_aperture_button.setToolTip(
+            "Perform scaling operation."
+        )
+        self.scale_aperture_button.setFixedWidth(40)
+        self.scale_aperture_grid.addWidget(self.scale_aperture_button, 0, 2)
 
 
         # start with apertures table hidden
         # start with apertures table hidden
         self.apertures_table.setVisible(False)
         self.apertures_table.setVisible(False)
-
-        # hide the plot column. for now I can't plot individually the apertures without making the plot really ugly
-        self.apertures_table.setColumnHidden(5, True)
-        #
-        # self.empty_label = QtWidgets.QLabel('')
-        # self.custom_box.addWidget(self.empty_label)
+        self.scale_aperture_label.setVisible(False)
+        self.scale_aperture_entry.setVisible(False)
+        self.scale_aperture_button.setVisible(False)
 
 
         # Isolation Routing
         # Isolation Routing
         self.isolation_routing_label = QtWidgets.QLabel("<b>Isolation Routing:</b>")
         self.isolation_routing_label = QtWidgets.QLabel("<b>Isolation Routing:</b>")
@@ -226,6 +254,7 @@ class GerberObjectUI(ObjectUI):
             "feature, use a negative value for\n"
             "feature, use a negative value for\n"
             "this parameter."
             "this parameter."
         )
         )
+        tdlabel.setFixedWidth(90)
         grid1.addWidget(tdlabel, 0, 0)
         grid1.addWidget(tdlabel, 0, 0)
         self.iso_tool_dia_entry = LengthEntry()
         self.iso_tool_dia_entry = LengthEntry()
         grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
         grid1.addWidget(self.iso_tool_dia_entry, 0, 1)
@@ -235,6 +264,7 @@ class GerberObjectUI(ObjectUI):
             "Width of the isolation gap in\n"
             "Width of the isolation gap in\n"
             "number (integer) of tool widths."
             "number (integer) of tool widths."
         )
         )
+        passlabel.setFixedWidth(90)
         grid1.addWidget(passlabel, 1, 0)
         grid1.addWidget(passlabel, 1, 0)
         self.iso_width_entry = IntEntry()
         self.iso_width_entry = IntEntry()
         grid1.addWidget(self.iso_width_entry, 1, 1)
         grid1.addWidget(self.iso_width_entry, 1, 1)
@@ -245,6 +275,7 @@ class GerberObjectUI(ObjectUI):
             "Example:\n"
             "Example:\n"
             "A value here of 0.25 means an overlap of 25% from the tool diameter found above."
             "A value here of 0.25 means an overlap of 25% from the tool diameter found above."
         )
         )
+        overlabel.setFixedWidth(90)
         grid1.addWidget(overlabel, 2, 0)
         grid1.addWidget(overlabel, 2, 0)
         self.iso_overlap_entry = FloatEntry()
         self.iso_overlap_entry = FloatEntry()
         grid1.addWidget(self.iso_overlap_entry, 2, 1)
         grid1.addWidget(self.iso_overlap_entry, 2, 1)
@@ -295,6 +326,15 @@ class GerberObjectUI(ObjectUI):
         hlay_1 = QtWidgets.QHBoxLayout()
         hlay_1 = QtWidgets.QHBoxLayout()
         self.custom_box.addLayout(hlay_1)
         self.custom_box.addLayout(hlay_1)
 
 
+        self.generate_iso_button = QtWidgets.QPushButton('FULL Geo')
+        self.generate_iso_button.setToolTip(
+            "Create the Geometry Object\n"
+            "for isolation routing. It contains both\n"
+            "the interiors and exteriors geometry."
+        )
+        self.generate_iso_button.setFixedWidth(90)
+        hlay_1.addWidget(self.generate_iso_button)
+
         hlay_1.addStretch()
         hlay_1.addStretch()
 
 
         self.generate_ext_iso_button = QtWidgets.QPushButton('Ext Geo')
         self.generate_ext_iso_button = QtWidgets.QPushButton('Ext Geo')
@@ -303,7 +343,7 @@ class GerberObjectUI(ObjectUI):
             "for isolation routing containing\n"
             "for isolation routing containing\n"
             "only the exteriors geometry."
             "only the exteriors geometry."
         )
         )
-        self.generate_ext_iso_button.setFixedWidth(60)
+        # self.generate_ext_iso_button.setFixedWidth(60)
         hlay_1.addWidget(self.generate_ext_iso_button)
         hlay_1.addWidget(self.generate_ext_iso_button)
 
 
         self.generate_int_iso_button = QtWidgets.QPushButton('Int Geo')
         self.generate_int_iso_button = QtWidgets.QPushButton('Int Geo')
@@ -312,18 +352,9 @@ class GerberObjectUI(ObjectUI):
             "for isolation routing containing\n"
             "for isolation routing containing\n"
             "only the interiors geometry."
             "only the interiors geometry."
         )
         )
-        self.generate_int_iso_button.setFixedWidth(60)
+        # self.generate_int_iso_button.setFixedWidth(60)
         hlay_1.addWidget(self.generate_int_iso_button)
         hlay_1.addWidget(self.generate_int_iso_button)
 
 
-        self.generate_iso_button = QtWidgets.QPushButton('FULL Geo')
-        self.generate_iso_button.setToolTip(
-            "Create the Geometry Object\n"
-            "for isolation routing. It contains both\n"
-            "the interiors and exteriors geometry."
-        )
-        self.generate_iso_button.setFixedWidth(80)
-        hlay_1.addWidget(self.generate_iso_button)
-
         # when the follow checkbox is checked then the exteriors and interiors isolation generation buttons
         # when the follow checkbox is checked then the exteriors and interiors isolation generation buttons
         # are disabled as is doesn't make sense to have them enabled due of the nature of "follow"
         # are disabled as is doesn't make sense to have them enabled due of the nature of "follow"
         self.ois_iso = OptionalInputSection(self.follow_cb,
         self.ois_iso = OptionalInputSection(self.follow_cb,
@@ -333,11 +364,12 @@ class GerberObjectUI(ObjectUI):
         self.custom_box.addLayout(grid2)
         self.custom_box.addLayout(grid2)
 
 
         ## Clear non-copper regions
         ## Clear non-copper regions
-        self.clearcopper_label = QtWidgets.QLabel("<b>Clear non-copper:</b>")
+        self.clearcopper_label = QtWidgets.QLabel("<b>Clear N-copper:</b>")
         self.clearcopper_label.setToolTip(
         self.clearcopper_label.setToolTip(
             "Create a Geometry object with\n"
             "Create a Geometry object with\n"
             "toolpaths to cut all non-copper regions."
             "toolpaths to cut all non-copper regions."
         )
         )
+        self.clearcopper_label.setFixedWidth(90)
         grid2.addWidget(self.clearcopper_label, 0, 0)
         grid2.addWidget(self.clearcopper_label, 0, 0)
 
 
         self.generate_ncc_button = QtWidgets.QPushButton('NCC Tool')
         self.generate_ncc_button = QtWidgets.QPushButton('NCC Tool')
@@ -385,15 +417,17 @@ class GerberObjectUI(ObjectUI):
             "objects with this minimum\n"
             "objects with this minimum\n"
             "distance."
             "distance."
         )
         )
+        bmlabel.setFixedWidth(90)
         grid4.addWidget(bmlabel, 0, 0)
         grid4.addWidget(bmlabel, 0, 0)
         self.noncopper_margin_entry = LengthEntry()
         self.noncopper_margin_entry = LengthEntry()
         grid4.addWidget(self.noncopper_margin_entry, 0, 1)
         grid4.addWidget(self.noncopper_margin_entry, 0, 1)
 
 
         # Rounded corners
         # Rounded corners
-        self.noncopper_rounded_cb = FCCheckBox(label="Rounded corners")
+        self.noncopper_rounded_cb = FCCheckBox(label="Rounded Geo")
         self.noncopper_rounded_cb.setToolTip(
         self.noncopper_rounded_cb.setToolTip(
             "Resulting geometry will have rounded corners."
             "Resulting geometry will have rounded corners."
         )
         )
+        self.noncopper_rounded_cb.setFixedWidth(90)
         grid4.addWidget(self.noncopper_rounded_cb, 1, 0)
         grid4.addWidget(self.noncopper_rounded_cb, 1, 0)
 
 
         self.generate_noncopper_button = QtWidgets.QPushButton('Generate Geo')
         self.generate_noncopper_button = QtWidgets.QPushButton('Generate Geo')
@@ -415,17 +449,19 @@ class GerberObjectUI(ObjectUI):
             "Distance of the edges of the box\n"
             "Distance of the edges of the box\n"
             "to the nearest polygon."
             "to the nearest polygon."
         )
         )
+        bbmargin.setFixedWidth(90)
         grid5.addWidget(bbmargin, 0, 0)
         grid5.addWidget(bbmargin, 0, 0)
         self.bbmargin_entry = LengthEntry()
         self.bbmargin_entry = LengthEntry()
         grid5.addWidget(self.bbmargin_entry, 0, 1)
         grid5.addWidget(self.bbmargin_entry, 0, 1)
 
 
-        self.bbrounded_cb = FCCheckBox(label="Rounded corners")
+        self.bbrounded_cb = FCCheckBox(label="Rounded Geo")
         self.bbrounded_cb.setToolTip(
         self.bbrounded_cb.setToolTip(
             "If the bounding box is \n"
             "If the bounding box is \n"
             "to have rounded corners\n"
             "to have rounded corners\n"
             "their radius is equal to\n"
             "their radius is equal to\n"
             "the margin."
             "the margin."
         )
         )
+        self.bbrounded_cb.setFixedWidth(90)
         grid5.addWidget(self.bbrounded_cb, 1, 0)
         grid5.addWidget(self.bbrounded_cb, 1, 0)
 
 
         self.generate_bb_button = QtWidgets.QPushButton('Generate Geo')
         self.generate_bb_button = QtWidgets.QPushButton('Generate Geo')

+ 3 - 1
PlotCanvas.py

@@ -165,7 +165,9 @@ class PlotCanvas(QtCore.QObject):
         """
         """
         self.vispy_canvas.view.camera.zoom(factor, center)
         self.vispy_canvas.view.camera.zoom(factor, center)
 
 
-    def new_shape_group(self):
+    def new_shape_group(self, shape_collection=None):
+        if shape_collection:
+            return ShapeGroup(shape_collection)
         return ShapeGroup(self.shape_collection)
         return ShapeGroup(self.shape_collection)
 
 
     def new_shape_collection(self, **kwargs):
     def new_shape_collection(self, **kwargs):

+ 5 - 0
README.md

@@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing.
 
 
 =================================================
 =================================================
 
 
+25.02.2019
+
+- fixed the Gerber object UI layout
+- added ability to mark individual apertures in Gerber file using the Gerber Aperture Table
+
 24.02.2019
 24.02.2019
 
 
 - fixed a small bug in the Tool Solder Paste: the App don't take into consideration pads already filled with solder paste.
 - fixed a small bug in the Tool Solder Paste: the App don't take into consideration pads already filled with solder paste.

+ 77 - 4
camlib.py

@@ -1858,7 +1858,8 @@ class Gerber (Geometry):
     +-----------+-----------------------------------+
     +-----------+-----------------------------------+
     | others    | Depend on ``type``                |
     | others    | Depend on ``type``                |
     +-----------+-----------------------------------+
     +-----------+-----------------------------------+
-
+    | solid_geometry      | (list)                  |
+    +-----------+-----------------------------------+
     * ``aperture_macros`` (dictionary): Are predefined geometrical structures
     * ``aperture_macros`` (dictionary): Are predefined geometrical structures
       that can be instantiated with different parameters in an aperture
       that can be instantiated with different parameters in an aperture
       definition. See ``apertures`` above. The key is the name of the macro,
       definition. See ``apertures`` above. The key is the name of the macro,
@@ -1921,9 +1922,7 @@ class Gerber (Geometry):
                 'size':float, 
                 'size':float, 
                 'width':float,
                 'width':float,
                 'height':float,
                 'height':float,
-                'light_solid_geometry': [],
-                'dark_solid_geometry': [],
-                'buff_solid_geometry': [],
+                'solid_geometry': [],
                 'follow_geometry': [],
                 'follow_geometry': [],
             }
             }
         }
         }
@@ -2247,6 +2246,11 @@ class Gerber (Geometry):
                         geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                         geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                         if not geo.is_empty:
                         if not geo.is_empty:
                             poly_buffer.append(geo)
                             poly_buffer.append(geo)
+                            try:
+                                self.apertures[current_aperture]['solid_geometry'].append(geo)
+                            except KeyError:
+                                self.apertures[current_aperture]['solid_geometry'] = []
+                                self.apertures[current_aperture]['solid_geometry'].append(geo)
 
 
                         path = [path[-1]]
                         path = [path[-1]]
 
 
@@ -2411,6 +2415,11 @@ class Gerber (Geometry):
                                 int(self.steps_per_circle))
                                 int(self.steps_per_circle))
                             if not flash.is_empty:
                             if not flash.is_empty:
                                 poly_buffer.append(flash)
                                 poly_buffer.append(flash)
+                                try:
+                                    self.apertures[current_aperture]['solid_geometry'].append(flash)
+                                except KeyError:
+                                    self.apertures[current_aperture]['solid_geometry'] = []
+                                    self.apertures[current_aperture]['solid_geometry'].append(flash)
                         except IndexError:
                         except IndexError:
                             log.warning("Line %d: %s -> Nothing there to flash!" % (line_num, gline))
                             log.warning("Line %d: %s -> Nothing there to flash!" % (line_num, gline))
 
 
@@ -2448,6 +2457,11 @@ class Gerber (Geometry):
                             geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                             geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                             if not geo.is_empty:
                             if not geo.is_empty:
                                 poly_buffer.append(geo)
                                 poly_buffer.append(geo)
+                                try:
+                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[last_path_aperture]['solid_geometry'] = []
+                                    self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
 
                             path = [path[-1]]
                             path = [path[-1]]
 
 
@@ -2468,6 +2482,11 @@ class Gerber (Geometry):
                         geo = LineString(path).buffer(width/1.999, int(self.steps_per_circle / 4))
                         geo = LineString(path).buffer(width/1.999, int(self.steps_per_circle / 4))
                         if not geo.is_empty:
                         if not geo.is_empty:
                             poly_buffer.append(geo)
                             poly_buffer.append(geo)
+                            try:
+                                self.apertures[current_aperture]['solid_geometry'].append(geo)
+                            except KeyError:
+                                self.apertures[current_aperture]['solid_geometry'] = []
+                                self.apertures[current_aperture]['solid_geometry'].append(geo)
 
 
                         path = [path[-1]]
                         path = [path[-1]]
 
 
@@ -2485,6 +2504,11 @@ class Gerber (Geometry):
                             if not geo.is_empty:
                             if not geo.is_empty:
                                 follow_buffer.append(geo)
                                 follow_buffer.append(geo)
                                 poly_buffer.append(geo)
                                 poly_buffer.append(geo)
+                                try:
+                                    self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[current_aperture]['solid_geometry'] = []
+                                    self.apertures[current_aperture]['solid_geometry'].append(geo)
                             continue
                             continue
 
 
                     # Only one path defines region?
                     # Only one path defines region?
@@ -2514,6 +2538,11 @@ class Gerber (Geometry):
 
 
                     if not region.is_empty:
                     if not region.is_empty:
                         poly_buffer.append(region)
                         poly_buffer.append(region)
+                        try:
+                            self.apertures[current_aperture]['solid_geometry'].append(region)
+                        except KeyError:
+                            self.apertures[current_aperture]['solid_geometry'] = []
+                            self.apertures[current_aperture]['solid_geometry'].append(region)
 
 
                     path = [[current_x, current_y]]  # Start new path
                     path = [[current_x, current_y]]  # Start new path
                     continue
                     continue
@@ -2584,6 +2613,11 @@ class Gerber (Geometry):
 
 
                                         geo = shply_box(minx, miny, maxx, maxy)
                                         geo = shply_box(minx, miny, maxx, maxy)
                                         poly_buffer.append(geo)
                                         poly_buffer.append(geo)
+                                        try:
+                                            self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                        except KeyError:
+                                            self.apertures[current_aperture]['solid_geometry'] = []
+                                            self.apertures[current_aperture]['solid_geometry'].append(geo)
                                 except:
                                 except:
                                     pass
                                     pass
                             last_path_aperture = current_aperture
                             last_path_aperture = current_aperture
@@ -2633,6 +2667,11 @@ class Gerber (Geometry):
                                         poly_buffer.append(geo)
                                         poly_buffer.append(geo)
                             except:
                             except:
                                 poly_buffer.append(geo)
                                 poly_buffer.append(geo)
+                                try:
+                                    self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                except KeyError:
+                                    self.apertures[current_aperture]['solid_geometry'] = []
+                                    self.apertures[current_aperture]['solid_geometry'].append(geo)
 
 
                         # if linear_x or linear_y are None, ignore those
                         # if linear_x or linear_y are None, ignore those
                         if linear_x is not None and linear_y is not None:
                         if linear_x is not None and linear_y is not None:
@@ -2665,8 +2704,18 @@ class Gerber (Geometry):
                                 try:
                                 try:
                                     if self.apertures[current_aperture]["type"] != 'R':
                                     if self.apertures[current_aperture]["type"] != 'R':
                                         poly_buffer.append(geo)
                                         poly_buffer.append(geo)
+                                        try:
+                                            self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                        except KeyError:
+                                            self.apertures[current_aperture]['solid_geometry'] = []
+                                            self.apertures[current_aperture]['solid_geometry'].append(geo)
                                 except:
                                 except:
                                     poly_buffer.append(geo)
                                     poly_buffer.append(geo)
+                                    try:
+                                        self.apertures[current_aperture]['solid_geometry'].append(geo)
+                                    except KeyError:
+                                        self.apertures[current_aperture]['solid_geometry'] = []
+                                        self.apertures[current_aperture]['solid_geometry'].append(geo)
 
 
                         # Reset path starting point
                         # Reset path starting point
                         path = [[linear_x, linear_y]]
                         path = [[linear_x, linear_y]]
@@ -2684,6 +2733,11 @@ class Gerber (Geometry):
                         )
                         )
                         if not flash.is_empty:
                         if not flash.is_empty:
                             poly_buffer.append(flash)
                             poly_buffer.append(flash)
+                            try:
+                                self.apertures[current_aperture]['solid_geometry'].append(flash)
+                            except KeyError:
+                                self.apertures[current_aperture]['solid_geometry'] = []
+                                self.apertures[current_aperture]['solid_geometry'].append(flash)
 
 
                     # maybe those lines are not exactly needed but it is easier to read the program as those coordinates
                     # maybe those lines are not exactly needed but it is easier to read the program as those coordinates
                     # are used in case that circular interpolation is encountered within the Gerber file
                     # are used in case that circular interpolation is encountered within the Gerber file
@@ -2773,6 +2827,11 @@ class Gerber (Geometry):
                             buffered = LineString(path).buffer(width / 1.999, int(self.steps_per_circle))
                             buffered = LineString(path).buffer(width / 1.999, int(self.steps_per_circle))
                             if not buffered.is_empty:
                             if not buffered.is_empty:
                                 poly_buffer.append(buffered)
                                 poly_buffer.append(buffered)
+                                try:
+                                    self.apertures[current_aperture]['solid_geometry'].append(buffered)
+                                except KeyError:
+                                    self.apertures[current_aperture]['solid_geometry'] = []
+                                    self.apertures[current_aperture]['solid_geometry'].append(buffered)
 
 
                         current_x = circular_x
                         current_x = circular_x
                         current_y = circular_y
                         current_y = circular_y
@@ -2900,6 +2959,11 @@ class Gerber (Geometry):
                     geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                     geo = LineString(path).buffer(width / 1.999, int(self.steps_per_circle / 4))
                     if not geo.is_empty:
                     if not geo.is_empty:
                         poly_buffer.append(geo)
                         poly_buffer.append(geo)
+                        try:
+                            self.apertures[last_path_aperture]['solid_geometry'].append(geo)
+                        except KeyError:
+                            self.apertures[last_path_aperture]['solid_geometry'] = []
+                            self.apertures[last_path_aperture]['solid_geometry'].append(geo)
 
 
             # --- Apply buffer ---
             # --- Apply buffer ---
 
 
@@ -3110,6 +3174,7 @@ class Gerber (Geometry):
         :type factor: float
         :type factor: float
         :rtype : None
         :rtype : None
         """
         """
+        log.debug("camlib.Gerber.scale()")
 
 
         try:
         try:
             xfactor = float(xfactor)
             xfactor = float(xfactor)
@@ -3143,6 +3208,14 @@ class Gerber (Geometry):
                                              yfactor, origin=(px, py))
                                              yfactor, origin=(px, py))
 
 
         self.solid_geometry = scale_geom(self.solid_geometry)
         self.solid_geometry = scale_geom(self.solid_geometry)
+
+        # we need to scale the geometry stored in the Gerber apertures, too
+        try:
+            for apid in self.apertures:
+                self.apertures[apid]['solid_geometry'] = scale_geom(self.apertures[apid]['solid_geometry'])
+        except Exception as e:
+            log.debug('FlatCAMGeometry.scale() --> %s' % str(e))
+
         self.app.inform.emit("[success]Gerber Scale done.")
         self.app.inform.emit("[success]Gerber Scale done.")
 
 
         ## solid_geometry ???
         ## solid_geometry ???