diff --git a/CHANGELOG.md b/CHANGELOG.md index 1128c258..0c2e77b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG for FlatCAM beta - in Milling Tool fixed the UI change as a result of preprocessor change - in Isolation Tool added possibility to have tools with the same diameter; remade the sorting of the tools storage +- some work in Milling Tool 29.11.2020 diff --git a/appTools/ToolMilling.py b/appTools/ToolMilling.py index 78568a26..2eb07754 100644 --- a/appTools/ToolMilling.py +++ b/appTools/ToolMilling.py @@ -12,17 +12,20 @@ from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, F FCComboBox, OptionalInputSection, FCSpinner, NumericalEvalEntry, OptionalHideInputSection, FCLabel from appParsers.ParseExcellon import Excellon +from camlib import Geometry, grace + from copy import deepcopy import math import simplejson as json import sys +import traceback from appObjects.FlatCAMObj import FlatCAMObj # import numpy as np # import math # from shapely.ops import unary_union -from shapely.geometry import Point, LineString +from shapely.geometry import Point, LineString, box from matplotlib.backend_bases import KeyEvent as mpl_key_event @@ -46,6 +49,7 @@ else: class ToolMilling(AppTool, Excellon): builduiSig = QtCore.pyqtSignal() + launch_job = QtCore.pyqtSignal() def __init__(self, app): self.app = app @@ -227,7 +231,7 @@ class ToolMilling(AppTool, Excellon): self.ui.apply_param_to_all.clicked.connect(self.on_apply_param_to_all_clicked) self.ui.generate_cnc_button.clicked.connect(self.on_cnc_button_click) - self.ui.tools_table.drag_drop_sig.connect(self.rebuild_ui) + self.ui.tools_table.drag_drop_sig.connect(self.on_exc_rebuild_ui) # Exclusion areas signals self.ui.exclusion_table.horizontalHeader().sectionClicked.connect(self.exclusion_table_toggle_all) @@ -238,6 +242,12 @@ class ToolMilling(AppTool, Excellon): self.ui.delete_sel_area_button.clicked.connect(self.on_delete_sel_areas) self.ui.strategy_radio.activated_custom.connect(self.on_strategy) + # Geo Tools Table signals + self.ui.geo_tools_table.drag_drop_sig.connect(self.on_geo_rebuild_ui) + self.ui.geo_tools_table.horizontalHeader().sectionClicked.connect(self.on_toggle_all_rows) + + self.launch_job.connect(self.mtool_gen_cncjob) + self.ui.reset_button.clicked.connect(self.set_tool_ui) # Cleanup on Graceful exit (CTRL+ALT+X combo key) self.app.cleanup.connect(self.set_tool_ui) @@ -452,7 +462,7 @@ class ToolMilling(AppTool, Excellon): def on_plot_clicked(self, state): self.target_obj.options['plot'] = True if state else False - def rebuild_ui(self): + def on_exc_rebuild_ui(self): # read the table tools uid current_uid_list = [] for row in range(self.ui.tools_table.rowCount()): @@ -469,6 +479,26 @@ class ToolMilling(AppTool, Excellon): # the tools table changed therefore we need to rebuild it QtCore.QTimer.singleShot(20, self.build_ui) + def on_geo_rebuild_ui(self): + # read the table tools uid + current_uid_list = [] + for row in range(self.ui.geo_tools_table.rowCount()): + uid = int(self.ui.geo_tools_table.item(row, 3).text()) + current_uid_list.append(uid) + + new_tools = {} + new_uid = 1 + + for current_uid in current_uid_list: + new_tools[new_uid] = deepcopy(self.tools[current_uid]) + new_uid += 1 + + self.tools = new_tools + + # the tools table changed therefore we need to reconnect the signals to the cellWidgets + self.ui_disconnect() + self.ui_connect() + def build_ui(self): self.ui_disconnect() @@ -2170,6 +2200,525 @@ class ToolMilling(AppTool, Excellon): return True, "" + def on_polish(self): + + def job_thread(obj): + with obj.app.proc_container.new('%s...' % _("Working")): + tooldia = obj.ui.polish_dia_entry.get_value() + depth = obj.ui.polish_pressure_entry.get_value() + travelz = obj.ui.polish_travelz_entry.get_value() + margin = obj.ui.polish_margin_entry.get_value() + overlap = obj.ui.polish_over_entry.get_value() / 100 + paint_method = obj.ui.polish_method_combo.get_value() + + # calculate the max uid form the keys of the self.tools + max_uid = max(list(obj.tools.keys())) + new_uid = max_uid + 1 + + # add a new key in the dict + new_data = deepcopy(obj.default_data) + new_data["travelz"] = travelz + new_data["cutz"] = depth + new_dict = { + new_uid: { + 'tooldia': obj.app.dec_format(float(tooldia), obj.decimals), + 'offset': 'Path', + 'offset_value': 0.0, + 'type': _('Polish'), + 'tool_type': 'C1', + 'data': new_data, + 'solid_geometry': [] + } + } + obj.tools.update(new_dict) + obj.sel_tools.update(new_dict) + + # make a box polygon out of the bounds of the current object + # apply the margin + xmin, ymin, xmax, ymax = obj.bounds() + bbox = box(xmin-margin, ymin-margin, xmax+margin, ymax+margin) + + # paint the box + try: + # provide the app with a way to process the GUI events when in a blocking loop + QtWidgets.QApplication.processEvents() + if self.app.abort_flag: + # graceful abort requested by the user + raise grace + + # Type(cpoly) == FlatCAMRTreeStorage | None + cpoly = None + if paint_method == 0: # Standard + cpoly = self.clear_polygon(bbox, + tooldia=tooldia, + steps_per_circle=obj.circle_steps, + overlap=overlap, + contour=True, + connect=True, + prog_plot=False) + elif paint_method == 1: # Seed + cpoly = self.clear_polygon2(bbox, + tooldia=tooldia, + steps_per_circle=obj.circle_steps, + overlap=overlap, + contour=True, + connect=True, + prog_plot=False) + elif paint_method == 2: # Lines + cpoly = self.clear_polygon3(bbox, + tooldia=tooldia, + steps_per_circle=obj.circle_steps, + overlap=overlap, + contour=True, + connect=True, + prog_plot=False) + + if not cpoly or not cpoly.objects: + obj.app.inform.emit('[ERROR_NOTCL] %s' % _('Geometry could not be painted completely')) + return + + paint_geo = [g for g in cpoly.get_objects() if g and not g.is_empty] + except grace: + return "fail" + except Exception as e: + log.debug("Could not Paint the polygons. %s" % str(e)) + mssg = '[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. " + "Or a different method of Paint"), str(e)) + self.app.inform.emit(mssg) + return + + obj.sel_tools[new_uid]['solid_geometry'] = paint_geo + + # and now create the CNCJob + obj.launch_job.emit() + + # Send to worker + self.app.worker_task.emit({'fcn': job_thread, 'params': [self]}) + + def on_generatecnc_button_click(self): + log.debug("Generating CNCJob from Geometry ...") + self.app.defaults.report_usage("geometry_on_generatecnc_button") + + + self.sel_tools = {} + + try: + if self.special_group: + self.app.inform.emit( + '[WARNING_NOTCL] %s %s %s.' % + (_("This Geometry can't be processed because it is"), str(self.special_group), _("Geometry")) + ) + return + except AttributeError: + pass + + # test to see if we have tools available in the tool table + if self.ui.geo_tools_table.selectedItems(): + for x in self.ui.geo_tools_table.selectedItems(): + tooluid = int(self.ui.geo_tools_table.item(x.row(), 3).text()) + + for tooluid_key, tooluid_value in self.tools.items(): + if int(tooluid_key) == tooluid: + self.sel_tools.update({ + tooluid: deepcopy(tooluid_value) + }) + + if self.ui.polish_cb.get_value(): + self.on_polish() + else: + self.mtool_gen_cncjob() + self.ui.geo_tools_table.clearSelection() + + elif self.ui.geo_tools_table.rowCount() == 1: + tooluid = int(self.ui.geo_tools_table.item(0, 3).text()) + + for tooluid_key, tooluid_value in self.tools.items(): + if int(tooluid_key) == tooluid: + self.sel_tools.update({ + tooluid: deepcopy(tooluid_value) + }) + if self.ui.polish_cb.get_value(): + self.on_polish() + else: + self.mtool_gen_cncjob() + self.ui.geo_tools_table.clearSelection() + else: + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed. No tool selected in the tool table ...")) + + def mtool_gen_cncjob(self, outname=None, tools_dict=None, tools_in_use=None, segx=None, segy=None, + plot=True, use_thread=True): + """ + Creates a multi-tool CNCJob out of this Geometry object. + The actual work is done by the target CNCJobObject object's + `generate_from_geometry_2()` method. + + :param outname: + :param tools_dict: a dictionary that holds the whole data needed to create the Gcode + (including the solid_geometry) + :param tools_in_use: the tools that are used, needed by some preprocessors + :type tools_in_use list of lists, each list in the list is made out of row elements of tools table from GUI + :param segx: number of segments on the X axis, for auto-levelling + :param segy: number of segments on the Y axis, for auto-levelling + :param plot: if True the generated object will be plotted; if False will not be plotted + :param use_thread: if True use threading + :return: None + """ + + # use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia + outname = "%s_%s" % (self.options["name"], 'cnc') if outname is None else outname + + tools_dict = self.sel_tools if tools_dict is None else tools_dict + tools_in_use = tools_in_use if tools_in_use is not None else self.get_selected_tools_table_items() + segx = segx if segx is not None else float(self.app.defaults['geometry_segx']) + segy = segy if segy is not None else float(self.app.defaults['geometry_segy']) + + try: + xmin = self.options['xmin'] + ymin = self.options['ymin'] + xmax = self.options['xmax'] + ymax = self.options['ymax'] + except Exception as e: + log.debug("FlatCAMObj.GeometryObject.mtool_gen_cncjob() --> %s\n" % str(e)) + + msg = '[ERROR] %s' % _("An internal error has occurred. See shell.\n") + msg += '%s' % str(e) + msg += traceback.format_exc() + self.app.inform.emit(msg) + return + + # force everything as MULTI-GEO + # self.multigeo = True + + # Object initialization function for app.app_obj.new_object() + # RUNNING ON SEPARATE THREAD! + def job_init_single_geometry(job_obj, app_obj): + log.debug("Creating a CNCJob out of a single-geometry") + assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj) + + job_obj.options['xmin'] = xmin + job_obj.options['ymin'] = ymin + job_obj.options['xmax'] = xmax + job_obj.options['ymax'] = ymax + + # count the tools + tool_cnt = 0 + + # dia_cnc_dict = {} + + # this turn on the FlatCAMCNCJob plot for multiple tools + job_obj.multitool = True + job_obj.multigeo = False + job_obj.cnc_tools.clear() + + job_obj.options['Tools_in_use'] = tools_in_use + job_obj.segx = segx if segx else float(self.app.defaults["geometry_segx"]) + job_obj.segy = segy if segy else float(self.app.defaults["geometry_segy"]) + + job_obj.z_pdepth = float(self.app.defaults["geometry_z_pdepth"]) + job_obj.feedrate_probe = float(self.app.defaults["geometry_feedrate_probe"]) + + total_gcode = '' + for tooluid_key in list(tools_dict.keys()): + tool_cnt += 1 + + dia_cnc_dict = deepcopy(tools_dict[tooluid_key]) + tooldia_val = app_obj.dec_format(float(tools_dict[tooluid_key]['tooldia']), self.decimals) + dia_cnc_dict.update({ + 'tooldia': tooldia_val + }) + + if dia_cnc_dict['offset'] == 'in': + tool_offset = -dia_cnc_dict['tooldia'] / 2 + elif dia_cnc_dict['offset'].lower() == 'out': + tool_offset = dia_cnc_dict['tooldia'] / 2 + elif dia_cnc_dict['offset'].lower() == 'custom': + try: + offset_value = float(self.ui.tool_offset_entry.get_value()) + except ValueError: + # try to convert comma to decimal point. if it's still not working error message and return + try: + offset_value = float(self.ui.tool_offset_entry.get_value().replace(',', '.')) + except ValueError: + app_obj.inform.emit('[ERROR_NOTCL] %s' % _("Wrong value format entered, use a number.")) + return + if offset_value: + tool_offset = float(offset_value) + else: + app_obj.inform.emit( + '[WARNING] %s' % _("Tool Offset is selected in Tool Table but no value is provided.\n" + "Add a Tool Offset or change the Offset Type.") + ) + return + else: + tool_offset = 0.0 + + dia_cnc_dict.update({ + 'offset_value': tool_offset + }) + + z_cut = tools_dict[tooluid_key]['data']["cutz"] + z_move = tools_dict[tooluid_key]['data']["travelz"] + feedrate = tools_dict[tooluid_key]['data']["feedrate"] + feedrate_z = tools_dict[tooluid_key]['data']["feedrate_z"] + feedrate_rapid = tools_dict[tooluid_key]['data']["feedrate_rapid"] + multidepth = tools_dict[tooluid_key]['data']["multidepth"] + extracut = tools_dict[tooluid_key]['data']["extracut"] + extracut_length = tools_dict[tooluid_key]['data']["extracut_length"] + depthpercut = tools_dict[tooluid_key]['data']["depthperpass"] + toolchange = tools_dict[tooluid_key]['data']["toolchange"] + toolchangez = tools_dict[tooluid_key]['data']["toolchangez"] + toolchangexy = tools_dict[tooluid_key]['data']["toolchangexy"] + startz = tools_dict[tooluid_key]['data']["startz"] + endz = tools_dict[tooluid_key]['data']["endz"] + endxy = self.options["endxy"] + spindlespeed = tools_dict[tooluid_key]['data']["spindlespeed"] + dwell = tools_dict[tooluid_key]['data']["dwell"] + dwelltime = tools_dict[tooluid_key]['data']["dwelltime"] + pp_geometry_name = tools_dict[tooluid_key]['data']["ppname_g"] + + spindledir = self.app.defaults['geometry_spindledir'] + tool_solid_geometry = self.solid_geometry + + job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"] + job_obj.fr_decimals = self.app.defaults["cncjob_fr_decimals"] + + # Propagate options + job_obj.options["tooldia"] = tooldia_val + job_obj.options['type'] = 'Geometry' + job_obj.options['tool_dia'] = tooldia_val + + 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 + # to a value of 0.0005 which is 20 times less than 0.01 + tol = float(self.app.defaults['global_tolerance']) / 20 + res, start_gcode = job_obj.generate_from_geometry_2( + self, tooldia=tooldia_val, offset=tool_offset, tolerance=tol, + z_cut=z_cut, z_move=z_move, + feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid, + spindlespeed=spindlespeed, spindledir=spindledir, dwell=dwell, dwelltime=dwelltime, + multidepth=multidepth, depthpercut=depthpercut, + extracut=extracut, extracut_length=extracut_length, startz=startz, endz=endz, endxy=endxy, + toolchange=toolchange, toolchangez=toolchangez, toolchangexy=toolchangexy, + pp_geometry_name=pp_geometry_name, + tool_no=tool_cnt, is_first=is_first) + + if res == 'fail': + log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed") + return 'fail' + + dia_cnc_dict['gcode'] = res + if start_gcode != '': + job_obj.gc_start = start_gcode + + total_gcode += res + + # 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" + + self.app.inform.emit('[success] %s' % _("G-Code parsing in progress...")) + dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() + app_obj.inform.emit('[success] %s' % _("G-Code parsing finished...")) + + # commented this; there is no need for the actual GCode geometry - the original one will serve as well + # for bounding box values + # dia_cnc_dict['solid_geometry'] = unary_union([geo['geom'] for geo in dia_cnc_dict['gcode_parsed']]) + try: + dia_cnc_dict['solid_geometry'] = tool_solid_geometry + app_obj.inform.emit('[success] %s...' % _("Finished G-Code processing")) + except Exception as er: + app_obj.inform.emit('[ERROR] %s: %s' % (_("G-Code processing failed with error"), str(er))) + + job_obj.cnc_tools.update({ + tooluid_key: deepcopy(dia_cnc_dict) + }) + dia_cnc_dict.clear() + + job_obj.source_file = job_obj.gc_start + total_gcode + + # Object initialization function for app.app_obj.new_object() + # RUNNING ON SEPARATE THREAD! + def job_init_multi_geometry(job_obj, app_obj): + log.debug("Creating a CNCJob out of a multi-geometry") + assert job_obj.kind == 'cncjob', "Initializer expected a CNCJobObject, got %s" % type(job_obj) + + job_obj.options['xmin'] = xmin + job_obj.options['ymin'] = ymin + job_obj.options['xmax'] = xmax + job_obj.options['ymax'] = ymax + + # count the tools + tool_cnt = 0 + + # dia_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.options['Tools_in_use'] = tools_in_use + job_obj.segx = segx if segx else float(self.app.defaults["geometry_segx"]) + job_obj.segy = segy if segy else float(self.app.defaults["geometry_segy"]) + + job_obj.z_pdepth = float(self.app.defaults["geometry_z_pdepth"]) + job_obj.feedrate_probe = float(self.app.defaults["geometry_feedrate_probe"]) + + # make sure that trying to make a CNCJob from an empty file is not creating an app crash + if not self.solid_geometry: + a = 0 + for tooluid_key in self.tools: + if self.tools[tooluid_key]['solid_geometry'] is None: + a += 1 + if a == len(self.tools): + app_obj.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry')) + return 'fail' + + total_gcode = '' + for tooluid_key in list(tools_dict.keys()): + tool_cnt += 1 + dia_cnc_dict = deepcopy(tools_dict[tooluid_key]) + tooldia_val = app_obj.dec_format(float(tools_dict[tooluid_key]['tooldia']), self.decimals) + dia_cnc_dict.update({ + 'tooldia': tooldia_val + }) + if "optimization_type" not in tools_dict[tooluid_key]['data']: + tools_dict[tooluid_key]['data']["optimization_type"] = \ + self.app.defaults["geometry_optimization_type"] + + # find the tool_dia associated with the tooluid_key + # 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('%.*f' % (self.decimals, float(v['tooldia']))) == tooldia_val: + # current_uid = int(k) + # break + + if dia_cnc_dict['offset'].lower() == 'in': + tool_offset = -tooldia_val / 2 + elif dia_cnc_dict['offset'].lower() == 'out': + tool_offset = tooldia_val / 2 + elif dia_cnc_dict['offset'].lower() == 'custom': + offset_value = float(self.ui.tool_offset_entry.get_value()) + if offset_value: + tool_offset = float(offset_value) + else: + self.app.inform.emit('[WARNING] %s' % + _("Tool Offset is selected in Tool Table but " + "no value is provided.\n" + "Add a Tool Offset or change the Offset Type.")) + return + else: + tool_offset = 0.0 + + dia_cnc_dict.update({ + 'offset_value': tool_offset + }) + + # z_cut = tools_dict[tooluid_key]['data']["cutz"] + # z_move = tools_dict[tooluid_key]['data']["travelz"] + # feedrate = tools_dict[tooluid_key]['data']["feedrate"] + # feedrate_z = tools_dict[tooluid_key]['data']["feedrate_z"] + # feedrate_rapid = tools_dict[tooluid_key]['data']["feedrate_rapid"] + # multidepth = tools_dict[tooluid_key]['data']["multidepth"] + # extracut = tools_dict[tooluid_key]['data']["extracut"] + # extracut_length = tools_dict[tooluid_key]['data']["extracut_length"] + # depthpercut = tools_dict[tooluid_key]['data']["depthperpass"] + # toolchange = tools_dict[tooluid_key]['data']["toolchange"] + # toolchangez = tools_dict[tooluid_key]['data']["toolchangez"] + # toolchangexy = tools_dict[tooluid_key]['data']["toolchangexy"] + # startz = tools_dict[tooluid_key]['data']["startz"] + # endz = tools_dict[tooluid_key]['data']["endz"] + # endxy = self.options["endxy"] + # spindlespeed = tools_dict[tooluid_key]['data']["spindlespeed"] + # dwell = tools_dict[tooluid_key]['data']["dwell"] + # dwelltime = tools_dict[tooluid_key]['data']["dwelltime"] + # pp_geometry_name = tools_dict[tooluid_key]['data']["ppname_g"] + # + # spindledir = self.app.defaults['geometry_spindledir'] + tool_solid_geometry = self.tools[tooluid_key]['solid_geometry'] + + job_obj.coords_decimals = self.app.defaults["cncjob_coords_decimals"] + job_obj.fr_decimals = self.app.defaults["cncjob_fr_decimals"] + + # Propagate options + job_obj.options["tooldia"] = tooldia_val + job_obj.options['type'] = 'Geometry' + job_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 + # to a value of 0.0005 which is 20 times less than 0.01 + tol = float(self.app.defaults['global_tolerance']) / 20 + + tool_lst = list(tools_dict.keys()) + is_first = True if tooluid_key == tool_lst[0] else False + is_last = True if tooluid_key == tool_lst[-1] else False + res, start_gcode = job_obj.geometry_tool_gcode_gen(tooluid_key, tools_dict, first_pt=(0, 0), + tolerance=tol, + is_first=is_first, is_last=is_last, + toolchange=True) + if res == 'fail': + log.debug("GeometryObject.mtool_gen_cncjob() --> generate_from_geometry2() failed") + return 'fail' + else: + dia_cnc_dict['gcode'] = res + total_gcode += res + + if start_gcode != '': + job_obj.gc_start = start_gcode + + app_obj.inform.emit('[success] %s' % _("G-Code parsing in progress...")) + dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() + app_obj.inform.emit('[success] %s' % _("G-Code parsing finished...")) + + # commented this; there is no need for the actual GCode geometry - the original one will serve as well + # for bounding box values + # geo_for_bound_values = unary_union([ + # geo['geom'] for geo in dia_cnc_dict['gcode_parsed'] if geo['geom'].is_valid is True + # ]) + try: + dia_cnc_dict['solid_geometry'] = deepcopy(tool_solid_geometry) + app_obj.inform.emit('[success] %s...' % _("Finished G-Code processing")) + except Exception as ee: + app_obj.inform.emit('[ERROR] %s: %s' % (_("G-Code processing failed with error"), str(ee))) + + # 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" + + job_obj.cnc_tools.update({ + tooluid_key: deepcopy(dia_cnc_dict) + }) + dia_cnc_dict.clear() + + job_obj.source_file = total_gcode + + if use_thread: + # To be run in separate thread + def job_thread(a_obj): + if self.multigeo is False: + with self.app.proc_container.new('%s...' % _("Generating")): + ret_val = a_obj.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) + if ret_val != 'fail': + a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname)) + else: + with self.app.proc_container.new('%s...' % _("Generating")): + ret_val = a_obj.app_obj.new_object("cncjob", outname, job_init_multi_geometry, plot=plot) + if ret_val != 'fail': + a_obj.inform.emit('[success] %s: %s' % (_("CNCjob created"), outname)) + + # 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: + if self.solid_geometry: + self.app.app_obj.new_object("cncjob", outname, job_init_single_geometry, plot=plot) + else: + self.app.app_obj.new_object("cncjob", outname, job_init_multi_geometry, plot=plot) + def on_pp_changed(self): current_pp = self.ui.pp_geo_name_cb.get_value()