diff --git a/CHANGELOG.md b/CHANGELOG.md
index 28daca36..4e45312f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ CHANGELOG for FlatCAM beta
20.03.2022
- added a last resort option to load old projects; the result is not guaranteed if the differences are too great
+- updated the code in FlatCAMCNCJob object such that more attributes will be serialized in order for loading a project correctly when it has CNCJob objects
19.03.2022
diff --git a/appObjects/FlatCAMCNCJob.py b/appObjects/FlatCAMCNCJob.py
index be7f9853..192ed882 100644
--- a/appObjects/FlatCAMCNCJob.py
+++ b/appObjects/FlatCAMCNCJob.py
@@ -195,10 +195,13 @@ class CNCJobObject(FlatCAMObj, CNCjob):
# Always append to it because it carries contents
# from predecessors.
self.ser_attrs += [
- 'obj_options', 'kind', 'tools', 'multitool', 'append_snippet',
- 'prepend_snippet', 'gc_header'
+ 'obj_options', 'kind', 'tools', 'multitool', 'append_snippet', 'prepend_snippet', 'gc_header', 'gc_start',
+ 'multigeo', 'travel_distance', 'routing_time', 'used_tools'
]
+ # this is used, so we don't recreate the GCode for loaded objects in set_ui(), it is already there
+ self.is_loaded_from_project = False
+
def build_ui(self):
self.ui_disconnect()
@@ -219,7 +222,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
try:
self.build_excellon_cnc_tools()
except Exception as err:
- self.app.log.error("camlib.CNCJobObject.build_ui -> %s" % str(err))
+ self.app.log.error("appObjects.CNCJobObject.build_ui -> %s" % str(err))
self.ui.exc_cnc_tools_table.show()
self.ui_connect()
@@ -363,11 +366,26 @@ class CNCJobObject(FlatCAMObj, CNCjob):
offset_val = self.app.dec_format(float(dia_value['offset']), self.decimals) + self.z_cut
except KeyError:
offset_val = self.app.dec_format(float(dia_value['offset_z']), self.decimals) + self.z_cut
+ except ValueError:
+ # for older loaded projects
+ offset_val = self.z_cut
+
+ try:
+ nr_drills = int(dia_value['nr_drills'])
+ except (KeyError, ValueError):
+ # for older loaded projects
+ nr_drills = 0
+
+ try:
+ nr_slots = int(dia_value['nr_slots'])
+ except (KeyError, ValueError):
+ # for older loaded projects
+ nr_slots = 0
t_id_item = QtWidgets.QTableWidgetItem('%d' % int(t_id))
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia)))
- nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_drills']))
- nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_slots']))
+ nr_drills_item = QtWidgets.QTableWidgetItem('%d' % nr_drills)
+ nr_slots_item = QtWidgets.QTableWidgetItem('%d' % nr_slots)
cutz_item = QtWidgets.QTableWidgetItem('%f' % offset_val)
t_id_item_2 = QtWidgets.QTableWidgetItem('%d' % int(t_id))
@@ -534,21 +552,18 @@ class CNCJobObject(FlatCAMObj, CNCjob):
# ###################################### END Signal connections ###############################################
# #############################################################################################################
- self.append_snippet = self.app.options['cncjob_append']
- self.prepend_snippet = self.app.options['cncjob_prepend']
-
- if self.append_snippet != '' or self.prepend_snippet != '':
- self.ui.snippets_cb.set_value(True)
-
# On CNCJob object creation, generate the GCode
- preamble = ''
- postamble = ''
- if self.append_snippet != '' or self.prepend_snippet != '':
- preamble = self.prepend_snippet
- postamble = self.append_snippet
+ if self.is_loaded_from_project is False:
+ self.prepend_snippet = self.app.options['cncjob_prepend']
+ self.append_snippet = self.app.options['cncjob_append']
+ self.gc_header = self.gcode_header()
+ else:
+ # this is dealt when loading the project, the header, prepend and append are already loaded
+ # by being 'serr_attrs' attributes
+ pass
- self.gc_header = self.gcode_header()
- gc = self.export_gcode(preamble=preamble, postamble=postamble, to_file=True)
+ gc = self.export_gcode(preamble=self.prepend_snippet, postamble=self.append_snippet, to_file=True,
+ s_code=self.gc_start)
# set the Source File attribute with the calculated GCode
try:
@@ -558,6 +573,9 @@ class CNCJobObject(FlatCAMObj, CNCjob):
# gc is text
self.source_file = gc
+ if self.append_snippet != '' or self.prepend_snippet != '':
+ self.ui.snippets_cb.set_value(True)
+
# Show/Hide Advanced Options
app_mode = self.app.options["global_app_level"]
self.change_level(app_mode)
@@ -719,18 +737,6 @@ class CNCJobObject(FlatCAMObj, CNCjob):
self.ui.name_entry.set_value(new_name)
self.on_name_activate(silent=True)
- # try:
- # if self.ui.snippets_cb.get_value():
- # preamble = self.prepend_snippet
- # postamble = self.append_snippet
- # gc = self.export_gcode(filename, preamble=preamble, postamble=postamble)
- # except Exception as err:
- # log.error("CNCJobObject.export_gcode_handler() --> %s" % str(err))
- # gc = self.export_gcode(filename)
- #
- # if gc == 'fail':
- # return
-
if self.source_file == '':
return 'fail'
@@ -968,7 +974,8 @@ class CNCJobObject(FlatCAMObj, CNCjob):
else:
return 'M02'
- def export_gcode(self, filename=None, preamble='', postamble='', to_file=False, from_tcl=False):
+ def export_gcode(self, filename=None, preamble='', postamble='', to_file=False, from_tcl=False, glob_gcode='',
+ s_code=''):
"""
This will save the GCode from the Gcode object to a file on the OS filesystem
@@ -977,13 +984,12 @@ class CNCJobObject(FlatCAMObj, CNCjob):
:param postamble: a custom Gcode block to be added at the end of the Gcode file
:param to_file: if False then no actual file is saved but the app will know that a file was created
:param from_tcl: True if run from Tcl Shell
+ :param glob_gcode: Passing an object attribute that is used to hold GCode; string
:return: None
"""
- # gcode = ''
- # roland = False
- # hpgl = False
- # isel_icp = False
+ global_gcode = self.gcode if glob_gcode == '' else glob_gcode
+ start_code = self.gc_start if s_code == '' else s_code
include_header = True
if preamble == '':
@@ -1009,9 +1015,16 @@ class CNCJobObject(FlatCAMObj, CNCjob):
try:
include_header = self.app.preprocessors[self.tools[first_key]['data']['tools_mill_ppname_g']]
except KeyError:
- # for older loaded projects
- self.app.log.debug("CNCJobObject.export_gcode() --> old project detected. Results are unreliable.")
- include_header = self.app.preprocessors[self.app.options['tools_mill_ppname_g']]
+ try:
+ # for older loaded projects
+ self.app.log.debug(
+ "CNCJobObject.export_gcode() --> old project detected. Results are unreliable.")
+ include_header = self.app.preprocessors[self.app.options['ppname_g']]
+ except KeyError:
+ # for older loaded projects
+ self.app.log.debug(
+ "CNCJobObject.export_gcode() --> old project detected. Results are unreliable.")
+ include_header = self.app.preprocessors[self.app.options['tools_mill_ppname_g']]
include_header = include_header.include_header
except (TypeError, IndexError):
@@ -1028,20 +1041,23 @@ class CNCJobObject(FlatCAMObj, CNCjob):
].include_header
except KeyError:
# for older loaded projects
- include_header = self.app.preprocessors[
- self.tools[first_key]['data']['ppname_e']
- ].include_header
- except Exception:
- self.app.log.debug("CNCJobObject.export_gcode() --> old project detected. Results are unreliable.")
- # for older loaded projects
- include_header = self.app.preprocessors[
- self.app.options['tools_drill_ppname_e']
- ].include_header
+ try:
+ include_header = self.app.preprocessors[
+ self.tools[first_key]['data']['ppname_e']
+ ].include_header
+ except KeyError:
+ self.app.log.debug(
+ "CNCJobObject.export_gcode() --> old project detected. Results are unreliable.")
+ # for older loaded projects
+ include_header = self.app.preprocessors[
+ self.app.options['tools_drill_ppname_e']
+ ].include_header
except TypeError:
# when self.tools is empty - old projects
include_header = self.app.preprocessors['default'].include_header
gcode = ''
+
if include_header is False:
# detect if using multi-tool and make the Gcode summation correctly for each case
if self.multitool is True:
@@ -1055,19 +1071,19 @@ class CNCJobObject(FlatCAMObj, CNCjob):
except TypeError:
pass
else:
- gcode += self.gcode
+ gcode += global_gcode
- # g = self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + postamble
+ # g = sstart_code + '\n' + preamble + '\n' + gcode + '\n' + postamble
g = ''
end_gcode = self.gcode_footer() if self.app.options['cncjob_footer'] is True else ''
if preamble != '' and postamble != '':
- g = self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
+ g = start_code + '\n' + preamble + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
if preamble == '':
- g = self.gc_start + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
+ g = start_code + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
if postamble == '':
- g = self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + end_gcode
+ g = start_code + '\n' + preamble + '\n' + gcode + '\n' + end_gcode
if preamble == '' and postamble == '':
- g = self.gc_start + '\n' + gcode + '\n' + end_gcode
+ g = start_code + '\n' + gcode + '\n' + end_gcode
else:
# detect if using multi-tool and make the Gcode summation correctly for each case
if self.multitool is True:
@@ -1089,7 +1105,7 @@ class CNCJobObject(FlatCAMObj, CNCjob):
except TypeError:
pass
else:
- gcode += self.gcode
+ gcode += global_gcode
end_gcode = self.gcode_footer() if self.app.options['cncjob_footer'] is True else ''
@@ -1128,51 +1144,19 @@ class CNCJobObject(FlatCAMObj, CNCjob):
processed_body_gcode += gline + '\n'
gcode = processed_body_gcode
- g = self.gc_header + '\n' + self.gc_start + '\n' + preamble + '\n' + \
+ g = self.gc_header + '\n' + start_code + '\n' + preamble + '\n' + \
gcode + '\n' + postamble + end_gcode
else:
- # try:
- # g_idx = gcode.index('G94')
- # if preamble != '' and postamble != '':
- # g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
- # gcode[(g_idx + 3):] + postamble + end_gcode
- # elif preamble == '':
- # g = self.gc_header + gcode[:g_idx + 3] + '\n' + \
- # gcode[(g_idx + 3):] + postamble + end_gcode
- # elif postamble == '':
- # g = self.gc_header + gcode[:g_idx + 3] + '\n' + preamble + '\n' + \
- # gcode[(g_idx + 3):] + end_gcode
- # else:
- # g = self.gc_header + gcode[:g_idx + 3] + gcode[(g_idx + 3):] + end_gcode
- # except ValueError:
- # self.app.inform.emit('[ERROR_NOTCL] %s' %
- # _("G-code does not have a G94 code.\n"
- # "Append Code snippet will not be used.."))
- # g = self.gc_header + '\n' + gcode + postamble + end_gcode
g = ''
if preamble != '' and postamble != '':
- g = self.gc_header + self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + \
+ g = self.gc_header + start_code + '\n' + preamble + '\n' + gcode + '\n' + \
postamble + '\n' + end_gcode
if preamble == '':
- g = self.gc_header + self.gc_start + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
+ g = self.gc_header + start_code + '\n' + gcode + '\n' + postamble + '\n' + end_gcode
if postamble == '':
- g = self.gc_header + self.gc_start + '\n' + preamble + '\n' + gcode + '\n' + end_gcode
+ g = self.gc_header + start_code + '\n' + preamble + '\n' + gcode + '\n' + end_gcode
if preamble == '' and postamble == '':
- g = self.gc_header + self.gc_start + '\n' + gcode + '\n' + end_gcode
-
- # if toolchange custom is used, replace M6 code with the code from the Toolchange Custom Text box
- # if self.ui.toolchange_cb.get_value() is True:
- # # match = self.re_toolchange.search(g)
- # if 'M6' in g:
- # m6_code = self.parse_custom_toolchange_code(self.ui.toolchange_text.get_value())
- # if m6_code is None or m6_code == '':
- # self.app.inform.emit(
- # '[ERROR_NOTCL] %s' % _("Cancelled. The Toolchange Custom code is enabled but it's empty.")
- # )
- # return 'fail'
- #
- # g = g.replace('M6', m6_code)
- # self.app.inform.emit('[success] %s' % _("Toolchange G-code was replaced by a custom code."))
+ g = self.gc_header + start_code + '\n' + gcode + '\n' + end_gcode
lines = StringIO(g)
@@ -1207,34 +1191,6 @@ class CNCJobObject(FlatCAMObj, CNCjob):
else:
return lines
- # def on_toolchange_custom_clicked(self, signal):
- # """
- # Handler for clicking toolchange custom.
- #
- # :param signal:
- # :return:
- # """
- #
- # try:
- # if 'toolchange_custom' not in str(self.obj_options['ppname_e']).lower():
- # if self.ui.toolchange_cb.get_value():
- # self.ui.toolchange_cb.set_value(False)
- # self.app.inform.emit('[WARNING_NOTCL] %s' %
- # _("The used preprocessor file has to have in it's name: 'toolchange_custom'")
- # )
- # except KeyError:
- # try:
- # for key in self.cnc_tools:
- # ppg = self.cnc_tools[key]['data']['ppname_g']
- # if 'toolchange_custom' not in str(ppg).lower():
- # if self.ui.toolchange_cb.get_value():
- # self.ui.toolchange_cb.set_value(False)
- # self.app.inform.emit('[WARNING_NOTCL] %s' %
- # _("The used preprocessor file has to have in it's name: "
- # "'toolchange_custom'"))
- # except KeyError:
- # self.app.inform.emit('[ERROR] %s' % _("There is no preprocessor file."))
-
def get_gcode(self, preamble='', postamble=''):
"""
We need this to be able to get_gcode separately for shell command export_gcode
@@ -1392,7 +1348,6 @@ class CNCJobObject(FlatCAMObj, CNCjob):
gcode_parsed = self.tools[tooluid_key]['gcode_parsed']
self.plot2(tooldia=dia_plot, obj=self, visible=visible, gcode_parsed=gcode_parsed,
kind=kind)
-
self.shapes.redraw()
except (ObjectDeleted, AttributeError):
self.shapes.clear(update=True)
diff --git a/app_Main.py b/app_Main.py
index 6700b6b5..c6846f46 100644
--- a/app_Main.py
+++ b/app_Main.py
@@ -11883,8 +11883,9 @@ class MenuFileHandlers(QtCore.QObject):
if not run_from_arg or not cli or from_tcl is False:
msgbox = FCMessageBox(parent=self.app.ui)
title = _("Legacy Project")
- txt = _("The loaded project was made for an older version.\n"
- "It may not load correctly. Do you want to continue?")
+ txt = _("The project was made with an older app version.\n"
+ "It may not load correctly.\n\n"
+ "Do you want to continue?")
msgbox.setWindowTitle(title) # taskbar still shows it
msgbox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/flatcam_icon128.png'))
msgbox.setText('%s' % title)
@@ -11915,13 +11916,13 @@ class MenuFileHandlers(QtCore.QObject):
if not run_from_arg or not cli or from_tcl is False:
msgbox = FCMessageBox(parent=self.app.ui)
- title = _("Save preferences")
+ title = _("Import Settings")
txt = _("Do you want to import the loaded project settings?")
msgbox.setWindowTitle(title) # taskbar still shows it
msgbox.setWindowIcon(QtGui.QIcon(self.app.resource_location + '/flatcam_icon128.png'))
msgbox.setText('%s' % title)
msgbox.setInformativeText(txt)
- msgbox.setIconPixmap(QtGui.QPixmap(self.app.resource_location + '/save_as.png'))
+ msgbox.setIconPixmap(QtGui.QPixmap(self.app.resource_location + '/import.png'))
bt_yes = msgbox.addButton(_('Yes'), QtWidgets.QMessageBox.ButtonRole.YesRole)
bt_no = msgbox.addButton(_('No'), QtWidgets.QMessageBox.ButtonRole.NoRole)
@@ -11973,17 +11974,39 @@ class MenuFileHandlers(QtCore.QObject):
app_inst.log.error('MenuFileHandlers.open_project() --> ' + str(erro))
return 'fail'
+ # make the 'obj_options' dict a LoudDict
+ try:
+ new_obj_options = LoudDict()
+ new_obj_options.update(new_obj.obj_options)
+ new_obj.obj_options = new_obj_options
+ except AttributeError:
+ new_obj_options = LoudDict()
+ new_obj_options.update(new_obj.options)
+ new_obj.obj_options = new_obj_options
+ except Exception as erro:
+ app_inst.log.error('MenuFileHandlers.open_project() make a LoudDict--> ' + str(erro))
+ return 'fail'
+
# #############################################################################################
# for older projects loading try to convert the 'apertures' or 'cnc_tools' or 'exc_cnc_tools'
- # attributes, if found, to 'tools'
+ # attributes, if found, to 'tools'
# #############################################################################################
# for older loaded projects
if 'apertures' in obj:
- new_obj.__dict__['tools'] = obj['apertures']
- if 'cnc_tools' in obj:
- new_obj.__dict__['tools'] = obj['cnc_tools']
- if 'exc_cnc_tools' in obj:
- new_obj.__dict__['tools'] = obj['exc_cnc_tools']
+ new_obj.tools = obj['apertures']
+ if 'cnc_tools' in obj and obj['cnc_tools']:
+ new_obj.tools = obj['cnc_tools']
+ # new_obj.used_tools = [int(k) for k in new_obj.tools.keys()]
+ # first_key = list(obj['cnc_tools'].keys())[0]
+ # used_preprocessor = obj['cnc_tools'][first_key]['data']['ppname_g']
+ # new_obj.gc_start = new_obj.doformat(self.app.preprocessors[used_preprocessor].start_code)
+ if 'exc_cnc_tools' in obj and obj['exc_cnc_tools']:
+ new_obj.tools = obj['exc_cnc_tools']
+ # add the used_tools (all of them will be used)
+ new_obj.used_tools = [float(k) for k in new_obj.tools.keys()]
+ # add a missing key, 'tooldia' used for plotting CNCJob objects
+ for td in new_obj.tools:
+ new_obj.tools[td]['tooldia'] = float(td)
# #############################################################################################
# #############################################################################################
@@ -12003,18 +12026,19 @@ class MenuFileHandlers(QtCore.QObject):
app_inst.log.error('MenuFileHandlers.open_project() keys to int--> ' + str(erro))
return 'fail'
- # make the 'obj_options' dict a LoudDict
- try:
- new_obj_options = LoudDict()
- new_obj_options.update(new_obj.obj_options)
- new_obj.obj_options = new_obj_options
- except AttributeError:
- new_obj_options = LoudDict()
- new_obj_options.update(new_obj.options)
- new_obj.obj_options = new_obj_options
- except Exception as erro:
- app_inst.log.error('MenuFileHandlers.open_project() make a LoudDict--> ' + str(erro))
- return 'fail'
+ # #############################################################################################
+ # for older loaded projects
+ # ony older CNCJob objects hold those
+ if 'cnc_tools' in obj:
+ new_obj.obj_options['type'] = 'Geometry'
+ if 'exc_cnc_tools' in obj:
+ new_obj.obj_options['type'] = 'Excellon'
+ # #############################################################################################
+
+ if new_obj.kind == 'cncjob':
+ # some attributes are serialized so we need t otake this into consideration in
+ # CNCJob.set_ui()
+ new_obj.is_loaded_from_project = True
worker_task()
# app_inst.worker_task.emit({'fcn': worker_task, 'params': []})