diff --git a/FlatCAMApp.py b/FlatCAMApp.py index d1ba0c8c..2274dc85 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -5311,17 +5311,19 @@ class App(QtCore.QObject): return # Options to scale - dimensions = ['gerber_isotooldia', 'gerber_noncoppermargin', 'gerber_bboxmargin', + dimensions = ['gerber_isotooldia', 'gerber_noncoppermargin', 'gerber_bboxmargin', "gerber_isooverlap", + "gerber_editor_newsize", "gerber_editor_lin_pitch", "gerber_editor_buff_f" 'excellon_drillz', 'excellon_travelz', "excellon_toolchangexy", 'excellon_feedrate', 'excellon_feedrate_rapid', 'excellon_toolchangez', 'excellon_tooldia', 'excellon_slot_tooldia', 'excellon_endz', "excellon_feedrate_probe", - "excellon_z_pdepth", + "excellon_z_pdepth", "excellon_editor_newdia", "excellon_editor_lin_pitch", + "excellon_editor_slot_lin_pitch", 'geometry_cutz', "geometry_depthperpass", 'geometry_travelz', 'geometry_feedrate', 'geometry_feedrate_rapid', "geometry_toolchangez", "geometry_feedrate_z", "geometry_toolchangexy", 'geometry_cnctooldia', 'geometry_endz', "geometry_z_pdepth", - "geometry_feedrate_probe", + "geometry_feedrate_probe", "geometry_startz", 'cncjob_tooldia', diff --git a/FlatCAMPool.py b/FlatCAMPool.py index 94a24278..53ef440e 100644 --- a/FlatCAMPool.py +++ b/FlatCAMPool.py @@ -1,5 +1,5 @@ from PyQt5 import QtCore -from multiprocessing import Pool +from multiprocessing import Pool, cpu_count import dill @@ -23,7 +23,7 @@ class WorkerPool(QtCore.QObject): def __init__(self): super(WorkerPool, self).__init__() - self.pool = Pool(2) + self.pool = Pool(cpu_count()) def add_task(self, task): print("adding task", task) diff --git a/README.md b/README.md index 000d1d14..6c68f088 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ CAD program, and create G-Code for Isolation routing. - changed the icon for Open Script and reused it for the Check Rules Tool - added a new tool named "Optimal Tool" which will determine the minimum distance between the copper features for a Gerber object, in fact determining the maximum diameter for a isolation tool that can be used for a complete isolation - fixed the ToolMeasurement geometry not being displayed +- fixed a bug in Excellon Editor that crashed the app when editing the first tool added automatically into a new black Excellon file +- made sure that if the big mouse cursor is selected, the utility geometry in Excellon Editor has a thicker line width (2 pixels now) so it is visible over the geometry of the mouse cursor 27.09.2019 diff --git a/flatcamEditors/FlatCAMExcEditor.py b/flatcamEditors/FlatCAMExcEditor.py index 4dc33d23..1e9257af 100644 --- a/flatcamEditors/FlatCAMExcEditor.py +++ b/flatcamEditors/FlatCAMExcEditor.py @@ -2014,7 +2014,10 @@ class FlatCAMExcEditor(QtCore.QObject): # VisPy Visuals if self.app.is_legacy is False: self.shapes = self.app.plotcanvas.new_shape_collection(layers=1) - self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1) + if self.app.plotcanvas.big_cursor is True: + self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1, line_width=2) + else: + self.tool_shape = self.app.plotcanvas.new_shape_collection(layers=1) else: from flatcamGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='shapes_exc_editor') @@ -2977,7 +2980,7 @@ class FlatCAMExcEditor(QtCore.QObject): # add a first tool in the Tool Table but only if the Excellon Object is empty if not self.tool2tooldia: - self.on_tool_add(tooldia=float(self.app.defaults['excellon_editor_newdia'])) + self.on_tool_add(tooldia=float('%.2f' % float(self.app.defaults['excellon_editor_newdia']))) def update_fcexcellon(self, exc_obj): """ diff --git a/flatcamEditors/FlatCAMGrbEditor.py b/flatcamEditors/FlatCAMGrbEditor.py index ee69683f..aa16b238 100644 --- a/flatcamEditors/FlatCAMGrbEditor.py +++ b/flatcamEditors/FlatCAMGrbEditor.py @@ -4096,7 +4096,7 @@ class FlatCAMGrbEditor(QtCore.QObject): if specific_shape: geo = specific_shape else: - geo = self.active_tool.geometry + geo = deepcopy(self.active_tool.geometry) if geo is None: return diff --git a/flatcamGUI/PlotCanvas.py b/flatcamGUI/PlotCanvas.py index 7911c81e..fbbedb9e 100644 --- a/flatcamGUI/PlotCanvas.py +++ b/flatcamGUI/PlotCanvas.py @@ -89,7 +89,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.text_collection.enabled = True self.c = None - + self.big_cursor = None # Keep VisPy canvas happy by letting it be "frozen" again. self.freeze() @@ -203,6 +203,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): :return: the mouse cursor object """ if big is True: + self.big_cursor = True self.c = CursorBig() # in case there are multiple new_cursor calls, best to disconnect first the signals @@ -218,6 +219,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas): self.c.mouse_state_updated.connect(self.on_mouse_state) self.c.mouse_position_updated.connect(self.on_mouse_position) else: + self.big_cursor = False self.c = Cursor(pos=np.empty((0, 2)), parent=self.view.scene) self.c.antialias = 0 diff --git a/flatcamTools/ToolRulesCheck.py b/flatcamTools/ToolRulesCheck.py index f4158700..e97ed978 100644 --- a/flatcamTools/ToolRulesCheck.py +++ b/flatcamTools/ToolRulesCheck.py @@ -10,6 +10,9 @@ from FlatCAMTool import FlatCAMTool from copy import copy, deepcopy from ObjectCollection import * import time +from FlatCAMPool import * +from os import getpid +import copyreg, types, sys import gettext import FlatCAMTranslation as fcTranslate @@ -20,6 +23,15 @@ if '_' not in builtins.__dict__: _ = gettext.gettext +def _pickle_method(m): + class_self = m.im_class if m.im_self is None else m.im_self + print(getattr, (class_self, m.im_func.func_name)) + return getattr, (class_self, m.im_func.func_name) + + +copyreg.pickle(types.MethodType, _pickle_method) + + class RulesCheck(FlatCAMTool): toolName = _("Check Rules") @@ -339,7 +351,7 @@ class RulesCheck(FlatCAMTool): # ####################################################### # ################ SIGNALS ############################## # ####################################################### - + self.run_button.clicked.connect(self.execute) # self.app.collection.rowsInserted.connect(self.on_object_loaded) # list to hold the temporary objects @@ -387,475 +399,34 @@ class RulesCheck(FlatCAMTool): def set_tool_ui(self): self.reset_fields() - # def on_panelize(self): - # name = self.object_combo.currentText() - # - # # Get source object. - # try: - # obj = self.app.collection.get_by_name(str(name)) - # except Exception as e: - # log.debug("Panelize.on_panelize() --> %s" % str(e)) - # self.app.inform.emit('[ERROR_NOTCL] %s: %s' % - # (_("Could not retrieve object"), name)) - # return "Could not retrieve object: %s" % name - # - # panel_obj = obj - # - # if panel_obj is None: - # self.app.inform.emit('[ERROR_NOTCL] %s: %s' % - # (_("Object not found"), panel_obj)) - # return "Object not found: %s" % panel_obj - # - # boxname = self.box_combo.currentText() - # - # try: - # box = self.app.collection.get_by_name(boxname) - # except Exception as e: - # log.debug("Panelize.on_panelize() --> %s" % str(e)) - # self.app.inform.emit('[ERROR_NOTCL] %s: %s' % - # (_("Could not retrieve object"), boxname)) - # return "Could not retrieve object: %s" % boxname - # - # if box is None: - # self.app.inform.emit('[WARNING_NOTCL]%s: %s' % - # (_("No object Box. Using instead"), panel_obj)) - # self.reference_radio.set_value('bbox') - # - # if self.reference_radio.get_value() == 'bbox': - # box = panel_obj - # - # self.outname = name + '_panelized' - # - # try: - # spacing_columns = float(self.spacing_columns.get_value()) - # except ValueError: - # # try to convert comma to decimal point. if it's still not working error message and return - # try: - # spacing_columns = float(self.spacing_columns.get_value().replace(',', '.')) - # except ValueError: - # self.app.inform.emit('[ERROR_NOTCL] %s' % - # _("Wrong value format entered, use a number.")) - # return - # spacing_columns = spacing_columns if spacing_columns is not None else 0 - # - # try: - # spacing_rows = float(self.spacing_rows.get_value()) - # except ValueError: - # # try to convert comma to decimal point. if it's still not working error message and return - # try: - # spacing_rows = float(self.spacing_rows.get_value().replace(',', '.')) - # except ValueError: - # self.app.inform.emit('[ERROR_NOTCL] %s' % - # _("Wrong value format entered, use a number.")) - # return - # spacing_rows = spacing_rows if spacing_rows is not None else 0 - # - # try: - # rows = int(self.rows.get_value()) - # except ValueError: - # # try to convert comma to decimal point. if it's still not working error message and return - # try: - # rows = float(self.rows.get_value().replace(',', '.')) - # rows = int(rows) - # except ValueError: - # self.app.inform.emit('[ERROR_NOTCL] %s' % - # _("Wrong value format entered, use a number.")) - # return - # rows = rows if rows is not None else 1 - # - # try: - # columns = int(self.columns.get_value()) - # except ValueError: - # # try to convert comma to decimal point. if it's still not working error message and return - # try: - # columns = float(self.columns.get_value().replace(',', '.')) - # columns = int(columns) - # except ValueError: - # self.app.inform.emit('[ERROR_NOTCL] %s' % - # _("Wrong value format entered, use a number.")) - # return - # columns = columns if columns is not None else 1 - # - # try: - # constrain_dx = float(self.x_width_entry.get_value()) - # except ValueError: - # # try to convert comma to decimal point. if it's still not working error message and return - # try: - # constrain_dx = float(self.x_width_entry.get_value().replace(',', '.')) - # except ValueError: - # self.app.inform.emit('[ERROR_NOTCL] %s' % - # _("Wrong value format entered, use a number.")) - # return - # - # try: - # constrain_dy = float(self.y_height_entry.get_value()) - # except ValueError: - # # try to convert comma to decimal point. if it's still not working error message and return - # try: - # constrain_dy = float(self.y_height_entry.get_value().replace(',', '.')) - # except ValueError: - # self.app.inform.emit('[ERROR_NOTCL] %s' % - # _("Wrong value format entered, use a number.")) - # return - # - # panel_type = str(self.panel_type_radio.get_value()) - # - # if 0 in {columns, rows}: - # self.app.inform.emit('[ERROR_NOTCL] %s' % - # _("Columns or Rows are zero value. Change them to a positive integer.")) - # return "Columns or Rows are zero value. Change them to a positive integer." - # - # xmin, ymin, xmax, ymax = box.bounds() - # lenghtx = xmax - xmin + spacing_columns - # lenghty = ymax - ymin + spacing_rows - # - # # check if constrain within an area is desired - # if self.constrain_cb.isChecked(): - # panel_lengthx = ((xmax - xmin) * columns) + (spacing_columns * (columns - 1)) - # panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1)) - # - # # adjust the number of columns and/or rows so the panel will fit within the panel constraint area - # if (panel_lengthx > constrain_dx) or (panel_lengthy > constrain_dy): - # self.constrain_flag = True - # - # while panel_lengthx > constrain_dx: - # columns -= 1 - # panel_lengthx = ((xmax - xmin) * columns) + (spacing_columns * (columns - 1)) - # while panel_lengthy > constrain_dy: - # rows -= 1 - # panel_lengthy = ((ymax - ymin) * rows) + (spacing_rows * (rows - 1)) - # - # def panelize_2(): - # if panel_obj is not None: - # self.app.inform.emit(_("Generating panel ... ")) - # - # self.app.progress.emit(0) - # - # def job_init_excellon(obj_fin, app_obj): - # currenty = 0.0 - # self.app.progress.emit(10) - # obj_fin.tools = panel_obj.tools.copy() - # obj_fin.drills = [] - # obj_fin.slots = [] - # obj_fin.solid_geometry = [] - # - # for option in panel_obj.options: - # if option is not 'name': - # try: - # obj_fin.options[option] = panel_obj.options[option] - # except KeyError: - # log.warning("Failed to copy option. %s" % str(option)) - # - # geo_len_drills = len(panel_obj.drills) if panel_obj.drills else 0 - # geo_len_slots = len(panel_obj.slots) if panel_obj.slots else 0 - # - # element = 0 - # for row in range(rows): - # currentx = 0.0 - # for col in range(columns): - # element += 1 - # disp_number = 0 - # old_disp_number = 0 - # - # if panel_obj.drills: - # drill_nr = 0 - # for tool_dict in panel_obj.drills: - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # point_offseted = affinity.translate(tool_dict['point'], currentx, currenty) - # obj_fin.drills.append( - # { - # "point": point_offseted, - # "tool": tool_dict['tool'] - # } - # ) - # - # drill_nr += 1 - # disp_number = int(np.interp(drill_nr, [0, geo_len_drills], [0, 100])) - # - # if disp_number > old_disp_number and disp_number <= 100: - # self.app.proc_container.update_view_text(' %s: %d D:%d%%' % - # (_("Copy"), - # int(element), - # disp_number)) - # old_disp_number = disp_number - # - # if panel_obj.slots: - # slot_nr = 0 - # for tool_dict in panel_obj.slots: - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # start_offseted = affinity.translate(tool_dict['start'], currentx, currenty) - # stop_offseted = affinity.translate(tool_dict['stop'], currentx, currenty) - # obj_fin.slots.append( - # { - # "start": start_offseted, - # "stop": stop_offseted, - # "tool": tool_dict['tool'] - # } - # ) - # - # slot_nr += 1 - # disp_number = int(np.interp(slot_nr, [0, geo_len_slots], [0, 100])) - # - # if disp_number > old_disp_number and disp_number <= 100: - # self.app.proc_container.update_view_text(' %s: %d S:%d%%' % - # (_("Copy"), - # int(element), - # disp_number)) - # old_disp_number = disp_number - # - # currentx += lenghtx - # currenty += lenghty - # - # obj_fin.create_geometry() - # obj_fin.zeros = panel_obj.zeros - # obj_fin.units = panel_obj.units - # self.app.proc_container.update_view_text('') - # - # def job_init_geometry(obj_fin, app_obj): - # currentx = 0.0 - # currenty = 0.0 - # - # def translate_recursion(geom): - # if type(geom) == list: - # geoms = list() - # for local_geom in geom: - # res_geo = translate_recursion(local_geom) - # try: - # geoms += res_geo - # except TypeError: - # geoms.append(res_geo) - # return geoms - # else: - # return affinity.translate(geom, xoff=currentx, yoff=currenty) - # - # obj_fin.solid_geometry = [] - # - # # create the initial structure on which to create the panel - # if isinstance(panel_obj, FlatCAMGeometry): - # obj_fin.multigeo = panel_obj.multigeo - # obj_fin.tools = deepcopy(panel_obj.tools) - # if panel_obj.multigeo is True: - # for tool in panel_obj.tools: - # obj_fin.tools[tool]['solid_geometry'][:] = [] - # elif isinstance(panel_obj, FlatCAMGerber): - # obj_fin.apertures = deepcopy(panel_obj.apertures) - # for ap in obj_fin.apertures: - # obj_fin.apertures[ap]['geometry'] = list() - # - # # find the number of polygons in the source solid_geometry - # geo_len = 0 - # if isinstance(panel_obj, FlatCAMGeometry): - # if panel_obj.multigeo is True: - # for tool in panel_obj.tools: - # try: - # for pol in panel_obj.tools[tool]['solid_geometry']: - # geo_len += 1 - # except TypeError: - # geo_len = 1 - # else: - # try: - # for pol in panel_obj.solid_geometry: - # geo_len += 1 - # except TypeError: - # geo_len = 1 - # elif isinstance(panel_obj, FlatCAMGerber): - # for ap in panel_obj.apertures: - # for elem in panel_obj.apertures[ap]['geometry']: - # geo_len += 1 - # - # self.app.progress.emit(0) - # element = 0 - # for row in range(rows): - # currentx = 0.0 - # - # for col in range(columns): - # element += 1 - # disp_number = 0 - # old_disp_number = 0 - # - # if isinstance(panel_obj, FlatCAMGeometry): - # if panel_obj.multigeo is True: - # for tool in panel_obj.tools: - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # # geo = translate_recursion(panel_obj.tools[tool]['solid_geometry']) - # # if isinstance(geo, list): - # # obj_fin.tools[tool]['solid_geometry'] += geo - # # else: - # # obj_fin.tools[tool]['solid_geometry'].append(geo) - # - # # calculate the number of polygons - # geo_len = len(panel_obj.tools[tool]['solid_geometry']) - # pol_nr = 0 - # for geo_el in panel_obj.tools[tool]['solid_geometry']: - # trans_geo = translate_recursion(geo_el) - # obj_fin.tools[tool]['solid_geometry'].append(trans_geo) - # - # pol_nr += 1 - # disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) - # - # if old_disp_number < disp_number <= 100: - # self.app.proc_container.update_view_text(' %s: %d %d%%' % - # (_("Copy"), - # int(element), - # disp_number)) - # old_disp_number = disp_number - # else: - # # geo = translate_recursion(panel_obj.solid_geometry) - # # if isinstance(geo, list): - # # obj_fin.solid_geometry += geo - # # else: - # # obj_fin.solid_geometry.append(geo) - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # try: - # # calculate the number of polygons - # geo_len = len(panel_obj.solid_geometry) - # except TypeError: - # geo_len = 1 - # pol_nr = 0 - # try: - # for geo_el in panel_obj.solid_geometry: - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # trans_geo = translate_recursion(geo_el) - # obj_fin.solid_geometry.append(trans_geo) - # - # pol_nr += 1 - # disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) - # - # if old_disp_number < disp_number <= 100: - # self.app.proc_container.update_view_text(' %s: %d %d%%' % - # (_("Copy"), - # int(element), - # disp_number)) - # old_disp_number = disp_number - # except TypeError: - # trans_geo = translate_recursion(panel_obj.solid_geometry) - # obj_fin.solid_geometry.append(trans_geo) - # else: - # # geo = translate_recursion(panel_obj.solid_geometry) - # # if isinstance(geo, list): - # # obj_fin.solid_geometry += geo - # # else: - # # obj_fin.solid_geometry.append(geo) - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # try: - # for geo_el in panel_obj.solid_geometry: - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # trans_geo = translate_recursion(geo_el) - # obj_fin.solid_geometry.append(trans_geo) - # except TypeError: - # trans_geo = translate_recursion(panel_obj.solid_geometry) - # obj_fin.solid_geometry.append(trans_geo) - # - # for apid in panel_obj.apertures: - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # try: - # # calculate the number of polygons - # geo_len = len(panel_obj.apertures[apid]['geometry']) - # except TypeError: - # geo_len = 1 - # pol_nr = 0 - # for el in panel_obj.apertures[apid]['geometry']: - # if self.app.abort_flag: - # # graceful abort requested by the user - # raise FlatCAMApp.GracefulException - # - # new_el = dict() - # if 'solid' in el: - # geo_aper = translate_recursion(el['solid']) - # new_el['solid'] = geo_aper - # - # if 'clear' in el: - # geo_aper = translate_recursion(el['clear']) - # new_el['clear'] = geo_aper - # - # if 'follow' in el: - # geo_aper = translate_recursion(el['follow']) - # new_el['follow'] = geo_aper - # - # obj_fin.apertures[apid]['geometry'].append(deepcopy(new_el)) - # - # pol_nr += 1 - # disp_number = int(np.interp(pol_nr, [0, geo_len], [0, 100])) - # - # if old_disp_number < disp_number <= 100: - # self.app.proc_container.update_view_text(' %s: %d %d%%' % - # (_("Copy"), - # int(element), - # disp_number)) - # old_disp_number = disp_number - # - # currentx += lenghtx - # currenty += lenghty - # - # if panel_type == 'gerber': - # self.app.inform.emit('%s' % - # _("Generating panel ... Adding the Gerber code.")) - # obj_fin.source_file = self.app.export_gerber(obj_name=self.outname, filename=None, - # local_use=obj_fin, use_thread=False) - # - # # app_obj.log.debug("Found %s geometries. Creating a panel geometry cascaded union ..." % - # # len(obj_fin.solid_geometry)) - # - # # obj_fin.solid_geometry = cascaded_union(obj_fin.solid_geometry) - # # app_obj.log.debug("Finished creating a cascaded union for the panel.") - # self.app.proc_container.update_view_text('') - # - # self.app.inform.emit('%s: %d' % - # (_("Generating panel... Spawning copies"), (int(rows * columns)))) - # if isinstance(panel_obj, FlatCAMExcellon): - # self.app.progress.emit(50) - # self.app.new_object("excellon", self.outname, job_init_excellon, plot=True, autoselected=True) - # else: - # self.app.progress.emit(50) - # self.app.new_object(panel_type, self.outname, job_init_geometry, - # plot=True, autoselected=True) - # - # if self.constrain_flag is False: - # self.app.inform.emit('[success] %s' % _("Panel done...")) - # else: - # self.constrain_flag = False - # self.app.inform.emit(_("{text} Too big for the constrain area. " - # "Final panel has {col} columns and {row} rows").format( - # text='[WARNING] ', col=columns, row=rows)) - # - # proc = self.app.proc_container.new(_("Working...")) - # - # def job_thread(app_obj): - # try: - # panelize_2() - # self.app.inform.emit('[success] %s' % _("Panel created successfully.")) - # except Exception as ee: - # proc.done() - # log.debug(str(ee)) - # return - # proc.done() - # - # self.app.collection.promise(self.outname) - # self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) + def foo(self, bar, baz): + print("start", getpid()) + bar = bar ** 2 + print(bar, getpid()) + print("end", getpid()) + return bar, baz + + def execute(self): + log.debug("started") + self.pool = Pool() + log.debug("executing") + self.results = list() + i = 50 + while i < 100: + j = i + 1 + while j < 150: + self.results.append(self.pool.apply_async(self.foo, args=(i, j))) + j = j + 1 + i = i + 1 + + output = [p.get() for p in self.results] + print(output) + log.debug("finished") + + def __getstate__(self): + self_dict = self.__dict__.copy() + del self_dict['pool'] + return self_dict def reset_fields(self): # self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))