Files
flatcam-wsl/appPlugins/ToolMarkers.py

1665 lines
68 KiB
Python

# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 5/17/2020 #
# MIT Licence #
# ##########################################################
from appTool import *
from appCommon.Common import LoudDict
from camlib import flatten_shapely_geometry
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolMarkers(AppTool):
def __init__(self, app):
AppTool.__init__(self, app)
self.app = app
self.canvas = self.app.plotcanvas
self.cursor_color_memory = None
# store the current cursor type to be restored after manual geo
self.old_cursor_type = self.app.options["global_cursor_type"]
self.decimals = self.app.decimals
self.units = ''
# here we store the locations of the selected locations
self.points = LoudDict()
self.points.set_change_callback(self.on_points_changed)
# #############################################################################
# ######################### Tool GUI ##########################################
# #############################################################################
self.ui = MarkersUI(layout=self.layout, app=self.app)
self.pluginName = self.ui.pluginName
# Objects involved in Copper thieving
self.grb_object = None
# store the flattened geometry here:
self.flat_geometry = []
self.mr = None
# Tool properties
self.fid_dia = None
self.grb_steps_per_circle = self.app.options["gerber_circle_steps"]
self.handlers_connected = False
# storage for temporary shapes when adding manual markers
self.temp_shapes = self.app.move_tool.sel_shapes
def on_insert_type_changed(self, val):
obj_type = 2 if val == 'geo' else 0
self.ui.obj_insert_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.ui.obj_insert_combo.setCurrentIndex(0)
self.ui.obj_insert_combo.obj_type = {
"grb": "gerber", "geo": "geometry"
}[self.ui.insert_type_radio.get_value()]
def on_object_selection_changed(self, current, previous):
found_idx = None
for tab_idx in range(self.app.ui.notebook.count()):
if self.app.ui.notebook.tabText(tab_idx) == self.ui.pluginName:
found_idx = True
break
if found_idx:
try:
name = current.indexes()[0].internalPointer().obj.obj_options['name']
kind = current.indexes()[0].internalPointer().obj.kind
if kind in ['gerber', 'geometry']:
obj_type = {'gerber': 'grb', 'geometry': 'geo'}[kind]
self.ui.insert_type_radio.set_value(obj_type)
self.ui.obj_insert_combo.set_value(name)
except Exception:
pass
def run(self, toggle=True):
self.app.defaults.report_usage("ToolMarkers()")
if toggle:
# if the splitter is hidden, display it
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
# if the Tool Tab is hidden display it, else hide it but only if the objectName is the same
found_idx = None
for idx in range(self.app.ui.notebook.count()):
if self.app.ui.notebook.widget(idx).objectName() == "plugin_tab":
found_idx = idx
break
# show the Tab
if not found_idx:
try:
self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
except RuntimeError:
self.app.ui.plugin_tab = QtWidgets.QWidget()
self.app.ui.plugin_tab.setObjectName("plugin_tab")
self.app.ui.plugin_tab_layout = QtWidgets.QVBoxLayout(self.app.ui.plugin_tab)
self.app.ui.plugin_tab_layout.setContentsMargins(2, 2, 2, 2)
self.app.ui.plugin_scroll_area = VerticalScrollArea()
self.app.ui.plugin_tab_layout.addWidget(self.app.ui.plugin_scroll_area)
self.app.ui.notebook.addTab(self.app.ui.plugin_tab, _("Plugin"))
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.plugin_tab)
try:
if self.app.ui.plugin_scroll_area.widget().objectName() == self.pluginName and found_idx:
# if the Tool Tab is not focused, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.plugin_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.plugin_tab)
else:
# else remove the Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
self.app.ui.notebook.removeTab(2)
# if there are no objects loaded in the app then hide the Notebook widget
if not self.app.collection.get_list():
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])
AppTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Markers"))
def install(self, icon=None, separator=None, **kwargs):
AppTool.install(self, icon, separator, shortcut='Alt+B', **kwargs)
def connect_signals_at_init(self):
# #############################################################################
# ############################ SIGNALS ########################################
# #############################################################################
self.ui.level.toggled.connect(self.on_level_changed)
self.ui.add_marker_button.clicked.connect(self.add_markers)
self.ui.toggle_all_cb.toggled.connect(self.on_toggle_all)
self.ui.drill_button.clicked.connect(self.on_create_drill_object)
self.ui.check_button.clicked.connect(self.on_create_check_object)
self.ui.mode_combo.currentIndexChanged.connect(self.on_selection_changed)
self.ui.insert_type_radio.activated_custom.connect(self.on_insert_type_changed)
self.app.proj_selection_changed.connect(self.on_object_selection_changed)
self.ui.big_cursor_cb.stateChanged.connect(self.on_cursor_change)
self.ui.insert_markers_button.clicked.connect(self.on_insert_markers_in_external_objects)
def set_tool_ui(self):
self.units = self.app.app_units
self.clear_ui(self.layout)
self.ui = MarkersUI(layout=self.layout, app=self.app)
self.pluginName = self.ui.pluginName
self.connect_signals_at_init()
self.ui.thick_entry.set_value(self.app.options["tools_markers_thickness"])
self.ui.l_entry.set_value(float(self.app.options["tools_markers_length"]))
self.ui.ref_radio.set_value(self.app.options["tools_markers_reference"])
self.ui.offset_x_entry.set_value(float(self.app.options["tools_markers_offset_x"]))
self.ui.offset_y_entry.set_value(float(self.app.options["tools_markers_offset_y"]))
self.ui.offset_link_button.setChecked(True)
self.ui.on_link_checked(True)
self.ui.toggle_all_cb.set_value(False)
self.ui.type_radio.set_value(self.app.options["tools_markers_type"])
self.ui.drill_dia_entry.set_value(self.app.options["tools_markers_drill_dia"])
self.ui.mode_combo.set_value(self.app.options["tools_markers_mode"])
self.on_selection_changed(self.app.options["tools_markers_mode"])
self.ui.insert_type_radio.set_value(val="grb")
self.ui.big_cursor_cb.set_value(self.app.options["tools_markers_big_cursor"])
self.points.clear()
self.on_points_changed(None)
# SELECT THE CURRENT OBJECT
obj = self.app.collection.get_active()
if obj and obj.kind == 'gerber':
obj_name = obj.obj_options['name']
self.ui.object_combo.set_value(obj_name)
if obj is None:
self.ui.object_combo.setCurrentIndex(0)
# Show/Hide Advanced Options
app_mode = self.app.options["global_app_level"]
self.change_level(app_mode)
# set cursor
self.old_cursor_type = self.app.options["global_cursor_type"]
def change_level(self, level):
"""
:param level: application level: either 'b' or 'a'
:type level: str
:return:
"""
if level == 'a':
self.ui.level.setChecked(True)
else:
self.ui.level.setChecked(False)
self.on_level_changed(self.ui.level.isChecked())
def on_level_changed(self, checked):
if not checked:
self.ui.level.setText('%s' % _('Beginner'))
self.ui.level.setStyleSheet("""
QToolButton
{
color: green;
}
""")
self.ui.drills_label.hide()
self.ui.drill_frame.hide()
self.ui.drill_button.hide()
self.ui.check_label.hide()
self.ui.check_button.hide()
self.ui.insert_label.hide()
self.ui.insert_frame.hide()
self.ui.insert_markers_button.hide()
else:
self.ui.level.setText('%s' % _('Advanced'))
self.ui.level.setStyleSheet("""
QToolButton
{
color: red;
}
""")
self.ui.drills_label.show()
self.ui.drill_frame.show()
self.ui.drill_button.show()
self.ui.check_label.show()
self.ui.check_button.show()
self.ui.insert_label.show()
self.ui.insert_frame.show()
self.ui.insert_markers_button.show()
def on_toggle_all(self, val):
self.ui.bl_cb.set_value(val)
self.ui.br_cb.set_value(val)
self.ui.tl_cb.set_value(val)
self.ui.tr_cb.set_value(val)
def on_selection_changed(self, val):
if val == 0: # 'auto'
self.ui.locs_label.setDisabled(False)
self.ui.loc_frame.setDisabled(False)
self.ui.type_label.setDisabled(False)
self.ui.type_radio.setDisabled(False)
self.ui.off_frame.setDisabled(False)
self.ui.marker_coords_lbl.setVisible(False)
self.ui.marker_coords_entry.setVisible(False)
self.ui.big_cursor_cb.hide()
elif val == 1: # 'manual'
self.ui.locs_label.setDisabled(True)
self.ui.loc_frame.setDisabled(True)
self.ui.type_label.setDisabled(True)
self.ui.type_radio.setDisabled(True)
self.ui.off_frame.setDisabled(True)
self.ui.type_radio.set_value('c')
self.ui.marker_coords_lbl.setVisible(False)
self.ui.marker_coords_entry.setVisible(False)
self.ui.big_cursor_cb.show()
else: # 'numeric'
self.ui.locs_label.setDisabled(True)
self.ui.loc_frame.setDisabled(True)
self.ui.type_radio.set_value('c')
self.ui.type_label.setDisabled(True)
self.ui.type_radio.setDisabled(True)
self.ui.off_frame.setDisabled(True)
self.ui.marker_coords_lbl.setVisible(True)
self.ui.marker_coords_entry.setVisible(True)
self.ui.big_cursor_cb.hide()
def on_cursor_change(self, val):
if val:
self.app.options['tools_markers_big_cursor'] = True
else:
self.app.options['tools_markers_big_cursor'] = False
def add_markers(self):
self.app.call_source = "markers_tool"
# cleanup previous possible markers
self.points.clear()
select_type = self.ui.mode_combo.get_value()
if select_type == 0: # 'auto'
self.handle_automatic_placement()
elif select_type == 1: # 'manual'
self.app.inform.emit('%s' % _("Click to add next marker or right click to finish."))
if self.ui.big_cursor_cb.get_value():
self.app.on_cursor_type(val="big", control_cursor=True)
self.cursor_color_memory = self.app.plotcanvas.cursor_color
if self.app.use_3d_engine is True:
self.app.plotcanvas.cursor_color = '#000000FF'
else:
self.app.plotcanvas.cursor_color = '#000000'
self.app.app_cursor.enabled = True
else:
self.app.on_cursor_type(val="small", control_cursor=True)
self.app.plotcanvas.cursor_color = self.cursor_color_memory
# it works only with cross markers
self.ui.type_radio.set_value('c')
self.app.ui.notebook.setDisabled(True)
self.connect_event_handlers()
else: # 'numeric'
self.handle_numeric_placement()
def handle_automatic_placement(self):
self.points.clear()
tl_state = self.ui.tl_cb.get_value()
tr_state = self.ui.tr_cb.get_value()
bl_state = self.ui.bl_cb.get_value()
br_state = self.ui.br_cb.get_value()
# get the Gerber object on which the corner marker will be inserted
selection_index = self.ui.object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
self.app.log.error("ToolMarkers.add_markers() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
self.app.call_source = "app"
return
xmin, ymin, xmax, ymax = self.grb_object.bounds()
if tl_state:
self.points['tl'] = (xmin, ymax)
if tr_state:
self.points['tr'] = (xmax, ymax)
if bl_state:
self.points['bl'] = (xmin, ymin)
if br_state:
self.points['br'] = (xmax, ymin)
ret_val = self.add_marker_geo(self.points, g_obj=self.grb_object)
self.app.call_source = "app"
if ret_val == 'fail':
self.app.call_source = "app"
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return
self.on_exit(ret_val)
def handle_manual_placement(self):
# self.app.inform.emit('[ERROR_NOTCL] %s' % "Not implemented yet.")
# get the Gerber object on which the corner marker will be inserted
selection_index = self.ui.object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
self.app.log.error("ToolMarkers.add_markers() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
self.on_exit()
return
ret_val = self.add_marker_geo(self.points, g_obj=self.grb_object)
if ret_val == 'fail':
self.on_exit(ok=False)
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return
self.on_exit()
def handle_numeric_placement(self):
location = self.ui.marker_coords_entry.get_value()
if not isinstance(location, tuple):
self.app.log.error("ToolMarkers.handle_numeric_placement() -> not a tuple")
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return
if 'manual' not in self.points:
self.points['manual'] = []
self.points['manual'].append(location)
self.draw_utility_geometry(pos=location)
# get the Gerber object on which the corner marker will be inserted
selection_index = self.ui.object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
self.app.log.error("ToolMarkers.add_markers() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
self.on_exit()
return
ret_val = self.add_marker_geo(self.points, g_obj=self.grb_object)
if ret_val == 'fail':
self.app.log.error("ToolMarkers.handle_numeric_placement() -> failed to add numeric geo")
self.on_exit(ok=False)
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return
self.on_exit()
def add_marker_geo(self, points_storage, g_obj):
"""
Add geometry to the solid_geometry of the copper Gerber object
:param points_storage: a dictionary holding the points where to add markers
:param g_obj: the Gerber object where to add the geometry
:return: None
"""
geo_list = self.create_marker_geometry(points_storage)
if geo_list == "fail" or not geo_list:
return "fail"
assert isinstance(geo_list, list), 'Geometry list should be a list but the type is: %s' % str(type(geo_list))
new_name = '%s_%s' % (str(self.grb_object.obj_options['name']), 'markers')
return_val = self.generate_gerber_obj_with_markers(
new_grb_obj=g_obj, marker_geometry=geo_list, outname=new_name)
return return_val
def offset_values(self, offset_reference=None, offset_x=None, offset_y=None):
"""
Will offset a set of x, y coordinates depending on the chosen reference
:param offset_reference: can be 'c' = center of the bounding box or 'e' = edge of the bounding box
:type offset_reference: str
:param offset_x: value to offset on X axis
:type offset_x: float
:param offset_y: value to offset on Y axis
:type offset_y: float
:return: a tuple of offsets (x, y)
:rtype: tuple
"""
if offset_reference is None:
offset_reference = self.ui.ref_radio.get_value()
if offset_x is None:
offset_x = self.ui.offset_x_entry.get_value()
if offset_y is None:
offset_y = self.ui.offset_y_entry.get_value()
if offset_reference == 'c': # reference from the bounding box center
# get the Gerber object on which the corner marker will be inserted
selection_index = self.ui.object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
self.app.log.error("ToolMarkers.add_markers() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
self.on_exit()
return
xmin, ymin, xmax, ymax = self.grb_object.bounds()
center_x = (xmax - xmin) / 2
center_y = (ymax - ymin) / 2
offset_x -= center_x
offset_y -= center_y
return offset_x, offset_y
def create_marker_geometry(self, points_storage):
"""
Will create the marker geometry in the specified points.
:param points_storage: a dictionary holding the marker locations
:type points_storage: dict
:return: a list of Shapely lines
:rtype: list
"""
marker_type = self.ui.type_radio.get_value()
line_thickness = self.ui.thick_entry.get_value()
line_length = self.ui.l_entry.get_value() / 2.0
mode = self.ui.mode_combo.get_value()
offset_x, offset_y = self.offset_values()
geo_list = []
if not points_storage:
self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
return 'fail'
if mode == 0: # 'automatic'
for key in points_storage:
if key == 'tl':
pt = points_storage[key]
x = pt[0] - offset_x - line_thickness / 2.0
y = pt[1] + offset_y + line_thickness / 2.0
if marker_type == 's':
line_geo_hor = LineString([
(x, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y - line_length)
])
else:
line_geo_hor = LineString([
(x - line_length, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y + line_length), (x, y - line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
if key == 'tr':
pt = points_storage[key]
x = pt[0] + offset_x + line_thickness / 2.0
y = pt[1] + offset_y + line_thickness / 2.0
if marker_type == 's':
line_geo_hor = LineString([
(x, y), (x - line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y - line_length)
])
else:
line_geo_hor = LineString([
(x + line_length, y), (x - line_length, y)
])
line_geo_vert = LineString([
(x, y + line_length), (x, y - line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
if key == 'bl':
pt = points_storage[key]
x = pt[0] - offset_x - line_thickness / 2.0
y = pt[1] - offset_y - line_thickness / 2.0
if marker_type == 's':
line_geo_hor = LineString([
(x, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y + line_length)
])
else:
line_geo_hor = LineString([
(x - line_length, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y - line_length), (x, y + line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
if key == 'br':
pt = points_storage[key]
x = pt[0] + offset_x + line_thickness / 2.0
y = pt[1] - offset_y - line_thickness / 2.0
if marker_type == 's':
line_geo_hor = LineString([
(x, y), (x - line_length, y)
])
line_geo_vert = LineString([
(x, y), (x, y + line_length)
])
else:
line_geo_hor = LineString([
(x + line_length, y), (x - line_length, y)
])
line_geo_vert = LineString([
(x, y - line_length), (x, y + line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
elif mode == 1 or mode == 2: # 'manual' or 'numeric'
if 'manual' in points_storage:
if points_storage['manual']:
for man_pt in points_storage['manual']:
x = man_pt[0] - line_thickness / 2.0
y = man_pt[1] + line_thickness / 2.0
line_geo_hor = LineString([
(x - line_length, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y + line_length), (x, y - line_length)
])
geo_list.append(line_geo_hor)
geo_list.append(line_geo_vert)
else:
self.app.log.warning("Not enough points.")
return "fail"
return geo_list
def generate_gerber_obj_with_markers(self, new_grb_obj, marker_geometry, outname):
"""
:param new_grb_obj: a Gerber App object
:type new_grb_obj:
:param marker_geometry: a list of Shapely geometries
:type marker_geometry: list
:param outname: the name for the new Gerber object
:type outname: str
:return:
:rtype:
"""
line_thickness = self.ui.thick_entry.get_value()
new_apertures = deepcopy(new_grb_obj.tools)
aperture_found = None
for ap_id, ap_val in new_apertures.items():
if ap_val['type'] == 'C' and ap_val['size'] == line_thickness:
aperture_found = ap_id
break
geo_buff_list = []
if aperture_found:
for geo in marker_geometry:
geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=2)
geo_buff_list.append(geo_buff)
dict_el = {'follow': geo, 'solid': geo_buff}
new_apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
else:
ap_keys = list(new_apertures.keys())
if ap_keys:
new_apid = int(max(ap_keys)) + 1
else:
new_apid = 10
new_apertures[new_apid] = {
'type': 'C',
'size': line_thickness,
'geometry': []
}
for geo in marker_geometry:
geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=3)
geo_buff_list.append(geo_buff)
dict_el = {'follow': geo, 'solid': geo_buff}
new_apertures[new_apid]['geometry'].append(deepcopy(dict_el))
s_list = []
if new_grb_obj.solid_geometry:
if isinstance(new_grb_obj.solid_geometry, MultiPolygon):
work_geo = new_grb_obj.solid_geometry.geoms
else:
work_geo = new_grb_obj.solid_geometry
try:
for poly in work_geo:
s_list.append(poly)
except TypeError:
s_list.append(work_geo)
geo_buff_list = MultiPolygon(geo_buff_list)
geo_buff_list = geo_buff_list.buffer(0)
geo_buff_list = flatten_shapely_geometry(geo_buff_list)
try:
for poly in geo_buff_list:
s_list.append(poly)
except TypeError:
s_list.append(geo_buff_list)
def initialize(grb_obj, app_obj):
grb_obj.obj_options = LoudDict()
for opt in new_grb_obj.obj_options:
if opt != 'name':
grb_obj.obj_options[opt] = deepcopy(new_grb_obj.obj_options[opt])
grb_obj.obj_options['name'] = outname
grb_obj.multitool = False
grb_obj.multigeo = False
grb_obj.follow = deepcopy(new_grb_obj.follow)
grb_obj.tools = new_apertures
grb_obj.solid_geometry = flatten_shapely_geometry(unary_union(s_list))
grb_obj.follow_geometry = flatten_shapely_geometry(new_grb_obj.follow_geometry + marker_geometry)
grb_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None, local_use=grb_obj,
use_thread=False)
ret = self.app.app_obj.new_object('gerber', outname, initialize, plot=True, autoselected=False)
return ret
def generate_geo_obj_with_markers(self, new_geo_obj, marker_geometry, outname):
"""
:param new_geo_obj: a Geometry App object
:type new_geo_obj:
:param marker_geometry: a list of Shapely geometries
:type marker_geometry: list
:param outname: the name for the new Geometry object
:type outname: str
:return:
:rtype:
"""
tooldia = self.ui.thick_entry.get_value()
new_tools = deepcopy(new_geo_obj.tools)
tool_found = None
for tool, tool_dict in new_tools.items():
if tool_dict['tooldia'] == tooldia:
tool_found = tool
break
if tool_found:
if isinstance(new_tools[tool_found]['solid_geometry'], list):
new_tools[tool_found]['solid_geometry'] += marker_geometry
else:
new_tools[tool_found]['solid_geometry'] = [new_tools[tool_found]['solid_geometry'], marker_geometry]
else:
obj_tools = list(new_tools.keys())
if obj_tools:
new_tool = max(obj_tools) + 1
else:
new_tool = 1
new_data = {}
for opt_key in self.app.options:
if opt_key.find('geometry' + "_") == 0:
oname = opt_key[len('geometry') + 1:]
new_data[oname] = self.app.options[opt_key]
for opt_key in self.app.options:
if opt_key.find('tools_') == 0:
new_data[opt_key] = self.app.options[opt_key]
new_tools.update(
{
new_tool: {
'tooldia': tooldia,
'data': deepcopy(new_data),
'solid_geometry': marker_geometry
}
}
)
# remove possible tools without geometry
for tool in list(new_tools.keys()):
if not new_tools[tool]['solid_geometry']:
new_tools.pop(tool)
s_list = []
if new_geo_obj.solid_geometry:
s_list = flatten_shapely_geometry(new_geo_obj.solid_geometry)
try:
for g_el in marker_geometry:
s_list.append(g_el)
except TypeError:
s_list.append(marker_geometry)
def initialize(geo_obj, app_obj):
geo_obj.obj_options = LoudDict()
for opt in new_geo_obj.obj_options:
geo_obj.obj_options[opt] = deepcopy(new_geo_obj.obj_options[opt])
geo_obj.obj_options['name'] = outname
# Propagate options
geo_obj.obj_options["tools_mill_tooldia"] = app_obj.defaults["tools_mill_tooldia"]
geo_obj.solid_geometry = flatten_shapely_geometry(s_list)
geo_obj.multitool = True
geo_obj.multigeo = True
geo_obj.tools = deepcopy(new_tools)
ret = self.app.app_obj.new_object('geometry', outname, initialize, plot=True, autoselected=False)
return ret
def on_create_drill_object(self):
self.app.call_source = "markers_tool"
tooldia = self.ui.drill_dia_entry.get_value()
if tooldia == 0:
self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("The tool diameter is zero.")))
return
line_thickness = self.ui.thick_entry.get_value()
tl_state = self.ui.tl_cb.get_value()
tr_state = self.ui.tr_cb.get_value()
bl_state = self.ui.bl_cb.get_value()
br_state = self.ui.br_cb.get_value()
# get the Gerber object on which the corner marker will be inserted
selection_index = self.ui.object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
self.app.log.error("ToolMarkers.add_markers() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
self.app.call_source = "app"
return
if tl_state is False and tr_state is False and bl_state is False and br_state is False and \
'manual' not in self.points:
self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
self.app.call_source = "app"
return
xmin, ymin, xmax, ymax = self.grb_object.bounds()
offset_x, offset_y = self.offset_values()
# list of (x,y) tuples. Store here the drill coordinates
drill_list = []
if 'manual' not in self.points:
if tl_state:
x = xmin - offset_x - line_thickness / 2.0
y = ymax + offset_y + line_thickness / 2.0
drill_list.append(
Point((x, y))
)
if tr_state:
x = xmax + offset_x + line_thickness / 2.0
y = ymax + offset_y + line_thickness / 2.0
drill_list.append(
Point((x, y))
)
if bl_state:
x = xmin - offset_x - line_thickness / 2.0
y = ymin - offset_y - line_thickness / 2.0
drill_list.append(
Point((x, y))
)
if br_state:
x = xmax + offset_x + line_thickness / 2.0
y = ymin - offset_y - line_thickness / 2.0
drill_list.append(
Point((x, y))
)
else:
if self.points['manual']:
for pt in self.points['manual']:
add_pt = (pt[0] - (line_thickness / 2)), (pt[1] + line_thickness / 2)
drill_list.append(
Point(add_pt)
)
else:
self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
self.app.call_source = "app"
return
tools = {
1: {
"tooldia": tooldia,
"drills": drill_list,
"slots": [],
"data": {},
"solid_geometry": []
}
}
def obj_init(obj_inst, app_inst):
obj_inst.obj_options.update({
'name': outname
})
obj_inst.tools = deepcopy(tools)
obj_inst.create_geometry()
obj_inst.source_file = app_inst.f_handlers.export_excellon(obj_name=obj_inst.obj_options['name'],
local_use=obj_inst,
filename=None,
use_thread=False)
outname = '%s_%s' % (str(self.grb_object.obj_options['name']), 'corner_drills')
ret_val = self.app.app_obj.new_object("excellon", outname, obj_init, autoselected=False)
self.app.call_source = "app"
if ret_val == 'fail':
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
else:
self.app.inform.emit('[success] %s' % _("Excellon object with corner drills created."))
def on_create_check_object(self):
self.app.call_source = "markers_tool"
tooldia = 0.1 if self.units == 'MM' else 0.0254
if tooldia == 0:
self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("The tool diameter is zero.")))
return
line_thickness = self.ui.thick_entry.get_value()
tl_state = self.ui.tl_cb.get_value()
tr_state = self.ui.tr_cb.get_value()
bl_state = self.ui.bl_cb.get_value()
br_state = self.ui.br_cb.get_value()
# get the Gerber object on which the corner marker will be inserted
selection_index = self.ui.object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
self.app.log.error("ToolMarkers.add_markers() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
self.app.call_source = "app"
return
if tl_state is False and tr_state is False and bl_state is False and br_state is False and \
'manual' not in self.points:
self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
self.app.call_source = "app"
return
xmin, ymin, xmax, ymax = self.grb_object.bounds()
offset_x, offset_y = self.offset_values()
# list of (x,y) tuples. Store here the drill coordinates
drill_list = []
if 'manual' not in self.points:
if tl_state:
x = xmin - offset_x - line_thickness / 2.0
y = ymax + offset_y + line_thickness / 2.0
drill_list.append(
Point((x, y))
)
if tr_state:
x = xmax + offset_x + line_thickness / 2.0
y = ymax + offset_y + line_thickness / 2.0
drill_list.append(
Point((x, y))
)
if bl_state:
x = xmin - offset_x - line_thickness / 2.0
y = ymin - offset_y - line_thickness / 2.0
drill_list.append(
Point((x, y))
)
if br_state:
x = xmax + offset_x + line_thickness / 2.0
y = ymin - offset_y - line_thickness / 2.0
drill_list.append(
Point((x, y))
)
else:
if self.points['manual']:
for pt in self.points['manual']:
add_pt = (pt[0] - (line_thickness / 2)), (pt[1] + line_thickness / 2)
drill_list.append(
Point(add_pt)
)
else:
self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
self.app.call_source = "app"
return
tools = {
1: {
"tooldia": tooldia,
"drills": drill_list,
"slots": [],
'data': {},
"solid_geometry": []
}
}
def obj_init(new_obj, app_inst):
new_obj.tools = deepcopy(tools)
# make sure we use the special preprocessor for checking
for tool in tools:
new_obj.tools[tool]['data']['tools_drill_ppname_e'] = 'Check_points'
new_obj.create_geometry()
new_obj.obj_options.update({
'name': outname,
'tools_drill_cutz': -0.1,
'tools_drill_ppname_e': 'Check_points'
})
new_obj.source_file = app_inst.f_handlers.export_excellon(obj_name=new_obj.obj_options['name'],
local_use=new_obj,
filename=None,
use_thread=False)
outname = '%s_%s' % (str(self.grb_object.obj_options['name']), 'verification')
ret_val = self.app.app_obj.new_object("excellon", outname, obj_init, autoselected=False)
self.app.call_source = "app"
if ret_val == 'fail':
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
else:
self.app.inform.emit('[success] %s' % _("Excellon object with corner drills created."))
def on_insert_markers_in_external_objects(self):
obj_type = self.ui.insert_type_radio.get_value() # values in ["grb", "geo"]
geo_list = self.create_marker_geometry(self.points)
if geo_list == "fail" or not geo_list:
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
return
assert isinstance(geo_list, list), 'Geometry list should be a list but the type is: %s' % str(
type(geo_list))
if obj_type == 'grb':
# get the Gerber object on which the corner marker will be inserted
selection_index = self.ui.obj_insert_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.ui.obj_insert_combo.rootModelIndex())
try:
new_grb_obj = model_index.internalPointer().obj
except Exception as e:
self.app.log.error("ToolMarkers.on_insert_markers_in_external_objects() Gerber --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
self.app.call_source = "app"
return
new_name = '%s_%s' % (str(new_grb_obj.obj_options['name']), 'markers')
return_val = self.generate_gerber_obj_with_markers(new_grb_obj=new_grb_obj, marker_geometry=geo_list,
outname=new_name)
else:
# get the Geometry object on which the corner marker will be inserted
geo_obj_name = self.ui.obj_insert_combo.get_value()
try:
new_geo_obj = self.app.collection.get_by_name(geo_obj_name)
except Exception as e:
self.app.log.error("ToolMarkers.on_insert_markers_in_external_objects() Geometry --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Geometry object available."))
self.app.call_source = "app"
return
new_name = '%s_%s' % (str(new_geo_obj.obj_options['name']), 'markers')
return_val = self.generate_geo_obj_with_markers(new_geo_obj=new_geo_obj, marker_geometry=geo_list,
outname=new_name)
if return_val == "fail":
self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
else:
self.app.inform.emit('[success] %s' % _("Done."))
def on_points_changed(self, val=None):
if self.points:
self.ui.insert_frame.setDisabled(False)
self.ui.insert_markers_button.setDisabled(False)
else:
self.ui.insert_frame.setDisabled(True)
self.ui.insert_markers_button.setDisabled(True)
def replot(self, obj, run_thread=True):
def worker_task():
with self.app.proc_container.new('%s ...' % _("Plotting")):
obj.plot()
self.app.app_obj.object_plotted.emit(obj)
if run_thread:
self.app.worker_task.emit({'fcn': worker_task, 'params': []})
else:
worker_task()
def on_exit(self, corner_gerber_obj=None, cancelled=None, ok=True):
self.clear_utility_geometry()
# restore cursor
self.app.on_cursor_type(val=self.old_cursor_type, control_cursor=False)
self.app.plotcanvas.cursor_color = self.cursor_color_memory
# plot the object
if corner_gerber_obj:
try:
for ob in corner_gerber_obj:
self.replot(obj=ob)
except (AttributeError, TypeError):
self.replot(obj=corner_gerber_obj)
except Exception:
return
# update the bounding box values
try:
a, b, c, d = self.grb_object.bounds()
self.grb_object.obj_options['xmin'] = a
self.grb_object.obj_options['ymin'] = b
self.grb_object.obj_options['xmax'] = c
self.grb_object.obj_options['ymax'] = d
except Exception as e:
self.app.log.error("ToolMarkers.on_exit() copper_obj bounds error --> %s" % str(e))
self.app.call_source = "app"
self.app.ui.notebook.setDisabled(False)
self.disconnect_event_handlers()
if cancelled is True:
self.app.delete_selection_shape()
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled by user request."))
return
if ok:
self.app.inform.emit('[success] %s' % _("A Gerber object with corner markers was created."))
def connect_event_handlers(self):
if self.handlers_connected is False:
if self.app.use_3d_engine:
self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_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.mr)
self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
self.handlers_connected = True
def disconnect_event_handlers(self):
if self.handlers_connected is True:
if self.app.use_3d_engine:
self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
else:
self.app.plotcanvas.graph_event_disconnect(self.mr)
self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
self.app.on_mouse_click_over_plot)
self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
self.app.on_mouse_click_release_over_plot)
self.handlers_connected = False
self.app.ui.notebook.setDisabled(False)
def on_mouse_move(self, event):
pass
def on_mouse_release(self, event):
if self.app.use_3d_engine:
event_pos = event.pos
right_button = 2
self.app.event_is_dragging = self.app.event_is_dragging
else:
event_pos = (event.xdata, event.ydata)
right_button = 3
self.app.event_is_dragging = self.app.ui.popMenu.mouse_is_panning
if event.button == 1:
pos_canvas = self.canvas.translate_coords(event_pos)
if self.app.grid_status():
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
else:
pos = (pos_canvas[0], pos_canvas[1])
if 'manual' not in self.points:
self.points['manual'] = []
self.points['manual'].append(pos)
self.draw_utility_geometry(pos=pos)
self.app.inform.emit(
'%s: %d. %s' %
(_("Added marker"), len(self.points['manual']),
_("Click to add next marker or right click to finish.")))
elif event.button == right_button and self.app.event_is_dragging is False:
self.handle_manual_placement()
def draw_utility_geometry(self, pos):
line_thickness = self.ui.thick_entry.get_value()
line_length = self.ui.l_entry.get_value() / 2.0
x = pos[0] - line_thickness / 2.0
y = pos[1] + line_thickness / 2.0
line_geo_hor = LineString([
(x - line_length, y), (x + line_length, y)
])
line_geo_vert = LineString([
(x, y + line_length), (x, y - line_length)
])
outline = '#0000FFAF'
for shp in [line_geo_hor, line_geo_vert]:
self.temp_shapes.add(shp, color=outline, update=True, layer=0, tolerance=None)
if self.app.use_3d_engine:
self.temp_shapes.redraw()
def clear_utility_geometry(self):
self.temp_shapes.clear(update=True)
self.temp_shapes.redraw()
def on_plugin_cleanup(self):
self.on_exit(ok=False)
class MarkersUI:
pluginName = _("Markers")
def __init__(self, layout, app):
self.app = app
self.decimals = self.app.decimals
self.layout = layout
self.title_box = QtWidgets.QHBoxLayout()
self.layout.addLayout(self.title_box)
# ## Title
title_label = FCLabel("%s" % self.pluginName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
self.title_box.addWidget(title_label)
# App Level label
self.level = QtWidgets.QToolButton()
self.level.setToolTip(
_(
"Beginner Mode - many parameters are hidden.\n"
"Advanced Mode - full control.\n"
"Permanent change is done in 'Preferences' menu."
)
)
self.level.setCheckable(True)
self.title_box.addWidget(self.level)
self.title_box = QtWidgets.QHBoxLayout()
self.layout.addLayout(self.title_box)
self.tools_frame = QtWidgets.QFrame()
self.tools_frame.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.tools_frame)
self.tools_box = QtWidgets.QVBoxLayout()
self.tools_box.setContentsMargins(0, 0, 0, 0)
self.tools_frame.setLayout(self.tools_box)
# #############################################################################################################
# Gerber Source Object
# #############################################################################################################
self.object_label = FCLabel('%s' % _("Source Object"), color='darkorange', bold=True)
self.object_label.setToolTip(_("The Gerber object to which will be added corner markers."))
self.object_combo = FCComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.is_last = False
self.object_combo.obj_type = "Gerber"
self.tools_box.addWidget(self.object_label)
self.tools_box.addWidget(self.object_combo)
# separator_line = QtWidgets.QFrame()
# separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
# separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
# self.layout.addWidget(separator_line)
# #############################################################################################################
# Parameters Frame
# #############################################################################################################
self.param_label = FCLabel('%s' % _("Parameters"), color='blue', bold=True)
self.param_label.setToolTip(_("Parameters used for this tool."))
self.tools_box.addWidget(self.param_label)
par_frame = FCFrame()
self.tools_box.addWidget(par_frame)
param_grid = GLay(v_spacing=5, h_spacing=3)
par_frame.setLayout(param_grid)
# Type of Marker
self.type_label = FCLabel('%s:' % _("Type"))
self.type_label.setToolTip(
_("Shape of the marker.")
)
self.type_radio = RadioSet([
{"label": _("Semi-Cross"), "value": "s"},
{"label": _("Cross"), "value": "c"},
])
param_grid.addWidget(self.type_label, 2, 0)
param_grid.addWidget(self.type_radio, 2, 1)
# Thickness #
self.thick_label = FCLabel('%s:' % _("Thickness"))
self.thick_label.setToolTip(
_("The thickness of the line that makes the corner marker.")
)
self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.thick_entry.set_range(0.0000, 10.0000)
self.thick_entry.set_precision(self.decimals)
self.thick_entry.setWrapping(True)
self.thick_entry.setSingleStep(10 ** -self.decimals)
param_grid.addWidget(self.thick_label, 4, 0)
param_grid.addWidget(self.thick_entry, 4, 1)
# Length #
self.l_label = FCLabel('%s:' % _("Length"))
self.l_label.setToolTip(
_("The length of the line that makes the corner marker.")
)
self.l_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.l_entry.set_range(-10000.0000, 10000.0000)
self.l_entry.set_precision(self.decimals)
self.l_entry.setSingleStep(10 ** -self.decimals)
param_grid.addWidget(self.l_label, 6, 0)
param_grid.addWidget(self.l_entry, 6, 1)
# #############################################################################################################
# Offset Frame
# #############################################################################################################
self.offset_title_label = FCLabel('%s' % _("Offset"), color='magenta', bold=True)
self.offset_title_label.setToolTip(_("Offset locations from the set reference."))
self.tools_box.addWidget(self.offset_title_label)
self.off_frame = FCFrame()
self.tools_box.addWidget(self.off_frame)
off_grid = GLay(v_spacing=5, h_spacing=3)
self.off_frame.setLayout(off_grid)
# Offset Reference
self.ref_label = FCLabel('%s:' % _("Reference"))
self.ref_label.setToolTip(
_("Reference for offseting the marker locations.\n"
"- Edge - referenced from the bounding box edge\n"
"- Center - referenced from the bounding box center")
)
self.ref_radio = RadioSet([
{"label": _("Edge"), "value": "e"},
{"label": _("Center"), "value": "c"},
])
off_grid.addWidget(self.ref_label, 0, 0)
off_grid.addWidget(self.ref_radio, 0, 1, 1, 2)
# Offset X #
self.offset_x_label = FCLabel('%s X:' % _("Offset"))
self.offset_x_label.setToolTip(
_("Offset on the X axis.")
)
self.offset_x_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.offset_x_entry.set_range(-10000.0000, 10000.0000)
self.offset_x_entry.set_precision(self.decimals)
self.offset_x_entry.setSingleStep(0.1)
off_grid.addWidget(self.offset_x_label, 2, 0)
off_grid.addWidget(self.offset_x_entry, 2, 1)
# Offset Y #
self.offset_y_label = FCLabel('%s Y:' % _("Offset"))
self.offset_y_label.setToolTip(
_("Offset on the Y axis.")
)
self.offset_y_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.offset_y_entry.set_range(-10000.0000, 10000.0000)
self.offset_y_entry.set_precision(self.decimals)
self.offset_y_entry.setSingleStep(0.1)
off_grid.addWidget(self.offset_y_label, 3, 0)
off_grid.addWidget(self.offset_y_entry, 3, 1)
# Margin link
self.offset_link_button = QtWidgets.QToolButton()
self.offset_link_button.setIcon(QtGui.QIcon(self.app.resource_location + '/link32.png'))
self.offset_link_button.setSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding,
QtWidgets.QSizePolicy.Policy.Expanding)
self.offset_link_button.setCheckable(True)
off_grid.addWidget(self.offset_link_button, 2, 2, 2, 1)
# #############################################################################################################
# Locations Frame
# #############################################################################################################
self.locs_label = FCLabel('%s' % _("Locations"), color='red', bold=True)
self.locs_label.setToolTip(_("Locations where to place corner markers."))
self.tools_box.addWidget(self.locs_label)
self.loc_frame = FCFrame()
self.tools_box.addWidget(self.loc_frame)
# Grid Layout
grid_loc = GLay(v_spacing=5, h_spacing=3)
self.loc_frame.setLayout(grid_loc)
# TOP LEFT
self.tl_cb = FCCheckBox(_("Top Left"))
grid_loc.addWidget(self.tl_cb, 0, 0)
# TOP RIGHT
self.tr_cb = FCCheckBox(_("Top Right"))
grid_loc.addWidget(self.tr_cb, 0, 1)
# BOTTOM LEFT
self.bl_cb = FCCheckBox(_("Bottom Left"))
grid_loc.addWidget(self.bl_cb, 2, 0)
# BOTTOM RIGHT
self.br_cb = FCCheckBox(_("Bottom Right"))
grid_loc.addWidget(self.br_cb, 2, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
grid_loc.addWidget(separator_line, 4, 0, 1, 2)
# Toggle ALL
self.toggle_all_cb = FCCheckBox(_("Toggle ALL"))
grid_loc.addWidget(self.toggle_all_cb, 6, 0, 1, 2)
# #############################################################################################################
# Selection Frame
# #############################################################################################################
self.mode_label = FCLabel('%s' % _("Selection"), color='green', bold=True)
self.tools_box.addWidget(self.mode_label)
self.s_frame = FCFrame()
self.tools_box.addWidget(self.s_frame)
# Grid Layout
grid_sel = GLay(v_spacing=5, h_spacing=3)
self.s_frame.setLayout(grid_sel)
# Type of placement of markers
self.mode_label = FCLabel('%s: ' % _("Mode"))
self.mode_label.setToolTip(
_("When the manual type is chosen, the markers\n"
"are manually placed on canvas.")
)
self.mode_combo = FCComboBox2()
self.mode_combo.addItems([_("Auto"), _("Manual"), _("Numeric")])
grid_sel.addWidget(self.mode_label, 0, 0)
grid_sel.addWidget(self.mode_combo, 0, 1)
# Big Cursor
self.big_cursor_cb = FCCheckBox('%s' % _("Big cursor"))
self.big_cursor_cb.setToolTip(
_("Use a big cursor."))
grid_sel.addWidget(self.big_cursor_cb, 2, 0, 1, 2)
# Parametric Entry
self.marker_coords_lbl = FCLabel('%s:' % _("Coordinates"))
self.marker_coords_lbl.setToolTip(
_("Tuple of marker coordinates.")
)
self.marker_coords_entry = NumericalEvalTupleEntry(border_color='#0069A9')
self.marker_coords_entry.setPlaceholderText(_("Comma separated values"))
grid_sel.addWidget(self.marker_coords_lbl, 4, 0)
grid_sel.addWidget(self.marker_coords_entry, 4, 1)
# #############################################################################################################
# ## Insert Corner Marker Button
# #############################################################################################################
self.add_marker_button = FCButton(_("Add Marker"))
self.add_marker_button.setIcon(QtGui.QIcon(self.app.resource_location + '/corners_32.png'))
self.add_marker_button.setToolTip(
_("Will add corner markers to the selected object.")
)
self.add_marker_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.tools_box.addWidget(self.add_marker_button,)
# #############################################################################################################
# Drill in markers Frame
# #############################################################################################################
# Drill is markers
self.drills_label = FCLabel('%s' % _("Drills in Locations"), color='brown', bold=True)
self.tools_box.addWidget(self.drills_label)
self.drill_frame = FCFrame()
self.tools_box.addWidget(self.drill_frame)
# Grid Layout
grid_drill = GLay(v_spacing=5, h_spacing=3)
self.drill_frame.setLayout(grid_drill)
# Drill Tooldia #
self.drill_dia_label = FCLabel('%s:' % _("Drill Dia"))
self.drill_dia_label.setToolTip(
'%s.' % _("Drill Diameter")
)
self.drill_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.drill_dia_entry.set_range(0.0000, 100.0000)
self.drill_dia_entry.set_precision(self.decimals)
self.drill_dia_entry.setWrapping(True)
grid_drill.addWidget(self.drill_dia_label, 0, 0)
grid_drill.addWidget(self.drill_dia_entry, 0, 1)
# ## Create an Excellon object
self.drill_button = FCButton(_("Create Excellon Object"))
self.drill_button.setIcon(QtGui.QIcon(self.app.resource_location + '/drill32.png'))
self.drill_button.setToolTip(
_("Will add drill holes in the center of the markers.")
)
self.drill_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.tools_box.addWidget(self.drill_button)
# #############################################################################################################
# Check in Locations Frame
# #############################################################################################################
self.check_label = FCLabel('%s' % _("Check in Locations"), color='indigo', bold=True)
self.tools_box.addWidget(self.check_label)
# ## Create an Excellon object for checking the positioning
self.check_button = FCButton(_("Create Excellon Object"))
self.check_button.setIcon(QtGui.QIcon(self.app.resource_location + '/drill32.png'))
self.check_button.setToolTip(
_("Will create an Excellon object using a special preprocessor.\n"
"The spindle will not start and the mounted probe will move to\n"
"the corner locations, wait for the user interaction and then\n"
"move to the next location until the last one.")
)
self.check_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.tools_box.addWidget(self.check_button)
# #############################################################################################################
# Insert Markers Frame
# #############################################################################################################
self.insert_label = FCLabel('%s' % _("Insert Markers"), color='teal', bold=True)
self.insert_label.setToolTip(
_("Enabled only if markers are available (added to an object).\n"
"Those markers will be inserted in yet another object.")
)
self.tools_box.addWidget(self.insert_label)
self.insert_frame = FCFrame()
self.tools_box.addWidget(self.insert_frame)
insert_grid = GLay(v_spacing=5, h_spacing=3)
self.insert_frame.setLayout(insert_grid)
self.insert_type_label = FCLabel('%s:' % _("Type"))
self.insert_type_label.setToolTip(
_("Specify the type of object where the markers are inserted.")
)
# Type of object into which to insert the markers
self.insert_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'grb'},
{'label': _('Geometry'), 'value': 'geo'}])
insert_grid.addWidget(self.insert_type_label, 0, 0)
insert_grid.addWidget(self.insert_type_radio, 0, 1)
# The object into which to insert existing markers
self.obj_insert_combo = FCComboBox()
self.obj_insert_combo.setModel(self.app.collection)
self.obj_insert_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_insert_combo.is_last = False
insert_grid.addWidget(self.obj_insert_combo, 2, 0, 1, 2)
# Insert Markers
self.insert_markers_button = FCButton(_("Insert Marker"))
self.insert_markers_button.setIcon(QtGui.QIcon(self.app.resource_location + '/corners_32.png'))
self.insert_markers_button.setToolTip(
_("Will add corner markers to the selected object.")
)
self.insert_markers_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.tools_box.addWidget(self.insert_markers_button)
GLay.set_common_column_size([grid_sel, param_grid, off_grid, grid_loc, grid_drill, insert_grid], 0)
self.layout.addStretch(1)
# ## Reset Tool
self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.layout.addWidget(self.reset_button)
# #################################### FINSIHED GUI ###########################
# #############################################################################
# Signals
self.offset_link_button.clicked.connect(self.on_link_checked)
self.offset_x_entry.returnPressed.connect(self.on_marginx_edited)
def on_link_checked(self, checked):
if checked:
self.offset_x_label.set_value('%s:' % _("Offset"))
self.offset_y_label.setDisabled(True)
self.offset_y_entry.setDisabled(True)
self.offset_y_entry.set_value(self.offset_x_entry.get_value())
else:
self.offset_x_label.set_value('%s X:' % _("Offset"))
self.offset_y_label.setDisabled(False)
self.offset_y_entry.setDisabled(False)
def on_marginx_edited(self):
if self.offset_link_button.isChecked():
self.offset_y_entry.set_value(self.offset_x_entry.get_value())
def confirmation_message(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
self.decimals,
minval,
self.decimals,
maxval), False)
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
def confirmation_message_int(self, accepted, minval, maxval):
if accepted is False:
self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval), False)
else:
self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)