diff --git a/FlatCAMApp.py b/FlatCAMApp.py index 5ae8b993..f3dc1f05 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -944,6 +944,8 @@ class App(QtCore.QObject): else: self.decimals = int(self.defaults['decimals_inch']) + self.current_units = self.defaults['units'] + # ############################################################################# # ##################### CREATE MULTIPROCESSING POOL ########################### # ############################################################################# @@ -5759,9 +5761,8 @@ class App(QtCore.QObject): self.defaults[dim] = val # The scaling factor depending on choice of units. - factor = 1/25.4 - if new_units == 'MM': - factor = 25.4 + + factor = 25.4 if new_units == 'MM' else 1/25.4 # Changing project units. Warn user. msgbox = QtWidgets.QMessageBox() @@ -5795,12 +5796,11 @@ class App(QtCore.QObject): self.plotcanvas.draw_workspace(pagesize=self.defaults['global_workspaceT']) # adjust the grid values on the main toolbar - dec = 6 if new_units == 'IN'else 4 val_x = float(self.ui.grid_gap_x_entry.get_value()) * factor - self.ui.grid_gap_x_entry.set_value(val_x, decimals=dec) + self.ui.grid_gap_x_entry.set_value(val_x, decimals=self.decimals) if not self.ui.grid_gap_link_cb.isChecked(): val_y = float(self.ui.grid_gap_y_entry.get_value()) * factor - self.ui.grid_gap_y_entry.set_value(val_y, decimals=dec) + self.ui.grid_gap_y_entry.set_value(val_y, decimals=self.decimals) for obj in self.collection.get_list(): obj.convert_units(new_units) @@ -5816,8 +5816,7 @@ class App(QtCore.QObject): current.to_form() self.plot_all() - self.inform.emit('[success] %s: %s' % - (_("Converted units to"), new_units)) + self.inform.emit('[success] %s: %s' % (_("Converted units to"), new_units)) # self.ui.units_label.setText("[" + self.options["units"] + "]") self.set_screen_units(new_units) else: @@ -5828,8 +5827,7 @@ class App(QtCore.QObject): else: self.ui.general_defaults_form.general_app_group.units_radio.set_value('MM') self.toggle_units_ignore = False - self.inform.emit('[WARNING_NOTCL]%s' % - _(" Units conversion cancelled.")) + self.inform.emit('[WARNING_NOTCL]%s' % _(" Units conversion cancelled.")) self.defaults_read_form() diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 5cf2d2e0..e1568ab5 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -5830,7 +5830,7 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): It is populated in the FlatCAMGeometry.mtool_gen_cncjob() BEWARE: I rely on the ordered nature of the Python 3.7 dictionary. Things might change ... ''' - self.cnc_tools = {} + self.cnc_tools = dict() ''' This is a dict of dictionaries. Each dict is associated with a tool present in the file. The key is the @@ -5848,10 +5848,10 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): ... } It is populated in the FlatCAMExcellon.on_create_cncjob_click() but actually - it's done in camlib.Excellon.generate_from_excellon_by_tool() + it's done in camlib.CNCJob.generate_from_excellon_by_tool() BEWARE: I rely on the ordered nature of the Python 3.7 dictionary. Things might change ... ''' - self.exc_cnc_tools = {} + self.exc_cnc_tools = dict() # flag to store if the CNCJob is part of a special group of CNCJob objects that can't be processed by the # default engine of FlatCAM. They generated by some of tools and are special cases of CNCJob objects. diff --git a/README.md b/README.md index 481f598c..5a8180f0 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ CAD program, and create G-Code for Isolation routing. - changed the FCDoubleSpinner, FCSpinner and FCEntry GUI elements to allow passing an alignment value: left, right or center (not yet available in the app) - fixed the GUI of the Geometry Editor Tool Transform and adapted it to use the precision setting - updated Gerber Editor to use the precision setting and the Gerber Editor Transform Tool to use the FCDoubleSpinner GUI element +- in Properties Tool added more information's regarding the Excellon tools, about travelled distance and job time; fixed issues when doing Properties on the CNCjob objects +- TODO: I need to solve the mess in units conversion: it's too convoluted 4.12.2019 diff --git a/camlib.py b/camlib.py index d181711f..79123680 100644 --- a/camlib.py +++ b/camlib.py @@ -2403,7 +2403,7 @@ class CNCjob(Geometry): # sort the tools list by the second item in tuple (here we have a dict with diameter of the tool) # so we actually are sorting the tools by diameter - #sorted_tools = sorted(exobj.tools.items(), key=lambda t1: t1['C']) + # sorted_tools = sorted(exobj.tools.items(), key=lambda t1: t1['C']) sort = [] for k, v in list(exobj.tools.items()): @@ -2421,6 +2421,32 @@ class CNCjob(Geometry): tools = [i for i, j in sorted_tools for k in selected_tools if i == k] log.debug("Tools selected and sorted are: %s" % str(tools)) + # fill the data into the self.exc_cnc_tools dictionary + for it in sorted_tools: + for to_ol in tools: + if to_ol == it[0]: + drill_no = 0 + sol_geo = list() + for dr in exobj.drills: + if dr['tool'] == it[0]: + drill_no += 1 + sol_geo.append(dr['point']) + slot_no = 0 + for dr in exobj.slots: + if dr['tool'] == it[0]: + slot_no += 1 + start = (dr['start'].x, dr['start'].y) + stop = (dr['stop'].x, dr['stop'].y) + sol_geo.append( + LineString([start, stop]).buffer((it[1] / 2.0), resolution=self.geo_steps_per_circle) + ) + + self.exc_cnc_tools[it[1]] = dict() + self.exc_cnc_tools[it[1]]['tool'] = it[0] + self.exc_cnc_tools[it[1]]['nr_drills'] = drill_no + self.exc_cnc_tools[it[1]]['nr_slots'] = slot_no + self.exc_cnc_tools[it[1]]['solid_geometry'] = deepcopy(sol_geo) + self.app.inform.emit(_("Creating a list of points to drill...")) # Points (Group by tool) points = {} @@ -3498,7 +3524,7 @@ class CNCjob(Geometry): # variables to display the percentage of work done geo_len = len(flat_geometry) - disp_number = 0 + old_disp_number = 0 log.warning("Number of paths for which to generate GCode: %s" % str(geo_len)) diff --git a/flatcamParsers/ParseExcellon.py b/flatcamParsers/ParseExcellon.py index 9725ecd3..bdecfc8c 100644 --- a/flatcamParsers/ParseExcellon.py +++ b/flatcamParsers/ParseExcellon.py @@ -9,6 +9,7 @@ import numpy as np import re import logging import traceback +from copy import deepcopy import FlatCAMTranslation as fcTranslate @@ -783,13 +784,13 @@ class Excellon(Geometry): # ## Units and number format # ## match = self.units_re.match(eline) if match: - self.units_found = match.group(1) + self.units = match.group(1) self.zeros = match.group(2) # "T" or "L". Might be empty self.excellon_format = match.group(3) if self.excellon_format: upper = len(self.excellon_format.partition('.')[0]) lower = len(self.excellon_format.partition('.')[2]) - if self.units == 'MM': + if self.units == 'METRIC': self.excellon_format_upper_mm = upper self.excellon_format_lower_mm = lower else: @@ -797,7 +798,7 @@ class Excellon(Geometry): self.excellon_format_lower_in = lower # Modified for issue #80 - self.convert_units({"INCH": "IN", "METRIC": "MM"}[self.units_found]) + self.convert_units({"INCH": "IN", "METRIC": "MM"}[self.units]) # log.warning(" Units/Format: %s %s" % (self.units, self.zeros)) log.warning("Units: %s" % self.units) if self.units == 'MM': @@ -841,13 +842,13 @@ class Excellon(Geometry): # ## Units and number format outside header# ## match = self.units_re.match(eline) if match: - self.units_found = match.group(1) + self.units = match.group(1) self.zeros = match.group(2) # "T" or "L". Might be empty self.excellon_format = match.group(3) if self.excellon_format: upper = len(self.excellon_format.partition('.')[0]) lower = len(self.excellon_format.partition('.')[2]) - if self.units == 'MM': + if self.units == 'METRIC': self.excellon_format_upper_mm = upper self.excellon_format_lower_mm = lower else: @@ -855,7 +856,7 @@ class Excellon(Geometry): self.excellon_format_lower_in = lower # Modified for issue #80 - self.convert_units({"INCH": "IN", "METRIC": "MM"}[self.units_found]) + self.convert_units({"INCH": "IN", "METRIC": "MM"}[self.units]) # log.warning(" Units/Format: %s %s" % (self.units, self.zeros)) log.warning("Units: %s" % self.units) if self.units == 'MM': @@ -875,7 +876,7 @@ class Excellon(Geometry): # is finished since the tools definitions are spread in the Excellon body. We use as units the value # from self.defaults['excellon_units'] log.info("Zeros: %s, Units %s." % (self.zeros, self.units)) - except Exception as e: + except Exception: log.error("Excellon PARSING FAILED. Line %d: %s" % (line_num, eline)) msg = '[ERROR_NOTCL] %s' % \ _("An internal error has ocurred. See shell.\n") diff --git a/flatcamParsers/ParseGerber.py b/flatcamParsers/ParseGerber.py index ba4ab780..1092bf9c 100644 --- a/flatcamParsers/ParseGerber.py +++ b/flatcamParsers/ParseGerber.py @@ -1612,8 +1612,8 @@ class Gerber(Geometry): the geometry appropriately. This call ``scale()``. Don't call it again in descendants. - :param units: "IN" or "MM" - :type units: str + :param obj_units: "IN" or "MM" + :type obj_units: str :return: Scaling factor resulting from unit change. :rtype: float """ diff --git a/flatcamTools/ToolProperties.py b/flatcamTools/ToolProperties.py index b25aff06..1772d485 100644 --- a/flatcamTools/ToolProperties.py +++ b/flatcamTools/ToolProperties.py @@ -12,6 +12,8 @@ from shapely.geometry import MultiPolygon, Polygon from shapely.ops import cascaded_union from copy import deepcopy +import math + import logging import gettext import FlatCAMTranslation as fcTranslate @@ -114,36 +116,54 @@ class Properties(FlatCAMTool): def properties(self): obj_list = self.app.collection.get_selected() if not obj_list: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Properties Tool was not displayed. No object selected.")) + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Properties Tool was not displayed. No object selected.")) self.app.ui.notebook.setTabText(2, _("Tools")) self.properties_frame.hide() self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab) return + + # delete the selection shape, if any + try: + self.app.delete_selection_shape() + except Exception as e: + log.debug("ToolProperties.Properties.properties() --> %s" % str(e)) + + # populate the properties items for obj in obj_list: self.addItems(obj) - self.app.inform.emit('[success] %s' % - _("Object Properties are displayed.")) + self.app.inform.emit('[success] %s' % _("Object Properties are displayed.")) self.app.ui.notebook.setTabText(2, _("Properties Tool")) def addItems(self, obj): parent = self.treeWidget.invisibleRootItem() apertures = '' tools = '' + drills = '' + slots = '' + others = '' font = QtGui.QFont() font.setBold(True) + + # main Items categories obj_type = self.addParent(parent, _('TYPE'), expanded=True, color=QtGui.QColor("#000000"), font=font) obj_name = self.addParent(parent, _('NAME'), expanded=True, color=QtGui.QColor("#000000"), font=font) dims = self.addParent(parent, _('Dimensions'), expanded=True, color=QtGui.QColor("#000000"), font=font) units = self.addParent(parent, _('Units'), expanded=True, color=QtGui.QColor("#000000"), font=font) - options = self.addParent(parent, _('Options'), color=QtGui.QColor("#000000"), font=font) + if obj.kind.lower() == 'gerber': apertures = self.addParent(parent, _('Apertures'), expanded=True, color=QtGui.QColor("#000000"), font=font) else: tools = self.addParent(parent, _('Tools'), expanded=True, color=QtGui.QColor("#000000"), font=font) + if obj.kind.lower() == 'excellon': + drills = self.addParent(parent, _('Drills'), expanded=True, color=QtGui.QColor("#000000"), font=font) + slots = self.addParent(parent, _('Slots'), expanded=True, color=QtGui.QColor("#000000"), font=font) + + if obj.kind.lower() == 'cncjob': + others = self.addParent(parent, _('Others'), expanded=True, color=QtGui.QColor("#000000"), font=font) + separator = self.addParent(parent, '') self.addChild(obj_type, ['%s:' % _('Object Type'), ('%s' % (obj.kind.capitalize()))], True) @@ -196,13 +216,34 @@ class Properties(FlatCAMTool): xmax = [] ymax = [] - for tool_k in obj_prop.tools: + if obj_prop.kind.lower() == 'cncjob': try: - x0, y0, x1, y1 = cascaded_union(obj_prop.tools[tool_k]['solid_geometry']).bounds - xmin.append(x0) - ymin.append(y0) - xmax.append(x1) - ymax.append(y1) + for tool_k in obj_prop.exc_cnc_tools: + x0, y0, x1, y1 = cascaded_union(obj_prop.exc_cnc_tools[tool_k]['solid_geometry']).bounds + xmin.append(x0) + ymin.append(y0) + xmax.append(x1) + ymax.append(y1) + except Exception as ee: + log.debug("PropertiesTool.addItems() --> %s" % str(ee)) + + try: + for tool_k in obj_prop.cnc_tools: + x0, y0, x1, y1 = cascaded_union(obj_prop.cnc_tools[tool_k]['solid_geometry']).bounds + xmin.append(x0) + ymin.append(y0) + xmax.append(x1) + ymax.append(y1) + except Exception as ee: + log.debug("PropertiesTool.addItems() --> %s" % str(ee)) + else: + try: + for tool_k in obj_prop.tools: + x0, y0, x1, y1 = cascaded_union(obj_prop.tools[tool_k]['solid_geometry']).bounds + xmin.append(x0) + ymin.append(y0) + xmax.append(x1) + ymax.append(y1) except Exception as ee: log.debug("PropertiesTool.addItems() --> %s" % str(ee)) @@ -273,14 +314,25 @@ class Properties(FlatCAMTool): self.app.worker_task.emit({'fcn': job_thread, 'params': [obj]}) - f_unit = {'in': _('Inch'), 'mm': _('Metric')}[str(self.app.defaults['units'].lower())] + # Units items + f_unit = {'in': _('Inch'), 'mm': _('MM')}[str(self.app.defaults['units'].lower())] self.addChild(units, ['FlatCAM units:', f_unit], True) + o_unit = { + 'in': _('Inch'), + 'mm': _('Metric'), + 'inch': _('Inch'), + 'metric': _('Metric') + }[str(obj.units_found.lower())] + self.addChild(units, ['Object units:', o_unit], True) + + # Options items for option in obj.options: if option is 'name': continue self.addChild(options, [str(option), str(obj.options[option])], True) + # Items that depend on the object type if obj.kind.lower() == 'gerber': temp_ap = dict() for ap in obj.apertures: @@ -311,10 +363,36 @@ class Properties(FlatCAMTool): apid = self.addParent(apertures, str(ap), expanded=False, color=QtGui.QColor("#000000"), font=font) for key in temp_ap: self.addChild(apid, [str(key), str(temp_ap[key])], True) - elif obj.kind.lower() == 'excellon': + tot_drill_cnt = 0 + tot_slot_cnt = 0 + for tool, value in obj.tools.items(): - self.addChild(tools, [str(tool), str(value['C'])], True) + toolid = self.addParent(tools, str(tool), expanded=False, color=QtGui.QColor("#000000"), font=font) + + drill_cnt = 0 # variable to store the nr of drills per tool + slot_cnt = 0 # variable to store the nr of slots per tool + + # Find no of drills for the current tool + for drill in obj.drills: + if drill['tool'] == tool: + drill_cnt += 1 + + tot_drill_cnt += drill_cnt + + # Find no of slots for the current tool + for slot in obj.slots: + if slot['tool'] == tool: + slot_cnt += 1 + + tot_slot_cnt += slot_cnt + + self.addChild(toolid, [_('Diameter'), str(value['C'])], True) + self.addChild(toolid, [_('Drills number'), str(drill_cnt)], True) + self.addChild(toolid, [_('Slots number'), str(slot_cnt)], True) + + self.addChild(drills, [_('Drills total number:'), str(tot_drill_cnt)], True) + self.addChild(slots, [_('Slots total number:'), str(tot_slot_cnt)], True) elif obj.kind.lower() == 'geometry': for tool, value in obj.tools.items(): geo_tool = self.addParent(tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font) @@ -330,6 +408,7 @@ class Properties(FlatCAMTool): else: self.addChild(geo_tool, [str(k), str(v)], True) elif obj.kind.lower() == 'cncjob': + # for cncjob objects made from gerber or geometry for tool, value in obj.cnc_tools.items(): geo_tool = self.addParent(tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font) for k, v in value.items(): @@ -350,6 +429,33 @@ class Properties(FlatCAMTool): else: self.addChild(geo_tool, [str(k), str(v)], True) + # for cncjob objects made from excellon + for tool, value in obj.exc_cnc_tools.items(): + exc_tool = self.addParent( + tools, str(value['tool']), expanded=False, color=QtGui.QColor("#000000"), font=font + ) + self.addChild(exc_tool, [_('Diameter'), str(tool)], True) + for k, v in value.items(): + if k == 'solid_geometry': + printed_value = _('Present') if v else _('None') + self.addChild(exc_tool, [str(k), printed_value], True) + elif k == 'nr_drills': + self.addChild(exc_tool, [_("Drills number"), str(v)], True) + elif k == 'nr_slots': + self.addChild(exc_tool, [_("Slots number"), str(v)], True) + else: + pass + + r_time = obj.routing_time + if r_time > 1: + units_lbl = 'min' + else: + r_time *= 60 + units_lbl = 'sec' + r_time = math.ceil(float(r_time)) + self.addChild(others, ['%s (%s):' % (_('Routing time'), units_lbl), str(r_time)], True) + self.addChild(others, ['%s (%s):' % (_('Travelled distance'), f_unit), str(obj.travel_distance)], True) + self.addChild(separator, ['']) def addParent(self, parent, title, expanded=False, color=None, font=None):