diff --git a/FlatCAMApp.py b/FlatCAMApp.py index ee69524b..7a922115 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -149,7 +149,7 @@ class App(QtCore.QObject): # Emitted by new_object() and passes the new object as argument, plot flag. # on_object_created() adds the object to the collection, plots on appropriate flag # and emits new_object_available. - object_created = QtCore.pyqtSignal(object, bool, bool, bool) + object_created = QtCore.pyqtSignal(object, bool, bool) # Emitted when a object has been changed (like scaled, mirrored) object_changed = QtCore.pyqtSignal(object) @@ -2298,7 +2298,7 @@ class App(QtCore.QObject): # Re-buid the recent items menu self.setup_recent_items() - def new_object(self, kind, name, initialize, active=True, fit=True, plot=True, autoselected=True, overwrite=False): + def new_object(self, kind, name, initialize, active=True, fit=True, plot=True, autoselected=True): """ Creates a new specalized FlatCAMObj and attaches it to the application, this is, updates the GUI accordingly, any other records and plots it. @@ -2325,7 +2325,6 @@ class App(QtCore.QObject): App.log.debug("new_object()") obj_plot = plot obj_autoselected = autoselected - obj_overwrite = overwrite t0 = time.time() # Debug @@ -2414,7 +2413,7 @@ class App(QtCore.QObject): # Move the object to the main thread and let the app know that it is available. obj.moveToThread(QtWidgets.QApplication.instance().thread()) - self.object_created.emit(obj, obj_plot, obj_autoselected, obj_overwrite) + self.object_created.emit(obj, obj_plot, obj_autoselected) return obj @@ -2431,7 +2430,7 @@ class App(QtCore.QObject): self.new_object('geometry', 'new_g', initialize, plot=False) - def on_object_created(self, obj, plot, autoselect, overwrite): + def on_object_created(self, obj, plot, autoselect): """ Event callback for object creation. @@ -2442,7 +2441,7 @@ class App(QtCore.QObject): self.log.debug("on_object_created()") # The Collection might change the name if there is a collision - self.collection.append(obj, overwrite=overwrite) + self.collection.append(obj) # after adding the object to the collection always update the list of objects that are in the collection self.all_objects_list = self.collection.get_list() diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 5d3764fd..be6d8a0b 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -5000,20 +5000,21 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): try: for key in self.cnc_tools: - if self.cnc_tools[key]['data']['ppname_g'] == 'marlin': + ppg = self.cnc_tools[key]['data']['ppname_g'] + if ppg == 'marlin' or ppg == 'Repetier': marlin = True break - if self.cnc_tools[key]['data']['ppname_g'] == 'hpgl': + if ppg == 'hpgl': hpgl = True break - if "toolchange_probe" in self.cnc_tools[key]['data']['ppname_g'].lower(): + if "toolchange_probe" in ppg.lower(): probe_pp = True break except Exception as e: log.debug("FlatCAMCNCJob.gcode_header() error: --> %s" % str(e)) try: - if self.options['ppname_e'] == 'marlin': + if self.options['ppname_e'] == 'marlin' or self.options['ppname_e'] == 'Repetier': marlin = True except Exception as e: log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e)) @@ -5025,7 +5026,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): log.debug("FlatCAMCNCJob.gcode_header(): --> There is no such self.option: %s" % str(e)) if marlin is True: - gcode = ';Marlin G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s\n' % \ + gcode = ';Marlin(Repetier) G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s\n' % \ (str(self.app.version), str(self.app.version_date)) + '\n' gcode += ';Name: ' + str(self.options['name']) + '\n' diff --git a/ObjectCollection.py b/ObjectCollection.py index d2ef99d6..291f3666 100644 --- a/ObjectCollection.py +++ b/ObjectCollection.py @@ -642,7 +642,7 @@ class ObjectCollection(QtCore.QAbstractItemModel): # return QtWidgets.QAbstractItemModel.flags(self, index) - def append(self, obj, active=False, overwrite=False): + def append(self, obj, active=False): FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + " --> OC.append()") name = obj.options["name"] @@ -653,12 +653,6 @@ class ObjectCollection(QtCore.QAbstractItemModel): # FlatCAMApp.App.log.debug("Promised object %s became available." % name) # FlatCAMApp.App.log.debug("%d promised objects remaining." % len(self.promises)) - # first delete the old object - if overwrite: - if name in self.get_names(): - self.set_active(name) - self.delete_active(select_project=False) - # Prevent same name while name in self.get_names(): ## Create a new name diff --git a/README.md b/README.md index 907db5f6..b47d4a37 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,9 @@ CAD program, and create G-Code for Isolation routing. 22.02.2019 -- +- added Repetier postprocessor file +- removed "added ability to regenerate objects (it's actually deletion followed by recreation)" because of the way Python pass parameters to functions by reference instead of copy +- 21.02.2019 diff --git a/camlib.py b/camlib.py index 0686bc0a..2a1f3cd8 100644 --- a/camlib.py +++ b/camlib.py @@ -5723,8 +5723,8 @@ class CNCjob(Geometry): command['Z'] = 1 else: command['Z'] = 0 - elif self.pp_solderpaste is not None: - if 'Paste' in self.pp_solderpaste: + elif self.pp_solderpaste_name is not None: + if 'Paste' in self.pp_solderpaste_name: match_paste = re.search(r"X([\+-]?\d+.[\+-]?\d+)\s*Y([\+-]?\d+.[\+-]?\d+)", gline) if match_paste: command['X'] = float(match_paste.group(1).replace(" ", "")) diff --git a/flatcamTools/ToolSolderPaste.py b/flatcamTools/ToolSolderPaste.py index 92f4a103..fc8fd0ab 100644 --- a/flatcamTools/ToolSolderPaste.py +++ b/flatcamTools/ToolSolderPaste.py @@ -352,7 +352,7 @@ class SolderPaste(FlatCAMTool): # self.gcode_frame.setDisabled(True) # self.save_gcode_frame.setDisabled(True) - self.tools = {} + self.tooltable_tools = {} self.tooluid = 0 self.options = LoudDict() @@ -363,8 +363,8 @@ class SolderPaste(FlatCAMTool): ## Signals self.addtool_btn.clicked.connect(self.on_tool_add) self.deltool_btn.clicked.connect(self.on_tool_delete) - self.soldergeo_btn.clicked.connect(self.on_create_geo) - self.solder_gcode_btn.clicked.connect(self.on_create_gcode) + self.soldergeo_btn.clicked.connect(self.on_create_geo_click) + self.solder_gcode_btn.clicked.connect(self.on_create_gcode_click) self.solder_gcode_view_btn.clicked.connect(self.on_view_gcode) self.solder_gcode_save_btn.clicked.connect(self.on_save_gcode) @@ -424,10 +424,10 @@ class SolderPaste(FlatCAMTool): self.tooluid = 0 - self.tools.clear() + self.tooltable_tools.clear() for tool_dia in dias: self.tooluid += 1 - self.tools.update({ + self.tooltable_tools.update({ int(self.tooluid): { 'tooldia': float('%.4f' % tool_dia), 'data': deepcopy(self.options), @@ -455,7 +455,7 @@ class SolderPaste(FlatCAMTool): self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper() sorted_tools = [] - for k, v in self.tools.items(): + for k, v in self.tooltable_tools.items(): sorted_tools.append(float('%.4f' % float(v['tooldia']))) sorted_tools.sort(reverse=True) @@ -464,7 +464,7 @@ class SolderPaste(FlatCAMTool): tool_id = 0 for tool_sorted in sorted_tools: - for tooluid_key, tooluid_value in self.tools.items(): + for tooluid_key, tooluid_value in self.tooltable_tools.items(): if float('%.4f' % tooluid_value['tooldia']) == tool_sorted: tool_id += 1 id = QtWidgets.QTableWidgetItem('%d' % int(tool_id)) @@ -543,7 +543,7 @@ class SolderPaste(FlatCAMTool): # update the form try: # set the form with data from the newly selected tool - for tooluid_key, tooluid_value in self.tools.items(): + for tooluid_key, tooluid_value in self.tooltable_tools.items(): if int(tooluid_key) == tooluid: self.set_form(deepcopy(tooluid_value['data'])) except Exception as e: @@ -601,7 +601,7 @@ class SolderPaste(FlatCAMTool): current_row = self.tools_table.currentRow() uid = tooluid if tooluid else int(self.tools_table.item(current_row, 2).text()) for key in self.form_fields: - self.tools[uid]['data'].update({ + self.tooltable_tools[uid]['data'].update({ key: self.form_fields[key].get_value() }) @@ -618,7 +618,7 @@ class SolderPaste(FlatCAMTool): """ Will read all the parameters of Solder Paste Tool from the provided val parameter and update the UI :param val: dictionary with values to store in the form - :param_type: dictionary + param_type: dictionary :return: """ @@ -656,9 +656,9 @@ class SolderPaste(FlatCAMTool): self.app.inform.emit("[WARNING_NOTCL] Please enter a tool diameter with non-zero value, in Float format.") return - # construct a list of all 'tooluid' in the self.tools + # construct a list of all 'tooluid' in the self.tooltable_tools tool_uid_list = [] - for tooluid_key in self.tools: + for tooluid_key in self.tooltable_tools: tool_uid_item = int(tooluid_key) tool_uid_list.append(tool_uid_item) @@ -670,7 +670,7 @@ class SolderPaste(FlatCAMTool): self.tooluid = int(max_uid + 1) tool_dias = [] - for k, v in self.tools.items(): + 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])) @@ -683,7 +683,7 @@ class SolderPaste(FlatCAMTool): else: if muted is None: self.app.inform.emit("[success] New Nozzle tool added to Tool Table.") - self.tools.update({ + self.tooltable_tools.update({ int(self.tooluid): { 'tooldia': float('%.4f' % tool_dia), 'data': deepcopy(self.options), @@ -697,7 +697,7 @@ class SolderPaste(FlatCAMTool): self.ui_disconnect() tool_dias = [] - for k, v in self.tools.items(): + 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])) @@ -719,13 +719,13 @@ class SolderPaste(FlatCAMTool): # identify the tool that was edited and get it's tooluid if new_tool_dia not in tool_dias: - self.tools[tooluid]['tooldia'] = new_tool_dia + self.tooltable_tools[tooluid]['tooldia'] = new_tool_dia self.app.inform.emit("[success] Nozzle tool from Tool Table was edited.") self.build_ui() return else: # identify the old tool_dia and restore the text in tool table - for k, v in self.tools.items(): + for k, v in self.tooltable_tools.items(): if k == tooluid: old_tool_dia = v['tooldia'] break @@ -739,7 +739,7 @@ class SolderPaste(FlatCAMTool): deleted_tools_list = [] if all: - self.tools.clear() + self.tooltable_tools.clear() self.build_ui() return @@ -752,7 +752,7 @@ class SolderPaste(FlatCAMTool): deleted_tools_list.append(rows_to_delete) for t in deleted_tools_list: - self.tools.pop(t, None) + self.tooltable_tools.pop(t, None) self.build_ui() return @@ -766,7 +766,7 @@ class SolderPaste(FlatCAMTool): deleted_tools_list.append(tooluid_del) for t in deleted_tools_list: - self.tools.pop(t, None) + self.tooltable_tools.pop(t, None) except AttributeError: self.app.inform.emit("[WARNING_NOTCL] Delete failed. Select a Nozzle tool to delete.") @@ -795,31 +795,34 @@ class SolderPaste(FlatCAMTool): def distance(pt1, pt2): return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) - def on_create_geo(self): - - proc = self.app.proc_container.new("Creating Solder Paste dispensing geometry.") + def on_create_geo_click(self, signal): name = self.obj_combo.currentText() - if name == '': self.app.inform.emit("[WARNING_NOTCL] No SolderPaste mask Gerber object loaded.") return + obj = self.app.collection.get_by_name(name) # update the self.options self.read_form_to_options() - obj = self.app.collection.get_by_name(name) + self.on_create_geo(name=name, work_object=obj) - if type(obj.solid_geometry) is not list and type(obj.solid_geometry) is not MultiPolygon: - obj.solid_geometry = [obj.solid_geometry] + def on_create_geo(self, name, work_object): + proc = self.app.proc_container.new("Creating Solder Paste dispensing geometry.") + obj = work_object # Sort tools in descending order sorted_tools = [] - for k, v in self.tools.items(): + 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.sort(reverse=True) + if not sorted_tools: + self.app.inform.emit("[WARNING_NOTCL] No Nozzle tools in the tool table.") + return 'fail' + def geo_init(geo_obj, app_obj): geo_obj.options.update(self.options) geo_obj.solid_geometry = [] @@ -830,7 +833,6 @@ class SolderPaste(FlatCAMTool): geo_obj.special_group = 'solder_paste_tool' def solder_line(p, offset): - xmin, ymin, xmax, ymax = p.bounds min = [xmin, ymin] @@ -875,21 +877,16 @@ class SolderPaste(FlatCAMTool): rest_geo = [] tooluid = 1 - if not sorted_tools: - self.app.inform.emit("[WARNING_NOTCL] No Nozzle tools in the tool table.") - return 'fail' - for tool in sorted_tools: offset = tool / 2 - - for uid, v in self.tools.items(): + for uid, v in self.tooltable_tools.items(): if float('%.4f' % float(v['tooldia'])) == tool: tooluid = int(uid) break geo_obj.tools[tooluid] = {} geo_obj.tools[tooluid]['tooldia'] = tool - geo_obj.tools[tooluid]['data'] = self.tools[tooluid]['data'] + geo_obj.tools[tooluid]['data'] = deepcopy(self.tooltable_tools[tooluid]['data']) geo_obj.tools[tooluid]['solid_geometry'] = [] geo_obj.tools[tooluid]['offset'] = 'Path' geo_obj.tools[tooluid]['offset_value'] = 0.0 @@ -933,7 +930,7 @@ class SolderPaste(FlatCAMTool): def job_thread(app_obj): try: - app_obj.new_object("geometry", name + "_solderpaste", geo_init, overwrite=True) + app_obj.new_object("geometry", name + "_solderpaste", geo_init) except Exception as e: proc.done() traceback.print_stack() @@ -945,9 +942,126 @@ class SolderPaste(FlatCAMTool): self.app.collection.promise(name) # Background - self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app.paste_tool]}) # self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab) + def on_create_gcode_click(self, signal): + name = self.geo_obj_combo.currentText() + obj = self.app.collection.get_by_name(name) + + if obj.special_group != 'solder_paste_tool': + self.app.inform.emit("[WARNING_NOTCL]This Geometry can't be processed. NOT a solder_paste_tool geometry.") + return 'fail' + + a = 0 + for tooluid_key in obj.tools: + if obj.tools[tooluid_key]['solid_geometry'] is None: + a += 1 + if a == len(obj.tools): + self.app.inform.emit('[ERROR_NOTCL]Cancelled. Empty file, it has no geometry...') + return 'fail' + + # use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia + originar_name = obj.options['name'].rpartition('_')[0] + outname = "%s_%s" % (originar_name, 'cnc_solderpaste') + + self.on_create_gcode(name=outname, workobject=obj) + + def on_create_gcode(self, name, workobject, use_thread=True): + """ + Creates a multi-tool CNCJob out of this Geometry object. + :return: None + """ + + obj = workobject + + try: + xmin = obj.options['xmin'] + ymin = obj.options['ymin'] + xmax = obj.options['xmax'] + ymax = obj.options['ymax'] + except Exception as e: + log.debug("FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s\n" % str(e)) + msg = "[ERROR] An internal error has ocurred. See shell.\n" + msg += 'FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s' % str(e) + msg += traceback.format_exc() + self.app.inform.emit(msg) + return + + # Object initialization function for app.new_object() + # RUNNING ON SEPARATE THREAD! + def job_init(job_obj, app_obj): + assert isinstance(job_obj, FlatCAMCNCjob), \ + "Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj) + + tool_cnc_dict = {} + + # this turn on the FlatCAMCNCJob plot for multiple tools + job_obj.multitool = True + job_obj.multigeo = True + job_obj.cnc_tools.clear() + job_obj.special_group = 'solder_paste_tool' + + job_obj.options['xmin'] = xmin + job_obj.options['ymin'] = ymin + job_obj.options['xmax'] = xmax + job_obj.options['ymax'] = ymax + + for tooluid_key, tooluid_value in obj.tools.items(): + app_obj.progress.emit(20) + + # find the tool_dia associated with the tooluid_key + tool_dia = tooluid_value['tooldia'] + tool_cnc_dict = deepcopy(tooluid_value) + + job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"] + job_obj.fr_decimals = self.app.defaults["cncjob_fr_decimals"] + job_obj.tool = int(tooluid_key) + + # Propagate options + job_obj.options["tooldia"] = tool_dia + job_obj.options['tool_dia'] = tool_dia + + ### CREATE GCODE ### + res = job_obj.generate_gcode_from_solderpaste_geo(**tooluid_value) + + if res == 'fail': + log.debug("FlatCAMGeometry.mtool_gen_cncjob() --> generate_from_geometry2() failed") + return 'fail' + else: + tool_cnc_dict['gcode'] = res + + ### PARSE GCODE ### + tool_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() + + # TODO this serve for bounding box creation only; should be optimized + tool_cnc_dict['solid_geometry'] = cascaded_union([geo['geom'] for geo in tool_cnc_dict['gcode_parsed']]) + + # tell gcode_parse from which point to start drawing the lines depending on what kind of + # object is the source of gcode + job_obj.toolchange_xy_type = "geometry" + app_obj.progress.emit(80) + + job_obj.cnc_tools.update({ + tooluid_key: deepcopy(tool_cnc_dict) + }) + tool_cnc_dict.clear() + + if use_thread: + # To be run in separate thread + def job_thread(app_obj): + with self.app.proc_container.new("Generating CNC Code"): + if app_obj.new_object("cncjob", name, job_init) != 'fail': + app_obj.inform.emit("[success]ToolSolderPaste CNCjob created: %s" % name) + app_obj.progress.emit(100) + + # Create a promise with the name + self.app.collection.promise(name) + # Send to worker + self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + else: + self.app.new_object("cncjob", name, job_init) + def on_view_gcode(self): time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now()) @@ -1065,122 +1179,6 @@ class SolderPaste(FlatCAMTool): self.app.file_saved.emit("gcode", filename) self.app.inform.emit("[success] Solder paste dispenser GCode file saved to: %s" % filename) - def on_create_gcode(self, signal, use_thread=True): - """ - Creates a multi-tool CNCJob out of this Geometry object. - :return: None - """ - - name = self.geo_obj_combo.currentText() - obj = self.app.collection.get_by_name(name) - - if obj.special_group != 'solder_paste_tool': - self.app.inform.emit("[WARNING_NOTCL]This Geometry can't be processed. NOT a solder_paste_tool geometry.") - return - - offset_str = '' - multitool_gcode = '' - - # use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia - originar_name = obj.options['name'].rpartition('_')[0] - outname = "%s_%s" % (originar_name, '_cnc_solderpaste') - - try: - xmin = obj.options['xmin'] - ymin = obj.options['ymin'] - xmax = obj.options['xmax'] - ymax = obj.options['ymax'] - except Exception as e: - log.debug("FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s\n" % str(e)) - msg = "[ERROR] An internal error has ocurred. See shell.\n" - msg += 'FlatCAMObj.FlatCAMGeometry.mtool_gen_cncjob() --> %s' % str(e) - msg += traceback.format_exc() - self.app.inform.emit(msg) - return - - - # Object initialization function for app.new_object() - # RUNNING ON SEPARATE THREAD! - def job_init(job_obj, app_obj): - assert isinstance(job_obj, FlatCAMCNCjob), \ - "Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj) - - tool_cnc_dict = {} - - # this turn on the FlatCAMCNCJob plot for multiple tools - job_obj.multitool = True - job_obj.multigeo = True - job_obj.cnc_tools.clear() - job_obj.special_group = 'solder_paste_tool' - - job_obj.options['xmin'] = xmin - job_obj.options['ymin'] = ymin - job_obj.options['xmax'] = xmax - job_obj.options['ymax'] = ymax - - a = 0 - for tooluid_key in obj.tools: - if obj.tools[tooluid_key]['solid_geometry'] is None: - a += 1 - if a == len(obj.tools): - self.app.inform.emit('[ERROR_NOTCL]Cancelled. Empty file, it has no geometry...') - return 'fail' - - for tooluid_key, tooluid_value in obj.tools.items(): - app_obj.progress.emit(20) - - # find the tool_dia associated with the tooluid_key - tool_dia = tooluid_value['tooldia'] - tool_cnc_dict = deepcopy(tooluid_value) - - job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"] - job_obj.fr_decimals = self.app.defaults["cncjob_fr_decimals"] - job_obj.tool = int(tooluid_key) - - # Propagate options - job_obj.options["tooldia"] = tool_dia - job_obj.options['tool_dia'] = tool_dia - - ### CREATE GCODE ### - res = job_obj.generate_gcode_from_solderpaste_geo(**tooluid_value) - - if res == 'fail': - log.debug("FlatCAMGeometry.mtool_gen_cncjob() --> generate_from_geometry2() failed") - return 'fail' - else: - tool_cnc_dict['gcode'] = res - - ### PARSE GCODE ### - tool_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() - - # TODO this serve for bounding box creation only; should be optimized - tool_cnc_dict['solid_geometry'] = cascaded_union([geo['geom'] for geo in tool_cnc_dict['gcode_parsed']]) - - # tell gcode_parse from which point to start drawing the lines depending on what kind of - # object is the source of gcode - job_obj.toolchange_xy_type = "geometry" - app_obj.progress.emit(80) - - job_obj.cnc_tools.update({ - tooluid_key: deepcopy(tool_cnc_dict) - }) - tool_cnc_dict.clear() - - if use_thread: - # To be run in separate thread - def job_thread(app_obj): - with self.app.proc_container.new("Generating CNC Code"): - if app_obj.new_object("cncjob", outname, job_init, overwrite=True) != 'fail': - app_obj.inform.emit("[success]ToolSolderPaste CNCjob created: %s" % outname) - app_obj.progress.emit(100) - - # Create a promise with the name - self.app.collection.promise(outname) - # Send to worker - self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) - else: - self.app.new_object("cncjob", outname, job_init, overwrite=True) - def reset_fields(self): self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) self.geo_obj_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex())) diff --git a/postprocessors/Repetier.py b/postprocessors/Repetier.py new file mode 100644 index 00000000..9e4bb8fb --- /dev/null +++ b/postprocessors/Repetier.py @@ -0,0 +1,198 @@ +from FlatCAMPostProc import * + + +class Repetier(FlatCAMPostProc): + + coordinate_format = "%.*f" + feedrate_format = '%.*f' + feedrate_rapid_format = feedrate_format + + def start_code(self, p): + units = ' ' + str(p['units']).lower() + coords_xy = p['toolchange_xy'] + gcode = '' + + xmin = '%.*f' % (p.coords_decimals, p['options']['xmin']) + xmax = '%.*f' % (p.coords_decimals, p['options']['xmax']) + ymin = '%.*f' % (p.coords_decimals, p['options']['ymin']) + ymax = '%.*f' % (p.coords_decimals, p['options']['ymax']) + + if str(p['options']['type']) == 'Geometry': + gcode += ';TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n' + '\n' + + gcode += ';Feedrate: ' + str(p['feedrate']) + units + '/min' + '\n' + + if str(p['options']['type']) == 'Geometry': + gcode += ';Feedrate_Z: ' + str(p['feedrate_z']) + units + '/min' + '\n' + + gcode += ';Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n' + gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n' + + if str(p['options']['type']) == 'Geometry': + if p['multidepth'] is True: + gcode += ';DepthPerCut: ' + str(p['depthpercut']) + units + ' <=>' + \ + str(math.ceil(abs(p['z_cut']) / p['depthpercut'])) + ' passes' + '\n' + + gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n' + gcode += ';Z Toolchange: ' + str(p['toolchangez']) + units + '\n' + + if coords_xy is not None: + gcode += ';X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + '\n' + else: + gcode += ';X,Y Toolchange: ' + "None" + units + '\n' + + gcode += ';Z Start: ' + str(p['startz']) + units + '\n' + gcode += ';Z End: ' + str(p['endz']) + units + '\n' + gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n' + + if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry': + gcode += ';Postprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n' + else: + gcode += ';Postprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n' + '\n' + + gcode += ';X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n' + gcode += ';Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n\n' + + gcode += ';Spindle Speed: ' + str(p['spindlespeed']) + ' RPM' + '\n' + '\n' + + gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n" + gcode += 'G90\n' + + return gcode + + def startz_code(self, p): + if p.startz is not None: + return 'G0 Z' + self.coordinate_format % (p.coords_decimals, p.startz) + else: + return '' + + def lift_code(self, p): + return 'G0 Z' + self.coordinate_format%(p.coords_decimals, p.z_move) + " " + self.feedrate_rapid_code(p) + + def down_code(self, p): + return 'G1 Z' + self.coordinate_format%(p.coords_decimals, p.z_cut) + " " + self.end_feedrate_code(p) + + def toolchange_code(self, p): + toolchangez = p.toolchangez + toolchangexy = p.toolchange_xy + f_plunge = p.f_plunge + gcode = '' + + if toolchangexy is not None: + toolchangex = toolchangexy[0] + toolchangey = toolchangexy[1] + + no_drills = 1 + + if int(p.tool) == 1 and p.startz is not None: + toolchangez = p.startz + + if p.units.upper() == 'MM': + toolC_formatted = format(p.toolC, '.2f') + else: + toolC_formatted = format(p.toolC, '.4f') + + if str(p['options']['type']) == 'Excellon': + for i in p['options']['Tools_in_use']: + if i[0] == p.tool: + no_drills = i[2] + + if toolchangexy is not None: + gcode = """ +G0 Z{toolchangez} +G0 X{toolchangex} Y{toolchangey} +M84 +@pause Change to Tool Dia = {toolC}, Total drills for tool T{tool} = {t_drills} +""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex), + toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey), + toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez), + tool=int(p.tool), + t_drills=no_drills, + toolC=toolC_formatted) + else: + gcode = """ +G0 Z{toolchangez} +M84 +@pause Change to Tool Dia = {toolC}, Total drills for tool T{tool} = {t_drills} +""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez), + tool=int(p.tool), + t_drills=no_drills, + toolC=toolC_formatted) + + if f_plunge is True: + gcode += '\nG0 Z%.*f' % (p.coords_decimals, p.z_move) + + return gcode + + else: + if toolchangexy is not None: + gcode = """ +G0 Z{toolchangez} +G0 X{toolchangex} Y{toolchangey} +M84 +@pause Change to tool T{tool} with Tool Dia = {toolC} +""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex), + toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey), + toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez), + tool=int(p.tool), + toolC=toolC_formatted) + else: + gcode = """ +G0 Z{toolchangez} +M84 +@pause Change to tool T{tool} with Tool Dia = {toolC} +""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez), + tool=int(p.tool), + toolC=toolC_formatted) + + if f_plunge is True: + gcode += '\nG0 Z%.*f' % (p.coords_decimals, p.z_move) + + return gcode + + def up_to_zero_code(self, p): + return 'G1 Z0' + " " + self.feedrate_code(p) + + def position_code(self, p): + return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \ + (p.coords_decimals, p.x, p.coords_decimals, p.y) + + def rapid_code(self, p): + return ('G0 ' + self.position_code(p)).format(**p) + " " + self.feedrate_rapid_code(p) + + def linear_code(self, p): + return ('G1 ' + self.position_code(p)).format(**p) + " " + self.end_feedrate_code(p) + + def end_code(self, p): + coords_xy = p['toolchange_xy'] + gcode = ('G0 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + " " + self.feedrate_rapid_code(p) + "\n") + + if coords_xy is not None: + gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n" + + return gcode + + def feedrate_code(self, p): + return 'G1 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate)) + + def end_feedrate_code(self, p): + return 'F' + self.feedrate_format %(p.fr_decimals, p.feedrate) + + def feedrate_z_code(self, p): + return 'G1 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate_z)) + + def feedrate_rapid_code(self, p): + return 'F' + self.feedrate_rapid_format % (p.fr_decimals, p.feedrate_rapid) + + def spindle_code(self,p): + if p.spindlespeed: + return 'M106 S%d' % p.spindlespeed + else: + return 'M106' + + def dwell_code(self, p): + if p.dwelltime: + return 'G4 P' + str(p.dwelltime) + + def spindle_stop_code(self,p): + return 'M107'