From 386c3408ca0dbbbf8e9197f631f55531ade8801d Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Tue, 26 Feb 2019 00:33:31 +0200 Subject: [PATCH] - added in Preferences a new Category: Gerber Advanced Options. For now it controls the display of Gerber Aperture Table and the "follow" attribute4 - fixed FlatCAMGerber.merge() to merge the self.apertures[ap]['solid_geometry'] too - started to work on a new feature that allow adding a ToolChange GCode macro - GUI added both in CNCJob Selected tab and in CNCJob Preferences - added a limited 'sort-of' Gerber Editor: it allows buffering and scaling of apertures --- FlatCAMApp.py | 30 ++++++- FlatCAMGUI.py | 169 ++++++++++++++++++++++++++++++++-- FlatCAMObj.py | 185 ++++++++++++++++++++++++++++++++++++-- ObjectUI.py | 140 ++++++++++++++++++++++++++--- README.md | 4 + postprocessors/default.py | 3 +- 6 files changed, 498 insertions(+), 33 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index b4266be3..89fea344 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -330,11 +330,13 @@ class App(QtCore.QObject): "global_workspace": self.ui.general_defaults_form.general_gui_group.workspace_cb, "global_workspaceT": self.ui.general_defaults_form.general_gui_group.wk_cb, + # Gerber General "gerber_plot": self.ui.gerber_defaults_form.gerber_gen_group.plot_cb, "gerber_solid": self.ui.gerber_defaults_form.gerber_gen_group.solid_cb, "gerber_multicolored": self.ui.gerber_defaults_form.gerber_gen_group.multicolored_cb, "gerber_circle_steps": self.ui.gerber_defaults_form.gerber_gen_group.circle_steps_entry, + # Gerber Options "gerber_isotooldia": self.ui.gerber_defaults_form.gerber_opt_group.iso_tool_dia_entry, "gerber_isopasses": self.ui.gerber_defaults_form.gerber_opt_group.iso_width_entry, "gerber_isooverlap": self.ui.gerber_defaults_form.gerber_opt_group.iso_overlap_entry, @@ -345,6 +347,12 @@ class App(QtCore.QObject): "gerber_bboxmargin": self.ui.gerber_defaults_form.gerber_opt_group.bbmargin_entry, "gerber_bboxrounded": self.ui.gerber_defaults_form.gerber_opt_group.bbrounded_cb, + # Gerber Advanced Options + "gerber_aperture_display": self.ui.gerber_defaults_form.gerber_adv_opt_group.aperture_table_visibility_cb, + "gerber_aperture_scale_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.scale_aperture_entry, + "gerber_aperture_buffer_factor": self.ui.gerber_defaults_form.gerber_adv_opt_group.buffer_aperture_entry, + "gerber_follow": self.ui.gerber_defaults_form.gerber_adv_opt_group.follow_cb, + # Excellon General "excellon_plot": self.ui.excellon_defaults_form.excellon_gen_group.plot_cb, "excellon_solid": self.ui.excellon_defaults_form.excellon_gen_group.solid_cb, @@ -428,9 +436,14 @@ class App(QtCore.QObject): "cncjob_fr_decimals": self.ui.cncjob_defaults_form.cncjob_gen_group.fr_dec_entry, "cncjob_steps_per_circle": self.ui.cncjob_defaults_form.cncjob_gen_group.steps_per_circle_entry, + # CNC Job Options "cncjob_prepend": self.ui.cncjob_defaults_form.cncjob_opt_group.prepend_text, "cncjob_append": self.ui.cncjob_defaults_form.cncjob_opt_group.append_text, + # CNC Job Advanced Options + "cncjob_toolchange_macro": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_text, + "cncjob_toolchange_macro_enable": self.ui.cncjob_defaults_form.cncjob_adv_opt_group.toolchange_cb, + # NCC Tool "tools_ncctools": self.ui.tools_defaults_form.tools_ncc_group.ncc_tool_dia_entry, "tools_nccoverlap": self.ui.tools_defaults_form.tools_ncc_group.ncc_overlap_entry, @@ -605,6 +618,7 @@ class App(QtCore.QObject): "global_point_clipboard_format": "(%.4f, %.4f)", "global_zdownrate": None, + # Gerber General "gerber_plot": True, "gerber_solid": True, "gerber_multicolored": False, @@ -612,6 +626,7 @@ class App(QtCore.QObject): "gerber_isopasses": 1, "gerber_isooverlap": 0.15, + # Gerber Options "gerber_combine_passes": False, "gerber_milling_type": "cl", "gerber_noncoppermargin": 0.1, @@ -621,6 +636,12 @@ class App(QtCore.QObject): "gerber_circle_steps": 64, "gerber_use_buffer_for_union": True, + # Gerber Advanced Options + "gerber_aperture_display": False, + "gerber_aperture_scale_factor": 1.0, + "gerber_aperture_buffer_factor": 0.0, + "gerber_follow": False, + # Excellon General "excellon_plot": True, "excellon_solid": True, @@ -696,14 +717,21 @@ class App(QtCore.QObject): "geometry_segx": 0.0, "geometry_segy": 0.0, + # CNC Job General "cncjob_plot": True, "cncjob_plot_kind": 'all', "cncjob_tooldia": 0.0393701, "cncjob_coords_decimals": 4, "cncjob_fr_decimals": 2, + "cncjob_steps_per_circle": 64, + + # CNC Job Options "cncjob_prepend": "", "cncjob_append": "", - "cncjob_steps_per_circle": 64, + + # CNC Job Advanced Options + "cncjob_toolchange_macro": "", + "cncjob_toolchange_macro_enable": False, "tools_ncctools": "1.0, 0.5", "tools_nccoverlap": 0.4, diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py index 768a9ee4..dee90af3 100644 --- a/FlatCAMGUI.py +++ b/FlatCAMGUI.py @@ -2576,10 +2576,14 @@ class GerberPreferencesUI(QtWidgets.QWidget): self.gerber_gen_group = GerberGenPrefGroupUI() self.gerber_gen_group.setFixedWidth(250) self.gerber_opt_group = GerberOptPrefGroupUI() - self.gerber_opt_group.setFixedWidth(250) + self.gerber_opt_group.setFixedWidth(200) + self.gerber_adv_opt_group = GerberAdvOptPrefGroupUI() + self.gerber_adv_opt_group.setFixedWidth(200) self.layout.addWidget(self.gerber_gen_group) self.layout.addWidget(self.gerber_opt_group) + self.layout.addWidget(self.gerber_adv_opt_group) + self.layout.addStretch() @@ -2700,9 +2704,13 @@ class CNCJobPreferencesUI(QtWidgets.QWidget): self.cncjob_gen_group.setFixedWidth(270) self.cncjob_opt_group = CNCJobOptPrefGroupUI() self.cncjob_opt_group.setFixedWidth(260) + self.cncjob_adv_opt_group = CNCJobAdvOptPrefGroupUI() + self.cncjob_adv_opt_group.setFixedWidth(260) self.layout.addWidget(self.cncjob_gen_group) self.layout.addWidget(self.cncjob_opt_group) + self.layout.addWidget(self.cncjob_adv_opt_group) + self.layout.addStretch() @@ -3288,26 +3296,27 @@ class GerberGenPrefGroupUI(OptionsGroupUI): grid0 = QtWidgets.QGridLayout() self.layout.addLayout(grid0) - # Plot CB - self.plot_cb = FCCheckBox(label='Plot') - self.plot_options_label.setToolTip( - "Plot (show) this object." - ) - grid0.addWidget(self.plot_cb, 0, 0) # Solid CB self.solid_cb = FCCheckBox(label='Solid') self.solid_cb.setToolTip( "Solid color polygons." ) - grid0.addWidget(self.solid_cb, 0, 1) + grid0.addWidget(self.solid_cb, 0, 0) # Multicolored CB self.multicolored_cb = FCCheckBox(label='M-Color') self.multicolored_cb.setToolTip( "Draw polygons in different colors." ) - grid0.addWidget(self.multicolored_cb, 0, 2) + grid0.addWidget(self.multicolored_cb, 0, 1) + + # Plot CB + self.plot_cb = FCCheckBox(label='Plot') + self.plot_options_label.setToolTip( + "Plot (show) this object." + ) + grid0.addWidget(self.plot_cb, 0, 2) # Number of circle steps for circular aperture linear approximation self.circle_steps_label = QtWidgets.QLabel("Circle Steps:") @@ -3447,6 +3456,73 @@ class GerberOptPrefGroupUI(OptionsGroupUI): self.layout.addStretch() +class GerberAdvOptPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "Gerber Adv. Options Preferences", parent=parent) + super(GerberAdvOptPrefGroupUI, self).__init__(self) + + self.setTitle(str("Gerber Adv. Options")) + + + ## Advanced Gerber Parameters + self.adv_param_label = QtWidgets.QLabel("Advanced Param.:") + self.adv_param_label.setToolTip( + "A list of Gerber advanced parameters.\n" + "Those parameters are available only for\n" + "Advanced App. Level." + ) + self.layout.addWidget(self.adv_param_label) + + grid0 = QtWidgets.QGridLayout() + self.layout.addLayout(grid0) + + # Follow Attribute + self.follow_cb = FCCheckBox(label='"Follow"') + self.follow_cb.setToolTip( + "Generate a 'Follow' geometry.\n" + "This means that it will cut through\n" + "the middle of the trace." + + ) + grid0.addWidget(self.follow_cb, 0, 0) + + # Aperture Table Visibility CB + self.aperture_table_visibility_cb = FCCheckBox(label='Table Show/Hide') + self.aperture_table_visibility_cb.setToolTip( + "Toggle the display of the Gerber Apertures Table.\n" + "Also, on hide, it will delete all mark shapes\n" + "that are drawn on canvas." + + ) + grid0.addWidget(self.aperture_table_visibility_cb, 1, 0) + + # Scale Aperture Factor + self.scale_aperture_label = QtWidgets.QLabel('Ap. Scale Factor:') + self.scale_aperture_label.setToolTip( + "Change the size of the selected apertures.\n" + "Factor by which to multiply\n" + "geometric features of this object." + ) + grid0.addWidget(self.scale_aperture_label, 2, 0) + + self.scale_aperture_entry = FloatEntry2() + grid0.addWidget(self.scale_aperture_entry, 2, 1) + + # Buffer Aperture Factor + self.buffer_aperture_label = QtWidgets.QLabel('Ap. Buffer Factor:') + self.buffer_aperture_label.setToolTip( + "Change the size of the selected apertures.\n" + "Factor by which to expand/shrink\n" + "geometric features of this object." + ) + grid0.addWidget(self.buffer_aperture_label, 3, 0) + + self.buffer_aperture_entry = FloatEntry2() + grid0.addWidget(self.buffer_aperture_entry, 3, 1) + + self.layout.addStretch() + + class ExcellonGenPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): @@ -4546,6 +4622,81 @@ class CNCJobOptPrefGroupUI(OptionsGroupUI): self.layout.addStretch() +class CNCJobAdvOptPrefGroupUI(OptionsGroupUI): + def __init__(self, parent=None): + # OptionsGroupUI.__init__(self, "CNC Job Advanced Options Preferences", parent=None) + super(CNCJobAdvOptPrefGroupUI, self).__init__(self) + + self.setTitle(str("CNC Job Adv. Options")) + + ## Export G-Code + self.export_gcode_label = QtWidgets.QLabel("Export G-Code:") + self.export_gcode_label.setToolTip( + "Export and save G-Code to\n" + "make this object to a file." + ) + self.layout.addWidget(self.export_gcode_label) + + # Prepend to G-Code + toolchangelabel = QtWidgets.QLabel('Toolchange G-Code:') + toolchangelabel.setToolTip( + "Type here any G-Code commands you would\n" + "like to be executed when Toolchange event is encountered.\n" + "This will constitute a Custom Toolchange GCode,\n" + "or a Toolchange Macro." + ) + self.layout.addWidget(toolchangelabel) + + self.toolchange_text = FCTextArea() + self.layout.addWidget(self.toolchange_text) + + hlay = QtWidgets.QHBoxLayout() + self.layout.addLayout(hlay) + + # Toolchange Replacement GCode + self.toolchange_cb = FCCheckBox(label='Use Toolchange Macro') + self.toolchange_cb.setToolTip( + "Check this box if you want to use\n" + "a Custom Toolchange GCode (macro)." + ) + hlay.addWidget(self.toolchange_cb) + hlay.addStretch() + + hlay1 = QtWidgets.QHBoxLayout() + self.layout.addLayout(hlay1) + + # Variable list + self.tc_variable_combo = FCComboBox() + self.tc_variable_combo.setToolTip( + "A list of the FlatCAM variables that can be used\n" + "in the Toolchange event.\n" + "They have to be surrounded by the '%' symbol" + ) + hlay1.addWidget(self.tc_variable_combo) + + # Populate the Combo Box + variables = ['tool', 'toolC', 't_drills', 'toolchangex', 'toolchangey', 'toolchangez'] + self.tc_variable_combo.addItems(variables) + self.tc_variable_combo.setItemData(0, "tool = tool number", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(1, "toolC = tool diameter", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(2, "t_drills = for Excellon, total number of drills", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(3, "toolchangex = X coord for Toolchange", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(4, "toolchangey = Y coord for Toolchange", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(5, "toolchangez = Z coord for Toolchange", Qt.ToolTipRole) + + hlay1.addStretch() + + # Insert Variable into the Toolchange G-Code Text Box + self.tc_insert_buton = FCButton("Insert") + self.tc_insert_buton.setToolTip( + "Insert the variable in the GCode Box\n" + "surrounded by the '%' symbol." + ) + hlay1.addWidget(self.tc_insert_buton) + + self.layout.addStretch() + + class ToolsNCCPrefGroupUI(OptionsGroupUI): def __init__(self, parent=None): # OptionsGroupUI.__init__(self, "NCC Tool Options", parent=parent) diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 75e9a19f..b2ae41fb 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -382,26 +382,39 @@ class FlatCAMGerber(FlatCAMObj, Gerber): grb_final.solid_geometry = [] grb_final.follow_geometry = [] + if not grb_final.apertures: + grb_final.apertures = {} + if type(grb_final.solid_geometry) is not list: grb_final.solid_geometry = [grb_final.solid_geometry] grb_final.follow_geometry = [grb_final.follow_geometry] for grb in grb_list: - for option in grb.options: - if option is not 'name': - try: - grb_final.options[option] = grb.options[option] - except: - log.warning("Failed to copy option.", option) # Expand lists if type(grb) is list: FlatCAMGerber.merge(grb, grb_final) else: # If not list, just append + for option in grb.options: + if option is not 'name': + try: + grb_final.options[option] = grb.options[option] + except: + log.warning("Failed to copy option.", option) + for geos in grb.solid_geometry: grb_final.solid_geometry.append(geos) grb_final.follow_geometry.append(geos) + for ap in grb.apertures: + if ap not in grb_final.apertures: + grb_final.apertures[ap] = grb.apertures[ap] + else: + if 'solid_geometry' not in grb_final.apertures[ap]: + grb_final.apertures[ap]['solid_geometry'] = [] + for geo in grb.apertures[ap]['solid_geometry']: + grb_final.apertures[ap]['solid_geometry'].append(geo) + grb_final.solid_geometry = MultiPolygon(grb_final.solid_geometry) grb_final.follow_geometry = MultiPolygon(grb_final.follow_geometry) @@ -425,7 +438,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber): "noncoppermargin": 0.0, "noncopperrounded": False, "bboxmargin": 0.0, - "bboxrounded": False + "bboxrounded": False, + "aperture_display": False, + "aperture_scale_factor": 1.0, + "aperture_buffer_factor": 0.0, + "follow": False }) # type of isolation: 0 = exteriors, 1 = interiors, 2 = complete isolation (both interiors and exteriors) @@ -479,7 +496,11 @@ class FlatCAMGerber(FlatCAMObj, Gerber): "noncoppermargin": self.ui.noncopper_margin_entry, "noncopperrounded": self.ui.noncopper_rounded_cb, "bboxmargin": self.ui.bbmargin_entry, - "bboxrounded": self.ui.bbrounded_cb + "bboxrounded": self.ui.bbrounded_cb, + "aperture_display": self.ui.aperture_table_visibility_cb, + "aperture_scale_factor": self.ui.scale_aperture_entry, + "aperture_buffer_factor": self.ui.buffer_aperture_entry, + "follow": self.ui.follow_cb }) # Fill form fields only on object create @@ -498,6 +519,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.ui.generate_noncopper_button.clicked.connect(self.on_generatenoncopper_button_click) self.ui.aperture_table_visibility_cb.stateChanged.connect(self.on_aperture_table_visibility_change) self.ui.follow_cb.stateChanged.connect(self.on_follow_cb_click) + self.ui.scale_aperture_button.clicked.connect(self.on_scale_aperture_click) + self.ui.buffer_aperture_button.clicked.connect(self.on_buffer_aperture_click) + self.ui.new_grb_button.clicked.connect(self.on_new_modified_gerber) # Show/Hide Advanced Options if self.app.defaults["global_app_level"] == 'b': @@ -513,6 +537,9 @@ class FlatCAMGerber(FlatCAMObj, Gerber): else: self.ui.level.setText('Advanced') + # set initial state of the aperture table and associated widgets + self.on_aperture_table_visibility_change() + self.build_ui() def build_ui(self): @@ -963,17 +990,151 @@ class FlatCAMGerber(FlatCAMObj, Gerber): self.ui.scale_aperture_label.setVisible(True) self.ui.scale_aperture_entry.setVisible(True) self.ui.scale_aperture_button.setVisible(True) + + self.ui.buffer_aperture_label.setVisible(True) + self.ui.buffer_aperture_entry.setVisible(True) + self.ui.buffer_aperture_button.setVisible(True) + + self.ui.new_grb_label.setVisible(True) + self.ui.new_grb_button.setVisible(True) + else: self.ui.apertures_table.setVisible(False) self.ui.scale_aperture_label.setVisible(False) self.ui.scale_aperture_entry.setVisible(False) self.ui.scale_aperture_button.setVisible(False) + self.ui.buffer_aperture_label.setVisible(False) + self.ui.buffer_aperture_entry.setVisible(False) + self.ui.buffer_aperture_button.setVisible(False) + + self.ui.new_grb_label.setVisible(False) + self.ui.new_grb_button.setVisible(False) + # on hide disable all mark plots for row in range(self.ui.apertures_table.rowCount()): self.ui.apertures_table.cellWidget(row, 5).set_value(False) self.mark_shapes.clear(update=True) + def on_scale_aperture_click(self, signal): + try: + factor = self.ui.scale_aperture_entry.get_value() + except Exception as e: + log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e)) + self.app.inform.emit("[ERROR_NOTCL] The aperture scale factor value is missing or wrong format.") + return + + def scale_recursion(geom): + if type(geom) == list or type(geom) is MultiPolygon: + geoms=list() + for local_geom in geom: + geoms.append(scale_recursion(local_geom)) + return geoms + else: + return affinity.scale(geom, factor, factor, origin='center') + + if not self.ui.apertures_table.selectedItems(): + self.app.inform.emit("[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again.") + return + + for x in self.ui.apertures_table.selectedItems(): + try: + apid = self.ui.apertures_table.item(x.row(), 1).text() + except Exception as e: + log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e)) + + self.apertures[apid]['solid_geometry'] = scale_recursion(self.apertures[apid]['solid_geometry']) + + self.on_mark_cb_click_table() + + def on_buffer_aperture_click(self, signal): + try: + buff_value = self.ui.buffer_aperture_entry.get_value() + except Exception as e: + log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e)) + self.app.inform.emit("[ERROR_NOTCL] The aperture buffer value is missing or wrong format.") + return + + def buffer_recursion(geom): + if type(geom) == list or type(geom) is MultiPolygon: + geoms=list() + for local_geom in geom: + geoms.append(buffer_recursion(local_geom)) + return geoms + else: + return geom.buffer(buff_value, join_style=2) + + if not self.ui.apertures_table.selectedItems(): + self.app.inform.emit("[WARNING_NOTCL] No aperture to scale. Select at least one aperture and try again.") + return + + for x in self.ui.apertures_table.selectedItems(): + try: + apid = self.ui.apertures_table.item(x.row(), 1).text() + except Exception as e: + log.debug("FlatCAMGerber.on_scale_aperture_click() --> %s" % str(e)) + + self.apertures[apid]['solid_geometry'] = buffer_recursion(self.apertures[apid]['solid_geometry']) + + self.on_mark_cb_click_table() + + def on_new_modified_gerber(self, signal): + + name = '%s_ap_mod' % str(self.options['name']) + apertures = deepcopy(self.apertures) + options = self.options + + # geometry storage + poly_buff = [] + + # How the object should be initialized + def obj_init(gerber_obj, app_obj): + assert isinstance(gerber_obj, FlatCAMGerber), \ + "Expected to initialize a FlatCAMGerber but got %s" % type(gerber_obj) + + gerber_obj.source_file = '' + gerber_obj.multigeo = False + gerber_obj.follow = False + + gerber_obj.apertures = apertures + for option in options: + # we don't want to overwrite the new name and we don't want to share the 'plot' state + # because the new object should ve visible even if the source is not visible + if option != 'name' and option != 'plot': + gerber_obj.options[option] = options[option] + + # regenerate solid_geometry + app_obj.log.debug("Creating new Gerber object. Joining %s polygons.") + for ap in apertures: + for geo in apertures[ap]['solid_geometry']: + poly_buff.append(geo) + + # buffering the poly_buff + new_geo = MultiPolygon(poly_buff) + new_geo = new_geo.buffer(0.0000001) + new_geo = new_geo.buffer(-0.0000001) + + gerber_obj.solid_geometry = new_geo + + app_obj.log.debug("Finished creation of a new Gerber object. Polygons joined.") + + log.debug("on_new_modified_gerber()") + + with self.app.proc_container.new("Generating Gerber") as proc: + + self.app.progress.emit(10) + + ### Object creation ### + ret = self.app.new_object("gerber", name, obj_init, autoselected=False) + if ret == 'fail': + self.app.inform.emit('[ERROR_NOTCL] Cretion of Gerber failed.') + return + + self.app.progress.emit(100) + + # GUI feedback + self.app.inform.emit("[success] Created: " + name) + def convert_units(self, units): """ Converts the units of the object by scaling dimensions in all geometry @@ -4698,7 +4859,9 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): "prepend": "", "dwell": False, "dwelltime": 1, - "type": 'Geometry' + "type": 'Geometry', + "toolchange_macro": '', + "toolchange_macro_enable": False }) ''' @@ -4900,6 +5063,8 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): # "tooldia": self.ui.tooldia_entry, "append": self.ui.append_text, "prepend": self.ui.prepend_text, + "toolchange_macro": self.ui.toolchange_text, + "toolchange_macro_enable": self.ui.toolchange_cb }) # Fill form fields only on object create @@ -4924,8 +5089,10 @@ class FlatCAMCNCjob(FlatCAMObj, CNCjob): if self.app.defaults["global_app_level"] == 'b': self.ui.level.setText('Basic') + self.ui.cnc_frame.hide() else: self.ui.level.setText('Advanced') + self.ui.cnc_frame.show() self.ui.updateplot_button.clicked.connect(self.on_updateplot_button_click) self.ui.export_gcode_button.clicked.connect(self.on_exportgcode_button_click) diff --git a/ObjectUI.py b/ObjectUI.py index ad365a29..57a1730c 100644 --- a/ObjectUI.py +++ b/ObjectUI.py @@ -1,7 +1,8 @@ import sys -from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets +from PyQt5 import QtGui, QtCore, QtWidgets +from PyQt5.QtCore import Qt from GUIElements import FCEntry, FloatEntry, EvalEntry, FCCheckBox, FCTable, \ - LengthEntry, FCTextArea, IntEntry, RadioSet, OptionalInputSection, FCComboBox, FloatEntry2, EvalEntry2 + LengthEntry, FCTextArea, IntEntry, RadioSet, OptionalInputSection, FCComboBox, FloatEntry2, EvalEntry2, FCButton from camlib import Excellon @@ -180,6 +181,12 @@ class GerberObjectUI(ObjectUI): # Aperture Table Visibility CB self.aperture_table_visibility_cb = FCCheckBox('Show/Hide') + self.aperture_table_visibility_cb.setToolTip( + "Toogle the display of the Gerber Apertures Table.\n" + "Also, on hide, it will delete all mark shapes\n" + "that are drawn on canvas." + + ) self.aperture_table_visibility_cb.setLayoutDirection(QtCore.Qt.RightToLeft) hlay_plot.addWidget(self.aperture_table_visibility_cb) @@ -207,30 +214,69 @@ class GerberObjectUI(ObjectUI): # self.apertures_table.setColumnHidden(5, True) #### Aperture Scale #### - self.scale_aperture_grid = QtWidgets.QGridLayout() - self.custom_box.addLayout(self.scale_aperture_grid) + self.transform_aperture_grid = QtWidgets.QGridLayout() + self.custom_box.addLayout(self.transform_aperture_grid) - # Factor - self.scale_aperture_label = QtWidgets.QLabel('Factor:') + # Scale Aperture Factor + self.scale_aperture_label = QtWidgets.QLabel('Scale Factor:') self.scale_aperture_label.setToolTip( "Change the size of the selected apertures.\n" "Factor by which to multiply\n" "geometric features of this object." ) self.scale_aperture_label.setFixedWidth(90) - self.scale_aperture_grid.addWidget(self.scale_aperture_label, 0, 0) + self.transform_aperture_grid.addWidget(self.scale_aperture_label, 0, 0) self.scale_aperture_entry = FloatEntry2() - self.scale_aperture_entry.set_value(1.0) - self.scale_aperture_grid.addWidget(self.scale_aperture_entry, 0, 1) + self.transform_aperture_grid.addWidget(self.scale_aperture_entry, 0, 1) # Scale Button self.scale_aperture_button = QtWidgets.QPushButton('Scale') self.scale_aperture_button.setToolTip( "Perform scaling operation." ) - self.scale_aperture_button.setFixedWidth(40) - self.scale_aperture_grid.addWidget(self.scale_aperture_button, 0, 2) + self.scale_aperture_button.setFixedWidth(50) + self.transform_aperture_grid.addWidget(self.scale_aperture_button, 0, 2) + + # Buffer Aperture Factor + self.buffer_aperture_label = QtWidgets.QLabel('Buffer Factor:') + self.buffer_aperture_label.setToolTip( + "Change the size of the selected apertures.\n" + "Factor by which to expand/shrink\n" + "geometric features of this object." + ) + self.buffer_aperture_label.setFixedWidth(90) + self.transform_aperture_grid.addWidget(self.buffer_aperture_label, 1, 0) + + self.buffer_aperture_entry = FloatEntry2() + self.transform_aperture_grid.addWidget(self.buffer_aperture_entry, 1, 1) + + # Buffer Button + self.buffer_aperture_button = QtWidgets.QPushButton('Buffer') + self.buffer_aperture_button.setToolTip( + "Perform scaling operation." + ) + self.buffer_aperture_button.setFixedWidth(50) + self.transform_aperture_grid.addWidget(self.buffer_aperture_button, 1, 2) + + new_hlay = QtWidgets.QHBoxLayout() + self.custom_box.addLayout(new_hlay) + + self.new_grb_label = QtWidgets.QLabel("Generate new Gerber Object:") + self.new_grb_label.setToolTip( + "Will generate a new Gerber object from the changed apertures.\n" + "This new object can then be isolated etc." + ) + new_hlay.addWidget(self.new_grb_label) + + new_hlay.addStretch() + + self.new_grb_button = FCButton('Go') + self.new_grb_button.setToolTip( + "Will generate a new Gerber object from the changed apertures.\n" + "This new object can then be isolated etc.") + self.new_grb_button.setFixedWidth(50) + new_hlay.addWidget(self.new_grb_button) # start with apertures table hidden self.apertures_table.setVisible(False) @@ -238,6 +284,10 @@ class GerberObjectUI(ObjectUI): self.scale_aperture_entry.setVisible(False) self.scale_aperture_button.setVisible(False) + self.buffer_aperture_label.setVisible(False) + self.buffer_aperture_entry.setVisible(False) + self.buffer_aperture_button.setVisible(False) + # Isolation Routing self.isolation_routing_label = QtWidgets.QLabel("Isolation Routing:") self.isolation_routing_label.setToolTip( @@ -1362,7 +1412,7 @@ class CNCObjectUI(ObjectUI): ) self.custom_box.addWidget(self.export_gcode_label) - # Prepend text to Gerber + # Prepend text to GCode prependlabel = QtWidgets.QLabel('Prepend to CNC Code:') prependlabel.setToolTip( "Type here any G-Code commands you would\n" @@ -1373,7 +1423,7 @@ class CNCObjectUI(ObjectUI): self.prepend_text = FCTextArea() self.custom_box.addWidget(self.prepend_text) - # Append text to Gerber + # Append text to GCode appendlabel = QtWidgets.QLabel('Append to CNC Code') appendlabel.setToolTip( "Type here any G-Code commands you would\n" @@ -1385,6 +1435,70 @@ class CNCObjectUI(ObjectUI): self.append_text = FCTextArea() self.custom_box.addWidget(self.append_text) + self.cnc_frame = QtWidgets.QFrame() + self.cnc_frame.setContentsMargins(0, 0, 0, 0) + self.custom_box.addWidget(self.cnc_frame) + self.cnc_box = QtWidgets.QVBoxLayout() + self.cnc_box.setContentsMargins(0, 0, 0, 0) + self.cnc_frame.setLayout(self.cnc_box) + + # Prepend to G-Code + toolchangelabel = QtWidgets.QLabel('Toolchange G-Code:') + toolchangelabel.setToolTip( + "Type here any G-Code commands you would\n" + "like to be executed when Toolchange event is encountered.\n" + "This will constitute a Custom Toolchange GCode,\n" + "or a Toolchange Macro." + ) + self.cnc_box.addWidget(toolchangelabel) + + self.toolchange_text = FCTextArea() + self.cnc_box.addWidget(self.toolchange_text) + + cnclay = QtWidgets.QHBoxLayout() + self.cnc_box.addLayout(cnclay) + + # Toolchange Replacement GCode + self.toolchange_cb = FCCheckBox(label='Use Toolchange Macro') + self.toolchange_cb.setToolTip( + "Check this box if you want to use\n" + "a Custom Toolchange GCode (macro)." + ) + cnclay.addWidget(self.toolchange_cb) + cnclay.addStretch() + + cnclay1 = QtWidgets.QHBoxLayout() + self.cnc_box.addLayout(cnclay1) + + # Variable list + self.tc_variable_combo = FCComboBox() + self.tc_variable_combo.setToolTip( + "A list of the FlatCAM variables that can be used\n" + "in the Toolchange event.\n" + "They have to be surrounded by the '%' symbol" + ) + cnclay1.addWidget(self.tc_variable_combo) + + # Populate the Combo Box + variables = ['tool', 'toolC', 't_drills', 'toolchangex', 'toolchangey', 'toolchangez'] + self.tc_variable_combo.addItems(variables) + self.tc_variable_combo.setItemData(0, "tool = tool number", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(1, "toolC = tool diameter", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(2, "t_drills = for Excellon, total number of drills", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(3, "toolchangex = X coord for Toolchange", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(4, "toolchangey = Y coord for Toolchange", Qt.ToolTipRole) + self.tc_variable_combo.setItemData(5, "toolchangez = Z coord for Toolchange", Qt.ToolTipRole) + + cnclay1.addStretch() + + # Insert Variable into the Toolchange G-Code Text Box + self.tc_insert_buton = FCButton("Insert") + self.tc_insert_buton.setToolTip( + "Insert the variable in the GCode Box\n" + "surrounded by the '%' symbol." + ) + cnclay1.addWidget(self.tc_insert_buton) + h_lay = QtWidgets.QHBoxLayout() h_lay.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.custom_box.addLayout(h_lay) diff --git a/README.md b/README.md index 242d6618..14ebdf99 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,10 @@ CAD program, and create G-Code for Isolation routing. - fixed the Gerber object UI layout - added ability to mark individual apertures in Gerber file using the Gerber Aperture Table - more modifications for the Gerber UI layout; made 'follow' an advanced Gerber option +- added in Preferences a new Category: Gerber Advanced Options. For now it controls the display of Gerber Aperture Table and the "follow" attribute4 +- fixed FlatCAMGerber.merge() to merge the self.apertures[ap]['solid_geometry'] too +- started to work on a new feature that allow adding a ToolChange GCode macro - GUI added both in CNCJob Selected tab and in CNCJob Preferences +- added a limited 'sort-of' Gerber Editor: it allows buffering and scaling of apertures 24.02.2019 diff --git a/postprocessors/default.py b/postprocessors/default.py index b944b1ab..49542562 100644 --- a/postprocessors/default.py +++ b/postprocessors/default.py @@ -105,7 +105,8 @@ G00 X{toolchangex} Y{toolchangey} T{tool} M6 (MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills}) -M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex), +M0 +""".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),