diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d1f9004..27af8936 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ CHANGELOG for FlatCAM Evo beta ================================================= +29.10.2023 + +- when failing to save a project make sure that we can still close the app correctly +- Milling Tool: when generating CNCJob's from a single_geometry type fixed an issue getting bad bounds values (Inf) which did not allow saving the project that included such a CNCJob object due of failing to serialize such an object +- fixed some redundancy in some RegEx expressions +- minor fixes + 19.10.2023 - fixed preprocessors issue where the start GCode was not added due of trying to access an object that did not exist (because its name was not changed to reflect the changes in other parts on the app) diff --git a/appHandlers/AppIO.py b/appHandlers/AppIO.py index e5044b99..1b966b1e 100644 --- a/appHandlers/AppIO.py +++ b/appHandlers/AppIO.py @@ -1,6 +1,6 @@ from PyQt6 import QtCore, QtGui, QtWidgets -from PyQt6.QtCore import Qt, QSettings +from PyQt6.QtCore import Qt from appEditors.AppExcEditor import AppExcEditor from appEditors.AppGeoEditor import AppGeoEditor @@ -378,7 +378,7 @@ class AppIO(QtCore.QObject): return else: if self.app.use_3d_engine: - write_png(filename, data) + write_png(filename, data) # noqa else: self.app.plotcanvas.figure.savefig(filename) @@ -2198,16 +2198,17 @@ class AppIO(QtCore.QObject): if match: detected_preprocessor = match.group(1) # determine if there is any tool data - match = re.findall(r'^.*Tool:\s*(\d*)\s*->\s*Dia:\s*(\d*\.?\d*)', gcode, re.MULTILINE) - if match: + match_list = re.findall(r'^.*Tool:\s*(\d*)\s*->\s*Dia:\s*(\d*\.?\d*)', gcode, re.MULTILINE) + if match_list: job_obj.tools = {} - for m in match: + for match in match_list: + tool = int(match[0]) if 'excellon' in gcode_origin.lower(): - job_obj.tools[int(m[0])] = { - 'tooldia': float(m[1]), + job_obj.tools[tool] = { + 'tooldia': float(match[1]), 'nr_drills': 0, 'nr_slots': 0, - 'offset_z': 0, + 'offset_z': 0.0, 'data': {'tools_drill_ppname_e': detected_preprocessor} } # if 'geometry' in gcode_origin.lower(): @@ -2223,13 +2224,14 @@ class AppIO(QtCore.QObject): # } job_obj.used_tools = list(job_obj.tools.keys()) # determine if there is any Cut Z data - match = re.findall(r'^.*Tool:\s*(\d*)\s*->\s*Z_Cut:\s*([\-|+]?\d*\.?\d*)', gcode, re.MULTILINE) - if match: - for m in match: + match_list = re.findall(r'^.*Tool:\s*(\d*)\s*->\s*Z_Cut:\s*([\-|+]?\d*\.?\d*)', gcode, re.MULTILINE) + if match_list: + for match in match_list: + tool = int(match[0]) if 'excellon' in gcode_origin.lower(): - if int(m[0]) in job_obj.tools: - job_obj.tools[int(m[0])]['offset_z'] = 0.0 - job_obj.tools[int(m[0])]['data']['tools_drill_cutz'] = float(m[1]) + if tool in job_obj.tools: + job_obj.tools[tool]['offset_z'] = 0.0 + job_obj.tools[tool]['data']['tools_drill_cutz'] = float(match[1]) # if 'geometry' in gcode_origin.lower(): # if int(m[0]) in job_obj.tools: # job_obj.tools[int(m[0])]['data']['tools_mill_cutz'] = float(m[1]) @@ -2777,6 +2779,7 @@ class AppIO(QtCore.QObject): self.log.error( "Failed to serialize file before compression: %s because: %s" % (str(filename), str(e))) self.inform.emit('[ERROR_NOTCL] %s' % _("Failed.")) + self.app.save_in_progress = False return try: @@ -2791,6 +2794,7 @@ class AppIO(QtCore.QObject): except Exception as errrr: self.log.error("Failed to save compressed file: %s because: %s" % (str(filename), str(errrr))) self.inform.emit('[ERROR_NOTCL] %s' % _("Failed.")) + self.app.save_in_progress = False return if project_zipped != b'': @@ -2801,6 +2805,7 @@ class AppIO(QtCore.QObject): else: self.log.error("Failed to save file: %s. Empty binary file.", str(filename)) self.inform.emit('[ERROR_NOTCL] %s' % _("Failed.")) + self.app.save_in_progress = False return else: # Open file @@ -2809,6 +2814,7 @@ class AppIO(QtCore.QObject): except IOError: self.log.error("Failed to open file for saving: %s", str(filename)) self.inform.emit('[ERROR_NOTCL] %s' % _("The object is used by another application.")) + self.app.save_in_progress = False return # Write @@ -2818,6 +2824,7 @@ class AppIO(QtCore.QObject): self.log.error( "Failed to serialize file: %s because: %s" % (str(filename), str(e))) self.inform.emit('[ERROR_NOTCL] %s' % _("Failed.")) + self.app.save_in_progress = False return f.close() @@ -2829,6 +2836,7 @@ class AppIO(QtCore.QObject): if silent is False: self.inform.emit('[ERROR_NOTCL] %s: %s %s' % (_("Failed to verify project file"), str(filename), _("Retry to save it."))) + self.app.save_in_progress = False return try: @@ -2839,6 +2847,7 @@ class AppIO(QtCore.QObject): str(filename), _("Retry to save it."))) # noqa f.close() + self.app.save_in_progress = False return except Exception: if silent is False: @@ -2847,6 +2856,7 @@ class AppIO(QtCore.QObject): str(filename), _("Retry to save it."))) # noqa f.close() + self.app.save_in_progress = False return saved_f.close() diff --git a/appObjects/AppObject.py b/appObjects/AppObject.py index 86f39512..8a831acd 100644 --- a/appObjects/AppObject.py +++ b/appObjects/AppObject.py @@ -161,7 +161,7 @@ class AppObject(QtCore.QObject): # Initialize as per user request # User must take care to implement initialize - # in a thread-safe way as is is likely that we + # in a thread-safe way as is likely that we # have been invoked in a separate thread. t1 = time.time() self.app.log.debug("%f seconds before initialize()." % (t1 - t0)) diff --git a/appPlugins/ToolMilling.py b/appPlugins/ToolMilling.py index d4e1b1e8..b9b7b7b0 100644 --- a/appPlugins/ToolMilling.py +++ b/appPlugins/ToolMilling.py @@ -731,7 +731,7 @@ class ToolMilling(AppTool, Excellon): # we made the decision here what to do with the hidden parameters # some will disable some of the hidden features but other are set by - # other plugins so we hide them but we do not disable (like the `multidepth`) + # other plugins so, we hide them, but we do not disable (like the `multidepth`) # tool_data['tools_mill_multidepth'] = False tool_data['tools_mill_extracut'] = self.app.options["tools_mill_extracut"] tool_data['tools_mill_dwell'] = self.app.options["tools_mill_dwell"] @@ -793,7 +793,7 @@ class ToolMilling(AppTool, Excellon): # we made the decision here what to do with the hidden parameters # some will disable some of the hidden features but other are set by - # other plugins so we hide them but we do not disable (like the `multidepth`) + # other plugins so, we hide them but, we do not disable (like the `multidepth`) # tool_data['tools_mill_multidepth'] = app_defaults['tools_mill_multidepth'] tool_data['tools_mill_extracut'] = app_defaults['tools_mill_extracut'] tool_data['tools_mill_dwell'] = app_defaults['tools_mill_dwell'] @@ -1049,8 +1049,8 @@ class ToolMilling(AppTool, Excellon): # make the diameter column editable for row in range(row_idx): self.ui.tools_table_mill_geo.item(row, 1).setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | - QtCore.Qt.ItemFlag.ItemIsEditable | - QtCore.Qt.ItemFlag.ItemIsEnabled) + QtCore.Qt.ItemFlag.ItemIsEditable | + QtCore.Qt.ItemFlag.ItemIsEnabled) # sort the tool diameter column # self.ui.tools_table_mill_geo.sortItems(1) @@ -1187,7 +1187,7 @@ class ToolMilling(AppTool, Excellon): self.ui.tools_table_mill_exc.setItem(self.tool_row, 3, tool_uid_item) # Number of slots per tool - # if the slot number is zero is better to not clutter the GUI with zero's so we print a space + # if the slot number is zero is better to not clutter the GUI with zero's so, we print a space slot_count_str = '%d' % slot_cnt if slot_cnt > 0 else '' slot_count_item = QtWidgets.QTableWidgetItem(slot_count_str) slot_count_item.setFlags(QtCore.Qt.ItemFlag.ItemIsEnabled) @@ -1838,7 +1838,7 @@ class ToolMilling(AppTool, Excellon): def storage_to_form(self, dict_storage): """ - Will update the GUI with data from the "storage" in this case the dict self.tools + Will update the GUI with data from the "storage" in this case the dict "self.tools" :param dict_storage: A dictionary holding the data relevant for generating Gcode :type dict_storage: dict @@ -1869,7 +1869,7 @@ class ToolMilling(AppTool, Excellon): def form_to_storage(self): """ - Will update the 'storage' attribute which is the dict self.tools with data collected from GUI + Will update the 'storage' attribute which is the dict "self.tools" with data collected from GUI :return: None :rtype: @@ -1882,7 +1882,7 @@ class ToolMilling(AppTool, Excellon): if self.ui.target_radio.get_value() == 'exc': used_tools_table = self.ui.tools_table_mill_exc if used_tools_table.rowCount() == 2: - # there is no tool in tool table so we can't save the GUI elements values to storage + # there is no tool in tool table so, we can't save the GUI elements values to storage # Excellon Tool Table has 2 rows by default return @@ -1890,7 +1890,7 @@ class ToolMilling(AppTool, Excellon): else: used_tools_table = self.ui.tools_table_mill_geo if used_tools_table.rowCount() == 0: - # there is no tool in tool table so we can't save the GUI elements values to storage + # there is no tool in tool table so, we can't save the GUI elements values to storage return self.ui_disconnect() @@ -2012,7 +2012,7 @@ class ToolMilling(AppTool, Excellon): def get_selected_tools_list(self): """ - Returns the keys to the self.tools dictionary corresponding + Returns the keys to the "self.tools" dictionary corresponding to the selections on the tool list in the appGUI. :return: List of tools. @@ -2023,7 +2023,7 @@ class ToolMilling(AppTool, Excellon): def on_apply_param_to_all_clicked(self): if self.ui.tools_table_mill_exc.rowCount() == 0: - # there is no tool in tool table so we can't save the GUI elements values to storage + # there is no tool in tool table so, we can't save the GUI elements values to storage self.app.log.debug("ToolDrilling.on_apply_param_to_all_clicked() --> no tool in Tools Table, aborting.") return @@ -2050,7 +2050,7 @@ class ToolMilling(AppTool, Excellon): self.ui_connect() def on_order_changed(self, order): - if order != 0: # "default" + if order != 0: # "default" choice self.build_ui() def on_tool_add(self, dia=None, new_geo=None): @@ -2119,7 +2119,7 @@ class ToolMilling(AppTool, Excellon): if db_tool_val['data']['tool_target'] != 1: # _('Milling') continue - # if we find a tool with the same diameter in the Tools DB just update it's data + # if we find a tool with the same diameter in the Tools DB just update its data if truncated_tooldia == db_tooldia: tool_found += 1 for d in db_tool_val['data']: @@ -2274,7 +2274,7 @@ class ToolMilling(AppTool, Excellon): def on_tool_from_db_inserted(self, tool): """ - Called from the Tools DB object through a App method when adding a tool from Tools Database + Called from the Tools DB object through an App method when adding a tool from Tools Database :param tool: a dict with the tool data :return: None """ @@ -2377,7 +2377,7 @@ class ToolMilling(AppTool, Excellon): if all_tools is None: if self.ui.tools_table_mill_geo.selectedItems(): for current_row in self.ui.tools_table_mill_geo.selectedItems(): - # sometime the header get selected and it has row number -1 + # sometimes the header get selected, and it has row number -1 # we don't want to do anything with the header :) if current_row.row() < 0: continue @@ -2427,7 +2427,7 @@ class ToolMilling(AppTool, Excellon): if all_tools is None: if self.ui.tools_table_mill_geo.selectedItems(): for current_row in self.ui.tools_table_mill_geo.selectedItems(): - # sometime the header get selected and it has row number -1 + # sometimes the header get selected, and it has row number -1 # we don't want to do anything with the header :) if current_row.row() < 0: continue @@ -2437,8 +2437,8 @@ class ToolMilling(AppTool, Excellon): temp_tools = deepcopy(self.target_obj.tools) for tooluid_key in self.target_obj.tools: if int(tooluid_key) == tooluid_del: - # if the self.tools has only one tool and we delete it then we move the solid_geometry - # as a property of the object otherwise there will be nothing to hold it + # if the "self.tools" has only one tool, and we delete it then we move the + # solid_geometry as a property of the object otherwise there will be nothing to hold it if len(self.target_obj.tools) == 1: self.target_obj.solid_geometry = deepcopy( self.target_obj.tools[tooluid_key]['solid_geometry'] @@ -2480,7 +2480,7 @@ class ToolMilling(AppTool, Excellon): self.app.inform.emit('[success] %s' % _("Tool was deleted in Tool Table.")) obj_active = self.target_obj - # if the object was MultiGeo and now it has no tool at all (therefore no geometry) + # if the object was MultiGeo, and now it has no tool at all (therefore no geometry) # we make it back SingleGeo if self.ui.tools_table_mill_geo.rowCount() <= 0: obj_active.multigeo = False @@ -2508,10 +2508,10 @@ class ToolMilling(AppTool, Excellon): def generate_milling_drills(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False): """ - Will generate an Geometry Object allowing to cut a drill hole instead of drilling it. + Will generate a Geometry Object allowing to cut a drill hole instead of drilling it. Note: This method is a good template for generic operations as - it takes it's options from parameters or otherwise from the + it takes its options from parameters or otherwise from the object's options and returns a (success, msg) tuple as feedback for shell operations. @@ -2613,10 +2613,10 @@ class ToolMilling(AppTool, Excellon): def generate_milling_slots(self, tools=None, outname=None, tooldia=None, plot=False, use_thread=False): """ - Will generate an Geometry Object allowing to cut/mill a slot hole. + Will generate a Geometry Object allowing to cut/mill a slot hole. Note: This method is a good template for generic operations as - it takes it's options from parameters or otherwise from the + it takes its options from parameters or otherwise from the object's options and returns a (success, msg) tuple as feedback for shell operations. @@ -3156,7 +3156,7 @@ class ToolMilling(AppTool, Excellon): pp_geometry_name = tools_dict[tooluid_key]['data']["tools_mill_ppname_g"] spindledir = self.app.options['tools_mill_spindledir'] - tool_solid_geometry = self.solid_geometry + tool_solid_geometry = geo_obj.solid_geometry new_cncjob_obj.coords_decimals = self.app.options["cncjob_coords_decimals"] new_cncjob_obj.fr_decimals = self.app.options["cncjob_fr_decimals"] @@ -3169,7 +3169,7 @@ class ToolMilling(AppTool, Excellon): tool_lst = list(tools_dict.keys()) is_first = True if tooluid_key == tool_lst[0] else False - # it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially + # it seems that the tolerance needs to be a lot lower value than 0.01, and it was hardcoded initially # to a value of 0.0005 which is 20 times less than 0.01 glob_tol = float(self.app.options['global_tolerance']) tol = glob_tol / 20 if self.units.lower() == 'in' else glob_tol @@ -3461,7 +3461,7 @@ class ToolMilling(AppTool, Excellon): new_cncjob_obj.obj_options['type'] = 'Geometry' new_cncjob_obj.obj_options['tool_dia'] = tooldia_val - # it seems that the tolerance needs to be a lot lower value than 0.01 and it was hardcoded initially + # it seems that the tolerance needs to be a lot lower value than 0.01, and it was hardcoded initially # to a value of 0.0005 which is 20 times less than 0.01 glob_tol = float(self.app.options['global_tolerance']) tol = glob_tol / 20 if self.units.lower() == 'in' else glob_tol @@ -3694,8 +3694,8 @@ class ToolMilling(AppTool, Excellon): self.target_obj.plot_element(element=solid_geometry, visible=True) self.target_obj.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 + # make sure that the general plot is disabled if one of the row plots are disabled and + # if all the row plots are enabled also enable the general plot checkbox cb_cnt = 0 total_row = self.ui.tools_table_mill_geo.rowCount() for row in range(total_row): @@ -3714,10 +3714,10 @@ class ToolMilling(AppTool, Excellon): # matplotlib_key_flag = False # events out of the self.app.collection view (it's about Project Tab) are of type int - if type(event) is int: + if isinstance(event, int): key = event # events from the GUI are of type QKeyEvent - elif type(event) == QtGui.QKeyEvent: + elif isinstance(event, QtGui.QKeyEvent): key = event.key() elif isinstance(event, mpl_key_event): # MatPlotLib key events are trickier to interpret than the rest # matplotlib_key_flag = True @@ -3777,7 +3777,7 @@ class ToolMilling(AppTool, Excellon): sel_model = self.ui.exclusion_table.selectionModel() sel_indexes = sel_model.selectedIndexes() - # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + # it will iterate over all indexes which means all items in all columns too, but I'm interested only on rows # so the duplicate rows will not be added sel_rows = set() for idx in sel_indexes: @@ -3794,7 +3794,7 @@ class ToolMilling(AppTool, Excellon): sel_model = self.ui.exclusion_table.selectionModel() sel_indexes = sel_model.selectedIndexes() - # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + # it will iterate over all indexes which means all items in all columns too, but I'm interested only on rows # so the duplicate rows will not be added sel_rows = set() for idx in sel_indexes: @@ -3815,7 +3815,7 @@ class ToolMilling(AppTool, Excellon): sel_model = self.ui.exclusion_table.selectionModel() sel_indexes = sel_model.selectedIndexes() - # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + # it will iterate over all indexes which means all items in all columns too, but I'm interested only on rows sel_rows = set() for idx in sel_indexes: sel_rows.add(idx.row()) @@ -3865,7 +3865,7 @@ class ToolMilling(AppTool, Excellon): sel_model = self.ui.exclusion_table.selectionModel() sel_indexes = sel_model.selectedIndexes() - # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows + # it will iterate over all indexes which means all items in all columns too, but I'm interested only on rows sel_rows = set() for idx in sel_indexes: sel_rows.add(idx.row()) diff --git a/camlib.py b/camlib.py index fbfdd649..0176b98d 100644 --- a/camlib.py +++ b/camlib.py @@ -3375,7 +3375,7 @@ class CNCjob(Geometry): self.xy_toolchange = None else: # either originally it was a string or not, xy_toolchange will be made string - self.xy_toolchange = re.sub('[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None + self.xy_toolchange = re.sub(r'[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None # and now, xy_toolchange is made into a list of floats in format [x, y] if self.xy_toolchange: @@ -3400,7 +3400,7 @@ class CNCjob(Geometry): self.xy_end = None else: # either originally it was a string or not, xy_end will be made string - self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) if self.xy_end else None + self.xy_end = re.sub(r'[()\[\]]', '', str(self.xy_end)) if self.xy_end else None # and now, xy_end is made into a list of floats in format [x, y] if self.xy_end: @@ -3862,7 +3862,7 @@ class CNCjob(Geometry): self.xy_end = None else: # either originally it was a string or not, xy_end will be made string - self.xy_end = re.sub('[\(\)\[\]]', '', str(self.xy_end)) if self.xy_end else None + self.xy_end = re.sub(r'[()\[\]]', '', str(self.xy_end)) if self.xy_end else None # and now, xy_end is made into a list of floats in format [x, y] if self.xy_end: @@ -3882,7 +3882,7 @@ class CNCjob(Geometry): self.xy_toolchange = None else: # either originally it was a string or not, xy_toolchange will be made string - self.xy_toolchange = re.sub('[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None + self.xy_toolchange = re.sub(r'[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None # and now, xy_toolchange is made into a list of floats in format [x, y] if self.xy_toolchange: @@ -4134,7 +4134,7 @@ class CNCjob(Geometry): if self.xy_toolchange == '': self.xy_toolchange = None else: - self.xy_toolchange = re.sub('[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None + self.xy_toolchange = re.sub(r'[()\[\]]', '', str(self.xy_toolchange)) if self.xy_toolchange else None if self.xy_toolchange: self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")] @@ -4149,7 +4149,7 @@ class CNCjob(Geometry): pass # XY_end parameter - self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) if self.xy_end else None + self.xy_end = re.sub(r'[()\[\]]', '', str(self.xy_end)) if self.xy_end else None if self.xy_end and self.xy_end != '': self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")] if self.xy_end and len(self.xy_end) < 2: @@ -4950,7 +4950,7 @@ class CNCjob(Geometry): self.startz = float(startz) if startz is not None else self.app.options["geometry_startz"] self.z_end = float(endz) if endz is not None else self.app.options["geometry_endz"] - self.xy_end = re.sub('[()\[\]]', '', str(endxy)) if endxy else self.app.options["geometry_endxy"] + self.xy_end = re.sub(r'[()\[\]]', '', str(endxy)) if endxy else self.app.options["geometry_endxy"] if self.xy_end and self.xy_end != '': self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")] @@ -4972,7 +4972,7 @@ class CNCjob(Geometry): if toolchangexy == '': self.xy_toolchange = None else: - self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) \ + self.xy_toolchange = re.sub(r'[()\[\]]', '', str(toolchangexy)) \ if toolchangexy else self.app.options["geometry_toolchangexy"] if self.xy_toolchange and self.xy_toolchange != '': @@ -5374,7 +5374,7 @@ class CNCjob(Geometry): self.z_end = float(endz) if endz is not None else self.app.options["tools_mill_endz"] self.xy_end = endxy if endxy != '' and endxy else self.app.options["tools_mill_endxy"] - self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) if self.xy_end else None + self.xy_end = re.sub(r'[()\[\]]', '', str(self.xy_end)) if self.xy_end else None if self.xy_end is not None and self.xy_end != '': self.xy_end = [float(eval(a)) for a in self.xy_end.split(",")] @@ -5397,7 +5397,7 @@ class CNCjob(Geometry): if toolchangexy == '': self.xy_toolchange = None else: - self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) if self.xy_toolchange else None + self.xy_toolchange = re.sub(r'[()\[\]]', '', str(toolchangexy)) if self.xy_toolchange else None if self.xy_toolchange and self.xy_toolchange != '': self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")] @@ -7304,7 +7304,7 @@ class CNCjob(Geometry): cmaxy = max(cmaxy, maxy_) return cminx, cminy, cmaxx, cmaxy else: - # it's a Shapely object, return it's bounds + # it's a Shapely object, return its bounds return obj.bounds if self.multitool is False: