- added a new FlatCAM Tool: Copper Fill Tool. It will pour copper into a Gerber filling all empty space with copper, at a clearance distance of the Gerber features

This commit is contained in:
Marius Stanciu
2019-10-25 19:52:11 +03:00
committed by Marius
parent 228dcfdac1
commit 2bde43ca3f
11 changed files with 710 additions and 18 deletions

View File

@@ -0,0 +1,678 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 10/25/2019 #
# MIT Licence #
# ##########################################################
from PyQt5 import QtWidgets, QtCore
import FlatCAMApp
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMExcellon
import shapely.geometry.base as base
from shapely.ops import cascaded_union, unary_union
from shapely.geometry import Polygon, MultiPolygon
import logging
from copy import deepcopy
import numpy as np
from collections import Iterable
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolCopperFill(FlatCAMTool):
toolName = _("Copper Fill Tool")
def __init__(self, app):
FlatCAMTool.__init__(self, app)
self.app = app
self.canvas = self.app.plotcanvas
self.decimals = 4
self.units = ''
# ## Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
self.layout.addWidget(title_label)
self.layout.addWidget(QtWidgets.QLabel(''))
# ## Grid Layout
i_grid_lay = QtWidgets.QGridLayout()
self.layout.addLayout(i_grid_lay)
i_grid_lay.setColumnStretch(0, 0)
i_grid_lay.setColumnStretch(1, 1)
self.grb_object_combo = QtWidgets.QComboBox()
self.grb_object_combo.setModel(self.app.collection)
self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.grb_object_combo.setCurrentIndex(1)
self.grbobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.grbobj_label.setToolTip(
_("Gerber Object to which will be added a copper fill.")
)
i_grid_lay.addWidget(self.grbobj_label, 0, 0)
i_grid_lay.addWidget(self.grb_object_combo, 0, 1, 1, 2)
i_grid_lay.addWidget(QtWidgets.QLabel(''), 1, 0)
# ## Grid Layout
grid_lay = QtWidgets.QGridLayout()
self.layout.addLayout(grid_lay)
grid_lay.setColumnStretch(0, 0)
grid_lay.setColumnStretch(1, 1)
self.copper_fill_label = QtWidgets.QLabel('<b>%s</b>' % _('Parameters'))
self.copper_fill_label.setToolTip(
_("Parameters used for this tool.")
)
grid_lay.addWidget(self.copper_fill_label, 0, 0, 1, 2)
# CLEARANCE #
self.clearance_label = QtWidgets.QLabel('%s:' % _("Clearance"))
self.clearance_label.setToolTip(
_("This set the distance between the copper fill components\n"
"(the polygon fill may be split in multiple polygons)\n"
"and the copper traces in the Gerber file.")
)
self.clearance_entry = FCDoubleSpinner()
self.clearance_entry.setMinimum(0.00001)
self.clearance_entry.set_precision(self.decimals)
self.clearance_entry.setSingleStep(0.1)
grid_lay.addWidget(self.clearance_label, 1, 0)
grid_lay.addWidget(self.clearance_entry, 1, 1)
# MARGIN #
self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
self.margin_label.setToolTip(
_("Bounding box margin.")
)
self.margin_entry = FCDoubleSpinner()
self.margin_entry.setMinimum(0.0)
self.margin_entry.set_precision(self.decimals)
self.margin_entry.setSingleStep(0.1)
grid_lay.addWidget(self.margin_label, 2, 0)
grid_lay.addWidget(self.margin_entry, 2, 1)
# Reference #
self.reference_radio = RadioSet([
{'label': _('Itself'), 'value': 'itself'},
{"label": _("Area Selection"), "value": "area"},
{'label': _("Reference Object"), 'value': 'box'}
], orientation='vertical', stretch=False)
self.reference_label = QtWidgets.QLabel(_("Reference:"))
self.reference_label.setToolTip(
_("- 'Itself' - the copper fill extent is based on the object that is copper cleared.\n "
"- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
"- 'Reference Object' - will do copper filling within the area specified by another object.")
)
grid_lay.addWidget(self.reference_label, 3, 0)
grid_lay.addWidget(self.reference_radio, 3, 1)
self.box_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
self.box_combo_type_label.setToolTip(
_("The type of FlatCAM object to be used as copper filling reference.\n"
"It can be Gerber, Excellon or Geometry.")
)
self.box_combo_type = QtWidgets.QComboBox()
self.box_combo_type.addItem(_("Gerber Reference Box Object"))
self.box_combo_type.addItem(_("Excellon Reference Box Object"))
self.box_combo_type.addItem(_("Geometry Reference Box Object"))
grid_lay.addWidget(self.box_combo_type_label, 4, 0)
grid_lay.addWidget(self.box_combo_type, 4, 1)
self.box_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))
self.box_combo_label.setToolTip(
_("The FlatCAM object to be used as non copper clearing reference.")
)
self.box_combo = QtWidgets.QComboBox()
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
grid_lay.addWidget(self.box_combo_label, 5, 0)
grid_lay.addWidget(self.box_combo, 5, 1)
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
# ## Insert Copper Fill
self.fill_button = QtWidgets.QPushButton(_("Insert Copper Fill"))
self.fill_button.setToolTip(
_("Will add a polygon (may be split in multiple parts)\n"
"that will surround the actual Gerber traces at a certain distance.")
)
self.layout.addWidget(self.fill_button)
self.layout.addStretch()
# Objects involved in Copper filling
self.grb_object = None
self.ref_obj = None
self.sel_rect = list()
# Events ID
self.mr = None
self.mm = None
# Mouse cursor positions
self.mouse_is_dragging = False
self.cursor_pos = (0, 0)
self.first_click = False
# Tool properties
self.clearance_val = None
self.margin_val = None
self.geo_steps_per_circle = 128
# SIGNALS
self.fill_button.clicked.connect(self.execute)
self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type)
self.reference_radio.group_toggle_fn = self.on_toggle_reference
def run(self, toggle=True):
self.app.report_usage("ToolCopperFill()")
if toggle:
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Copper Fill Tool"))
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+F', **kwargs)
def set_tool_ui(self):
self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value()
# self.clearance_entry.set_value(float(self.app.defaults["tools_copperfill_clearance"]))
# self.margin_entry.set_value(float(self.app.defaults["tools_copperfill_margin"]))
# self.reference_radio.set_value(self.app.defaults["tools_copperfill_reference"])
# self.geo_steps_per_circle = int(self.app.defaults["tools_copperfill_circle_steps"])
self.clearance_entry.set_value(0.5)
self.margin_entry.set_value(1.0)
self.reference_radio.set_value('itself')
def on_combo_box_type(self):
obj_type = self.box_combo_type.currentIndex()
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(0)
def on_toggle_reference(self):
if self.reference_radio.get_value() == "itself" or self.reference_radio.get_value() == "area":
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
else:
self.box_combo.show()
self.box_combo_label.show()
self.box_combo_type.show()
self.box_combo_type_label.show()
def execute(self):
self.clearance_val = self.clearance_entry.get_value()
self.margin_val = self.margin_entry.get_value()
reference_method = self.reference_radio.get_value()
# get the Gerber object on which the Copper fill will be inserted
selection_index = self.grb_object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.grb_object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
log.debug("ToolCopperFill.execute() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
return 'fail'
if reference_method == 'itself':
bound_obj_name = self.grb_object_combo.currentText()
# Get reference object.
try:
self.ref_obj = self.app.collection.get_by_name(bound_obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(e)))
return "Could not retrieve object: %s" % self.obj_name
self.on_copper_fill(
fill_obj=self.grb_object,
c_val=self.clearance_val,
margin=self.margin_val
)
elif reference_method == 'area':
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the start point of the area."))
if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
else:
self.app.plotcanvas.graph_event_disconnect(self.app.mp)
self.app.plotcanvas.graph_event_disconnect(self.app.mm)
self.app.plotcanvas.graph_event_disconnect(self.app.mr)
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
elif reference_method == 'box':
bound_obj_name = self.box_combo.currentText()
# Get reference object.
try:
self.ref_obj = self.app.collection.get_by_name(bound_obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), bound_obj_name))
return "Could not retrieve object: %s. Error: %s" % (bound_obj_name, str(e))
self.on_copper_fill(
fill_obj=self.grb_object,
ref_obj=self.ref_obj,
c_val=self.clearance_val,
margin=self.margin_val
)
# To be called after clicking on the plot.
def on_mouse_release(self, event):
if self.app.is_legacy is False:
event_pos = event.pos
event_is_dragging = event.is_dragging
right_button = 2
else:
event_pos = (event.xdata, event.ydata)
event_is_dragging = self.app.plotcanvas.is_dragging
right_button = 3
event_pos = self.app.plotcanvas.translate_coords(event_pos)
# do clear area only for left mouse clicks
if event.button == 1:
if self.first_click is False:
self.first_click = True
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Click the end point of the filling area."))
self.cursor_pos = self.app.plotcanvas.translate_coords(event_pos)
if self.app.grid_status() == True:
self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
else:
self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish."))
if self.app.grid_status() == True:
curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
else:
curr_pos = (event_pos[0], event_pos[1])
x0, y0 = self.cursor_pos[0], self.cursor_pos[1]
x1, y1 = curr_pos[0], curr_pos[1]
pt1 = (x0, y0)
pt2 = (x1, y0)
pt3 = (x1, y1)
pt4 = (x0, y1)
self.sel_rect.append(Polygon([pt1, pt2, pt3, pt4]))
self.first_click = False
return
elif event.button == right_button and self.mouse_is_dragging == False:
self.app.delete_selection_shape()
self.first_click = False
if self.app.is_legacy is False:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
else:
self.app.plotcanvas.graph_event_disconnect(self.mr)
self.app.plotcanvas.graph_event_disconnect(self.mm)
self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
self.app.on_mouse_click_over_plot)
self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
self.app.on_mouse_move_over_plot)
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
self.app.on_mouse_click_release_over_plot)
if len(self.sel_rect) == 0:
return
self.sel_rect = cascaded_union(self.sel_rect)
if not isinstance(self.sel_rect, Iterable):
self.sel_rect = [self.sel_rect]
self.on_copper_fill(
fill_obj=self.grb_object,
ref_obj=self.sel_rect,
c_val=self.clearance_val,
margin=self.margin_val
)
# called on mouse move
def on_mouse_move(self, event):
if self.app.is_legacy is False:
event_pos = event.pos
event_is_dragging = event.is_dragging
right_button = 2
else:
event_pos = (event.xdata, event.ydata)
event_is_dragging = self.app.plotcanvas.is_dragging
right_button = 3
curr_pos = self.app.plotcanvas.translate_coords(event_pos)
# detect mouse dragging motion
if event_is_dragging is True:
self.mouse_is_dragging = True
else:
self.mouse_is_dragging = False
# update the cursor position
if self.app.grid_status() == True:
# Update cursor
curr_pos = self.app.geo_editor.snap(curr_pos[0], curr_pos[1])
self.app.app_cursor.set_data(np.asarray([(curr_pos[0], curr_pos[1])]),
symbol='++', edge_color=self.app.cursor_color_3D,
size=self.app.defaults["global_cursor_size"])
# update the positions on status bar
self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f" % (curr_pos[0], curr_pos[1]))
if self.cursor_pos is None:
self.cursor_pos = (0, 0)
dx = curr_pos[0] - float(self.cursor_pos[0])
dy = curr_pos[1] - float(self.cursor_pos[1])
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
# draw the utility geometry
if self.first_click:
self.app.delete_selection_shape()
self.app.draw_moving_selection_shape(old_coords=(self.cursor_pos[0], self.cursor_pos[1]),
coords=(curr_pos[0], curr_pos[1]))
def on_copper_fill(self, fill_obj, ref_obj=None, c_val=None, margin=None, run_threaded=True):
"""
:param fill_obj:
:param ref_obj:
:param c_val:
:param margin:
:param run_threaded:
:return:
"""
if run_threaded:
proc = self.app.proc_container.new('%s ...' % _("Copper filling"))
else:
self.app.proc_container.view.set_busy('%s ...' % _("Copper filling"))
QtWidgets.QApplication.processEvents()
# #####################################################################
# ####### Read the parameters #########################################
# #####################################################################
log.debug("Copper Filling Tool started. Reading parameters.")
self.app.inform.emit(_("Copper Filling Tool started. Reading parameters."))
units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value()
ref_selected = self.reference_radio.get_value()
if c_val is None:
c_val = float(self.app.defaults["tools_copperfill_clearance"])
if margin is None:
margin = float(self.app.defaults["tools_copperfill_margin"])
# make sure that the source object solid geometry is an Iterable
if not isinstance(self.grb_object.solid_geometry, Iterable):
self.grb_object.solid_geometry = [self.grb_object.solid_geometry]
# #########################################################################################
# Prepare isolation polygon. This will create the clearance over the Gerber features ######
# #########################################################################################
log.debug("Copper Filling Tool. Preparing isolation polygons.")
self.app.inform.emit(_("Copper Filling Tool. Preparing isolation polygons."))
# variables to display the percentage of work done
geo_len = 0
try:
for pol in self.grb_object.solid_geometry:
geo_len += 1
except TypeError:
geo_len = 1
old_disp_number = 0
pol_nr = 0
clearance_geometry = []
try:
for pol in self.grb_object.solid_geometry:
if self.app.abort_flag:
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
clearance_geometry.append(
pol.buffer(c_val, int(int(self.geo_steps_per_circle) / 4))
)
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%%' %
(_("Buffering"), int(disp_number)))
old_disp_number = disp_number
except TypeError:
# taking care of the case when the self.solid_geometry is just a single Polygon, not a list or a
# MultiPolygon (not an iterable)
clearance_geometry.append(
self.grb_object.solid_geometry.buffer(c_val, int(int(self.geo_steps_per_circle) / 4))
)
self.app.proc_container.update_view_text(' %s' % _("Buffering"))
clearance_geometry = unary_union(clearance_geometry)
# #########################################################################################
# Prepare the area to fill with copper. ###################################################
# #########################################################################################
log.debug("Copper Filling Tool. Preparing areas to fill with copper.")
self.app.inform.emit(_("Copper Filling Tool. Preparing areas to fill with copper."))
try:
if ref_obj is None or ref_obj == 'itself':
working_obj = fill_obj
else:
working_obj = ref_obj
except Exception as e:
log.debug("ToolCopperFIll.on_copper_fill() --> %s" % str(e))
return 'fail'
bounding_box = None
if ref_selected == 'itself':
geo_n = working_obj.solid_geometry
try:
if isinstance(geo_n, MultiPolygon):
env_obj = geo_n.convex_hull
elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
(isinstance(geo_n, list) and len(geo_n) == 1) and isinstance(geo_n[0], Polygon):
env_obj = cascaded_union(geo_n)
else:
env_obj = cascaded_union(geo_n)
env_obj = env_obj.convex_hull
bounding_box = env_obj.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
except Exception as e:
log.debug("ToolCopperFIll.on_copper_fill() 'itself' --> %s" % str(e))
self.app.inform.emit('[ERROR_NOTCL] %s' % _("No object available."))
return 'fail'
elif ref_selected == 'area':
geo_n = cascaded_union(working_obj)
try:
__ = iter(geo_n)
except Exception as e:
log.debug("ToolCopperFIll.on_copper_fill() 'area' --> %s" % str(e))
geo_n = [geo_n]
geo_buff_list = []
for poly in geo_n:
if self.app.abort_flag:
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
geo_buff_list.append(poly.buffer(distance=0.0, join_style=base.JOIN_STYLE.mitre))
bounding_box = cascaded_union(geo_buff_list)
elif ref_selected == 'box':
geo_n = working_obj.solid_geometry
if isinstance(working_obj, FlatCAMGeometry):
try:
__ = iter(geo_n)
except Exception as e:
log.debug("ToolCopperFIll.on_copper_fill() 'box' --> %s" % str(e))
geo_n = [geo_n]
geo_buff_list = []
for poly in geo_n:
if self.app.abort_flag:
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
geo_buff_list.append(poly.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre))
bounding_box = cascaded_union(geo_buff_list)
elif isinstance(working_obj, FlatCAMGerber):
geo_n = cascaded_union(geo_n).convex_hull
bounding_box = cascaded_union(self.ncc_obj.solid_geometry).convex_hull.intersection(geo_n)
bounding_box = bounding_box.buffer(distance=margin, join_style=base.JOIN_STYLE.mitre)
else:
self.app.inform.emit('[ERROR_NOTCL] %s' % _("The reference object type is not supported."))
return 'fail'
log.debug("Copper Filling Tool. Finished creating areas to fill with copper.")
self.app.inform.emit(_("Copper Filling Tool. Appending new geometry and buffering."))
new_solid_geometry = bounding_box.difference(clearance_geometry)
geo_list = self.grb_object.solid_geometry
if isinstance(self.grb_object.solid_geometry, MultiPolygon):
geo_list = list(self.grb_object.solid_geometry.geoms)
if '0' not in self.grb_object.apertures:
self.grb_object.apertures['0'] = dict()
self.grb_object.apertures['0']['geometry'] = list()
self.grb_object.apertures['0']['type'] = 'REG'
self.grb_object.apertures['0']['size'] = 0.0
try:
for poly in new_solid_geometry:
# append to the new solid geometry
geo_list.append(poly)
# append into the '0' aperture
geo_elem = dict()
geo_elem['solid'] = poly
geo_elem['follow'] = poly.exterior
self.grb_object.apertures['0']['geometry'].append(deepcopy(geo_elem))
except TypeError:
# append to the new solid geometry
geo_list.append(new_solid_geometry)
# append into the '0' aperture
geo_elem = dict()
geo_elem['solid'] = new_solid_geometry
geo_elem['follow'] = new_solid_geometry.exterior
self.grb_object.apertures['0']['geometry'].append(deepcopy(geo_elem))
self.grb_object.solid_geometry = MultiPolygon(geo_list).buffer(0.0000001).buffer(-0.0000001)
# update the source file with the new geometry:
self.grb_object.source_file = self.app.export_gerber(obj_name=self.grb_object.options['name'], filename=None,
local_use=self.grb_object, use_thread=False)
self.on_exit()
self.app.inform.emit('[success] %s' % _("Copper Fill Tool done."))
def replot(self, obj):
def worker_task():
with self.app.proc_container.new('%s...' % _("Plotting")):
obj.plot()
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
def on_exit(self):
# plot the object
self.replot(obj=self.grb_object)
# update the bounding box values
try:
a, b, c, d = self.grb_object.bounds()
self.grb_object.options['xmin'] = a
self.grb_object.options['ymin'] = b
self.grb_object.options['xmax'] = c
self.grb_object.options['ymax'] = d
except Exception as e:
log.debug("ToolCopperFill.on_exit() bounds error --> %s" % str(e))
# reset the variables
self.grb_object = None
self.ref_obj = None
self.sel_rect = list()
# Events ID
self.mr = None
self.mm = None
# Mouse cursor positions
self.mouse_is_dragging = False
self.cursor_pos = (0, 0)
self.first_click = False

View File

@@ -429,12 +429,9 @@ class NonCopperClear(FlatCAMTool, Gerber):
], orientation='vertical', stretch=False)
self.reference_label = QtWidgets.QLabel(_("Reference:"))
self.reference_label.setToolTip(
_("- 'Itself' - the non copper clearing extent\n"
"is based on the object that is copper cleared.\n "
_("- 'Itself' - the non copper clearing extent is based on the object that is copper cleared.\n "
"- 'Area Selection' - left mouse click to start selection of the area to be painted.\n"
"Keeping a modifier key pressed (CTRL or SHIFT) will allow to add multiple areas.\n"
"- 'Reference Object' - will do non copper clearing within the area\n"
"specified by another object.")
"- 'Reference Object' - will do non copper clearing within the area specified by another object.")
)
grid3.addWidget(self.reference_label, 10, 0)
grid3.addWidget(self.reference_radio, 10, 1)
@@ -1174,8 +1171,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
try:
self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.obj_name))
return "Could not retrieve object: %s" % self.obj_name
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.bound_obj_name))
return "Could not retrieve object: %s" % self.bound_obj_name
self.clear_copper(ncc_obj=self.ncc_obj,
ncctooldia=self.ncc_dia_list,
@@ -1244,7 +1241,6 @@ class NonCopperClear(FlatCAMTool, Gerber):
self.cursor_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
else:
self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish."))
self.app.delete_selection_shape()
if self.app.grid_status() == True:
curr_pos = self.app.geo_editor.snap(event_pos[0], event_pos[1])
@@ -1263,6 +1259,8 @@ class NonCopperClear(FlatCAMTool, Gerber):
return
elif event.button == right_button and self.mouse_is_dragging == False:
self.app.delete_selection_shape()
self.first_click = False
if self.app.is_legacy is False:

View File

@@ -1122,7 +1122,6 @@ class ToolPaint(FlatCAMTool, Gerber):
self.cursor_pos = self.app.geo_editor.snap(self.cursor_pos[0], self.cursor_pos[1])
else:
self.app.inform.emit(_("Zone added. Click to start adding next zone or right click to finish."))
self.app.delete_selection_shape()
curr_pos = self.app.plotcanvas.translate_coords(event_pos)
if self.app.grid_status() == True:
@@ -1139,6 +1138,8 @@ class ToolPaint(FlatCAMTool, Gerber):
return
elif event.button == right_button and self.mouse_is_dragging is False:
self.app.delete_selection_shape()
self.first_click = False
if self.app.is_legacy is False:

View File

@@ -9,7 +9,7 @@ from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import Qt
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox, OptionalInputSection
from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox
from flatcamParsers.ParseSVG import *
from shapely.geometry.base import *
@@ -91,8 +91,7 @@ class QRCode(FlatCAMTool):
self.qrcode_label = QtWidgets.QLabel('<b>%s</b>' % _('QRCode Parameters'))
self.qrcode_label.setToolTip(
_("Contain the expected calibration points and the\n"
"ones measured.")
_("The parameters used to shape the QRCode.")
)
grid_lay.addWidget(self.qrcode_label, 0, 0, 1, 2)
@@ -445,9 +444,9 @@ class QRCode(FlatCAMTool):
def make(self, pos):
self.on_exit()
# add the svg geometry to the selected Gerber object solid_geometry and in obj.apertures, apid = 0
# make sure that the source object solid geometry is an Iterable
if not isinstance(self.grb_object.solid_geometry, Iterable):
self.grb_object.solid_geometry = list(self.grb_object.solid_geometry)
self.grb_object.solid_geometry = [self.grb_object.solid_geometry]
# I use the utility geometry (self.qrcode_utility_geometry) because it is already buffered
geo_list = self.grb_object.solid_geometry
@@ -553,6 +552,7 @@ class QRCode(FlatCAMTool):
local_use=self.grb_object, use_thread=False)
self.replot(obj=self.grb_object)
self.app.inform.emit('[success] %s' % _("QRCode Tool done."))
def draw_utility_geo(self, pos):

View File

@@ -29,6 +29,8 @@ from flatcamTools.ToolProperties import Properties
from flatcamTools.ToolQRCode import QRCode
from flatcamTools.ToolRulesCheck import RulesCheck
from flatcamTools.ToolCopperFill import ToolCopperFill
from flatcamTools.ToolShell import FCShell
from flatcamTools.ToolSolderPaste import SolderPaste
from flatcamTools.ToolSub import ToolSub