diff --git a/CHANGELOG.md b/CHANGELOG.md index c569621c..7059bb24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ CHANGELOG for FlatCAM beta ================================================= +21.11.2020 + +- Tool Copper Thieving - made sure that the clearance for pattern plating mask is applied also for positive values +- Tool Copper Thieving - when creating pattern plating masks now the user can select to use only the pads and that's useful when the soldermsk Gerber is actually a copper Gerber +- Tool Copper Thieving - changed the units for plated area from mm2 in cm2 when the app units are Metric +- Calculator Tool - Electroplating Calculator - changing the area will update the current value +- GUI Elements FCDoubleSpinner and FCSpinner: modified the context menu to not allow cut/paste/delete/step_up/step_down when the GUI element is set as Read Only + 20.11.2020 - fixed the Distance adn Minimal Distance Tools not showing diff --git a/appGUI/GUIElements.py b/appGUI/GUIElements.py index ac49bac7..f3b1c852 100644 --- a/appGUI/GUIElements.py +++ b/appGUI/GUIElements.py @@ -974,6 +974,11 @@ class FCSpinner(QtWidgets.QSpinBox): self.menu = QtWidgets.QMenu() line_edit = self.lineEdit() + if line_edit.isReadOnly(): + undo_action = QAction('%s' % _("Read Only"), self) + self.menu.addAction(undo_action) + self.menu.addSeparator() + # UNDO undo_action = QAction('%s\t%s' % (_("Undo"), _('Ctrl+Z')), self) self.menu.addAction(undo_action) @@ -994,7 +999,7 @@ class FCSpinner(QtWidgets.QSpinBox): cut_action = QAction('%s\t%s' % (_("Cut"), _('Ctrl+X')), self) self.menu.addAction(cut_action) cut_action.triggered.connect(self.cut_text) - if not line_edit.hasSelectedText(): + if not line_edit.hasSelectedText() or line_edit.isReadOnly(): cut_action.setDisabled(True) # COPY @@ -1008,11 +1013,15 @@ class FCSpinner(QtWidgets.QSpinBox): paste_action = QAction('%s\t%s' % (_("Paste"), _('Ctrl+V')), self) self.menu.addAction(paste_action) paste_action.triggered.connect(self.paste_text) + if line_edit.isReadOnly(): + paste_action.setDisabled(True) # DELETE delete_action = QAction('%s\t%s' % (_("Delete"), _('Del')), self) self.menu.addAction(delete_action) delete_action.triggered.connect(line_edit.del_) + if line_edit.isReadOnly(): + delete_action.setDisabled(True) self.menu.addSeparator() @@ -1027,11 +1036,15 @@ class FCSpinner(QtWidgets.QSpinBox): step_up_action = QAction('%s\t%s' % (_("Step Up"), ''), self) self.menu.addAction(step_up_action) step_up_action.triggered.connect(self.stepUp) + if line_edit.isReadOnly(): + step_up_action.setDisabled(True) # STEP DOWN step_down_action = QAction('%s\t%s' % (_("Step Down"), ''), self) self.menu.addAction(step_down_action) step_down_action.triggered.connect(self.stepDown) + if line_edit.isReadOnly(): + step_down_action.setDisabled(True) self.menu.exec_(event.globalPos()) @@ -1360,6 +1373,11 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): self.menu = QtWidgets.QMenu() line_edit = self.lineEdit() + if line_edit.isReadOnly(): + undo_action = QAction('%s' % _("Read Only"), self) + self.menu.addAction(undo_action) + self.menu.addSeparator() + # UNDO undo_action = QAction('%s\t%s' % (_("Undo"), _('Ctrl+Z')), self) self.menu.addAction(undo_action) @@ -1380,7 +1398,7 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): cut_action = QAction('%s\t%s' % (_("Cut"), _('Ctrl+X')), self) self.menu.addAction(cut_action) cut_action.triggered.connect(self.cut_text) - if not line_edit.hasSelectedText(): + if not line_edit.hasSelectedText() or line_edit.isReadOnly(): cut_action.setDisabled(True) # COPY @@ -1394,11 +1412,15 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): paste_action = QAction('%s\t%s' % (_("Paste"), _('Ctrl+V')), self) self.menu.addAction(paste_action) paste_action.triggered.connect(self.paste_text) + if line_edit.isReadOnly(): + paste_action.setDisabled(True) # DELETE delete_action = QAction('%s\t%s' % (_("Delete"), _('Del')), self) self.menu.addAction(delete_action) delete_action.triggered.connect(line_edit.del_) + if line_edit.isReadOnly(): + delete_action.setDisabled(True) self.menu.addSeparator() @@ -1413,11 +1435,15 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox): step_up_action = QAction('%s\t%s' % (_("Step Up"), ''), self) self.menu.addAction(step_up_action) step_up_action.triggered.connect(self.stepUp) + if line_edit.isReadOnly(): + step_up_action.setDisabled(True) # STEP DOWN step_down_action = QAction('%s\t%s' % (_("Step Down"), ''), self) self.menu.addAction(step_down_action) step_down_action.triggered.connect(self.stepDown) + if line_edit.isReadOnly(): + step_down_action.setDisabled(True) self.menu.exec_(event.globalPos()) diff --git a/appGUI/preferences/PreferencesUIManager.py b/appGUI/preferences/PreferencesUIManager.py index 9c4b5e73..563b619f 100644 --- a/appGUI/preferences/PreferencesUIManager.py +++ b/appGUI/preferences/PreferencesUIManager.py @@ -611,6 +611,7 @@ class PreferencesUIManager: "tools_copper_thieving_lines_spacing": self.ui.tools2_defaults_form.tools2_cfill_group.lines_spacing_entry, "tools_copper_thieving_rb_margin": self.ui.tools2_defaults_form.tools2_cfill_group.rb_margin_entry, "tools_copper_thieving_rb_thickness": self.ui.tools2_defaults_form.tools2_cfill_group.rb_thickness_entry, + "tools_copper_thieving_only_apds": self.ui.tools2_defaults_form.tools2_cfill_group.only_pads_cb, "tools_copper_thieving_mask_clearance": self.ui.tools2_defaults_form.tools2_cfill_group.clearance_ppm_entry, "tools_copper_thieving_geo_choice": self.ui.tools2_defaults_form.tools2_cfill_group.ppm_choice_radio, diff --git a/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py b/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py index 81e96a7d..de13fd9b 100644 --- a/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py +++ b/appGUI/preferences/tools/Tools2CThievingPrefGroupUI.py @@ -1,7 +1,7 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QSettings -from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCLabel +from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCLabel, FCCheckBox from appGUI.preferences.OptionsGroupUI import OptionsGroupUI import gettext @@ -264,12 +264,20 @@ class Tools2CThievingPrefGroupUI(OptionsGroupUI): grid_lay.addWidget(self.rb_thickness_label, 40, 0) grid_lay.addWidget(self.rb_thickness_entry, 40, 1) + # Pattern Plating Mask Title self.patern_mask_label = FCLabel('%s' % _('Pattern Plating Mask')) self.patern_mask_label.setToolTip( _("Generate a mask for pattern plating.") ) grid_lay.addWidget(self.patern_mask_label, 42, 0, 1, 2) + # Use Only Pads + self.only_pads_cb = FCCheckBox(_("Only Pads")) + self.only_pads_cb.setToolTip( + _("Select only pads in case the selected object is a copper Gerber.") + ) + grid_lay.addWidget(self.only_pads_cb, 43, 0, 1, 2) + # Openings CLEARANCE # self.clearance_ppm_label = FCLabel('%s:' % _("Clearance")) self.clearance_ppm_label.setToolTip( diff --git a/appTools/ToolCalculators.py b/appTools/ToolCalculators.py index 48f4b8e3..35556a3a 100644 --- a/appTools/ToolCalculators.py +++ b/appTools/ToolCalculators.py @@ -214,7 +214,7 @@ class ToolCalculator(AppTool): inch_val = float(self.ui.inch_entry.get_value()) self.ui.mm_entry.set_value('%.*f' % (self.decimals, (inch_val * 25.4))) - def on_calculate_eplate(self): + def on_calculate_current(self): """ :return: @@ -229,7 +229,30 @@ class ToolCalculator(AppTool): [(10cm x 10cm x 2 sides] * 0.001076391] x 20 =~ 4.3 Amps = C or: (10cm x 10cm) * 0.0021527820833419] x 20 =~ 4.3 Amps = C - + ''' + self.ui_disconnect() + area_calc_sel = self.ui.area_sel_radio.get_value() + length = self.ui.pcblength_entry.get_value() + width = self.ui.pcbwidth_entry.get_value() + area = self.ui.area_entry.get_value() + + density = self.ui.cdensity_entry.get_value() + + if area_calc_sel == 'd': + calculated_current = (length * width * density) * 0.0021527820833419 + else: + calculated_current = (area * density) * 0.0021527820833419 + + self.ui.cvalue_entry.set_value('%.2f' % calculated_current) + self.ui_connect() + + def on_calculate_time(self): + """ + + :return: + """ + + ''' Calculated time for a copper growth of 10 microns is: [10um / (28um/hr)] x 60 min/hr = 21.42 minutes = TC (at 20ASF) or: @@ -239,25 +262,20 @@ class ToolCalculator(AppTool): (with new_density = 20ASF amd copper groth of 10 um) ''' self.ui_disconnect() - area_calc_sel = self.ui.area_sel_radio.get_value() - length = self.ui.pcblength_entry.get_value() - width = self.ui.pcbwidth_entry.get_value() - area = self.ui.area_entry.get_value() density = self.ui.cdensity_entry.get_value() - copper = self.ui.growth_entry.get_value() + growth = self.ui.growth_entry.get_value() - if area_calc_sel == 'd': - calculated_current = (length * width * density) * 0.0021527820833419 - else: - calculated_current = (area * density) * 0.0021527820833419 - calculated_time = copper * 2.142857142857143 * float(20 / density) + calculated_time = growth * 2.142857142857143 * float(20 / density) - self.ui.cvalue_entry.set_value('%.2f' % calculated_current) self.ui.time_entry.set_value('%.1f' % calculated_time) - self.app.inform.emit('[success] %s' % _("Done.")) self.ui_connect() + def on_calculate_eplate(self): + self.on_calculate_time() + self.on_calculate_current() + self.app.inform.emit('[success] %s' % _("Done.")) + def on_calculate_growth(self): self.ui_disconnect() density = self.ui.cdensity_entry.get_value() @@ -287,11 +305,11 @@ class ToolCalculator(AppTool): self.ui.cdensity_entry.valueChanged.connect(self.on_calculate_eplate) self.ui.cdensity_entry.returnPressed.connect(self.on_calculate_eplate) - self.ui.growth_entry.valueChanged.connect(self.on_calculate_eplate) - self.ui.growth_entry.returnPressed.connect(self.on_calculate_eplate) + self.ui.growth_entry.valueChanged.connect(self.on_calculate_time) + self.ui.growth_entry.returnPressed.connect(self.on_calculate_time) - self.ui.cdensity_entry.valueChanged.connect(self.on_calculate_growth) - self.ui.cdensity_entry.returnPressed.connect(self.on_calculate_growth) + self.ui.area_entry.valueChanged.connect(self.on_calculate_current) + self.ui.area_entry.returnPressed.connect(self.on_calculate_current) self.ui.time_entry.valueChanged.connect(self.on_calculate_growth) self.ui.time_entry.returnPressed.connect(self.on_calculate_growth) @@ -308,7 +326,7 @@ class ToolCalculator(AppTool): self.ui.cutDepth_entry.returnPressed.disconnect() except (AttributeError, TypeError): pass - + # ## try: self.ui.effectiveToolDia_entry.valueChanged.disconnect() except (AttributeError, TypeError): @@ -317,6 +335,7 @@ class ToolCalculator(AppTool): self.ui.effectiveToolDia_entry.returnPressed.disconnect() except (AttributeError, TypeError): pass + # ### try: self.ui.tipDia_entry.returnPressed.disconnect() @@ -332,6 +351,7 @@ class ToolCalculator(AppTool): pass # Electroplating Calculator + # Density try: self.ui.cdensity_entry.valueChanged.disconnect() except (AttributeError, TypeError): @@ -340,6 +360,7 @@ class ToolCalculator(AppTool): self.ui.cdensity_entry.returnPressed.disconnect() except (AttributeError, TypeError): pass + # Growth try: self.ui.growth_entry.valueChanged.disconnect() except (AttributeError, TypeError): @@ -348,14 +369,16 @@ class ToolCalculator(AppTool): self.ui.growth_entry.returnPressed.disconnect() except (AttributeError, TypeError): pass + # Area try: - self.ui.cdensity_entry.valueChanged.disconnect() + self.ui.area_entry.valueChanged.disconnect() except (AttributeError, TypeError): pass try: - self.ui.cdensity_entry.returnPressed.disconnect() + self.ui.area_entry.returnPressed.disconnect() except (AttributeError, TypeError): pass + # Time try: self.ui.time_entry.valueChanged.disconnect() except (AttributeError, TypeError): @@ -364,6 +387,7 @@ class ToolCalculator(AppTool): self.ui.time_entry.returnPressed.disconnect() except (AttributeError, TypeError): pass + # Calculate try: self.ui.calculate_plate_button.clicked.disconnect() except (AttributeError, TypeError): diff --git a/appTools/ToolCopperThieving.py b/appTools/ToolCopperThieving.py index bc9d7855..e0ebc433 100644 --- a/appTools/ToolCopperThieving.py +++ b/appTools/ToolCopperThieving.py @@ -9,7 +9,7 @@ from PyQt5 import QtWidgets, QtCore, QtGui from camlib import grace from appTool import AppTool -from appGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox, FCLabel +from appGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox, FCLabel, FCCheckBox import shapely.geometry.base as base from shapely.ops import unary_union @@ -166,6 +166,7 @@ class ToolCopperThieving(AppTool): self.ui.rb_margin_entry.set_value(self.app.defaults["tools_copper_thieving_rb_margin"]) self.ui.rb_thickness_entry.set_value(self.app.defaults["tools_copper_thieving_rb_thickness"]) + self.ui.only_pads_cb.set_value(self.app.defaults["tools_copper_thieving_only_apds"]) self.ui.clearance_ppm_entry.set_value(self.app.defaults["tools_copper_thieving_mask_clearance"]) self.ui.ppm_choice_radio.set_value(self.app.defaults["tools_copper_thieving_geo_choice"]) @@ -951,7 +952,8 @@ class ToolCopperThieving(AppTool): def on_new_pattern_plating_object(self): ppm_clearance = self.ui.clearance_ppm_entry.get_value() geo_choice = self.ui.ppm_choice_radio.get_value() - rb_thickness = self.rb_thickness + rb_thickness = self.ui.rb_thickness_entry.get_value() + only_pads = self.ui.only_pads_cb.get_value() # get the Gerber object on which the Copper thieving will be inserted selection_index = self.ui.sm_object_combo.currentIndex() @@ -965,15 +967,26 @@ class ToolCopperThieving(AppTool): return self.app.proc_container.update_view_text(' %s' % _("Append PP-M geometry")) - geo_list = deepcopy(self.sm_object.solid_geometry) - if isinstance(geo_list, MultiPolygon): - geo_list = list(geo_list.geoms) + + geo_list = [] + if only_pads is False: + geo_list = deepcopy(self.sm_object.solid_geometry) + if isinstance(geo_list, MultiPolygon): + geo_list = list(geo_list.geoms) + else: + for apid in self.sm_object.apertures: + for k in self.sm_object.apertures[apid]: + if k == 'geometry': + for elem in self.sm_object.apertures[apid]['geometry']: + if 'follow' in elem and isinstance(elem['follow'], Point): + if 'solid' in elem: + geo_list.append(elem['solid']) # create a copy of the source apertures so we can manipulate them without altering the source object new_apertures = deepcopy(self.sm_object.apertures) - # if the clearance is negative apply it to the original soldermask geometry too - if ppm_clearance < 0: + # if the clearance is not zero apply it to the original soldermask geometry too + if ppm_clearance != 0: temp_geo_list = [] for geo in geo_list: temp_geo_list.append(geo.buffer(ppm_clearance)) @@ -1088,6 +1101,10 @@ class ToolCopperThieving(AppTool): geo_list.append(robber_solid_geo.buffer(ppm_clearance)) # and then set the total plated area value to the GUI element + # the area is in mm2 when using Metric units, make it in cm2 for Metric units + print(plated_area) + if self.units.lower() == 'mm': + plated_area /= 100 self.ui.plated_area_entry.set_value(plated_area) new_solid_geometry = MultiPolygon(geo_list).buffer(0.0000001).buffer(-0.0000001) @@ -1644,6 +1661,13 @@ class ThievingUI: grid_lay_1.addWidget(self.sm_obj_label, 14, 0, 1, 3) grid_lay_1.addWidget(self.sm_object_combo, 16, 0, 1, 3) + # Only Pads + self.only_pads_cb = FCCheckBox(_("Only Pads")) + self.only_pads_cb.setToolTip( + _("Select only pads in case the selected object is a copper Gerber.") + ) + grid_lay_1.addWidget(self.only_pads_cb, 17, 0, 1, 3) + # Openings CLEARANCE # self.clearance_ppm_label = FCLabel('%s:' % _("Clearance")) self.clearance_ppm_label.setToolTip( @@ -1669,10 +1693,10 @@ class ThievingUI: "calculated from the soldermask openings.") ) self.plated_area_entry = FCEntry() - self.plated_area_entry.setDisabled(True) + self.plated_area_entry.setReadOnly(True) if self.units.upper() == 'MM': - self.units_area_label = FCLabel('%s2' % _("mm")) + self.units_area_label = FCLabel('%s2' % _("cm")) else: self.units_area_label = FCLabel('%s2' % _("in")) diff --git a/defaults.py b/defaults.py index ed29bf94..0e8a39bd 100644 --- a/defaults.py +++ b/defaults.py @@ -678,6 +678,7 @@ class FlatCAMDefaults: "tools_copper_thieving_lines_spacing": 2.0, "tools_copper_thieving_rb_margin": 1.0, "tools_copper_thieving_rb_thickness": 1.0, + "tools_copper_thieving_only_apds": True, "tools_copper_thieving_mask_clearance": 0.0, "tools_copper_thieving_geo_choice": 'b',