diff --git a/FlatCAMObj.py b/FlatCAMObj.py index f88e8a24..0d459ed8 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -548,6 +548,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber): # list of rows with apertures plotted self.marked_rows = [] + # Number of decimals to be used by tools in this class + self.decimals = 4 + # Attributes to be included in serialization # Always append to it because it carries contents # from predecessors. @@ -565,6 +568,13 @@ class FlatCAMGerber(FlatCAMObj, Gerber): FlatCAMObj.set_ui(self, ui) FlatCAMApp.App.log.debug("FlatCAMGerber.set_ui()") + self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + + if self.units == 'MM': + self.decimals = 2 + else: + self.decimals = 4 + self.replotApertures.connect(self.on_mark_cb_click_table) self.form_fields.update({ @@ -682,15 +692,15 @@ class FlatCAMGerber(FlatCAMObj, Gerber): if str(self.apertures[ap_code]['type']) == 'R' or str(self.apertures[ap_code]['type']) == 'O': ap_dim_item = QtWidgets.QTableWidgetItem( - '%.4f, %.4f' % (self.apertures[ap_code]['width'] * self.file_units_factor, - self.apertures[ap_code]['height'] * self.file_units_factor + '%.*f, %.*f' % (self.decimals, self.apertures[ap_code]['width'] * self.file_units_factor, + self.decimals, self.apertures[ap_code]['height'] * self.file_units_factor ) ) ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled) elif str(self.apertures[ap_code]['type']) == 'P': ap_dim_item = QtWidgets.QTableWidgetItem( - '%.4f, %.4f' % (self.apertures[ap_code]['diam'] * self.file_units_factor, - self.apertures[ap_code]['nVertices'] * self.file_units_factor) + '%.*f, %.*f' % (self.decimals, self.apertures[ap_code]['diam'] * self.file_units_factor, + self.decimals, self.apertures[ap_code]['nVertices'] * self.file_units_factor) ) ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled) else: @@ -699,9 +709,8 @@ class FlatCAMGerber(FlatCAMObj, Gerber): try: if self.apertures[ap_code]['size'] is not None: - ap_size_item = QtWidgets.QTableWidgetItem('%.4f' % - float(self.apertures[ap_code]['size'] * - self.file_units_factor)) + ap_size_item = QtWidgets.QTableWidgetItem( + '%.*f' % (self.decimals, float(self.apertures[ap_code]['size'] * self.file_units_factor))) else: ap_size_item = QtWidgets.QTableWidgetItem('') except KeyError: @@ -1897,6 +1906,9 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): self.multigeo = False + # Number fo decimals to be used for tools in this class + self.decimals = 4 + # Attributes to be included in serialization # Always append to it because it carries contents # from predecessors. @@ -1944,7 +1956,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): exc.app.log.warning("Failed to copy option.", option) for drill in exc.drills: - exc_tool_dia = float('%.4f' % exc.tools[drill['tool']]['C']) + exc_tool_dia = float('%.*f' % (self.decimals, exc.tools[drill['tool']]['C'])) if exc_tool_dia not in custom_dict_drills: custom_dict_drills[exc_tool_dia] = [drill['point']] @@ -1952,7 +1964,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): custom_dict_drills[exc_tool_dia].append(drill['point']) for slot in exc.slots: - exc_tool_dia = float('%.4f' % exc.tools[slot['tool']]['C']) + exc_tool_dia = float('%.*f' % (self.decimals, exc.tools[slot['tool']]['C'])) if exc_tool_dia not in custom_dict_slots: custom_dict_slots[exc_tool_dia] = [[slot['start'], slot['stop']]] @@ -2045,7 +2057,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): temp_tools[tool_name_temp] = spec_temp for drill in exc_final.drills: - exc_tool_dia = float('%.4f' % exc_final.tools[drill['tool']]['C']) + exc_tool_dia = float('%.*f' % (self.decimals, exc_final.tools[drill['tool']]['C'])) if exc_tool_dia == ordered_dia: temp_drills.append( { @@ -2055,7 +2067,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): ) for slot in exc_final.slots: - slot_tool_dia = float('%.4f' % exc_final.tools[slot['tool']]['C']) + slot_tool_dia = float('%.*f' % (self.decimals, exc_final.tools[slot['tool']]['C'])) if slot_tool_dia == ordered_dia: temp_slots.append( { @@ -2130,10 +2142,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): # Make sure that the drill diameter when in MM is with no more than 2 decimals # There are no drill bits in MM with more than 3 decimals diameter # For INCH the decimals should be no more than 3. There are no drills under 10mils - if self.units == 'MM': - dia = QtWidgets.QTableWidgetItem('%.2f' % (self.tools[tool_no]['C'])) - else: - dia = QtWidgets.QTableWidgetItem('%.4f' % (self.tools[tool_no]['C'])) + + dia = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.tools[tool_no]['C'])) dia.setFlags(QtCore.Qt.ItemIsEnabled) @@ -2148,10 +2158,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): slot_count.setFlags(QtCore.Qt.ItemIsEnabled) try: - if self.units == 'MM': - t_offset = self.tool_offset[float('%.2f' % float(self.tools[tool_no]['C']))] - else: - t_offset = self.tool_offset[float('%.4f' % float(self.tools[tool_no]['C']))] + t_offset = self.tool_offset[float('%.*f' % (self.decimals, float(self.tools[tool_no]['C'])))] except KeyError: t_offset = self.app.defaults['excellon_offset'] @@ -2307,6 +2314,11 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + if self.units == 'MM': + self.decimals = 2 + else: + self.decimals = 4 + self.form_fields.update({ "plot": self.ui.plot_cb, "solid": self.ui.solid_cb, @@ -2347,10 +2359,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): t_default_offset = self.app.defaults["excellon_offset"] if not self.tool_offset: for value in self.tools.values(): - if self.units == 'MM': - dia = float('%.2f' % float(value['C'])) - else: - dia = float('%.4f' % float(value['C'])) + dia = float('%.*f' % (self.decimals, float(value['C']))) self.tool_offset[dia] = t_default_offset # Show/Hide Advanced Options @@ -2407,10 +2416,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): self.is_modified = True row_of_item_changed = self.ui.tools_table.currentRow() - if self.units == 'MM': - dia = float('%.2f' % float(self.ui.tools_table.item(row_of_item_changed, 1).text())) - else: - dia = float('%.4f' % float(self.ui.tools_table.item(row_of_item_changed, 1).text())) + dia = float('%.*f' % (self.decimals, float(self.ui.tools_table.item(row_of_item_changed, 1).text()))) current_table_offset_edited = None if self.ui.tools_table.currentItem() is not None: @@ -2455,8 +2461,21 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): 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() - 1)]) + txt = '' + elem = list() + + for column in range(0, self.ui.tools_table.columnCount() - 1): + try: + txt = self.ui.tools_table.item(x.row(), column).text() + except AttributeError: + try: + txt = self.ui.tools_table.cellWidget(x.row(), column).currentText() + except AttributeError: + pass + elem.append(txt) + table_tools_items.append(deepcopy(elem)) + # table_tools_items.append([self.ui.tools_table.item(x.row(), column).text() + # 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 @@ -2762,8 +2781,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): for tool in tools: # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse - adj_toolstable_tooldia = float('%.4f' % float(tooldia)) - adj_file_tooldia = float('%.4f' % float(self.tools[tool]["C"])) + adj_toolstable_tooldia = float('%.*f' % (self.decimals, float(tooldia))) + adj_file_tooldia = float('%.*f' % (self.decimals, float(self.tools[tool]["C"]))) if adj_toolstable_tooldia > adj_file_tooldia + 0.0001: self.app.inform.emit('[ERROR_NOTCL] %s' % _("Milling tool for SLOTS is larger than hole size. Cancelled.")) @@ -2792,8 +2811,8 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): # we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero" for slot in self.slots: if slot['tool'] in tools: - toolstable_tool = float('%.4f' % float(tooldia)) - file_tool = float('%.4f' % float(self.tools[tool]["C"])) + toolstable_tool = float('%.*f' % (self.decimals, float(tooldia))) + file_tool = float('%.*f' % (self.decimals, float(self.tools[tool]["C"]))) # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse # for the file_tool (tooldia actually) @@ -3337,6 +3356,9 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): self.old_pp_state = self.app.defaults["geometry_multidepth"] self.old_toolchangeg_state = self.app.defaults["geometry_toolchange"] + # Number of decimals to be used for tools in this class + self.decimals = 4 + # Attributes to be included in serialization # Always append to it because it carries contents # from predecessors. @@ -3365,10 +3387,8 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # Make sure that the tool diameter when in MM is with no more than 2 decimals. # There are no tool bits in MM with more than 3 decimals diameter. # For INCH the decimals should be no more than 3. There are no tools under 10mils. - if self.units == 'MM': - dia_item = QtWidgets.QTableWidgetItem('%.2f' % float(tooluid_value['tooldia'])) - else: - dia_item = QtWidgets.QTableWidgetItem('%.4f' % float(tooluid_value['tooldia'])) + + dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooluid_value['tooldia']))) dia_item.setFlags(QtCore.Qt.ItemIsEnabled) @@ -3495,6 +3515,13 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): assert isinstance(self.ui, GeometryObjectUI), \ "Expected a GeometryObjectUI, got %s" % type(self.ui) + self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + + if self.units == 'MM': + self.decimals = 2 + else: + self.decimals = 4 + # populate postprocessor names in the combobox for name in list(self.app.postprocessors.keys()): self.ui.pp_geometry_name_cb.addItem(name) @@ -3584,7 +3611,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): for toold in tools_list: self.tools.update({ self.tooluid: { - 'tooldia': float(toold), + 'tooldia': float('%.*f' % (self.decimals, float(toold))), 'offset': 'Path', 'offset_value': 0.0, 'type': _('Rough'), @@ -3846,10 +3873,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): max_uid = max(tool_uid_list) self.tooluid = max_uid + 1 - if self.units == 'IN': - tooldia = float('%.4f' % tooldia) - else: - tooldia = float('%.2f' % tooldia) + tooldia = float('%.*f' % (self.decimals, tooldia)) # here we actually add the new tool; if there is no tool in the tool table we add a tool with default data # otherwise we add a tool with data copied from last tool @@ -3996,7 +4020,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): _("Wrong value format entered, use a number.")) return - tool_dia = float('%.4f' % d) + tool_dia = float('%.*f' % (self.decimals, d)) tooluid = int(self.ui.geo_tools_table.item(current_row, 5).text()) self.tools[tooluid]['tooldia'] = tool_dia @@ -4203,7 +4227,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): tooldia = float(self.ui.geo_tools_table.item(row, 1).text()) new_cutz = (tooldia - vdia) / (2 * math.tan(math.radians(half_vangle))) - new_cutz = float('%.4f' % -new_cutz) # this value has to be negative + new_cutz = float('%.*f' % (self.decimals, -new_cutz)) # this value has to be negative self.ui.cutz_entry.set_value(new_cutz) # store the new CutZ value into storage (self.tools) @@ -4426,8 +4450,21 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): table_tools_items = [] if self.multigeo: for x in self.ui.geo_tools_table.selectedItems(): - table_tools_items.append([self.ui.geo_tools_table.item(x.row(), column).text() - for column in range(0, self.ui.geo_tools_table.columnCount())]) + elem = list() + txt = '' + + for column in range(0, self.ui.geo_tools_table.columnCount()): + try: + txt = self.ui.geo_tools_table.item(x.row(), column).text() + except AttributeError: + try: + txt = self.ui.geo_tools_table.cellWidget(x.row(), column).currentText() + except AttributeError: + pass + elem.append(txt) + table_tools_items.append(deepcopy(elem)) + # table_tools_items.append([self.ui.geo_tools_table.item(x.row(), column).text() + # for column in range(0, self.ui.geo_tools_table.columnCount())]) else: for x in self.ui.geo_tools_table.selectedItems(): r = [] @@ -4441,9 +4478,10 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): try: txt = self.ui.geo_tools_table.item(x.row(), column).text() except AttributeError: - txt = self.ui.geo_tools_table.cellWidget(x.row(), column).currentText() - except Exception as e: - pass + try: + txt = self.ui.geo_tools_table.cellWidget(x.row(), column).currentText() + except AttributeError: + pass r.append(txt) table_tools_items.append(r) @@ -4625,7 +4663,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): tool_cnt += 1 dia_cnc_dict = deepcopy(tools_dict[tooluid_key]) - tooldia_val = float('%.4f' % float(tools_dict[tooluid_key]['tooldia'])) + tooldia_val = float('%.*f' % (self.decimals, float(tools_dict[tooluid_key]['tooldia']))) dia_cnc_dict.update({ 'tooldia': tooldia_val }) @@ -4779,7 +4817,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): for tooluid_key in list(tools_dict.keys()): tool_cnt += 1 dia_cnc_dict = deepcopy(tools_dict[tooluid_key]) - tooldia_val = float('%.4f' % float(tools_dict[tooluid_key]['tooldia'])) + tooldia_val = float('%.*f' % (self.decimals, float(tools_dict[tooluid_key]['tooldia']))) dia_cnc_dict.update({ 'tooldia': tooldia_val @@ -4789,7 +4827,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # search in the self.tools for the sel_tool_dia and when found see what tooluid has # on the found tooluid in self.tools we also have the solid_geometry that interest us for k, v in self.tools.items(): - if float('%.4f' % float(v['tooldia'])) == tooldia_val: + if float('%.*f' % (self.decimals, float(v['tooldia']))) == tooldia_val: current_uid = int(k) break @@ -5307,7 +5345,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): for dia_key, dia_value in tooluid_value.items(): if dia_key == 'tooldia': dia_value *= factor - dia_value = float('%.4f' % dia_value) + dia_value = float('%.*f' % (self.decimals, dia_value)) tool_dia_copy[dia_key] = dia_value if dia_key == 'offset': tool_dia_copy[dia_key] = dia_value @@ -5578,6 +5616,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): # from predecessors. self.ser_attrs += ['options', 'kind', 'cnc_tools', 'multitool'] + self.decimals = 4 + if self.app.is_legacy is False: self.text_col = self.app.plotcanvas.new_text_collection() self.text_col.enabled = True @@ -5614,10 +5654,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): # Make sure that the tool diameter when in MM is with no more than 2 decimals. # There are no tool bits in MM with more than 2 decimals diameter. # For INCH the decimals should be no more than 4. There are no tools under 10mils. - if self.units == 'MM': - dia_item = QtWidgets.QTableWidgetItem('%.2f' % float(dia_value['tooldia'])) - else: - dia_item = QtWidgets.QTableWidgetItem('%.4f' % float(dia_value['tooldia'])) + + dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(dia_value['tooldia']))) offset_txt = list(str(dia_value['offset'])) offset_txt[0] = offset_txt[0].upper() @@ -5706,6 +5744,13 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): assert isinstance(self.ui, CNCObjectUI), \ "Expected a CNCObjectUI, got %s" % type(self.ui) + self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + + if self.units == "IN": + self.decimals = 4 + else: + self.decimals = 2 + # this signal has to be connected to it's slot before the defaults are populated # the decision done in the slot has to override the default value set bellow self.ui.toolchange_cb.toggled.connect(self.on_toolchange_custom_clicked) @@ -5728,7 +5773,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): self.ui.t_distance_label.show() self.ui.t_distance_entry.setVisible(True) self.ui.t_distance_entry.setDisabled(True) - self.ui.t_distance_entry.set_value('%.4f' % float(self.travel_distance)) + self.ui.t_distance_entry.set_value('%.*f' % (self.decimals, float(self.travel_distance))) self.ui.units_label.setText(str(self.units).lower()) self.ui.units_label.setDisabled(True) @@ -5737,11 +5782,11 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): self.ui.t_time_entry.setDisabled(True) # if time is more than 1 then we have minutes, else we have seconds if self.routing_time > 1: - self.ui.t_time_entry.set_value('%.4f' % math.ceil(float(self.routing_time))) + self.ui.t_time_entry.set_value('%.*f' % (self.decimals, math.ceil(float(self.routing_time)))) self.ui.units_time_label.setText('min') else: time_r = self.routing_time * 60 - self.ui.t_time_entry.set_value('%.4f' % math.ceil(float(time_r))) + self.ui.t_time_entry.set_value('%.*f' % (self.decimals, math.ceil(float(time_r)))) self.ui.units_time_label.setText('sec') self.ui.units_time_label.setDisabled(True) except AttributeError: @@ -6174,7 +6219,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): self.shapes.clear(update=True) for tooluid_key in self.cnc_tools: - tooldia = float('%.4f' % float(self.cnc_tools[tooluid_key]['tooldia'])) + tooldia = float('%.*f' % (self.decimals, float(self.cnc_tools[tooluid_key]['tooldia']))) gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed'] # tool_uid = int(self.ui.cnc_tools_table.item(cw_row, 3).text()) @@ -6227,7 +6272,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): else: # multiple tools usage for tooluid_key in self.cnc_tools: - tooldia = float('%.4f' % float(self.cnc_tools[tooluid_key]['tooldia'])) + tooldia = float('%.*f' % (self.decimals, float(self.cnc_tools[tooluid_key]['tooldia']))) gcode_parsed = self.cnc_tools[tooluid_key]['gcode_parsed'] self.plot2(tooldia=tooldia, obj=self, visible=visible, gcode_parsed=gcode_parsed, kind=kind) self.shapes.redraw() @@ -6266,7 +6311,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): for dia_key, dia_value in tooluid_value.items(): if dia_key == 'tooldia': dia_value *= factor - dia_value = float('%.4f' % dia_value) + dia_value = float('%.*f' % (self.decimals, dia_value)) tool_dia_copy[dia_key] = dia_value if dia_key == 'offset': tool_dia_copy[dia_key] = dia_value diff --git a/README.md b/README.md index 6c68f088..43368293 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ CAD program, and create G-Code for Isolation routing. - fixed the ToolMeasurement geometry not being displayed - fixed a bug in Excellon Editor that crashed the app when editing the first tool added automatically into a new black Excellon file - made sure that if the big mouse cursor is selected, the utility geometry in Excellon Editor has a thicker line width (2 pixels now) so it is visible over the geometry of the mouse cursor +- fixed issue #319 where generating a CNCJob from a geometry made with NCC Tool made the app crash +- replaced in FlatCAM Tools and in FLatCAMObj.py and in Editors all references to hardcoded decimals in string formats for tools with a variable declared in the __init__() 27.09.2019 diff --git a/camlib.py b/camlib.py index 08f76b8b..c229a6de 100644 --- a/camlib.py +++ b/camlib.py @@ -4129,10 +4129,10 @@ class Excellon(Geometry): self.index_per_tool = {} # Dictionary to store the indexed points for each tool # ## IN|MM -> Units are inherited from Geometry - #self.units = units + # self.units = units # Trailing "T" or leading "L" (default) - #self.zeros = "T" + # self.zeros = "T" self.zeros = zeros or self.defaults["zeros"] self.zeros_found = self.zeros self.units_found = self.units @@ -4409,7 +4409,7 @@ class Excellon(Geometry): # object's units. match = self.meas_re.match(eline) if match: - #self.units = {"1": "MM", "2": "IN"}[match.group(1)] + # self.units = {"1": "MM", "2": "IN"}[match.group(1)] # Modified for issue #80 self.convert_units({"1": "MM", "2": "IN"}[match.group(1)]) diff --git a/flatcamEditors/FlatCAMExcEditor.py b/flatcamEditors/FlatCAMExcEditor.py index 1e9257af..5e46c2e0 100644 --- a/flatcamEditors/FlatCAMExcEditor.py +++ b/flatcamEditors/FlatCAMExcEditor.py @@ -2046,6 +2046,9 @@ class FlatCAMExcEditor(QtCore.QObject): self.complete = False + # Number of decimals used by tools in this class + self.decimals = 4 + def make_callback(thetool): def f(): self.on_tool_select(thetool) @@ -2116,16 +2119,18 @@ class FlatCAMExcEditor(QtCore.QObject): # updated units self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + if self.units == "IN": + self.decimals = 4 + else: + self.decimals = 2 + self.olddia_newdia.clear() self.tool2tooldia.clear() # build the self.points_edit dict {dimaters: [point_list]} for drill in self.exc_obj.drills: if drill['tool'] in self.exc_obj.tools: - if self.units == 'IN': - tool_dia = float('%.4f' % self.exc_obj.tools[drill['tool']]['C']) - else: - tool_dia = float('%.2f' % self.exc_obj.tools[drill['tool']]['C']) + tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[drill['tool']]['C'])) try: self.points_edit[tool_dia].append(drill['point']) @@ -2135,10 +2140,7 @@ class FlatCAMExcEditor(QtCore.QObject): # build the self.slot_points_edit dict {dimaters: {"start": Point, "stop": Point}} for slot in self.exc_obj.slots: if slot['tool'] in self.exc_obj.tools: - if self.units == 'IN': - tool_dia = float('%.4f' % self.exc_obj.tools[slot['tool']]['C']) - else: - tool_dia = float('%.2f' % self.exc_obj.tools[slot['tool']]['C']) + tool_dia = float('%.*f' % (self.decimals, self.exc_obj.tools[slot['tool']]['C'])) try: self.slot_points_edit[tool_dia].append({ @@ -2174,10 +2176,7 @@ class FlatCAMExcEditor(QtCore.QObject): # Excellon file has no tool diameter information. In this case do not order the diameter in the table # but use the real order found in the exc_obj.tools for k, v in self.exc_obj.tools.items(): - if self.units == 'IN': - tool_dia = float('%.4f' % v['C']) - else: - tool_dia = float('%.2f' % v['C']) + tool_dia = float('%.*f' % (self.decimals, v['C'])) self.tool2tooldia[int(k)] = tool_dia # Init GUI @@ -2274,12 +2273,9 @@ class FlatCAMExcEditor(QtCore.QObject): self.tools_table_exc.setItem(self.tool_row, 0, idd) # Tool name/id # Make sure that the drill diameter when in MM is with no more than 2 decimals - # There are no drill bits in MM with more than 3 decimals diameter - # For INCH the decimals should be no more than 3. There are no drills under 10mils - if self.units == 'MM': - dia = QtWidgets.QTableWidgetItem('%.2f' % self.olddia_newdia[tool_no]) - else: - dia = QtWidgets.QTableWidgetItem('%.4f' % self.olddia_newdia[tool_no]) + # There are no drill bits in MM with more than 2 decimals diameter + # For INCH the decimals should be no more than 4. There are no drills under 10mils + dia = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, self.olddia_newdia[tool_no])) dia.setFlags(QtCore.Qt.ItemIsEnabled) @@ -2477,9 +2473,9 @@ class FlatCAMExcEditor(QtCore.QObject): else: if isinstance(dia, list): for dd in dia: - deleted_tool_dia_list.append(float('%.4f' % dd)) + deleted_tool_dia_list.append(float('%.*f' % (self.decimals, dd))) else: - deleted_tool_dia_list.append(float('%.4f' % dia)) + deleted_tool_dia_list.append(float('%.*f' % (self.decimals, dia))) except Exception as e: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Select a tool in Tool Table")) diff --git a/flatcamEditors/FlatCAMGeoEditor.py b/flatcamEditors/FlatCAMGeoEditor.py index 4a235daa..29fa105e 100644 --- a/flatcamEditors/FlatCAMGeoEditor.py +++ b/flatcamEditors/FlatCAMGeoEditor.py @@ -3104,6 +3104,9 @@ class FlatCAMGeoEditor(QtCore.QObject): self.rtree_index = rtindex.Index() + # Number of decimals used by tools in this class + self.decimals = 4 + def entry2option(option, entry): try: self.options[option] = float(entry.text()) @@ -3602,6 +3605,14 @@ class FlatCAMGeoEditor(QtCore.QObject): self.replot() + # updated units + self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + + if self.units == "IN": + self.decimals = 4 + else: + self.decimals = 2 + # start with GRID toolbar activated if self.app.ui.grid_snap_btn.isChecked() is False: self.app.ui.grid_snap_btn.trigger() diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index aa16b238..538e1223 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -2955,6 +2955,9 @@ class FlatCAMGrbEditor(QtCore.QObject): self.conversion_factor = 1 + # number of decimals for the tool diameters to be used in this editor + self.decimals = 4 + self.set_ui() log.debug("Initialization of the FlatCAM Gerber Editor is finished ...") @@ -2966,6 +2969,11 @@ class FlatCAMGrbEditor(QtCore.QObject): # updated units self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + if self.units == "IN": + self.decimals = 4 + else: + self.decimals = 2 + self.olddia_newdia.clear() self.tool2tooldia.clear() @@ -3056,15 +3064,15 @@ class FlatCAMGrbEditor(QtCore.QObject): if str(self.storage_dict[ap_code]['type']) == 'R' or str(self.storage_dict[ap_code]['type']) == 'O': ap_dim_item = QtWidgets.QTableWidgetItem( - '%.4f, %.4f' % (self.storage_dict[ap_code]['width'], - self.storage_dict[ap_code]['height'] + '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['width'], + self.decimals, self.storage_dict[ap_code]['height'] ) ) ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled) elif str(self.storage_dict[ap_code]['type']) == 'P': ap_dim_item = QtWidgets.QTableWidgetItem( - '%.4f, %.4f' % (self.storage_dict[ap_code]['diam'], - self.storage_dict[ap_code]['nVertices']) + '%.*f, %.*f' % (self.decimals, self.storage_dict[ap_code]['diam'], + self.decimals, self.storage_dict[ap_code]['nVertices']) ) ap_dim_item.setFlags(QtCore.Qt.ItemIsEnabled) else: @@ -3073,8 +3081,8 @@ class FlatCAMGrbEditor(QtCore.QObject): try: if self.storage_dict[ap_code]['size'] is not None: - ap_size_item = QtWidgets.QTableWidgetItem('%.4f' % float( - self.storage_dict[ap_code]['size'])) + ap_size_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, + float(self.storage_dict[ap_code]['size']))) else: ap_size_item = QtWidgets.QTableWidgetItem('') except KeyError: diff --git a/flatcamTools/ToolNonCopperClear.py b/flatcamTools/ToolNonCopperClear.py index 2e1c2164..5c34ef7b 100644 --- a/flatcamTools/ToolNonCopperClear.py +++ b/flatcamTools/ToolNonCopperClear.py @@ -488,6 +488,11 @@ class NonCopperClear(FlatCAMTool, Gerber): # store here solid_geometry when there are tool with isolation job self.solid_geometry = [] + # the number of decimals for the tools used in this FlatCAM Tool + self.decimals = 4 + + self.select_method = None + self.tool_type_item_options = [] self.addtool_btn.clicked.connect(self.on_tool_add) @@ -546,6 +551,13 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.ui.notebook.setTabText(2, _("NCC Tool")) def set_tool_ui(self): + self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + + if self.units == "IN": + self.decimals = 4 + else: + self.decimals = 2 + self.tools_frame.show() self.ncc_order_radio.set_value(self.app.defaults["tools_nccorder"]) @@ -619,7 +631,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.tooluid += 1 self.ncc_tools.update({ int(self.tooluid): { - 'tooldia': float('%.4f' % tool_dia), + 'tooldia': float('%.*f' % (self.decimals, tool_dia)), 'offset': 'Path', 'offset_value': 0.0, 'type': 'Iso', @@ -651,7 +663,10 @@ class NonCopperClear(FlatCAMTool, Gerber): sorted_tools = [] for k, v in self.ncc_tools.items(): - sorted_tools.append(float('%.4f' % float(v['tooldia']))) + if self.units == "IN": + sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia'])))) + else: + sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia'])))) order = self.ncc_order_radio.get_value() if order == 'fwd': @@ -667,7 +682,7 @@ class NonCopperClear(FlatCAMTool, Gerber): for tool_sorted in sorted_tools: for tooluid_key, tooluid_value in self.ncc_tools.items(): - if float('%.4f' % tooluid_value['tooldia']) == tool_sorted: + if float('%.*f' % (self.decimals, tooluid_value['tooldia'])) == tool_sorted: tool_id += 1 id_ = QtWidgets.QTableWidgetItem('%d' % int(tool_id)) id_.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -675,12 +690,9 @@ class NonCopperClear(FlatCAMTool, Gerber): self.tools_table.setItem(row_no, 0, id_) # Tool name/id # Make sure that the drill diameter when in MM is with no more than 2 decimals - # There are no drill bits in MM with more than 3 decimals diameter - # For INCH the decimals should be no more than 3. There are no drills under 10mils - if self.units == 'MM': - dia = QtWidgets.QTableWidgetItem('%.2f' % tooluid_value['tooldia']) - else: - dia = QtWidgets.QTableWidgetItem('%.4f' % tooluid_value['tooldia']) + # There are no drill bits in MM with more than 2 decimals diameter + # For INCH the decimals should be no more than 4. There are no drills under 10mils + dia = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, tooluid_value['tooldia'])) dia.setFlags(QtCore.Qt.ItemIsEnabled) @@ -921,10 +933,8 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter to add, in Float format.")) return - if self.units == 'MM': - tool_dia = float('%.2f' % tool_dia) - else: - tool_dia = float('%.4f' % tool_dia) + + tool_dia = float('%.*f' % (self.decimals, tool_dia)) if tool_dia == 0: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Please enter a tool diameter with non-zero value, " @@ -948,9 +958,9 @@ class NonCopperClear(FlatCAMTool, Gerber): for k, v in self.ncc_tools.items(): for tool_v in v.keys(): if tool_v == 'tooldia': - tool_dias.append(float('%.4f' % v[tool_v])) + tool_dias.append(float('%.*f' % self.decimals, (v[tool_v]))) - if float('%.4f' % tool_dia) in tool_dias: + if float('%.*f' % (self.decimals, tool_dia)) in tool_dias: if muted is None: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding tool cancelled. Tool already in Tool Table.")) self.tools_table.itemChanged.connect(self.on_tool_edit) @@ -960,7 +970,7 @@ class NonCopperClear(FlatCAMTool, Gerber): self.app.inform.emit('[success] %s' % _("New tool added to Tool Table.")) self.ncc_tools.update({ int(self.tooluid): { - 'tooldia': float('%.4f' % tool_dia), + 'tooldia': float('%.*f' % (self.decimals, tool_dia)), 'offset': 'Path', 'offset_value': 0.0, 'type': 'Iso', @@ -981,7 +991,7 @@ class NonCopperClear(FlatCAMTool, Gerber): for k, v in self.ncc_tools.items(): for tool_v in v.keys(): if tool_v == 'tooldia': - tool_dias.append(float('%.4f' % v[tool_v])) + tool_dias.append(float('%.*f' % (self.decimals, v[tool_v]))) for row in range(self.tools_table.rowCount()): @@ -1641,7 +1651,8 @@ class NonCopperClear(FlatCAMTool, Gerber): break for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool_iso): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, + tool_iso)): current_uid = int(k) # add the solid_geometry to the current too in self.paint_tools dictionary # and then reset the temporary list that stored that solid_geometry @@ -1799,7 +1810,8 @@ class NonCopperClear(FlatCAMTool, Gerber): # find the tooluid associated with the current tool_dia so we know where to add the tool # solid_geometry for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, + tool)): current_uid = int(k) # add the solid_geometry to the current too in self.paint_tools dictionary @@ -1972,7 +1984,8 @@ class NonCopperClear(FlatCAMTool, Gerber): break for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool_iso): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, + tool_iso)): current_uid = int(k) # add the solid_geometry to the current too in self.paint_tools dictionary # and then reset the temporary list that stored that solid_geometry @@ -2172,7 +2185,8 @@ class NonCopperClear(FlatCAMTool, Gerber): # find the tooluid associated with the current tool_dia so we know # where to add the tool solid_geometry for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, + tool)): current_uid = int(k) # add the solid_geometry to the current too in self.paint_tools dictionary diff --git a/flatcamTools/ToolPaint.py b/flatcamTools/ToolPaint.py index 49e931f4..2c2c6deb 100644 --- a/flatcamTools/ToolPaint.py +++ b/flatcamTools/ToolPaint.py @@ -376,6 +376,9 @@ class ToolPaint(FlatCAMTool, Gerber): self.sel_rect = [] + # Number of decimals for tools used in this Tool + self.decimals = 4 + # store here the default data for Geometry Data self.default_data = {} self.default_data.update({ @@ -540,8 +543,10 @@ class ToolPaint(FlatCAMTool, Gerber): self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() if self.units == "IN": + self.decimals = 4 self.addtool_entry.set_value(0.039) else: + self.decimals = 2 self.addtool_entry.set_value(1) self.tools_table.setupContextMenu() @@ -608,7 +613,7 @@ class ToolPaint(FlatCAMTool, Gerber): sorted_tools = [] for k, v in self.paint_tools.items(): - sorted_tools.append(float('%.4f' % float(v['tooldia']))) + sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia'])))) order = self.order_radio.get_value() if order == 'fwd': @@ -624,7 +629,7 @@ class ToolPaint(FlatCAMTool, Gerber): for tool_sorted in sorted_tools: for tooluid_key, tooluid_value in self.paint_tools.items(): - if float('%.4f' % tooluid_value['tooldia']) == tool_sorted: + if float('%.*f' % (self.decimals, tooluid_value['tooldia'])) == tool_sorted: tool_id += 1 id = QtWidgets.QTableWidgetItem('%d' % int(tool_id)) id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -632,12 +637,10 @@ class ToolPaint(FlatCAMTool, Gerber): self.tools_table.setItem(row_no, 0, id) # Tool name/id # Make sure that the drill diameter when in MM is with no more than 2 decimals - # There are no drill bits in MM with more than 3 decimals diameter - # For INCH the decimals should be no more than 3. There are no drills under 10mils - if self.units == 'MM': - dia = QtWidgets.QTableWidgetItem('%.2f' % tooluid_value['tooldia']) - else: - dia = QtWidgets.QTableWidgetItem('%.4f' % tooluid_value['tooldia']) + # There are no drill bits in MM with more than 2 decimals diameter + # For INCH the decimals should be no more than 4. There are no drills under 10mils + + dia = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, tooluid_value['tooldia'])) dia.setFlags(QtCore.Qt.ItemIsEnabled) @@ -736,9 +739,9 @@ class ToolPaint(FlatCAMTool, Gerber): for k, v in self.paint_tools.items(): for tool_v in v.keys(): if tool_v == 'tooldia': - tool_dias.append(float('%.4f' % v[tool_v])) + tool_dias.append(float('%.*f' % (self.decimals, v[tool_v]))) - if float('%.4f' % tool_dia) in tool_dias: + if float('%.*f' % (self.decimals, tool_dia)) in tool_dias: if muted is None: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding tool cancelled. Tool already in Tool Table.")) @@ -750,7 +753,7 @@ class ToolPaint(FlatCAMTool, Gerber): _("New tool added to Tool Table.")) self.paint_tools.update({ int(self.tooluid): { - 'tooldia': float('%.4f' % tool_dia), + 'tooldia': float('%.*f' % (self.decimals, tool_dia)), 'offset': 'Path', 'offset_value': 0.0, 'type': 'Iso', @@ -774,7 +777,7 @@ class ToolPaint(FlatCAMTool, Gerber): for k, v in self.paint_tools.items(): for tool_v in v.keys(): if tool_v == 'tooldia': - tool_dias.append(float('%.4f' % v[tool_v])) + tool_dias.append(float('%.*f' % (self.decimals, v[tool_v]))) for row in range(self.tools_table.rowCount()): try: @@ -1392,7 +1395,7 @@ class ToolPaint(FlatCAMTool, Gerber): for tool_dia in sorted_tools: # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool_dia)): current_uid = int(k) break @@ -1688,7 +1691,7 @@ class ToolPaint(FlatCAMTool, Gerber): # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool_dia)): current_uid = int(k) break @@ -1916,7 +1919,7 @@ class ToolPaint(FlatCAMTool, Gerber): # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool_dia)): current_uid = int(k) break @@ -2162,7 +2165,7 @@ class ToolPaint(FlatCAMTool, Gerber): # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool_dia)): current_uid = int(k) break @@ -2391,7 +2394,7 @@ class ToolPaint(FlatCAMTool, Gerber): # find the tooluid associated with the current tool_dia so we know where to add the tool solid_geometry for k, v in tools_storage.items(): - if float('%.4f' % v['tooldia']) == float('%.4f' % tool_dia): + if float('%.*f' % (self.decimals, v['tooldia'])) == float('%.*f' % (self.decimals, tool_dia)): current_uid = int(k) break diff --git a/flatcamTools/ToolSolderPaste.py b/flatcamTools/ToolSolderPaste.py index 8c1999b0..6e6cbbf7 100644 --- a/flatcamTools/ToolSolderPaste.py +++ b/flatcamTools/ToolSolderPaste.py @@ -402,6 +402,9 @@ class SolderPaste(FlatCAMTool): self.units = '' self.name = "" + # Number of decimals to be used for tools/nozzles in this FlatCAM Tool + self.decimals = 4 + # this will be used in the combobox context menu, for delete entry self.obj_to_be_deleted_name = '' @@ -499,7 +502,7 @@ class SolderPaste(FlatCAMTool): self.tooluid += 1 self.tooltable_tools.update({ int(self.tooluid): { - 'tooldia': float('%.4f' % tool_dia), + 'tooldia': float('%.*f' % (self.decimals, tool_dia)), 'data': deepcopy(self.options), 'solid_geometry': [] } @@ -510,6 +513,11 @@ class SolderPaste(FlatCAMTool): self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value().upper() + if self.units == "IN": + self.decimals = 4 + else: + self.decimals = 2 + for name in list(self.app.postprocessors.keys()): # populate only with postprocessor files that start with 'Paste_' if name.partition('_')[0] != 'Paste': @@ -530,7 +538,7 @@ class SolderPaste(FlatCAMTool): sorted_tools = [] for k, v in self.tooltable_tools.items(): - sorted_tools.append(float('%.4f' % float(v['tooldia']))) + sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia'])))) sorted_tools.sort(reverse=True) n = len(sorted_tools) @@ -539,7 +547,7 @@ class SolderPaste(FlatCAMTool): for tool_sorted in sorted_tools: for tooluid_key, tooluid_value in self.tooltable_tools.items(): - if float('%.4f' % tooluid_value['tooldia']) == tool_sorted: + if float('%.*f' % (self.decimals, tooluid_value['tooldia'])) == tool_sorted: tool_id += 1 id = QtWidgets.QTableWidgetItem('%d' % int(tool_id)) id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -547,12 +555,9 @@ class SolderPaste(FlatCAMTool): self.tools_table.setItem(row_no, 0, id) # Tool name/id # Make sure that the drill diameter when in MM is with no more than 2 decimals - # There are no drill bits in MM with more than 3 decimals diameter - # For INCH the decimals should be no more than 3. There are no drills under 10mils - if self.units == 'MM': - dia = QtWidgets.QTableWidgetItem('%.2f' % tooluid_value['tooldia']) - else: - dia = QtWidgets.QTableWidgetItem('%.4f' % tooluid_value['tooldia']) + # There are no drill bits in MM with more than 2 decimals diameter + # For INCH the decimals should be no more than 4. There are no drills under 10mils + dia = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, tooluid_value['tooldia'])) dia.setFlags(QtCore.Qt.ItemIsEnabled) @@ -791,9 +796,9 @@ class SolderPaste(FlatCAMTool): for k, v in self.tooltable_tools.items(): for tool_v in v.keys(): if tool_v == 'tooldia': - tool_dias.append(float('%.4f' % v[tool_v])) + tool_dias.append(float('%.*f' % (self.decimals, v[tool_v]))) - if float('%.4f' % tool_dia) in tool_dias: + if float('%.*f' % (self.decimals, tool_dia)) in tool_dias: if muted is None: self.app.inform.emit('[WARNING_NOTCL] %s' % _("Adding Nozzle tool cancelled. Tool already in Tool Table.")) @@ -805,7 +810,7 @@ class SolderPaste(FlatCAMTool): _("New Nozzle tool added to Tool Table.")) self.tooltable_tools.update({ int(self.tooluid): { - 'tooldia': float('%.4f' % tool_dia), + 'tooldia': float('%.*f' % (self.decimals, tool_dia)), 'data': deepcopy(self.options), 'solid_geometry': [] } @@ -824,7 +829,7 @@ class SolderPaste(FlatCAMTool): for k, v in self.tooltable_tools.items(): for tool_v in v.keys(): if tool_v == 'tooldia': - tool_dias.append(float('%.4f' % v[tool_v])) + tool_dias.append(float('%.*f' % (self.decimals, v[tool_v]))) for row in range(self.tools_table.rowCount()): @@ -991,7 +996,7 @@ class SolderPaste(FlatCAMTool): for k, v in self.tooltable_tools.items(): # make sure that the tools diameter is more than zero and not zero if float(v['tooldia']) > 0: - sorted_tools.append(float('%.4f' % float(v['tooldia']))) + sorted_tools.append(float('%.*f' % (self.decimals, float(v['tooldia'])))) sorted_tools.sort(reverse=True) if not sorted_tools: @@ -1049,7 +1054,7 @@ class SolderPaste(FlatCAMTool): for tool in sorted_tools: offset = tool / 2 for uid, vl in self.tooltable_tools.items(): - if float('%.4f' % float(vl['tooldia'])) == tool: + if float('%.*f' % (self.decimals, float(vl['tooldia']))) == tool: tooluid = int(uid) break