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

- stored solid_geometry of Excellon object in the self.tools dictionary
- finished the solid_geometry restore after edit in Excellon Editor
- finished plotting selection for each tool in the Excellon Tool Table
- fixed the camlib.Excellon.bounds() function for the new type of Excellon geometry therefore fixed the canvas selection, too

Marius Stanciu 7 лет назад
Родитель
Сommit
c94679919d
5 измененных файлов с 261 добавлено и 85 удалено
  1. 6 0
      FlatCAMEditor.py
  2. 207 68
      FlatCAMObj.py
  3. 19 9
      ObjectUI.py
  4. 6 1
      README.md
  5. 23 7
      camlib.py

+ 6 - 0
FlatCAMEditor.py

@@ -4306,6 +4306,9 @@ class FlatCAMExcEditor(QtCore.QObject):
             spec = {"C": float(tool_dia[0])}
             self.new_tools[name] = spec
 
+            # add in self.tools the 'solid_geometry' key, the value (a list) is populated bellow
+            self.new_tools[name]['solid_geometry'] = []
+
             # create the self.drills for the new Excellon object (the one with edited content)
             for point in tool_dia[1]:
                 self.new_drills.append(
@@ -4314,6 +4317,9 @@ class FlatCAMExcEditor(QtCore.QObject):
                         'tool': str(current_tool)
                     }
                 )
+                # repopulate the 'solid_geometry' for each tool
+                poly = Point(point).buffer(float(tool_dia[0]) / 2.0, int(int(exc_obj.geo_steps_per_circle) / 4))
+                self.new_tools[name]['solid_geometry'].append(poly)
 
         if self.is_modified is True:
             if "_edit" in self.edited_obj_name:

+ 207 - 68
FlatCAMObj.py

@@ -1134,10 +1134,16 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
                     t_offset = self.app.defaults['excellon_offset']
             tool_offset_item = QtWidgets.QTableWidgetItem('%s' % str(t_offset))
 
+            plot_item = FCCheckBox()
+            plot_item.setLayoutDirection(QtCore.Qt.RightToLeft)
+            if self.ui.plot_cb.isChecked():
+                plot_item.setChecked(True)
+
             self.ui.tools_table.setItem(self.tool_row, 1, dia)  # Diameter
             self.ui.tools_table.setItem(self.tool_row, 2, drill_count)  # Number of drills per tool
             self.ui.tools_table.setItem(self.tool_row, 3, slot_count)  # Number of drills per tool
             self.ui.tools_table.setItem(self.tool_row, 4, tool_offset_item)  # Tool offset
+            self.ui.tools_table.setCellWidget(self.tool_row, 5, plot_item)
 
             self.tool_row += 1
 
@@ -1201,12 +1207,28 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         self.ui.tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
 
         horizontal_header = self.ui.tools_table.horizontalHeader()
-        horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setMinimumSectionSize(10)
+        horizontal_header.setDefaultSectionSize(70)
+        horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
+        horizontal_header.resizeSection(0, 20)
         horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
         horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
         horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.ResizeToContents)
+        horizontal_header.setSectionResizeMode(5, QtWidgets.QHeaderView.Fixed)
+        horizontal_header.resizeSection(5, 17)
+        self.ui.tools_table.setColumnWidth(5, 17)
+
         # horizontal_header.setStretchLastSection(True)
 
+
+
+
+        # horizontal_header.setColumnWidth(2, QtWidgets.QHeaderView.ResizeToContents)
+
+        # horizontal_header.setStretchLastSection(True)
+        self.ui.tools_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+
         self.ui.tools_table.setSortingEnabled(False)
 
         self.ui.tools_table.setMinimumHeight(self.ui.tools_table.getHeight())
@@ -1233,6 +1255,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         # we reactivate the signals after the after the tool adding as we don't need to see the tool been populated
         self.ui.tools_table.itemChanged.connect(self.on_tool_offset_edit)
 
+        self.ui_connect()
+
     def set_ui(self, ui):
         """
         Configures the user interface for this object.
@@ -1297,6 +1321,24 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
 
         self.ui.pp_excellon_name_cb.activated.connect(self.on_pp_changed)
 
+    def ui_connect(self):
+
+        for row in range(self.ui.tools_table.rowCount() - 2):
+            self.ui.tools_table.cellWidget(row, 5).clicked.connect(self.on_plot_cb_click_table)
+        self.ui.plot_cb.stateChanged.connect(self.on_plot_cb_click)
+
+    def ui_disconnect(self):
+        for row in range(self.ui.tools_table.rowCount()):
+            try:
+                self.ui.tools_table.cellWidget(row, 5).clicked.disconnect()
+            except:
+                pass
+
+        try:
+            self.ui.plot_cb.stateChanged.disconnect()
+        except:
+            pass
+
     def on_tool_offset_edit(self):
         # if connected, disconnect the signal from the slot on item_changed as it creates issues
         self.ui.tools_table.itemChanged.disconnect()
@@ -1351,8 +1393,10 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         """
         table_tools_items = []
         for x in self.ui.tools_table.selectedItems():
+            # from the columnCount we subtract a value of 1 which represent the last column (plot column)
+            # which does not have text
             table_tools_items.append([self.ui.tools_table.item(x.row(), column).text()
-                                      for column in range(0, self.ui.tools_table.columnCount())])
+                                      for column in range(0, self.ui.tools_table.columnCount() - 1)])
         for item in table_tools_items:
             item[0] = str(item[0])
         return table_tools_items
@@ -1839,17 +1883,6 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
         # self.app.worker.add_task(job_thread, [self.app])
         self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
 
-    def on_plot_cb_click(self, *args):
-        if self.muted_ui:
-            return
-        self.read_form_item('plot')
-
-    def on_solid_cb_click(self, *args):
-        if self.muted_ui:
-            return
-        self.read_form_item('solid')
-        self.plot()
-
     def convert_units(self, units):
         factor = Excellon.convert_units(self, units)
 
@@ -1875,6 +1908,89 @@ class FlatCAMExcellon(FlatCAMObj, Excellon):
             self.options['startz'] = float(self.options['startz']) * factor
         self.options['endz'] = float(self.options['endz']) * factor
 
+    def on_solid_cb_click(self, *args):
+        if self.muted_ui:
+            return
+        self.read_form_item('solid')
+        self.plot()
+
+    def on_plot_cb_click(self, *args):
+        if self.muted_ui:
+            return
+        self.plot()
+        self.read_form_item('plot')
+
+        self.ui_disconnect()
+        cb_flag = self.ui.plot_cb.isChecked()
+        for row in range(self.ui.tools_table.rowCount() - 2):
+            table_cb = self.ui.tools_table.cellWidget(row, 5)
+            if cb_flag:
+                table_cb.setChecked(True)
+            else:
+                table_cb.setChecked(False)
+
+        self.ui_connect()
+
+    def on_plot_cb_click_table(self):
+        # self.ui.cnc_tools_table.cellWidget(row, 2).widget().setCheckState(QtCore.Qt.Unchecked)
+        self.ui_disconnect()
+        # cw = self.sender()
+        # cw_index = self.ui.tools_table.indexAt(cw.pos())
+        # cw_row = cw_index.row()
+        check_row = 0
+
+        self.shapes.clear(update=True)
+        for tool_key in self.tools:
+            solid_geometry = self.tools[tool_key]['solid_geometry']
+
+            # find the geo_tool_table row associated with the tool_key
+            for row in range(self.ui.tools_table.rowCount()):
+                tool_item = int(self.ui.tools_table.item(row, 0).text())
+                if tool_item == int(tool_key):
+                    check_row = row
+                    break
+            if self.ui.tools_table.cellWidget(check_row, 5).isChecked():
+                self.options['plot'] = True
+                # self.plot_element(element=solid_geometry, visible=True)
+                # Plot excellon (All polygons?)
+                if self.options["solid"]:
+                    for geo in solid_geometry:
+                        self.add_shape(shape=geo, color='#750000BF', face_color='#C40000BF',
+                                       visible=self.options['plot'],
+                                       layer=2)
+                else:
+                    for geo in solid_geometry:
+                        self.add_shape(shape=geo.exterior, color='red', visible=self.options['plot'])
+                        for ints in geo.interiors:
+                            self.add_shape(shape=ints, color='green', visible=self.options['plot'])
+        self.shapes.redraw()
+
+        # make sure that the general plot is disabled if one of the row plot's are disabled and
+        # if all the row plot's are enabled also enable the general plot checkbox
+        cb_cnt = 0
+        total_row = self.ui.tools_table.rowCount()
+        for row in range(total_row - 2):
+            if self.ui.tools_table.cellWidget(row, 5).isChecked():
+                cb_cnt += 1
+            else:
+                cb_cnt -= 1
+        if cb_cnt < total_row - 2:
+            self.ui.plot_cb.setChecked(False)
+        else:
+            self.ui.plot_cb.setChecked(True)
+        self.ui_connect()
+
+    # def plot_element(self, element, color='red', visible=None, layer=None):
+    #
+    #     visible = visible if visible else self.options['plot']
+    #
+    #     try:
+    #         for sub_el in element:
+    #             self.plot_element(sub_el)
+    #
+    #     except TypeError:  # Element is not iterable...
+    #         self.add_shape(shape=element, color=color, visible=visible, layer=0)
+
     def plot(self):
 
         # Does all the required setup and returns False
@@ -3971,62 +4087,65 @@ class FlatCAMGeometry(FlatCAMObj, Geometry):
         param_list = ['cutz', 'depthperpass', 'travelz', 'feedrate', 'feedrate_z', 'feedrate_rapid',
                       'endz', 'toolchangez']
 
-        temp_tools_dict = {}
-        tool_dia_copy = {}
-        data_copy = {}
-        for tooluid_key, tooluid_value in self.tools.items():
-            for dia_key, dia_value in tooluid_value.items():
-                if dia_key == 'tooldia':
-                    dia_value *= factor
-                    dia_value = float('%.4f' % dia_value)
-                    tool_dia_copy[dia_key] = dia_value
-                if dia_key == 'offset':
-                    tool_dia_copy[dia_key] = dia_value
-                if dia_key == 'offset_value':
-                    dia_value *= factor
-                    tool_dia_copy[dia_key] = dia_value
-
-                    # convert the value in the Custom Tool Offset entry in UI
-                    try:
-                        custom_offset = float(self.ui.tool_offset_entry.get_value())
-                    except ValueError:
-                        # try to convert comma to decimal point. if it's still not working error message and return
+        if isinstance(self, FlatCAMGeometry):
+            temp_tools_dict = {}
+            tool_dia_copy = {}
+            data_copy = {}
+            for tooluid_key, tooluid_value in self.tools.items():
+                for dia_key, dia_value in tooluid_value.items():
+                    if dia_key == 'tooldia':
+                        dia_value *= factor
+                        dia_value = float('%.4f' % dia_value)
+                        tool_dia_copy[dia_key] = dia_value
+                    if dia_key == 'offset':
+                        tool_dia_copy[dia_key] = dia_value
+                    if dia_key == 'offset_value':
+                        dia_value *= factor
+                        tool_dia_copy[dia_key] = dia_value
+
+                        # convert the value in the Custom Tool Offset entry in UI
+                        custom_offset = None
                         try:
-                            custom_offset = float(self.ui.tool_offset_entry.get_value().replace(',', '.')
-                            )
+                            custom_offset = float(self.ui.tool_offset_entry.get_value())
                         except ValueError:
-                            self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
-                                                 "use a number.")
-                            return
-
-                    if custom_offset:
-                        custom_offset *= factor
-                        self.ui.tool_offset_entry.set_value(custom_offset)
-
-                if dia_key == 'type':
-                    tool_dia_copy[dia_key] = dia_value
-                if dia_key == 'tool_type':
-                    tool_dia_copy[dia_key] = dia_value
-                if dia_key == 'data':
-                    for data_key, data_value in dia_value.items():
-                        # convert the form fields that are convertible
-                        for param in param_list:
-                            if data_key == param and data_value is not None:
-                                data_copy[data_key] = data_value * factor
-                        # copy the other dict entries that are not convertible
-                        if data_key not in param_list:
-                            data_copy[data_key] = data_value
-                    tool_dia_copy[dia_key] = copy.deepcopy(data_copy)
-                    data_copy.clear()
-
-            temp_tools_dict.update({
-                tooluid_key: copy.deepcopy(tool_dia_copy)
-            })
-            tool_dia_copy.clear()
-
+                            # try to convert comma to decimal point. if it's still not working error message and return
+                            try:
+                                custom_offset = float(self.ui.tool_offset_entry.get_value().replace(',', '.')
+                                )
+                            except ValueError:
+                                self.app.inform.emit("[ERROR_NOTCL]Wrong value format entered, "
+                                                     "use a number.")
+                                return
+                        except TypeError:
+                            pass
+
+                        if custom_offset:
+                            custom_offset *= factor
+                            self.ui.tool_offset_entry.set_value(custom_offset)
+
+                    if dia_key == 'type':
+                        tool_dia_copy[dia_key] = dia_value
+                    if dia_key == 'tool_type':
+                        tool_dia_copy[dia_key] = dia_value
+                    if dia_key == 'data':
+                        for data_key, data_value in dia_value.items():
+                            # convert the form fields that are convertible
+                            for param in param_list:
+                                if data_key == param and data_value is not None:
+                                    data_copy[data_key] = data_value * factor
+                            # copy the other dict entries that are not convertible
+                            if data_key not in param_list:
+                                data_copy[data_key] = data_value
+                        tool_dia_copy[dia_key] = copy.deepcopy(data_copy)
+                        data_copy.clear()
+
+                temp_tools_dict.update({
+                    tooluid_key: copy.deepcopy(tool_dia_copy)
+                })
+                tool_dia_copy.clear()
 
-        self.tools.clear()
-        self.tools = copy.deepcopy(temp_tools_dict)
+            self.tools.clear()
+            self.tools = copy.deepcopy(temp_tools_dict)
 
         # if there is a value in the new tool field then convert that one too
         tooldia = self.ui.addtool_entry.get_value()
@@ -4188,6 +4307,27 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         '''
         self.cnc_tools = {}
 
+        '''
+           This is a dict of dictionaries. Each dict is associated with a tool present in the file. The key is the 
+           diameter of the tools and the value is another dict that will hold the data under the following form:
+              {tooldia:   {
+                          'tool': int,
+                          'nr_drills': int,
+                          'nr_slots': int,
+                          'offset': float,
+                          'data': {} # a dict to hold the parameters
+                          'gcode': "" # a string with the actual GCODE
+                          'gcode_parsed': {} # dictionary holding the CNCJob geometry and type of geometry (cut or move)
+                          'solid_geometry': []
+                          },
+                          ...
+              }
+           It is populated in the FlatCAMExcellon.on_create_cncjob_click() but actually 
+           it's done in camlib.Excellon.generate_from_excellon_by_tool()
+           BEWARE: I rely on the ordered nature of the Python 3.7 dictionary. Things might change ...
+       '''
+        self.exc_cnc_tools = {}
+
         # for now it show if the plot will be done for multi-tool CNCJob (True) or for single tool
         # (like the one in the TCL Command), False
         self.multitool = False
@@ -4223,10 +4363,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob):
         # if the FlatCAM object is Excellon don't build the CNC Tools Table but hide it
         if self.cnc_tools:
             self.ui.cnc_tools_table.show()
-            self.ui.plot_options_label.show()
         else:
             self.ui.cnc_tools_table.hide()
-            self.ui.plot_options_label.hide()
+
 
         offset = 0
         tool_idx = 0

+ 19 - 9
ObjectUI.py

@@ -388,16 +388,12 @@ class ExcellonObjectUI(ObjectUI):
 
         grid0 = QtWidgets.QGridLayout()
         self.custom_box.addLayout(grid0)
-        self.plot_cb = FCCheckBox(label='Plot')
-        self.plot_cb.setToolTip(
-            "Plot (show) this object."
-        )
-        grid0.addWidget(self.plot_cb, 0, 0)
+
         self.solid_cb = FCCheckBox(label='Solid')
         self.solid_cb.setToolTip(
             "Solid circles."
         )
-        grid0.addWidget(self.solid_cb, 0, 1)
+        grid0.addWidget(self.solid_cb, 0, 0)
 
         # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
         # this way I can hide/show the frame
@@ -408,19 +404,31 @@ class ExcellonObjectUI(ObjectUI):
         self.tools_box.setContentsMargins(0, 0, 0, 0)
         self.drills_frame.setLayout(self.tools_box)
 
+        hlay_plot = QtWidgets.QHBoxLayout()
+        self.tools_box.addLayout(hlay_plot)
+
         #### Tools Drills ####
         self.tools_table_label = QtWidgets.QLabel('<b>Tools Table</b>')
         self.tools_table_label.setToolTip(
             "Tools in this Excellon object\n"
             "when are used for drilling."
         )
-        self.tools_box.addWidget(self.tools_table_label)
+        hlay_plot.addWidget(self.tools_table_label)
+
+        # Plot CB
+        self.plot_cb = FCCheckBox('Plot Object')
+        self.plot_cb.setToolTip(
+            "Plot (show) this object."
+        )
+        self.plot_cb.setLayoutDirection(QtCore.Qt.RightToLeft)
+        hlay_plot.addStretch()
+        hlay_plot.addWidget(self.plot_cb)
 
         self.tools_table = FCTable()
         self.tools_box.addWidget(self.tools_table)
 
-        self.tools_table.setColumnCount(5)
-        self.tools_table.setHorizontalHeaderLabels(['#', 'Diameter', 'D', 'S', 'Offset'])
+        self.tools_table.setColumnCount(6)
+        self.tools_table.setHorizontalHeaderLabels(['#', 'Diameter', 'D', 'S', 'Offset', 'P'])
         self.tools_table.setSortingEnabled(False)
 
         self.tools_table.horizontalHeaderItem(0).setToolTip(
@@ -440,6 +448,8 @@ class ExcellonObjectUI(ObjectUI):
             "Some drill bits (the larger ones) need to drill deeper\n"
             "to create the desired exit hole diameter due of the tip shape.\n"
             "The value here can compensate the Cut Z parameter.")
+        self.tools_table.horizontalHeaderItem(5).setToolTip(
+            "Toggle display of the drills for the current tool.")
 
         self.empty_label = QtWidgets.QLabel('')
         self.tools_box.addWidget(self.empty_label)

+ 6 - 1
README.md

@@ -12,11 +12,16 @@ CAD program, and create G-Code for Isolation routing.
 12.02.2019
 
 - whenever a FlatCAM tool is activated, if the notebook side is hidden it will be unhidden
-- reactivated the Voronoi classed
+- reactivated the Voronoi classes
 - added a new parameter named Offset in the Excellon tool table - work in progress
 - finished work on Offset parameter in Excellon Object (Excellon Editor, camlib, FlatCAMObj updated to take this param in consideration)
 - fixed a bug where in Excellon editor when editing a file, a tool was automatically added. That is supposed to happen only for empty newly created Excellon Objects.
 - starting to work on storing the solid_geometry for each tool in part in Excellon Object
+- stored solid_geometry of Excellon object in the self.tools dictionary
+- finished the solid_geometry restore after edit in Excellon Editor
+- finished plotting selection for each tool in the Excellon Tool Table
+- fixed the camlib.Excellon.bounds() function for the new type of Excellon geometry therefore fixed the canvas selection, too
+
 
 10.02.2019
 

+ 23 - 7
camlib.py

@@ -4073,7 +4073,12 @@ class Excellon(Geometry):
         :return: None
         """
         self.solid_geometry = []
+
         try:
+            # clear the solid_geometry in self.tools
+            for tool in self.tools:
+                self.tools[tool]['solid_geometry'][:] = []
+
             for drill in self.drills:
                 # poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0)
                 if drill['tool'] is '':
@@ -4096,7 +4101,7 @@ class Excellon(Geometry):
                 lines_string = LineString([start, stop])
                 poly = lines_string.buffer(slot_tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
                 # self.solid_geometry.append(poly)
-                self.tools[drill['tool']]['solid_geometry'].append(poly)
+                self.tools[slot['tool']]['solid_geometry'].append(poly)
 
         except Exception as e:
             log.debug("Excellon geometry creation failed due of ERROR: %s" % str(e))
@@ -4139,9 +4144,9 @@ class Excellon(Geometry):
         # now it can get bounds for nested lists of objects
 
         log.debug("Excellon() -> bounds()")
-        if self.solid_geometry is None:
-            log.debug("solid_geometry is None")
-            return 0, 0, 0, 0
+        # if self.solid_geometry is None:
+        #     log.debug("solid_geometry is None")
+        #     return 0, 0, 0, 0
 
         def bounds_rec(obj):
             if type(obj) is list:
@@ -4169,8 +4174,19 @@ class Excellon(Geometry):
                 # it's a Shapely object, return it's bounds
                 return obj.bounds
 
-        bounds_coords = bounds_rec(self.solid_geometry)
-        return bounds_coords
+        minx_list = []
+        miny_list = []
+        maxx_list = []
+        maxy_list = []
+
+        for tool in self.tools:
+            minx, miny, maxx, maxy = bounds_rec(self.tools[tool]['solid_geometry'])
+            minx_list.append(minx)
+            miny_list.append(miny)
+            maxx_list.append(maxx)
+            maxy_list.append(maxy)
+
+        return (min(minx_list), min(miny_list), max(maxx_list), max(maxy_list))
 
     def convert_units(self, units):
         """
@@ -5535,7 +5551,7 @@ class CNCjob(Geometry):
                 return "fail"
 
             gobj = self.codes_split(line)
-
+            print(gobj)
             ## Units
             if 'G' in gobj and (gobj['G'] == 20.0 or gobj['G'] == 21.0):
                 self.units = {20.0: "IN", 21.0: "MM"}[gobj['G']]