From 60512f09a7e685905a2b1ed0e44f3c45e120a021 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Mon, 2 Sep 2019 21:39:46 +0300 Subject: [PATCH] - in Properties Tool made threaded the calculation of convex_hull area and to work for multi-geo objects - in NCC tool the type of tool that is used is transferred to the Geometry object - in NCC tool the type of isolation done with the tools selected as isolation tools can now be selected and it has also an Edit -> Preferences entry --- FlatCAMApp.py | 2 + README.md | 3 ++ flatcamGUI/FlatCAMGUI.py | 59 +++++++++++++------- flatcamTools/ToolNonCopperClear.py | 87 +++++++++++++++++++++++++++--- flatcamTools/ToolProperties.py | 61 ++++++++++++++------- 5 files changed, 165 insertions(+), 47 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 69b21cae..434c0ddf 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -630,6 +630,7 @@ class App(QtCore.QObject): "tools_ncc_offset_choice": self.ui.tools_defaults_form.tools_ncc_group.ncc_choice_offset_cb, "tools_ncc_offset_value": self.ui.tools_defaults_form.tools_ncc_group.ncc_offset_spinner, "tools_nccref": self.ui.tools_defaults_form.tools_ncc_group.reference_radio, + "tools_nccmilling_type": self.ui.tools_defaults_form.tools_ncc_group.milling_type_radio, # CutOut Tool "tools_cutouttooldia": self.ui.tools_defaults_form.tools_cutout_group.cutout_tooldia_entry, @@ -1012,6 +1013,7 @@ class App(QtCore.QObject): "tools_ncc_offset_choice": False, "tools_ncc_offset_value": 0.0000, "tools_nccref": 'itself', + "tools_nccmilling_type": 'cl', "tools_cutouttooldia": 0.0944882, "tools_cutoutkind": "single", diff --git a/README.md b/README.md index 3810884b..a133ea56 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,9 @@ CAD program, and create G-Code for Isolation routing. - made changes in the Excellon Tools Table to make it more clear that the tools are selected in the # column and not in the Plot column - in Excellon and Gerber Seleted tab made the Plot (mark) columns not selectable - some ToolTips were modified +- in Properties Tool made threaded the calculation of convex_hull area and to work for multi-geo objects +- in NCC tool the type of tool that is used is transferred to the Geometry object +- in NCC tool the type of isolation done with the tools selected as isolation tools can now be selected and it has also an Edit -> Preferences entry 1.09.2019 diff --git a/flatcamGUI/FlatCAMGUI.py b/flatcamGUI/FlatCAMGUI.py index 6eb7e1b1..9742df95 100644 --- a/flatcamGUI/FlatCAMGUI.py +++ b/flatcamGUI/FlatCAMGUI.py @@ -6394,6 +6394,25 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): self.ncc_tool_dia_entry = FCEntry() grid0.addWidget(self.ncc_tool_dia_entry, 0, 1) + # Milling Type Radio Button + self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) + self.milling_type_label.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, + {'label': _('Conv.'), 'value': 'cv'}]) + self.milling_type_radio.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + grid0.addWidget(self.milling_type_label, 1, 0) + grid0.addWidget(self.milling_type_radio, 1, 1) + self.ncc_order_label = QtWidgets.QLabel('%s:' % _('Tool order')) self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n" "'No' --> means that the used order is the one in the tool table\n" @@ -6411,8 +6430,8 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "'Reverse' --> menas that the tools will ordered from big to small\n\n" "WARNING: using rest machining will automatically set the order\n" "in reverse and disable this control.")) - grid0.addWidget(self.ncc_order_label, 1, 0) - grid0.addWidget(self.ncc_order_radio, 1, 1) + grid0.addWidget(self.ncc_order_label, 2, 0) + grid0.addWidget(self.ncc_order_radio, 2, 1) nccoverlabel = QtWidgets.QLabel('%s:' % _('Overlap Rate')) nccoverlabel.setToolTip( @@ -6426,17 +6445,17 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "Higher values = slow processing and slow execution on CNC\n" "due of too many paths.") ) - grid0.addWidget(nccoverlabel, 2, 0) + grid0.addWidget(nccoverlabel, 3, 0) self.ncc_overlap_entry = FloatEntry() - grid0.addWidget(self.ncc_overlap_entry, 2, 1) + grid0.addWidget(self.ncc_overlap_entry, 3, 1) nccmarginlabel = QtWidgets.QLabel('%s:' % _('Margin')) nccmarginlabel.setToolTip( _("Bounding box margin.") ) - grid0.addWidget(nccmarginlabel, 3, 0) + grid0.addWidget(nccmarginlabel, 4, 0) self.ncc_margin_entry = FloatEntry() - grid0.addWidget(self.ncc_margin_entry, 3, 1) + grid0.addWidget(self.ncc_margin_entry, 4, 1) # Method methodlabel = QtWidgets.QLabel('%s:' % _('Method')) @@ -6446,13 +6465,13 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "Seed-based: Outwards from seed.
" "Line-based: Parallel lines.") ) - grid0.addWidget(methodlabel, 4, 0) + grid0.addWidget(methodlabel, 5, 0) self.ncc_method_radio = RadioSet([ {"label": _("Standard"), "value": "standard"}, {"label": _("Seed-based"), "value": "seed"}, {"label": _("Straight lines"), "value": "lines"} ], orientation='vertical', stretch=False) - grid0.addWidget(self.ncc_method_radio, 4, 1) + grid0.addWidget(self.ncc_method_radio, 5, 1) # Connect lines pathconnectlabel = QtWidgets.QLabel('%s:' % _("Connect")) @@ -6460,18 +6479,18 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): _("Draw lines between resulting\n" "segments to minimize tool lifts.") ) - grid0.addWidget(pathconnectlabel, 5, 0) + grid0.addWidget(pathconnectlabel, 6, 0) self.ncc_connect_cb = FCCheckBox() - grid0.addWidget(self.ncc_connect_cb, 5, 1) + grid0.addWidget(self.ncc_connect_cb, 6, 1) contourlabel = QtWidgets.QLabel('%s:' % _("Contour")) contourlabel.setToolTip( _("Cut around the perimeter of the polygon\n" "to trim rough edges.") ) - grid0.addWidget(contourlabel, 6, 0) + grid0.addWidget(contourlabel, 7, 0) self.ncc_contour_cb = FCCheckBox() - grid0.addWidget(self.ncc_contour_cb, 6, 1) + grid0.addWidget(self.ncc_contour_cb, 7, 1) restlabel = QtWidgets.QLabel('%s:' % _("Rest M.")) restlabel.setToolTip( @@ -6483,9 +6502,9 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "no more copper to clear or there are no more tools.\n" "If not checked, use the standard algorithm.") ) - grid0.addWidget(restlabel, 7, 0) + grid0.addWidget(restlabel, 8, 0) self.ncc_rest_cb = FCCheckBox() - grid0.addWidget(self.ncc_rest_cb, 7, 1) + grid0.addWidget(self.ncc_rest_cb, 8, 1) # ## NCC Offset choice self.ncc_offset_choice_label = QtWidgets.QLabel('%s:' % _("Offset")) @@ -6495,9 +6514,9 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "from the copper features.\n" "The value can be between 0 and 10 FlatCAM units.") ) - grid0.addWidget(self.ncc_offset_choice_label, 8, 0) + grid0.addWidget(self.ncc_offset_choice_label, 9, 0) self.ncc_choice_offset_cb = FCCheckBox() - grid0.addWidget(self.ncc_choice_offset_cb, 8, 1) + grid0.addWidget(self.ncc_choice_offset_cb, 9, 1) # ## NCC Offset value self.ncc_offset_label = QtWidgets.QLabel('%s:' % _("Offset value")) @@ -6507,14 +6526,14 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "from the copper features.\n" "The value can be between 0 and 10 FlatCAM units.") ) - grid0.addWidget(self.ncc_offset_label, 9, 0) + grid0.addWidget(self.ncc_offset_label, 10, 0) self.ncc_offset_spinner = FCDoubleSpinner() self.ncc_offset_spinner.set_range(0.00, 10.00) self.ncc_offset_spinner.set_precision(4) self.ncc_offset_spinner.setWrapping(True) self.ncc_offset_spinner.setSingleStep(0.1) - grid0.addWidget(self.ncc_offset_spinner, 9, 1) + grid0.addWidget(self.ncc_offset_spinner, 10, 1) # ## Reference self.reference_radio = RadioSet([{'label': _('Itself'), 'value': 'itself'}, @@ -6529,8 +6548,8 @@ class ToolsNCCPrefGroupUI(OptionsGroupUI): "- 'Reference Object' - will do non copper clearing within the area\n" "specified by another object.") ) - grid0.addWidget(reference_label, 10, 0) - grid0.addWidget(self.reference_radio, 10, 1) + grid0.addWidget(reference_label, 11, 0) + grid0.addWidget(self.reference_radio, 11, 1) self.layout.addStretch() diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index a7922f56..f03d1b48 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -140,6 +140,23 @@ class NonCopperClear(FlatCAMTool, Gerber): "If it's not successful then the non-copper clearing will fail, too.\n" "- Clear -> the regular non-copper clearing.")) + # Milling Type Radio Button + self.milling_type_label = QtWidgets.QLabel('%s:' % _('Milling Type')) + self.milling_type_label.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + self.milling_type_radio = RadioSet([{'label': _('Climb'), 'value': 'cl'}, + {'label': _('Conv.'), 'value': 'cv'}]) + self.milling_type_radio.setToolTip( + _("Milling type when the selected tool is of type: 'iso_op':\n" + "- climb / best for precision milling and to reduce tool usage\n" + "- conventional / useful when there is no backlash compensation") + ) + + # Tool order self.ncc_order_label = QtWidgets.QLabel('%s:' % _('Tool order')) self.ncc_order_label.setToolTip(_("This set the way that the tools in the tools table are used.\n" "'No' --> means that the used order is the one in the tool table\n" @@ -159,9 +176,14 @@ class NonCopperClear(FlatCAMTool, Gerber): "in reverse and disable this control.")) form = QtWidgets.QFormLayout() self.tools_box.addLayout(form) + form.addRow(QtWidgets.QLabel(''), QtWidgets.QLabel('')) + form.addRow(self.milling_type_label, self.milling_type_radio) form.addRow(self.ncc_order_label, self.ncc_order_radio) + self.milling_type_label.hide() + self.milling_type_radio.hide() + # ### Add a new Tool #### self.addtool_entry_lbl = QtWidgets.QLabel('%s:' % _('Tool Dia')) self.addtool_entry_lbl.setToolTip( @@ -397,6 +419,8 @@ class NonCopperClear(FlatCAMTool, Gerber): # store here solid_geometry when there are tool with isolation job self.solid_geometry = [] + self.tool_type_item_options = [] + self.addtool_btn.clicked.connect(self.on_tool_add) self.addtool_entry.returnPressed.connect(self.on_tool_add) self.deltool_btn.clicked.connect(self.on_tool_delete) @@ -463,6 +487,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"]) self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"]) self.reference_radio.set_value(self.app.defaults["tools_nccref"]) + self.milling_type_radio.set_value(self.app.defaults["tools_nccmilling_type"]) # init the working variables self.default_data.clear() @@ -524,7 +549,7 @@ class NonCopperClear(FlatCAMTool, Gerber): 'offset_value': 0.0, 'type': 'Iso', 'tool_type': 'V', - 'operation': 'clear', + 'operation': 'clear_op', 'data': dict(self.default_data), 'solid_geometry': [] } @@ -594,9 +619,9 @@ class NonCopperClear(FlatCAMTool, Gerber): tool_uid_item = QtWidgets.QTableWidgetItem(str(int(tooluid_key))) operation_type = QtWidgets.QComboBox() - operation_type.addItem('iso') + operation_type.addItem('iso_op') operation_type.setStyleSheet('background-color: rgb(255,255,255)') - operation_type.addItem('clear') + operation_type.addItem('clear_op') operation_type.setStyleSheet('background-color: rgb(255,255,255)') op_idx = operation_type.findText(tooluid_value['operation']) operation_type.setCurrentIndex(op_idx) @@ -642,6 +667,10 @@ class NonCopperClear(FlatCAMTool, Gerber): def ui_connect(self): self.tools_table.itemChanged.connect(self.on_tool_edit) + for row in range(self.tools_table.rowCount()): + for col in [2, 4]: + self.tools_table.cellWidget(row, col).currentIndexChanged.connect(self.on_tooltable_cellwidget_change) + def ui_disconnect(self): try: # if connected, disconnect the signal from the slot on item_changed as it creates issues @@ -649,6 +678,13 @@ class NonCopperClear(FlatCAMTool, Gerber): except (TypeError, AttributeError): pass + for row in range(self.tools_table.rowCount()): + for col in [2, 4]: + try: + self.ui.geo_tools_table.cellWidget(row, col).currentIndexChanged.disconnect() + except (TypeError, AttributeError): + pass + def on_combo_box_type(self): obj_type = self.box_combo_type.currentIndex() self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex())) @@ -687,6 +723,40 @@ class NonCopperClear(FlatCAMTool, Gerber): self.ncc_order_label.setDisabled(False) self.ncc_order_radio.setDisabled(False) + def on_tooltable_cellwidget_change(self): + cw = self.sender() + cw_index = self.tools_table.indexAt(cw.pos()) + cw_row = cw_index.row() + cw_col = cw_index.column() + + current_uid = int(self.tools_table.item(cw_row, 3).text()) + + hide_iso_type = True + for row in range(self.tools_table.rowCount()): + if self.tools_table.cellWidget(row, 4).currentText() == 'iso_op': + hide_iso_type = False + break + + if hide_iso_type is False: + self.milling_type_label.show() + self.milling_type_radio.show() + else: + self.milling_type_label.hide() + self.milling_type_radio.hide() + + # if the sender is in the column with index 2 then we update the tool_type key + if cw_col == 2: + tt = cw.currentText() + if tt == 'V': + typ = 'Iso' + else: + typ = "Rough" + + self.ncc_tools[current_uid].update({ + 'type': typ, + 'tool_type': tt, + }) + def on_tool_add(self, dia=None, muted=None): self.ui_disconnect() @@ -748,7 +818,7 @@ class NonCopperClear(FlatCAMTool, Gerber): 'offset_value': 0.0, 'type': 'Iso', 'tool_type': 'V', - 'operation': 'clear', + 'operation': 'clear_op', 'data': dict(self.default_data), 'solid_geometry': [] } @@ -759,6 +829,7 @@ class NonCopperClear(FlatCAMTool, Gerber): def on_tool_edit(self): self.ui_disconnect() + old_tool_dia = '' tool_dias = [] for k, v in self.ncc_tools.items(): for tool_v in v.keys(): @@ -901,7 +972,7 @@ class NonCopperClear(FlatCAMTool, Gerber): "use a number.")) continue - if self.tools_table.cellWidget(x.row(), 4).currentText() == 'iso': + if self.tools_table.cellWidget(x.row(), 4).currentText() == 'iso_op': iso_dia_list.append(tooldia) else: ncc_dia_list.append(tooldia) @@ -1167,7 +1238,7 @@ class NonCopperClear(FlatCAMTool, Gerber): sorted_tools = ncctooldia else: for row in range(self.tools_table.rowCount()): - if self.tools_table.cellWidget(row, 1).currentText() == 'clear': + if self.tools_table.cellWidget(row, 1).currentText() == 'clear_op': sorted_tools.append(float(self.tools_table.item(row, 1).text())) # ############################################################################################################## @@ -1287,7 +1358,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.solid_geometry = ncc_obj.solid_geometry # if milling type is climb then the move is counter-clockwise around features - milling_type = 'cl' + milling_type = self.milling_type_radio.get_value() for tool_iso in isotooldia: new_geometry = [] @@ -1503,7 +1574,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.solid_geometry = ncc_obj.solid_geometry # if milling type is climb then the move is counter-clockwise around features - milling_type = 'cl' + milling_type = self.milling_type_radio.get_value() for tool_iso in isotooldia: new_geometry = [] diff --git a/flatcamTools/ToolProperties.py b/flatcamTools/ToolProperties.py index f733b953..b0a665d3 100644 --- a/flatcamTools/ToolProperties.py +++ b/flatcamTools/ToolProperties.py @@ -21,9 +21,10 @@ if '_' not in builtins.__dict__: class Properties(FlatCAMTool): - toolName = _("Properties") + area_finished = pyqtSignal(float, object) + def __init__(self, app): FlatCAMTool.__init__(self, app) @@ -64,6 +65,8 @@ class Properties(FlatCAMTool): self.vlay.addWidget(self.treeWidget) self.vlay.setStretch(0, 0) + self.area_finished.connect(self.show_area_chull) + def run(self, toggle=True): self.app.report_usage("ToolProperties()") @@ -170,25 +173,38 @@ class Properties(FlatCAMTool): self.addChild(dims, ['%s:' % _('Box Area'), '%.4f %s' % (area, 'in2')], True) if not isinstance(obj, FlatCAMCNCjob): - # calculate and add convex hull area - geo = obj.solid_geometry - if isinstance(geo, MultiPolygon): - env_obj = geo.convex_hull - elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \ - (isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon): - env_obj = cascaded_union(obj.solid_geometry) - env_obj = env_obj.convex_hull - else: - env_obj = cascaded_union(obj.solid_geometry) - env_obj = env_obj.convex_hull - area_chull = env_obj.area - if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm': - area_chull = area_chull / 100 - self.addChild(dims, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'cm2')], True) - else: - area_chull = area_chull - self.addChild(dims, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'in2')], True) + def job_thread(): + proc = self.app.proc_container.new(_("Calculating area ... Please wait.")) + + # calculate and add convex hull area + geo = obj.solid_geometry + if geo: + if isinstance(geo, MultiPolygon): + env_obj = geo.convex_hull + elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \ + (isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon): + env_obj = cascaded_union(obj.solid_geometry) + env_obj = env_obj.convex_hull + else: + env_obj = cascaded_union(obj.solid_geometry) + env_obj = env_obj.convex_hull + + area_chull = env_obj.area + else: + try: + area_chull = [] + for tool in obj.tools: + area_el = cascaded_union(obj.tools[tool]['solid_geometry']).convex_hull + area_chull.append(area_el.area) + area_chull = max(area_chull) + except Exception as e: + area_chull = None + log.debug("Properties.addItems() --> %s" % str(e)) + + self.area_finished.emit(area_chull, dims) + + self.app.worker_task.emit({'fcn': job_thread, 'params': []}) self.addChild(units, ['FlatCAM units:', @@ -294,4 +310,11 @@ class Properties(FlatCAMTool): if column1 is not None: item.setText(1, str(title[1])) + def show_area_chull(self, area, location): + if self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().lower() == 'mm': + area_chull = area / 100 + self.addChild(location, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'cm2')], True) + else: + area_chull = area + self.addChild(location, ['%s:' % _('Convex_Hull Area'), '%.4f %s' % (area_chull, 'in2')], True) # end of file