diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33ecda42..cfb46daf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,7 @@ CHANGELOG for FlatCAM beta
- in CNCJob UI Autolevelling - adding manual probe points now show some geometry (circles) for the added points until the adding is finished
- 2Sided Tool - finished the feature that allows picking an Excellon drill hole center as a Point mirror reference
- Tool Align Objects - moved the Tool Ui into its own class
+- for Tools: Calculators, Calibration, Copper Thieving, Corners, Fiducials - moved the Tool UI in its own class
24.08.2020
diff --git a/appTools/ToolCalculators.py b/appTools/ToolCalculators.py
index 6cf62f06..26efd73c 100644
--- a/appTools/ToolCalculators.py
+++ b/appTools/ToolCalculators.py
@@ -21,26 +21,152 @@ if '_' not in builtins.__dict__:
class ToolCalculator(AppTool):
- toolName = _("Calculators")
- v_shapeName = _("V-Shape Tool Calculator")
- unitsName = _("Units Calculator")
- eplateName = _("ElectroPlating Calculator")
-
def __init__(self, app):
AppTool.__init__(self, app)
self.app = app
self.decimals = self.app.decimals
+ # #############################################################################
+ # ######################### Tool GUI ##########################################
+ # #############################################################################
+ self.ui = CalcUI(layout=self.layout, app=self.app)
+ self.toolName = self.ui.toolName
+
+ self.units = ''
+
+ # ## Signals
+ self.ui.cutDepth_entry.valueChanged.connect(self.on_calculate_tool_dia)
+ self.ui.cutDepth_entry.returnPressed.connect(self.on_calculate_tool_dia)
+ self.ui.tipDia_entry.returnPressed.connect(self.on_calculate_tool_dia)
+ self.ui.tipAngle_entry.returnPressed.connect(self.on_calculate_tool_dia)
+ self.ui.calculate_vshape_button.clicked.connect(self.on_calculate_tool_dia)
+
+ self.ui.mm_entry.editingFinished.connect(self.on_calculate_inch_units)
+ self.ui.inch_entry.editingFinished.connect(self.on_calculate_mm_units)
+
+ self.ui.calculate_plate_button.clicked.connect(self.on_calculate_eplate)
+ self.ui.reset_button.clicked.connect(self.set_tool_ui)
+
+ def run(self, toggle=True):
+ self.app.defaults.report_usage("ToolCalculators()")
+
+ 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])
+
+ AppTool.run(self)
+
+ self.set_tool_ui()
+
+ self.app.ui.notebook.setTabText(2, _("Calc. Tool"))
+
+ def install(self, icon=None, separator=None, **kwargs):
+ AppTool.install(self, icon, separator, shortcut='Alt+C', **kwargs)
+
+ def set_tool_ui(self):
+ self.units = self.app.defaults['units'].upper()
+
+ # ## Initialize form
+ self.ui.mm_entry.set_value('%.*f' % (self.decimals, 0))
+ self.ui.inch_entry.set_value('%.*f' % (self.decimals, 0))
+
+ length = self.app.defaults["tools_calc_electro_length"]
+ width = self.app.defaults["tools_calc_electro_width"]
+ density = self.app.defaults["tools_calc_electro_cdensity"]
+ growth = self.app.defaults["tools_calc_electro_growth"]
+ self.ui.pcblength_entry.set_value(length)
+ self.ui.pcbwidth_entry.set_value(width)
+ self.ui.cdensity_entry.set_value(density)
+ self.ui.growth_entry.set_value(growth)
+ self.ui.cvalue_entry.set_value(0.00)
+ self.ui.time_entry.set_value(0.0)
+
+ tip_dia = self.app.defaults["tools_calc_vshape_tip_dia"]
+ tip_angle = self.app.defaults["tools_calc_vshape_tip_angle"]
+ cut_z = self.app.defaults["tools_calc_vshape_cut_z"]
+
+ self.ui.tipDia_entry.set_value(tip_dia)
+ self.ui.tipAngle_entry.set_value(tip_angle)
+ self.ui.cutDepth_entry.set_value(cut_z)
+ self.ui.effectiveToolDia_entry.set_value('0.0000')
+
+ def on_calculate_tool_dia(self):
+ # Calculation:
+ # Manufacturer gives total angle of the the tip but we need only half of it
+ # tangent(half_tip_angle) = opposite side / adjacent = part_of _real_dia / depth_of_cut
+ # effective_diameter = tip_diameter + part_of_real_dia_left_side + part_of_real_dia_right_side
+ # tool is symmetrical therefore: part_of_real_dia_left_side = part_of_real_dia_right_side
+ # effective_diameter = tip_diameter + (2 * part_of_real_dia_left_side)
+ # effective diameter = tip_diameter + (2 * depth_of_cut * tangent(half_tip_angle))
+
+ tip_diameter = float(self.ui.tipDia_entry.get_value())
+
+ half_tip_angle = float(self.ui.tipAngle_entry.get_value()) / 2.0
+
+ cut_depth = float(self.ui.cutDepth_entry.get_value())
+ cut_depth = -cut_depth if cut_depth < 0 else cut_depth
+
+ tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle)))
+ self.ui.effectiveToolDia_entry.set_value("%.*f" % (self.decimals, tool_diameter))
+
+ def on_calculate_inch_units(self):
+ mm_val = float(self.mm_entry.get_value())
+ self.ui.inch_entry.set_value('%.*f' % (self.decimals, (mm_val / 25.4)))
+
+ def on_calculate_mm_units(self):
+ inch_val = float(self.inch_entry.get_value())
+ self.ui.mm_entry.set_value('%.*f' % (self.decimals, (inch_val * 25.4)))
+
+ def on_calculate_eplate(self):
+ length = float(self.ui.pcblength_entry.get_value())
+ width = float(self.ui.pcbwidth_entry.get_value())
+ density = float(self.ui.cdensity_entry.get_value())
+ copper = float(self.ui.growth_entry.get_value())
+
+ calculated_current = (length * width * density) * 0.0021527820833419
+ calculated_time = copper * 2.142857142857143 * float(20 / density)
+
+ self.ui.cvalue_entry.set_value('%.2f' % calculated_current)
+ self.ui.time_entry.set_value('%.1f' % calculated_time)
+
+
+class CalcUI:
+
+ toolName = _("Calculators")
+ v_shapeName = _("V-Shape Tool Calculator")
+ unitsName = _("Units Calculator")
+ eplateName = _("ElectroPlating Calculator")
+
+ def __init__(self, layout, app):
+ self.app = app
+ self.decimals = self.app.decimals
+ self.layout = layout
+
# ## Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
- QLabel
- {
- font-size: 16px;
- font-weight: bold;
- }
- """)
+ QLabel
+ {
+ font-size: 16px;
+ font-weight: bold;
+ }
+ """)
self.layout.addWidget(title_label)
# #####################
@@ -249,123 +375,29 @@ class ToolCalculator(AppTool):
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
self.layout.addWidget(self.reset_button)
- self.units = ''
+ # #################################### FINSIHED GUI ###########################
+ # #############################################################################
- # ## Signals
- self.cutDepth_entry.valueChanged.connect(self.on_calculate_tool_dia)
- self.cutDepth_entry.returnPressed.connect(self.on_calculate_tool_dia)
- self.tipDia_entry.returnPressed.connect(self.on_calculate_tool_dia)
- self.tipAngle_entry.returnPressed.connect(self.on_calculate_tool_dia)
- self.calculate_vshape_button.clicked.connect(self.on_calculate_tool_dia)
-
- self.mm_entry.editingFinished.connect(self.on_calculate_inch_units)
- self.inch_entry.editingFinished.connect(self.on_calculate_mm_units)
-
- self.calculate_plate_button.clicked.connect(self.on_calculate_eplate)
- self.reset_button.clicked.connect(self.set_tool_ui)
-
- def run(self, toggle=True):
- self.app.defaults.report_usage("ToolCalculators()")
-
- 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
+ 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:
- if self.app.ui.splitter.sizes()[0] == 0:
- self.app.ui.splitter.setSizes([1, 1])
+ self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
- AppTool.run(self)
-
- self.set_tool_ui()
-
- self.app.ui.notebook.setTabText(2, _("Calc. Tool"))
-
- def install(self, icon=None, separator=None, **kwargs):
- AppTool.install(self, icon, separator, shortcut='Alt+C', **kwargs)
-
- def set_tool_ui(self):
- self.units = self.app.defaults['units'].upper()
-
- # ## Initialize form
- self.mm_entry.set_value('%.*f' % (self.decimals, 0))
- self.inch_entry.set_value('%.*f' % (self.decimals, 0))
-
- length = self.app.defaults["tools_calc_electro_length"]
- width = self.app.defaults["tools_calc_electro_width"]
- density = self.app.defaults["tools_calc_electro_cdensity"]
- growth = self.app.defaults["tools_calc_electro_growth"]
- self.pcblength_entry.set_value(length)
- self.pcbwidth_entry.set_value(width)
- self.cdensity_entry.set_value(density)
- self.growth_entry.set_value(growth)
- self.cvalue_entry.set_value(0.00)
- self.time_entry.set_value(0.0)
-
- tip_dia = self.app.defaults["tools_calc_vshape_tip_dia"]
- tip_angle = self.app.defaults["tools_calc_vshape_tip_angle"]
- cut_z = self.app.defaults["tools_calc_vshape_cut_z"]
-
- self.tipDia_entry.set_value(tip_dia)
- self.tipAngle_entry.set_value(tip_angle)
- self.cutDepth_entry.set_value(cut_z)
- self.effectiveToolDia_entry.set_value('0.0000')
-
- def on_calculate_tool_dia(self):
- # Calculation:
- # Manufacturer gives total angle of the the tip but we need only half of it
- # tangent(half_tip_angle) = opposite side / adjacent = part_of _real_dia / depth_of_cut
- # effective_diameter = tip_diameter + part_of_real_dia_left_side + part_of_real_dia_right_side
- # tool is symmetrical therefore: part_of_real_dia_left_side = part_of_real_dia_right_side
- # effective_diameter = tip_diameter + (2 * part_of_real_dia_left_side)
- # effective diameter = tip_diameter + (2 * depth_of_cut * tangent(half_tip_angle))
-
- tip_diameter = float(self.tipDia_entry.get_value())
-
- half_tip_angle = float(self.tipAngle_entry.get_value()) / 2.0
-
- cut_depth = float(self.cutDepth_entry.get_value())
- cut_depth = -cut_depth if cut_depth < 0 else cut_depth
-
- tool_diameter = tip_diameter + (2 * cut_depth * math.tan(math.radians(half_tip_angle)))
- self.effectiveToolDia_entry.set_value("%.*f" % (self.decimals, tool_diameter))
-
- def on_calculate_inch_units(self):
- mm_val = float(self.mm_entry.get_value())
- self.inch_entry.set_value('%.*f' % (self.decimals, (mm_val / 25.4)))
-
- def on_calculate_mm_units(self):
- inch_val = float(self.inch_entry.get_value())
- self.mm_entry.set_value('%.*f' % (self.decimals, (inch_val * 25.4)))
-
- def on_calculate_eplate(self):
- length = float(self.pcblength_entry.get_value())
- width = float(self.pcbwidth_entry.get_value())
- density = float(self.cdensity_entry.get_value())
- copper = float(self.growth_entry.get_value())
-
- calculated_current = (length * width * density) * 0.0021527820833419
- calculated_time = copper * 2.142857142857143 * float(20 / density)
-
- self.cvalue_entry.set_value('%.2f' % calculated_current)
- self.time_entry.set_value('%.1f' % calculated_time)
-
-# end of file
+ 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)
diff --git a/appTools/ToolCalibration.py b/appTools/ToolCalibration.py
index a868d48e..17c0abde 100644
--- a/appTools/ToolCalibration.py
+++ b/appTools/ToolCalibration.py
@@ -34,8 +34,6 @@ log = logging.getLogger('base')
class ToolCalibration(AppTool):
- toolName = _("Calibration Tool")
-
def __init__(self, app):
AppTool.__init__(self, app)
@@ -44,18 +42,715 @@ class ToolCalibration(AppTool):
self.decimals = self.app.decimals
+ # #############################################################################
+ # ######################### Tool GUI ##########################################
+ # #############################################################################
+ self.ui = CalibUI(layout=self.layout, app=self.app)
+ self.toolName = self.ui.toolName
+
+ self.mr = None
+ self.units = ''
+
+ # here store 4 points to be used for calibration
+ self.click_points = [[], [], [], []]
+
+ # store the status of the grid
+ self.grid_status_memory = None
+
+ self.target_obj = None
+
+ # if the mouse events are connected to a local method set this True
+ self.local_connected = False
+
+ # reference for the tab where to open and view the verification GCode
+ self.gcode_editor_tab = None
+
+ # calibrated object
+ self.cal_object = None
+
+ # ## Signals
+ self.ui.cal_source_radio.activated_custom.connect(self.on_cal_source_radio)
+ self.ui.obj_type_combo.currentIndexChanged.connect(self.on_obj_type_combo)
+ self.ui.adj_object_type_combo.currentIndexChanged.connect(self.on_adj_obj_type_combo)
+
+ self.ui.start_button.clicked.connect(self.on_start_collect_points)
+
+ self.ui.gcode_button.clicked.connect(self.generate_verification_gcode)
+ self.ui.adj_gcode_button.clicked.connect(self.generate_verification_gcode)
+
+ self.ui.generate_factors_button.clicked.connect(self.calculate_factors)
+
+ self.ui.scale_button.clicked.connect(self.on_scale_button)
+ self.ui.skew_button.clicked.connect(self.on_skew_button)
+
+ self.ui.cal_button.clicked.connect(self.on_cal_button_click)
+ self.ui.reset_button.clicked.connect(self.set_tool_ui)
+
+ def run(self, toggle=True):
+ self.app.defaults.report_usage("ToolCalibration()")
+
+ 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])
+
+ AppTool.run(self)
+
+ self.set_tool_ui()
+
+ self.app.ui.notebook.setTabText(2, _("Calibration Tool"))
+
+ def install(self, icon=None, separator=None, **kwargs):
+ AppTool.install(self, icon, separator, shortcut='Alt+E', **kwargs)
+
+ def set_tool_ui(self):
+ self.units = self.app.defaults['units'].upper()
+
+ if self.local_connected is True:
+ self.disconnect_cal_events()
+
+ self.ui.bottom_left_coordx_found.set_value(_("Origin"))
+ self.ui.bottom_left_coordy_found.set_value(_("Origin"))
+
+ self.reset_calibration_points()
+
+ self.ui.cal_source_radio.set_value(self.app.defaults['tools_cal_calsource'])
+ self.ui.travelz_entry.set_value(self.app.defaults['tools_cal_travelz'])
+ self.ui.verz_entry.set_value(self.app.defaults['tools_cal_verz'])
+ self.ui.zeroz_cb.set_value(self.app.defaults['tools_cal_zeroz'])
+ self.ui.toolchangez_entry.set_value(self.app.defaults['tools_cal_toolchangez'])
+ self.ui.toolchange_xy_entry.set_value(self.app.defaults['tools_cal_toolchange_xy'])
+
+ self.ui.second_point_radio.set_value(self.app.defaults['tools_cal_sec_point'])
+
+ self.ui.scalex_entry.set_value(1.0)
+ self.ui.scaley_entry.set_value(1.0)
+ self.ui.skewx_entry.set_value(0.0)
+ self.ui.skewy_entry.set_value(0.0)
+
+ # default object selection is Excellon = index_1
+ self.ui.obj_type_combo.setCurrentIndex(1)
+ self.on_obj_type_combo()
+
+ self.ui.adj_object_type_combo.setCurrentIndex(0)
+ self.on_adj_obj_type_combo()
+ # self.adj_object_combo.setCurrentIndex(0)
+
+ # calibrated object
+ self.cal_object = None
+
+ self.app.inform.emit('%s...' % _("Tool initialized"))
+
+ def on_obj_type_combo(self):
+ obj_type = self.ui.obj_type_combo.currentIndex()
+ self.ui.object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
+ # self.object_combo.setCurrentIndex(0)
+ self.ui.object_combo.obj_type = {
+ _("Gerber"): "Gerber", _("Excellon"): "Excellon"
+ }[self.ui.obj_type_combo.get_value()]
+
+ def on_adj_obj_type_combo(self):
+ obj_type = self.ui.adj_object_type_combo.currentIndex()
+ self.ui.adj_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
+ # self.adj_object_combo.setCurrentIndex(0)
+ self.ui.adj_object_combo.obj_type = {
+ _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
+ }[self.ui.adj_object_type_combo.get_value()]
+
+ def on_cal_source_radio(self, val):
+ if val == 'object':
+ self.ui.obj_type_label.setDisabled(False)
+ self.ui.obj_type_combo.setDisabled(False)
+ self.ui.object_label.setDisabled(False)
+ self.ui.object_combo.setDisabled(False)
+ else:
+ self.ui.obj_type_label.setDisabled(True)
+ self.ui.obj_type_combo.setDisabled(True)
+ self.ui.object_label.setDisabled(True)
+ self.ui.object_combo.setDisabled(True)
+
+ def on_start_collect_points(self):
+
+ if self.ui.cal_source_radio.get_value() == 'object':
+ selection_index = self.ui.object_combo.currentIndex()
+ model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
+ try:
+ self.target_obj = model_index.internalPointer().obj
+ except AttributeError:
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no source FlatCAM object selected..."))
+ return
+
+ # disengage the grid snapping since it will be hard to find the drills on grid
+ if self.app.ui.grid_snap_btn.isChecked():
+ self.grid_status_memory = True
+ self.app.ui.grid_snap_btn.trigger()
+ else:
+ self.grid_status_memory = False
+
+ self.mr = self.canvas.graph_event_connect('mouse_release', self.on_mouse_click_release)
+
+ if self.app.is_legacy is False:
+ self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
+ else:
+ self.canvas.graph_event_disconnect(self.app.mr)
+
+ self.local_connected = True
+
+ self.reset_calibration_points()
+
+ self.app.inform.emit(_("Get First calibration point. Bottom Left..."))
+
+ def on_mouse_click_release(self, event):
+ if self.app.is_legacy is False:
+ 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
+
+ pos_canvas = self.canvas.translate_coords(event_pos)
+
+ if event.button == 1:
+ click_pt = Point([pos_canvas[0], pos_canvas[1]])
+
+ if self.app.selection_type is not None:
+ # delete previous selection shape
+ self.app.delete_selection_shape()
+ self.app.selection_type = None
+ else:
+ if self.ui.cal_source_radio.get_value() == 'object':
+ if self.target_obj.kind.lower() == 'excellon':
+ for tool, tool_dict in self.target_obj.tools.items():
+ for geo in tool_dict['solid_geometry']:
+ if click_pt.within(geo):
+ center_pt = geo.centroid
+ self.click_points.append(
+ [
+ float('%.*f' % (self.decimals, center_pt.x)),
+ float('%.*f' % (self.decimals, center_pt.y))
+ ]
+ )
+ self.check_points()
+ else:
+ for apid, apid_val in self.target_obj.apertures.items():
+ for geo_el in apid_val['geometry']:
+ if 'solid' in geo_el:
+ if click_pt.within(geo_el['solid']):
+ if isinstance(geo_el['follow'], Point):
+ center_pt = geo_el['solid'].centroid
+ self.click_points.append(
+ [
+ float('%.*f' % (self.decimals, center_pt.x)),
+ float('%.*f' % (self.decimals, center_pt.y))
+ ]
+ )
+ self.check_points()
+ else:
+ self.click_points.append(
+ [
+ float('%.*f' % (self.decimals, click_pt.x)),
+ float('%.*f' % (self.decimals, click_pt.y))
+ ]
+ )
+ self.check_points()
+ elif event.button == right_button and self.app.event_is_dragging is False:
+ if len(self.click_points) != 4:
+ self.reset_calibration_points()
+ self.disconnect_cal_events()
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled by user request."))
+
+ def check_points(self):
+ if len(self.click_points) == 1:
+ self.ui.bottom_left_coordx_tgt.set_value(self.click_points[0][0])
+ self.ui.bottom_left_coordy_tgt.set_value(self.click_points[0][1])
+ self.app.inform.emit(_("Get Second calibration point. Bottom Right (Top Left)..."))
+ elif len(self.click_points) == 2:
+ self.ui.bottom_right_coordx_tgt.set_value(self.click_points[1][0])
+ self.ui.bottom_right_coordy_tgt.set_value(self.click_points[1][1])
+ self.app.inform.emit(_("Get Third calibration point. Top Left (Bottom Right)..."))
+ elif len(self.click_points) == 3:
+ self.ui.top_left_coordx_tgt.set_value(self.click_points[2][0])
+ self.ui.top_left_coordy_tgt.set_value(self.click_points[2][1])
+ self.app.inform.emit(_("Get Forth calibration point. Top Right..."))
+ elif len(self.click_points) == 4:
+ self.ui.top_right_coordx_tgt.set_value(self.click_points[3][0])
+ self.ui.top_right_coordy_tgt.set_value(self.click_points[3][1])
+ self.app.inform.emit('[success] %s' % _("Done. All four points have been acquired."))
+ self.disconnect_cal_events()
+
+ def reset_calibration_points(self):
+ self.click_points = []
+
+ self.ui.bottom_left_coordx_tgt.set_value('')
+ self.ui.bottom_left_coordy_tgt.set_value('')
+
+ self.ui.bottom_right_coordx_tgt.set_value('')
+ self.ui.bottom_right_coordy_tgt.set_value('')
+
+ self.ui.top_left_coordx_tgt.set_value('')
+ self.ui.top_left_coordy_tgt.set_value('')
+
+ self.ui.top_right_coordx_tgt.set_value('')
+ self.ui.top_right_coordy_tgt.set_value('')
+
+ self.ui.bottom_right_coordx_found.set_value('')
+ self.ui.bottom_right_coordy_found.set_value('')
+
+ self.ui.top_left_coordx_found.set_value('')
+ self.ui.top_left_coordy_found.set_value('')
+
+ def gcode_header(self):
+ log.debug("ToolCalibration.gcode_header()")
+ time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
+
+ gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \
+ (str(self.app.version), str(self.app.version_date)) + '\n'
+
+ gcode += '(Name: ' + _('Verification GCode for FlatCAM Calibration Tool') + ')\n'
+
+ gcode += '(Units: ' + self.units.upper() + ')\n\n'
+ gcode += '(Created on ' + time_str + ')\n\n'
+ gcode += 'G20\n' if self.units.upper() == 'IN' else 'G21\n'
+ gcode += 'G90\n'
+ gcode += 'G17\n'
+ gcode += 'G94\n\n'
+ return gcode
+
+ def close_tab(self):
+ for idx in range(self.app.ui.plot_tab_area.count()):
+ if self.app.ui.plot_tab_area.tabText(idx) == _("Gcode Viewer"):
+ wdg = self.app.ui.plot_tab_area.widget(idx)
+ wdg.deleteLater()
+ self.app.ui.plot_tab_area.removeTab(idx)
+
+ def generate_verification_gcode(self):
+ sec_point = self.ui.second_point_radio.get_value()
+
+ travel_z = '%.*f' % (self.decimals, self.ui.travelz_entry.get_value())
+ toolchange_z = '%.*f' % (self.decimals, self.ui.toolchangez_entry.get_value())
+ toolchange_xy_temp = self.ui.toolchange_xy_entry.get_value().split(",")
+ toolchange_xy = [float(eval(a)) for a in toolchange_xy_temp if a != '']
+
+ verification_z = '%.*f' % (self.decimals, self.ui.verz_entry.get_value())
+
+ if len(self.click_points) != 4:
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Four points are needed for GCode generation."))
+ return 'fail'
+
+ gcode = self.gcode_header()
+ if self.ui.zeroz_cb.get_value():
+ gcode += 'M5\n'
+ gcode += 'G00 Z%s\n' % toolchange_z
+ if toolchange_xy:
+ gcode += 'G00 X%s Y%s\n' % (toolchange_xy[0], toolchange_xy[1])
+ gcode += 'M0\n'
+ gcode += 'G01 Z0\n'
+ gcode += 'M0\n'
+ gcode += 'G00 Z%s\n' % toolchange_z
+ gcode += 'M0\n'
+
+ # first point: bottom - left -> ORIGIN set
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X%s Y%s\n' % (self.click_points[0][0], self.click_points[0][1])
+ gcode += 'G01 Z%s\n' % verification_z
+ gcode += 'M0\n'
+
+ if sec_point == 'tl':
+ # second point: top - left -> align the PCB to this point
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X%s Y%s\n' % (self.click_points[2][0], self.click_points[2][1])
+ gcode += 'G01 Z%s\n' % verification_z
+ gcode += 'M0\n'
+
+ # third point: bottom - right -> check for scale on X axis or for skew on Y axis
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X%s Y%s\n' % (self.click_points[1][0], self.click_points[1][1])
+ gcode += 'G01 Z%s\n' % verification_z
+ gcode += 'M0\n'
+
+ # forth point: top - right -> verification point
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X%s Y%s\n' % (self.click_points[3][0], self.click_points[3][1])
+ gcode += 'G01 Z%s\n' % verification_z
+ gcode += 'M0\n'
+ else:
+ # second point: bottom - right -> align the PCB to this point
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X%s Y%s\n' % (self.click_points[1][0], self.click_points[1][1])
+ gcode += 'G01 Z%s\n' % verification_z
+ gcode += 'M0\n'
+
+ # third point: top - left -> check for scale on Y axis or for skew on X axis
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X%s Y%s\n' % (self.click_points[2][0], self.click_points[2][1])
+ gcode += 'G01 Z%s\n' % verification_z
+ gcode += 'M0\n'
+
+ # forth point: top - right -> verification point
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X%s Y%s\n' % (self.click_points[3][0], self.click_points[3][1])
+ gcode += 'G01 Z%s\n' % verification_z
+ gcode += 'M0\n'
+
+ # return to (toolchange_xy[0], toolchange_xy[1], toolchange_z) point for toolchange event
+ gcode += 'G00 Z%s\n' % travel_z
+ gcode += 'G00 X0 Y0\n'
+ gcode += 'G00 Z%s\n' % toolchange_z
+ if toolchange_xy:
+ gcode += 'G00 X%s Y%s\n' % (toolchange_xy[0], toolchange_xy[1])
+
+ gcode += 'M2'
+
+ self.gcode_editor_tab = AppTextEditor(app=self.app, plain_text=True)
+
+ # add the tab if it was closed
+ self.app.ui.plot_tab_area.addTab(self.gcode_editor_tab, '%s' % _("Gcode Viewer"))
+ self.gcode_editor_tab.setObjectName('gcode_viewer_tab')
+
+ # delete the absolute and relative position and messages in the infobar
+ self.app.ui.position_label.setText("")
+ self.app.ui.rel_position_label.setText("")
+
+ self.gcode_editor_tab.code_editor.completer_enable = False
+ self.gcode_editor_tab.buttonRun.hide()
+
+ # Switch plot_area to CNCJob tab
+ self.app.ui.plot_tab_area.setCurrentWidget(self.gcode_editor_tab)
+
+ self.gcode_editor_tab.t_frame.hide()
+ # then append the text from GCode to the text editor
+ try:
+ self.gcode_editor_tab.load_text(gcode, move_to_start=True, clear_text=True)
+ except Exception as e:
+ self.app.inform.emit('[ERROR] %s %s' % ('ERROR -->', str(e)))
+ return
+
+ self.gcode_editor_tab.t_frame.show()
+ self.app.proc_container.view.set_idle()
+
+ self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
+
+ _filter_ = "G-Code Files (*.nc);;All Files (*.*)"
+ self.gcode_editor_tab.buttonSave.clicked.disconnect()
+ self.gcode_editor_tab.buttonSave.clicked.connect(
+ lambda: self.gcode_editor_tab.handleSaveGCode(name='fc_ver_gcode', filt=_filter_, callback=self.close_tab))
+
+ def calculate_factors(self):
+ origin_x = self.click_points[0][0]
+ origin_y = self.click_points[0][1]
+
+ top_left_x = self.click_points[2][0]
+ top_left_y = self.click_points[2][1]
+
+ bot_right_x = self.click_points[1][0]
+ bot_right_y = self.click_points[1][1]
+
+ try:
+ top_left_dx = float(self.ui.top_left_coordx_found.get_value())
+ except TypeError:
+ top_left_dx = top_left_x
+
+ try:
+ top_left_dy = float(self.ui.top_left_coordy_found.get_value())
+ except TypeError:
+ top_left_dy = top_left_y
+
+ try:
+ bot_right_dx = float(self.ui.bottom_right_coordx_found.get_value())
+ except TypeError:
+ bot_right_dx = bot_right_x
+
+ try:
+ bot_right_dy = float(self.ui.bottom_right_coordy_found.get_value())
+ except TypeError:
+ bot_right_dy = bot_right_y
+
+ # ------------------------------------------------------------------------------- #
+ # --------------------------- FACTORS CALCULUS ---------------------------------- #
+ # ------------------------------------------------------------------------------- #
+ if bot_right_dx != float('%.*f' % (self.decimals, bot_right_x)):
+ # we have scale on X
+ scale_x = (bot_right_dx / (bot_right_x - origin_x)) + 1
+ self.ui.scalex_entry.set_value(scale_x)
+
+ if top_left_dy != float('%.*f' % (self.decimals, top_left_y)):
+ # we have scale on Y
+ scale_y = (top_left_dy / (top_left_y - origin_y)) + 1
+ self.ui.scaley_entry.set_value(scale_y)
+
+ if top_left_dx != float('%.*f' % (self.decimals, top_left_x)):
+ # we have skew on X
+ dx = top_left_dx
+ dy = top_left_y - origin_y
+ skew_angle_x = math.degrees(math.atan(dx / dy))
+ self.ui.skewx_entry.set_value(skew_angle_x)
+
+ if bot_right_dy != float('%.*f' % (self.decimals, bot_right_y)):
+ # we have skew on Y
+ dx = bot_right_x - origin_x
+ dy = bot_right_dy + origin_y
+ skew_angle_y = math.degrees(math.atan(dy / dx))
+ self.ui.skewy_entry.set_value(skew_angle_y)
+
+ @property
+ def target_values_in_table(self):
+ self.click_points[0][0] = self.ui.bottom_left_coordx_tgt.get_value()
+ self.click_points[0][1] = self.ui.bottom_left_coordy_tgt.get_value()
+
+ self.click_points[1][0] = self.ui.bottom_right_coordx_tgt.get_value()
+ self.click_points[1][1] = self.ui.bottom_right_coordy_tgt.get_value()
+
+ self.click_points[2][0] = self.ui.top_left_coordx_tgt.get_value()
+ self.click_points[2][1] = self.ui.top_left_coordy_tgt.get_value()
+
+ self.click_points[3][0] = self.ui.top_right_coordx_tgt.get_value()
+ self.click_points[3][1] = self.ui.top_right_coordy_tgt.get_value()
+
+ return self.click_points
+
+ @target_values_in_table.setter
+ def target_values_in_table(self, param):
+ bl_pt, br_pt, tl_pt, tr_pt = param
+
+ self.click_points[0] = [bl_pt[0], bl_pt[1]]
+ self.click_points[1] = [br_pt[0], br_pt[1]]
+ self.click_points[2] = [tl_pt[0], tl_pt[1]]
+ self.click_points[3] = [tr_pt[0], tr_pt[1]]
+
+ self.ui.bottom_left_coordx_tgt.set_value(float('%.*f' % (self.decimals, bl_pt[0])))
+ self.ui.bottom_left_coordy_tgt.set_value(float('%.*f' % (self.decimals, bl_pt[1])))
+
+ self.ui.bottom_right_coordx_tgt.set_value(float('%.*f' % (self.decimals, br_pt[0])))
+ self.ui.bottom_right_coordy_tgt.set_value(float('%.*f' % (self.decimals, br_pt[1])))
+
+ self.ui.top_left_coordx_tgt.set_value(float('%.*f' % (self.decimals, tl_pt[0])))
+ self.ui.top_left_coordy_tgt.set_value(float('%.*f' % (self.decimals, tl_pt[1])))
+
+ self.ui.top_right_coordx_tgt.set_value(float('%.*f' % (self.decimals, tr_pt[0])))
+ self.ui.top_right_coordy_tgt.set_value(float('%.*f' % (self.decimals, tr_pt[1])))
+
+ def on_scale_button(self):
+ scalex_fact = self.ui.scalex_entry.get_value()
+ scaley_fact = self.ui.scaley_entry.get_value()
+ bl, br, tl, tr = self.target_values_in_table
+
+ bl_geo = Point(bl[0], bl[1])
+ br_geo = Point(br[0], br[1])
+ tl_geo = Point(tl[0], tl[1])
+ tr_geo = Point(tr[0], tr[1])
+
+ bl_scaled = scale(bl_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
+ br_scaled = scale(br_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
+ tl_scaled = scale(tl_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
+ tr_scaled = scale(tr_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
+
+ scaled_values = [
+ [bl_scaled.x, bl_scaled.y],
+ [br_scaled.x, br_scaled.y],
+ [tl_scaled.x, tl_scaled.y],
+ [tr_scaled.x, tr_scaled.y]
+ ]
+ self.target_values_in_table = scaled_values
+
+ def on_skew_button(self):
+ skewx_angle = self.ui.skewx_entry.get_value()
+ skewy_angle = self.ui.skewy_entry.get_value()
+ bl, br, tl, tr = self.target_values_in_table
+
+ bl_geo = Point(bl[0], bl[1])
+ br_geo = Point(br[0], br[1])
+ tl_geo = Point(tl[0], tl[1])
+ tr_geo = Point(tr[0], tr[1])
+
+ bl_skewed = skew(bl_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
+ br_skewed = skew(br_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
+ tl_skewed = skew(tl_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
+ tr_skewed = skew(tr_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
+
+ skewed_values = [
+ [bl_skewed.x, bl_skewed.y],
+ [br_skewed.x, br_skewed.y],
+ [tl_skewed.x, tl_skewed.y],
+ [tr_skewed.x, tr_skewed.y]
+ ]
+ self.target_values_in_table = skewed_values
+
+ def on_cal_button_click(self):
+ # get the FlatCAM object to calibrate
+ selection_index = self.ui.adj_object_combo.currentIndex()
+ model_index = self.app.collection.index(selection_index, 0, self.ui.adj_object_combo.rootModelIndex())
+
+ try:
+ self.cal_object = model_index.internalPointer().obj
+ except Exception as e:
+ log.debug("ToolCalibration.on_cal_button_click() --> %s" % str(e))
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected..."))
+ return 'fail'
+
+ obj_name = self.cal_object.options["name"] + "_calibrated"
+
+ self.app.worker_task.emit({'fcn': self.new_calibrated_object, 'params': [obj_name]})
+
+ def new_calibrated_object(self, obj_name):
+
+ try:
+ origin_x = self.click_points[0][0]
+ origin_y = self.click_points[0][1]
+ except IndexError as e:
+ log.debug("ToolCalibration.new_calibrated_object() --> %s" % str(e))
+ return 'fail'
+
+ scalex = self.ui.scalex_entry.get_value()
+ scaley = self.ui.scaley_entry.get_value()
+
+ skewx = self.ui.skewx_entry.get_value()
+ skewy = self.ui.skewy_entry.get_value()
+
+ # create a new object adjusted (calibrated)
+ def initialize_geometry(obj_init, app):
+ obj_init.solid_geometry = deepcopy(obj.solid_geometry)
+ try:
+ obj_init.follow_geometry = deepcopy(obj.follow_geometry)
+ except AttributeError:
+ pass
+
+ try:
+ obj_init.apertures = deepcopy(obj.apertures)
+ except AttributeError:
+ pass
+
+ try:
+ if obj.tools:
+ obj_init.tools = deepcopy(obj.tools)
+ except Exception as ee:
+ log.debug("ToolCalibration.new_calibrated_object.initialize_geometry() --> %s" % str(ee))
+
+ obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
+ obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
+
+ try:
+ obj_init.source_file = deepcopy(obj.source_file)
+ except (AttributeError, TypeError):
+ pass
+
+ def initialize_gerber(obj_init, app):
+ obj_init.solid_geometry = deepcopy(obj.solid_geometry)
+ try:
+ obj_init.follow_geometry = deepcopy(obj.follow_geometry)
+ except AttributeError:
+ pass
+
+ try:
+ obj_init.apertures = deepcopy(obj.apertures)
+ except AttributeError:
+ pass
+
+ try:
+ if obj.tools:
+ obj_init.tools = deepcopy(obj.tools)
+ except Exception as err:
+ log.debug("ToolCalibration.new_calibrated_object.initialize_gerber() --> %s" % str(err))
+
+ obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
+ obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
+
+ try:
+ obj_init.source_file = self.export_gerber(obj_name=obj_name, filename=None, local_use=obj_init,
+ use_thread=False)
+ except (AttributeError, TypeError):
+ pass
+
+ def initialize_excellon(obj_init, app):
+ obj_init.tools = deepcopy(obj.tools)
+
+ # drills are offset, so they need to be deep copied
+ obj_init.drills = deepcopy(obj.drills)
+ # slots are offset, so they need to be deep copied
+ obj_init.slots = deepcopy(obj.slots)
+
+ obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
+ obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
+
+ obj_init.create_geometry()
+
+ obj_init.source_file = self.app.export_excellon(obj_name=obj_name, local_use=obj, filename=None,
+ use_thread=False)
+
+ obj = self.cal_object
+ obj_name = obj_name
+
+ if obj is None:
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected..."))
+ log.debug("ToolCalibration.new_calibrated_object() --> No object to calibrate")
+ return 'fail'
+
+ try:
+ if obj.kind.lower() == 'excellon':
+ self.app.app_obj.new_object("excellon", str(obj_name), initialize_excellon)
+ elif obj.kind.lower() == 'gerber':
+ self.app.app_obj.new_object("gerber", str(obj_name), initialize_gerber)
+ elif obj.kind.lower() == 'geometry':
+ self.app.app_obj.new_object("geometry", str(obj_name), initialize_geometry)
+ except Exception as e:
+ log.debug("ToolCalibration.new_calibrated_object() --> %s" % str(e))
+ return "Operation failed: %s" % str(e)
+
+ def disconnect_cal_events(self):
+ # restore the Grid snapping if it was active before
+ if self.grid_status_memory is True:
+ self.app.ui.grid_snap_btn.trigger()
+
+ self.app.mr = self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
+
+ if self.app.is_legacy is False:
+ self.canvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
+ else:
+ self.canvas.graph_event_disconnect(self.mr)
+
+ self.local_connected = False
+
+ def reset_fields(self):
+ self.ui.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+ self.ui.adj_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+
+
+class CalibUI:
+
+ toolName = _("Calibration Tool")
+
+ def __init__(self, layout, app):
+ self.app = app
+ self.decimals = self.app.decimals
+ self.layout = layout
+
# ## Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
- QLabel
- {
- font-size: 16px;
- font-weight: bold;
- }
- """)
+ QLabel
+ {
+ font-size: 16px;
+ font-weight: bold;
+ }
+ """)
self.layout.addWidget(title_label)
- self.layout.addWidget(QtWidgets.QLabel(''))
+ self.layout.addWidget(QtWidgets.QLabel(""))
# ## Grid Layout
grid_lay = QtWidgets.QGridLayout()
@@ -372,11 +1067,11 @@ class ToolCalibration(AppTool):
"the object.")
)
self.start_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.start_button, 17, 0, 1, 3)
separator_line = QtWidgets.QFrame()
@@ -411,11 +1106,11 @@ class ToolCalibration(AppTool):
"- forth point -> final verification point. Just for evaluation.")
)
self.gcode_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.gcode_button, 21, 0, 1, 3)
separator_line1 = QtWidgets.QFrame()
@@ -442,11 +1137,11 @@ class ToolCalibration(AppTool):
"in the fields Found (Delta).")
)
self.generate_factors_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.generate_factors_button, 25, 0, 1, 3)
separator_line1 = QtWidgets.QFrame()
@@ -493,11 +1188,11 @@ class ToolCalibration(AppTool):
_("Apply Scale factors on the calibration points.")
)
self.scale_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.scale_button, 31, 0, 1, 3)
self.skewx_label = QtWidgets.QLabel(_("Skew Angle X:"))
@@ -531,11 +1226,11 @@ class ToolCalibration(AppTool):
_("Apply Skew factors on the calibration points.")
)
self.skew_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.skew_button, 34, 0, 1, 3)
# final_factors_lbl = QtWidgets.QLabel('%s' % _("Final Factors"))
@@ -605,11 +1300,11 @@ class ToolCalibration(AppTool):
"before clicking this button.")
)
self.adj_gcode_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.adj_gcode_button, 42, 0, 1, 3)
separator_line1 = QtWidgets.QFrame()
@@ -663,11 +1358,11 @@ class ToolCalibration(AppTool):
"with the factors determined above.")
)
self.cal_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.cal_button, 50, 0, 1, 3)
separator_line2 = QtWidgets.QFrame()
@@ -686,693 +1381,28 @@ class ToolCalibration(AppTool):
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
self.layout.addWidget(self.reset_button)
+ # #################################### FINSIHED GUI ###########################
+ # #############################################################################
- self.mr = None
- self.units = ''
-
- # here store 4 points to be used for calibration
- self.click_points = [[], [], [], []]
-
- # store the status of the grid
- self.grid_status_memory = None
-
- self.target_obj = None
-
- # if the mouse events are connected to a local method set this True
- self.local_connected = False
-
- # reference for the tab where to open and view the verification GCode
- self.gcode_editor_tab = None
-
- # calibrated object
- self.cal_object = None
-
- # ## Signals
- self.cal_source_radio.activated_custom.connect(self.on_cal_source_radio)
- self.obj_type_combo.currentIndexChanged.connect(self.on_obj_type_combo)
- self.adj_object_type_combo.currentIndexChanged.connect(self.on_adj_obj_type_combo)
-
- self.start_button.clicked.connect(self.on_start_collect_points)
-
- self.gcode_button.clicked.connect(self.generate_verification_gcode)
- self.adj_gcode_button.clicked.connect(self.generate_verification_gcode)
-
- self.generate_factors_button.clicked.connect(self.calculate_factors)
-
- self.scale_button.clicked.connect(self.on_scale_button)
- self.skew_button.clicked.connect(self.on_skew_button)
-
- self.cal_button.clicked.connect(self.on_cal_button_click)
- self.reset_button.clicked.connect(self.set_tool_ui)
-
- def run(self, toggle=True):
- self.app.defaults.report_usage("ToolCalibration()")
-
- 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
+ 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:
- if self.app.ui.splitter.sizes()[0] == 0:
- self.app.ui.splitter.setSizes([1, 1])
+ self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
- AppTool.run(self)
-
- self.set_tool_ui()
-
- self.app.ui.notebook.setTabText(2, _("Calibration Tool"))
-
- def install(self, icon=None, separator=None, **kwargs):
- AppTool.install(self, icon, separator, shortcut='Alt+E', **kwargs)
-
- def set_tool_ui(self):
- self.units = self.app.defaults['units'].upper()
-
- if self.local_connected is True:
- self.disconnect_cal_events()
-
- self.bottom_left_coordx_found.set_value(_("Origin"))
- self.bottom_left_coordy_found.set_value(_("Origin"))
-
- self.reset_calibration_points()
-
- self.cal_source_radio.set_value(self.app.defaults['tools_cal_calsource'])
- self.travelz_entry.set_value(self.app.defaults['tools_cal_travelz'])
- self.verz_entry.set_value(self.app.defaults['tools_cal_verz'])
- self.zeroz_cb.set_value(self.app.defaults['tools_cal_zeroz'])
- self.toolchangez_entry.set_value(self.app.defaults['tools_cal_toolchangez'])
- self.toolchange_xy_entry.set_value(self.app.defaults['tools_cal_toolchange_xy'])
-
- self.second_point_radio.set_value(self.app.defaults['tools_cal_sec_point'])
-
- self.scalex_entry.set_value(1.0)
- self.scaley_entry.set_value(1.0)
- self.skewx_entry.set_value(0.0)
- self.skewy_entry.set_value(0.0)
-
- # default object selection is Excellon = index_1
- self.obj_type_combo.setCurrentIndex(1)
- self.on_obj_type_combo()
-
- self.adj_object_type_combo.setCurrentIndex(0)
- self.on_adj_obj_type_combo()
- # self.adj_object_combo.setCurrentIndex(0)
-
- # calibrated object
- self.cal_object = None
-
- self.app.inform.emit('%s...' % _("Tool initialized"))
-
- def on_obj_type_combo(self):
- obj_type = self.obj_type_combo.currentIndex()
- self.object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
- # self.object_combo.setCurrentIndex(0)
- self.object_combo.obj_type = {
- _("Gerber"): "Gerber", _("Excellon"): "Excellon"
- }[self.obj_type_combo.get_value()]
-
- def on_adj_obj_type_combo(self):
- obj_type = self.adj_object_type_combo.currentIndex()
- self.adj_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
- # self.adj_object_combo.setCurrentIndex(0)
- self.adj_object_combo.obj_type = {
- _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
- }[self.adj_object_type_combo.get_value()]
-
- def on_cal_source_radio(self, val):
- if val == 'object':
- self.obj_type_label.setDisabled(False)
- self.obj_type_combo.setDisabled(False)
- self.object_label.setDisabled(False)
- self.object_combo.setDisabled(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.obj_type_label.setDisabled(True)
- self.obj_type_combo.setDisabled(True)
- self.object_label.setDisabled(True)
- self.object_combo.setDisabled(True)
-
- def on_start_collect_points(self):
-
- if self.cal_source_radio.get_value() == 'object':
- selection_index = self.object_combo.currentIndex()
- model_index = self.app.collection.index(selection_index, 0, self.object_combo.rootModelIndex())
- try:
- self.target_obj = model_index.internalPointer().obj
- except AttributeError:
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no source FlatCAM object selected..."))
- return
-
- # disengage the grid snapping since it will be hard to find the drills on grid
- if self.app.ui.grid_snap_btn.isChecked():
- self.grid_status_memory = True
- self.app.ui.grid_snap_btn.trigger()
- else:
- self.grid_status_memory = False
-
- self.mr = self.canvas.graph_event_connect('mouse_release', self.on_mouse_click_release)
-
- if self.app.is_legacy is False:
- self.canvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
- else:
- self.canvas.graph_event_disconnect(self.app.mr)
-
- self.local_connected = True
-
- self.reset_calibration_points()
-
- self.app.inform.emit(_("Get First calibration point. Bottom Left..."))
-
- def on_mouse_click_release(self, event):
- if self.app.is_legacy is False:
- 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
-
- pos_canvas = self.canvas.translate_coords(event_pos)
-
- if event.button == 1:
- click_pt = Point([pos_canvas[0], pos_canvas[1]])
-
- if self.app.selection_type is not None:
- # delete previous selection shape
- self.app.delete_selection_shape()
- self.app.selection_type = None
- else:
- if self.cal_source_radio.get_value() == 'object':
- if self.target_obj.kind.lower() == 'excellon':
- for tool, tool_dict in self.target_obj.tools.items():
- for geo in tool_dict['solid_geometry']:
- if click_pt.within(geo):
- center_pt = geo.centroid
- self.click_points.append(
- [
- float('%.*f' % (self.decimals, center_pt.x)),
- float('%.*f' % (self.decimals, center_pt.y))
- ]
- )
- self.check_points()
- else:
- for apid, apid_val in self.target_obj.apertures.items():
- for geo_el in apid_val['geometry']:
- if 'solid' in geo_el:
- if click_pt.within(geo_el['solid']):
- if isinstance(geo_el['follow'], Point):
- center_pt = geo_el['solid'].centroid
- self.click_points.append(
- [
- float('%.*f' % (self.decimals, center_pt.x)),
- float('%.*f' % (self.decimals, center_pt.y))
- ]
- )
- self.check_points()
- else:
- self.click_points.append(
- [
- float('%.*f' % (self.decimals, click_pt.x)),
- float('%.*f' % (self.decimals, click_pt.y))
- ]
- )
- self.check_points()
- elif event.button == right_button and self.app.event_is_dragging is False:
- if len(self.click_points) != 4:
- self.reset_calibration_points()
- self.disconnect_cal_events()
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled by user request."))
-
- def check_points(self):
- if len(self.click_points) == 1:
- self.bottom_left_coordx_tgt.set_value(self.click_points[0][0])
- self.bottom_left_coordy_tgt.set_value(self.click_points[0][1])
- self.app.inform.emit(_("Get Second calibration point. Bottom Right (Top Left)..."))
- elif len(self.click_points) == 2:
- self.bottom_right_coordx_tgt.set_value(self.click_points[1][0])
- self.bottom_right_coordy_tgt.set_value(self.click_points[1][1])
- self.app.inform.emit(_("Get Third calibration point. Top Left (Bottom Right)..."))
- elif len(self.click_points) == 3:
- self.top_left_coordx_tgt.set_value(self.click_points[2][0])
- self.top_left_coordy_tgt.set_value(self.click_points[2][1])
- self.app.inform.emit(_("Get Forth calibration point. Top Right..."))
- elif len(self.click_points) == 4:
- self.top_right_coordx_tgt.set_value(self.click_points[3][0])
- self.top_right_coordy_tgt.set_value(self.click_points[3][1])
- self.app.inform.emit('[success] %s' % _("Done. All four points have been acquired."))
- self.disconnect_cal_events()
-
- def reset_calibration_points(self):
- self.click_points = []
-
- self.bottom_left_coordx_tgt.set_value('')
- self.bottom_left_coordy_tgt.set_value('')
-
- self.bottom_right_coordx_tgt.set_value('')
- self.bottom_right_coordy_tgt.set_value('')
-
- self.top_left_coordx_tgt.set_value('')
- self.top_left_coordy_tgt.set_value('')
-
- self.top_right_coordx_tgt.set_value('')
- self.top_right_coordy_tgt.set_value('')
-
- self.bottom_right_coordx_found.set_value('')
- self.bottom_right_coordy_found.set_value('')
-
- self.top_left_coordx_found.set_value('')
- self.top_left_coordy_found.set_value('')
-
- def gcode_header(self):
- log.debug("ToolCalibration.gcode_header()")
- time_str = "{:%A, %d %B %Y at %H:%M}".format(datetime.now())
-
- gcode = '(G-CODE GENERATED BY FLATCAM v%s - www.flatcam.org - Version Date: %s)\n' % \
- (str(self.app.version), str(self.app.version_date)) + '\n'
-
- gcode += '(Name: ' + _('Verification GCode for FlatCAM Calibration Tool') + ')\n'
-
- gcode += '(Units: ' + self.units.upper() + ')\n\n'
- gcode += '(Created on ' + time_str + ')\n\n'
- gcode += 'G20\n' if self.units.upper() == 'IN' else 'G21\n'
- gcode += 'G90\n'
- gcode += 'G17\n'
- gcode += 'G94\n\n'
- return gcode
-
- def close_tab(self):
- for idx in range(self.app.ui.plot_tab_area.count()):
- if self.app.ui.plot_tab_area.tabText(idx) == _("Gcode Viewer"):
- wdg = self.app.ui.plot_tab_area.widget(idx)
- wdg.deleteLater()
- self.app.ui.plot_tab_area.removeTab(idx)
-
- def generate_verification_gcode(self):
- sec_point = self.second_point_radio.get_value()
-
- travel_z = '%.*f' % (self.decimals, self.travelz_entry.get_value())
- toolchange_z = '%.*f' % (self.decimals, self.toolchangez_entry.get_value())
- toolchange_xy_temp = self.toolchange_xy_entry.get_value().split(",")
- toolchange_xy = [float(eval(a)) for a in toolchange_xy_temp if a != '']
-
- verification_z = '%.*f' % (self.decimals, self.verz_entry.get_value())
-
- if len(self.click_points) != 4:
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled. Four points are needed for GCode generation."))
- return 'fail'
-
- gcode = self.gcode_header()
- if self.zeroz_cb.get_value():
- gcode += 'M5\n'
- gcode += 'G00 Z%s\n' % toolchange_z
- if toolchange_xy:
- gcode += 'G00 X%s Y%s\n' % (toolchange_xy[0], toolchange_xy[1])
- gcode += 'M0\n'
- gcode += 'G01 Z0\n'
- gcode += 'M0\n'
- gcode += 'G00 Z%s\n' % toolchange_z
- gcode += 'M0\n'
-
- # first point: bottom - left -> ORIGIN set
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X%s Y%s\n' % (self.click_points[0][0], self.click_points[0][1])
- gcode += 'G01 Z%s\n' % verification_z
- gcode += 'M0\n'
-
- if sec_point == 'tl':
- # second point: top - left -> align the PCB to this point
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X%s Y%s\n' % (self.click_points[2][0], self.click_points[2][1])
- gcode += 'G01 Z%s\n' % verification_z
- gcode += 'M0\n'
-
- # third point: bottom - right -> check for scale on X axis or for skew on Y axis
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X%s Y%s\n' % (self.click_points[1][0], self.click_points[1][1])
- gcode += 'G01 Z%s\n' % verification_z
- gcode += 'M0\n'
-
- # forth point: top - right -> verification point
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X%s Y%s\n' % (self.click_points[3][0], self.click_points[3][1])
- gcode += 'G01 Z%s\n' % verification_z
- gcode += 'M0\n'
- else:
- # second point: bottom - right -> align the PCB to this point
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X%s Y%s\n' % (self.click_points[1][0], self.click_points[1][1])
- gcode += 'G01 Z%s\n' % verification_z
- gcode += 'M0\n'
-
- # third point: top - left -> check for scale on Y axis or for skew on X axis
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X%s Y%s\n' % (self.click_points[2][0], self.click_points[2][1])
- gcode += 'G01 Z%s\n' % verification_z
- gcode += 'M0\n'
-
- # forth point: top - right -> verification point
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X%s Y%s\n' % (self.click_points[3][0], self.click_points[3][1])
- gcode += 'G01 Z%s\n' % verification_z
- gcode += 'M0\n'
-
- # return to (toolchange_xy[0], toolchange_xy[1], toolchange_z) point for toolchange event
- gcode += 'G00 Z%s\n' % travel_z
- gcode += 'G00 X0 Y0\n'
- gcode += 'G00 Z%s\n' % toolchange_z
- if toolchange_xy:
- gcode += 'G00 X%s Y%s\n' % (toolchange_xy[0], toolchange_xy[1])
-
- gcode += 'M2'
-
- self.gcode_editor_tab = AppTextEditor(app=self.app, plain_text=True)
-
- # add the tab if it was closed
- self.app.ui.plot_tab_area.addTab(self.gcode_editor_tab, '%s' % _("Gcode Viewer"))
- self.gcode_editor_tab.setObjectName('gcode_viewer_tab')
-
- # delete the absolute and relative position and messages in the infobar
- self.app.ui.position_label.setText("")
- self.app.ui.rel_position_label.setText("")
-
- self.gcode_editor_tab.code_editor.completer_enable = False
- self.gcode_editor_tab.buttonRun.hide()
-
- # Switch plot_area to CNCJob tab
- self.app.ui.plot_tab_area.setCurrentWidget(self.gcode_editor_tab)
-
- self.gcode_editor_tab.t_frame.hide()
- # then append the text from GCode to the text editor
- try:
- self.gcode_editor_tab.load_text(gcode, move_to_start=True, clear_text=True)
- except Exception as e:
- self.app.inform.emit('[ERROR] %s %s' % ('ERROR -->', str(e)))
- return
-
- self.gcode_editor_tab.t_frame.show()
- self.app.proc_container.view.set_idle()
-
- self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
-
- _filter_ = "G-Code Files (*.nc);;All Files (*.*)"
- self.gcode_editor_tab.buttonSave.clicked.disconnect()
- self.gcode_editor_tab.buttonSave.clicked.connect(
- lambda: self.gcode_editor_tab.handleSaveGCode(name='fc_ver_gcode', filt=_filter_, callback=self.close_tab))
-
- def calculate_factors(self):
- origin_x = self.click_points[0][0]
- origin_y = self.click_points[0][1]
-
- top_left_x = self.click_points[2][0]
- top_left_y = self.click_points[2][1]
-
- bot_right_x = self.click_points[1][0]
- bot_right_y = self.click_points[1][1]
-
- try:
- top_left_dx = float(self.top_left_coordx_found.get_value())
- except TypeError:
- top_left_dx = top_left_x
-
- try:
- top_left_dy = float(self.top_left_coordy_found.get_value())
- except TypeError:
- top_left_dy = top_left_y
-
- try:
- bot_right_dx = float(self.bottom_right_coordx_found.get_value())
- except TypeError:
- bot_right_dx = bot_right_x
-
- try:
- bot_right_dy = float(self.bottom_right_coordy_found.get_value())
- except TypeError:
- bot_right_dy = bot_right_y
-
- # ------------------------------------------------------------------------------- #
- # --------------------------- FACTORS CALCULUS ---------------------------------- #
- # ------------------------------------------------------------------------------- #
- if bot_right_dx != float('%.*f' % (self.decimals, bot_right_x)):
- # we have scale on X
- scale_x = (bot_right_dx / (bot_right_x - origin_x)) + 1
- self.scalex_entry.set_value(scale_x)
-
- if top_left_dy != float('%.*f' % (self.decimals, top_left_y)):
- # we have scale on Y
- scale_y = (top_left_dy / (top_left_y - origin_y)) + 1
- self.scaley_entry.set_value(scale_y)
-
- if top_left_dx != float('%.*f' % (self.decimals, top_left_x)):
- # we have skew on X
- dx = top_left_dx
- dy = top_left_y - origin_y
- skew_angle_x = math.degrees(math.atan(dx / dy))
- self.skewx_entry.set_value(skew_angle_x)
-
- if bot_right_dy != float('%.*f' % (self.decimals, bot_right_y)):
- # we have skew on Y
- dx = bot_right_x - origin_x
- dy = bot_right_dy + origin_y
- skew_angle_y = math.degrees(math.atan(dy / dx))
- self.skewy_entry.set_value(skew_angle_y)
-
- @property
- def target_values_in_table(self):
- self.click_points[0][0] = self.bottom_left_coordx_tgt.get_value()
- self.click_points[0][1] = self.bottom_left_coordy_tgt.get_value()
-
- self.click_points[1][0] = self.bottom_right_coordx_tgt.get_value()
- self.click_points[1][1] = self.bottom_right_coordy_tgt.get_value()
-
- self.click_points[2][0] = self.top_left_coordx_tgt.get_value()
- self.click_points[2][1] = self.top_left_coordy_tgt.get_value()
-
- self.click_points[3][0] = self.top_right_coordx_tgt.get_value()
- self.click_points[3][1] = self.top_right_coordy_tgt.get_value()
-
- return self.click_points
-
- @target_values_in_table.setter
- def target_values_in_table(self, param):
- bl_pt, br_pt, tl_pt, tr_pt = param
-
- self.click_points[0] = [bl_pt[0], bl_pt[1]]
- self.click_points[1] = [br_pt[0], br_pt[1]]
- self.click_points[2] = [tl_pt[0], tl_pt[1]]
- self.click_points[3] = [tr_pt[0], tr_pt[1]]
-
- self.bottom_left_coordx_tgt.set_value(float('%.*f' % (self.decimals, bl_pt[0])))
- self.bottom_left_coordy_tgt.set_value(float('%.*f' % (self.decimals, bl_pt[1])))
-
- self.bottom_right_coordx_tgt.set_value(float('%.*f' % (self.decimals, br_pt[0])))
- self.bottom_right_coordy_tgt.set_value(float('%.*f' % (self.decimals, br_pt[1])))
-
- self.top_left_coordx_tgt.set_value(float('%.*f' % (self.decimals, tl_pt[0])))
- self.top_left_coordy_tgt.set_value(float('%.*f' % (self.decimals, tl_pt[1])))
-
- self.top_right_coordx_tgt.set_value(float('%.*f' % (self.decimals, tr_pt[0])))
- self.top_right_coordy_tgt.set_value(float('%.*f' % (self.decimals, tr_pt[1])))
-
- def on_scale_button(self):
- scalex_fact = self.scalex_entry.get_value()
- scaley_fact = self.scaley_entry.get_value()
- bl, br, tl, tr = self.target_values_in_table
-
- bl_geo = Point(bl[0], bl[1])
- br_geo = Point(br[0], br[1])
- tl_geo = Point(tl[0], tl[1])
- tr_geo = Point(tr[0], tr[1])
-
- bl_scaled = scale(bl_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
- br_scaled = scale(br_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
- tl_scaled = scale(tl_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
- tr_scaled = scale(tr_geo, xfact=scalex_fact, yfact=scaley_fact, origin=(bl[0], bl[1]))
-
- scaled_values = [
- [bl_scaled.x, bl_scaled.y],
- [br_scaled.x, br_scaled.y],
- [tl_scaled.x, tl_scaled.y],
- [tr_scaled.x, tr_scaled.y]
- ]
- self.target_values_in_table = scaled_values
-
- def on_skew_button(self):
- skewx_angle = self.skewx_entry.get_value()
- skewy_angle = self.skewy_entry.get_value()
- bl, br, tl, tr = self.target_values_in_table
-
- bl_geo = Point(bl[0], bl[1])
- br_geo = Point(br[0], br[1])
- tl_geo = Point(tl[0], tl[1])
- tr_geo = Point(tr[0], tr[1])
-
- bl_skewed = skew(bl_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
- br_skewed = skew(br_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
- tl_skewed = skew(tl_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
- tr_skewed = skew(tr_geo, xs=skewx_angle, ys=skewy_angle, origin=(bl[0], bl[1]))
-
- skewed_values = [
- [bl_skewed.x, bl_skewed.y],
- [br_skewed.x, br_skewed.y],
- [tl_skewed.x, tl_skewed.y],
- [tr_skewed.x, tr_skewed.y]
- ]
- self.target_values_in_table = skewed_values
-
- def on_cal_button_click(self):
- # get the FlatCAM object to calibrate
- selection_index = self.adj_object_combo.currentIndex()
- model_index = self.app.collection.index(selection_index, 0, self.adj_object_combo.rootModelIndex())
-
- try:
- self.cal_object = model_index.internalPointer().obj
- except Exception as e:
- log.debug("ToolCalibration.on_cal_button_click() --> %s" % str(e))
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected..."))
- return 'fail'
-
- obj_name = self.cal_object.options["name"] + "_calibrated"
-
- self.app.worker_task.emit({'fcn': self.new_calibrated_object, 'params': [obj_name]})
-
- def new_calibrated_object(self, obj_name):
-
- try:
- origin_x = self.click_points[0][0]
- origin_y = self.click_points[0][1]
- except IndexError as e:
- log.debug("ToolCalibration.new_calibrated_object() --> %s" % str(e))
- return 'fail'
-
- scalex = self.scalex_entry.get_value()
- scaley = self.scaley_entry.get_value()
-
- skewx = self.skewx_entry.get_value()
- skewy = self.skewy_entry.get_value()
-
- # create a new object adjusted (calibrated)
- def initialize_geometry(obj_init, app):
- obj_init.solid_geometry = deepcopy(obj.solid_geometry)
- try:
- obj_init.follow_geometry = deepcopy(obj.follow_geometry)
- except AttributeError:
- pass
-
- try:
- obj_init.apertures = deepcopy(obj.apertures)
- except AttributeError:
- pass
-
- try:
- if obj.tools:
- obj_init.tools = deepcopy(obj.tools)
- except Exception as ee:
- log.debug("ToolCalibration.new_calibrated_object.initialize_geometry() --> %s" % str(ee))
-
- obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
- obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
-
- try:
- obj_init.source_file = deepcopy(obj.source_file)
- except (AttributeError, TypeError):
- pass
-
- def initialize_gerber(obj_init, app):
- obj_init.solid_geometry = deepcopy(obj.solid_geometry)
- try:
- obj_init.follow_geometry = deepcopy(obj.follow_geometry)
- except AttributeError:
- pass
-
- try:
- obj_init.apertures = deepcopy(obj.apertures)
- except AttributeError:
- pass
-
- try:
- if obj.tools:
- obj_init.tools = deepcopy(obj.tools)
- except Exception as err:
- log.debug("ToolCalibration.new_calibrated_object.initialize_gerber() --> %s" % str(err))
-
- obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
- obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
-
- try:
- obj_init.source_file = self.export_gerber(obj_name=obj_name, filename=None, local_use=obj_init,
- use_thread=False)
- except (AttributeError, TypeError):
- pass
-
- def initialize_excellon(obj_init, app):
- obj_init.tools = deepcopy(obj.tools)
-
- # drills are offset, so they need to be deep copied
- obj_init.drills = deepcopy(obj.drills)
- # slots are offset, so they need to be deep copied
- obj_init.slots = deepcopy(obj.slots)
-
- obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
- obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
-
- obj_init.create_geometry()
-
- obj_init.source_file = self.app.export_excellon(obj_name=obj_name, local_use=obj, filename=None,
- use_thread=False)
-
- obj = self.cal_object
- obj_name = obj_name
-
- if obj is None:
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no FlatCAM object selected..."))
- log.debug("ToolCalibration.new_calibrated_object() --> No object to calibrate")
- return 'fail'
-
- try:
- if obj.kind.lower() == 'excellon':
- self.app.app_obj.new_object("excellon", str(obj_name), initialize_excellon)
- elif obj.kind.lower() == 'gerber':
- self.app.app_obj.new_object("gerber", str(obj_name), initialize_gerber)
- elif obj.kind.lower() == 'geometry':
- self.app.app_obj.new_object("geometry", str(obj_name), initialize_geometry)
- except Exception as e:
- log.debug("ToolCalibration.new_calibrated_object() --> %s" % str(e))
- return "Operation failed: %s" % str(e)
-
- def disconnect_cal_events(self):
- # restore the Grid snapping if it was active before
- if self.grid_status_memory is True:
- self.app.ui.grid_snap_btn.trigger()
-
- self.app.mr = self.canvas.graph_event_connect('mouse_release', self.app.on_mouse_click_release_over_plot)
-
- if self.app.is_legacy is False:
- self.canvas.graph_event_disconnect('mouse_release', self.on_mouse_click_release)
- else:
- self.canvas.graph_event_disconnect(self.mr)
-
- self.local_connected = False
-
- def reset_fields(self):
- self.object_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
- self.adj_exc_object_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
- self.adj_geo_object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
-
-# end of file
+ self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
diff --git a/appTools/ToolCopperThieving.py b/appTools/ToolCopperThieving.py
index 7c9dafe6..b900d5c1 100644
--- a/appTools/ToolCopperThieving.py
+++ b/appTools/ToolCopperThieving.py
@@ -36,8 +36,6 @@ log = logging.getLogger('base')
class ToolCopperThieving(AppTool):
work_finished = QtCore.pyqtSignal()
- toolName = _("Copper Thieving Tool")
-
def __init__(self, app):
AppTool.__init__(self, app)
@@ -47,456 +45,11 @@ class ToolCopperThieving(AppTool):
self.decimals = self.app.decimals
self.units = self.app.defaults['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 = FCComboBox()
- 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.is_last = True
- self.grb_object_combo.obj_type = 'Gerber'
-
- self.grbobj_label = QtWidgets.QLabel("%s:" % _("GERBER"))
- self.grbobj_label.setToolTip(
- _("Gerber Object to which will be added a copper thieving.")
- )
-
- i_grid_lay.addWidget(self.grbobj_label, 0, 0)
- i_grid_lay.addWidget(self.grb_object_combo, 1, 0, 1, 2)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
- i_grid_lay.addWidget(separator_line, 2, 0, 1, 2)
-
- # ## 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('%s' % _('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 thieving components\n"
- "(the polygon fill may be split in multiple polygons)\n"
- "and the copper traces in the Gerber file.")
- )
- self.clearance_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.clearance_entry.set_range(0.00001, 9999.9999)
- 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(callback=self.confirmation_message)
- self.margin_entry.set_range(0.0, 9999.9999)
- 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 thieving extent is based on the object extent.\n"
- "- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
- "- 'Reference Object' - will do copper thieving within the area specified by another object.")
- )
- grid_lay.addWidget(self.reference_label, 3, 0)
- grid_lay.addWidget(self.reference_radio, 3, 1)
-
- self.ref_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
- self.ref_combo_type_label.setToolTip(
- _("The type of FlatCAM object to be used as copper thieving reference.\n"
- "It can be Gerber, Excellon or Geometry.")
- )
- self.ref_combo_type = FCComboBox()
- self.ref_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
-
- grid_lay.addWidget(self.ref_combo_type_label, 4, 0)
- grid_lay.addWidget(self.ref_combo_type, 4, 1)
-
- self.ref_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))
- self.ref_combo_label.setToolTip(
- _("The FlatCAM object to be used as non copper clearing reference.")
- )
- self.ref_combo = FCComboBox()
- self.ref_combo.setModel(self.app.collection)
- self.ref_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
- self.ref_combo.is_last = True
- self.ref_combo.obj_type = {
- _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
- }[self.ref_combo_type.get_value()]
-
- grid_lay.addWidget(self.ref_combo_label, 5, 0)
- grid_lay.addWidget(self.ref_combo, 5, 1)
-
- self.ref_combo.hide()
- self.ref_combo_label.hide()
- self.ref_combo_type.hide()
- self.ref_combo_type_label.hide()
-
- # Bounding Box Type #
- self.bbox_type_radio = RadioSet([
- {'label': _('Rectangular'), 'value': 'rect'},
- {"label": _("Minimal"), "value": "min"}
- ], stretch=False)
- self.bbox_type_label = QtWidgets.QLabel(_("Box Type:"))
- self.bbox_type_label.setToolTip(
- _("- 'Rectangular' - the bounding box will be of rectangular shape.\n"
- "- 'Minimal' - the bounding box will be the convex hull shape.")
- )
- grid_lay.addWidget(self.bbox_type_label, 6, 0)
- grid_lay.addWidget(self.bbox_type_radio, 6, 1)
- self.bbox_type_label.hide()
- self.bbox_type_radio.hide()
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
- grid_lay.addWidget(separator_line, 7, 0, 1, 2)
-
- # Fill Type
- self.fill_type_radio = RadioSet([
- {'label': _('Solid'), 'value': 'solid'},
- {"label": _("Dots Grid"), "value": "dot"},
- {"label": _("Squares Grid"), "value": "square"},
- {"label": _("Lines Grid"), "value": "line"}
- ], orientation='vertical', stretch=False)
- self.fill_type_label = QtWidgets.QLabel(_("Fill Type:"))
- self.fill_type_label.setToolTip(
- _("- 'Solid' - copper thieving will be a solid polygon.\n"
- "- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n"
- "- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n"
- "- 'Lines Grid' - the empty area will be filled with a pattern of lines.")
- )
- grid_lay.addWidget(self.fill_type_label, 8, 0)
- grid_lay.addWidget(self.fill_type_radio, 8, 1)
-
- # DOTS FRAME
- self.dots_frame = QtWidgets.QFrame()
- self.dots_frame.setContentsMargins(0, 0, 0, 0)
- self.layout.addWidget(self.dots_frame)
- dots_grid = QtWidgets.QGridLayout()
- dots_grid.setColumnStretch(0, 0)
- dots_grid.setColumnStretch(1, 1)
- dots_grid.setContentsMargins(0, 0, 0, 0)
- self.dots_frame.setLayout(dots_grid)
- self.dots_frame.hide()
-
- self.dots_label = QtWidgets.QLabel('%s:' % _("Dots Grid Parameters"))
- dots_grid.addWidget(self.dots_label, 0, 0, 1, 2)
-
- # Dot diameter #
- self.dotdia_label = QtWidgets.QLabel('%s:' % _("Dia"))
- self.dotdia_label.setToolTip(
- _("Dot diameter in Dots Grid.")
- )
- self.dot_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.dot_dia_entry.set_range(0.0, 9999.9999)
- self.dot_dia_entry.set_precision(self.decimals)
- self.dot_dia_entry.setSingleStep(0.1)
-
- dots_grid.addWidget(self.dotdia_label, 1, 0)
- dots_grid.addWidget(self.dot_dia_entry, 1, 1)
-
- # Dot spacing #
- self.dotspacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
- self.dotspacing_label.setToolTip(
- _("Distance between each two dots in Dots Grid.")
- )
- self.dot_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.dot_spacing_entry.set_range(0.0, 9999.9999)
- self.dot_spacing_entry.set_precision(self.decimals)
- self.dot_spacing_entry.setSingleStep(0.1)
-
- dots_grid.addWidget(self.dotspacing_label, 2, 0)
- dots_grid.addWidget(self.dot_spacing_entry, 2, 1)
-
- # SQUARES FRAME
- self.squares_frame = QtWidgets.QFrame()
- self.squares_frame.setContentsMargins(0, 0, 0, 0)
- self.layout.addWidget(self.squares_frame)
- squares_grid = QtWidgets.QGridLayout()
- squares_grid.setColumnStretch(0, 0)
- squares_grid.setColumnStretch(1, 1)
- squares_grid.setContentsMargins(0, 0, 0, 0)
- self.squares_frame.setLayout(squares_grid)
- self.squares_frame.hide()
-
- self.squares_label = QtWidgets.QLabel('%s:' % _("Squares Grid Parameters"))
- squares_grid.addWidget(self.squares_label, 0, 0, 1, 2)
-
- # Square Size #
- self.square_size_label = QtWidgets.QLabel('%s:' % _("Size"))
- self.square_size_label.setToolTip(
- _("Square side size in Squares Grid.")
- )
- self.square_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.square_size_entry.set_range(0.0, 9999.9999)
- self.square_size_entry.set_precision(self.decimals)
- self.square_size_entry.setSingleStep(0.1)
-
- squares_grid.addWidget(self.square_size_label, 1, 0)
- squares_grid.addWidget(self.square_size_entry, 1, 1)
-
- # Squares spacing #
- self.squares_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
- self.squares_spacing_label.setToolTip(
- _("Distance between each two squares in Squares Grid.")
- )
- self.squares_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.squares_spacing_entry.set_range(0.0, 9999.9999)
- self.squares_spacing_entry.set_precision(self.decimals)
- self.squares_spacing_entry.setSingleStep(0.1)
-
- squares_grid.addWidget(self.squares_spacing_label, 2, 0)
- squares_grid.addWidget(self.squares_spacing_entry, 2, 1)
-
- # LINES FRAME
- self.lines_frame = QtWidgets.QFrame()
- self.lines_frame.setContentsMargins(0, 0, 0, 0)
- self.layout.addWidget(self.lines_frame)
- lines_grid = QtWidgets.QGridLayout()
- lines_grid.setColumnStretch(0, 0)
- lines_grid.setColumnStretch(1, 1)
- lines_grid.setContentsMargins(0, 0, 0, 0)
- self.lines_frame.setLayout(lines_grid)
- self.lines_frame.hide()
-
- self.lines_label = QtWidgets.QLabel('%s:' % _("Lines Grid Parameters"))
- lines_grid.addWidget(self.lines_label, 0, 0, 1, 2)
-
- # Square Size #
- self.line_size_label = QtWidgets.QLabel('%s:' % _("Size"))
- self.line_size_label.setToolTip(
- _("Line thickness size in Lines Grid.")
- )
- self.line_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.line_size_entry.set_range(0.0, 9999.9999)
- self.line_size_entry.set_precision(self.decimals)
- self.line_size_entry.setSingleStep(0.1)
-
- lines_grid.addWidget(self.line_size_label, 1, 0)
- lines_grid.addWidget(self.line_size_entry, 1, 1)
-
- # Lines spacing #
- self.lines_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
- self.lines_spacing_label.setToolTip(
- _("Distance between each two lines in Lines Grid.")
- )
- self.lines_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.lines_spacing_entry.set_range(0.0, 9999.9999)
- self.lines_spacing_entry.set_precision(self.decimals)
- self.lines_spacing_entry.setSingleStep(0.1)
-
- lines_grid.addWidget(self.lines_spacing_label, 2, 0)
- lines_grid.addWidget(self.lines_spacing_entry, 2, 1)
-
- # ## Insert Copper Thieving
- self.fill_button = QtWidgets.QPushButton(_("Insert Copper thieving"))
- 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.fill_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
- self.layout.addWidget(self.fill_button)
-
- # ## Grid Layout
- grid_lay_1 = QtWidgets.QGridLayout()
- self.layout.addLayout(grid_lay_1)
- grid_lay_1.setColumnStretch(0, 0)
- grid_lay_1.setColumnStretch(1, 1)
- grid_lay_1.setColumnStretch(2, 0)
-
- separator_line_1 = QtWidgets.QFrame()
- separator_line_1.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line_1.setFrameShadow(QtWidgets.QFrame.Sunken)
- grid_lay_1.addWidget(separator_line_1, 0, 0, 1, 3)
-
- grid_lay_1.addWidget(QtWidgets.QLabel(''))
-
- self.robber_bar_label = QtWidgets.QLabel('%s' % _('Robber Bar Parameters'))
- self.robber_bar_label.setToolTip(
- _("Parameters used for the robber bar.\n"
- "Robber bar = copper border to help in pattern hole plating.")
- )
- grid_lay_1.addWidget(self.robber_bar_label, 1, 0, 1, 3)
-
- # ROBBER BAR MARGIN #
- self.rb_margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
- self.rb_margin_label.setToolTip(
- _("Bounding box margin for robber bar.")
- )
- self.rb_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.rb_margin_entry.set_range(-9999.9999, 9999.9999)
- self.rb_margin_entry.set_precision(self.decimals)
- self.rb_margin_entry.setSingleStep(0.1)
-
- grid_lay_1.addWidget(self.rb_margin_label, 2, 0)
- grid_lay_1.addWidget(self.rb_margin_entry, 2, 1, 1, 2)
-
- # THICKNESS #
- self.rb_thickness_label = QtWidgets.QLabel('%s:' % _("Thickness"))
- self.rb_thickness_label.setToolTip(
- _("The robber bar thickness.")
- )
- self.rb_thickness_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.rb_thickness_entry.set_range(0.0000, 9999.9999)
- self.rb_thickness_entry.set_precision(self.decimals)
- self.rb_thickness_entry.setSingleStep(0.1)
-
- grid_lay_1.addWidget(self.rb_thickness_label, 3, 0)
- grid_lay_1.addWidget(self.rb_thickness_entry, 3, 1, 1, 2)
-
- # ## Insert Robber Bar
- self.rb_button = QtWidgets.QPushButton(_("Insert Robber Bar"))
- self.rb_button.setToolTip(
- _("Will add a polygon with a defined thickness\n"
- "that will surround the actual Gerber object\n"
- "at a certain distance.\n"
- "Required when doing holes pattern plating.")
- )
- self.rb_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
- grid_lay_1.addWidget(self.rb_button, 4, 0, 1, 3)
-
- separator_line_2 = QtWidgets.QFrame()
- separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
- grid_lay_1.addWidget(separator_line_2, 5, 0, 1, 3)
-
- self.patern_mask_label = QtWidgets.QLabel('%s' % _('Pattern Plating Mask'))
- self.patern_mask_label.setToolTip(
- _("Generate a mask for pattern plating.")
- )
- grid_lay_1.addWidget(self.patern_mask_label, 6, 0, 1, 3)
-
- self.sm_obj_label = QtWidgets.QLabel("%s:" % _("Select Soldermask object"))
- self.sm_obj_label.setToolTip(
- _("Gerber Object with the soldermask.\n"
- "It will be used as a base for\n"
- "the pattern plating mask.")
- )
-
- self.sm_object_combo = FCComboBox()
- self.sm_object_combo.setModel(self.app.collection)
- self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
- self.sm_object_combo.is_last = True
- self.sm_object_combo.obj_type = 'Gerber'
-
- grid_lay_1.addWidget(self.sm_obj_label, 7, 0, 1, 3)
- grid_lay_1.addWidget(self.sm_object_combo, 8, 0, 1, 3)
-
- # Openings CLEARANCE #
- self.clearance_ppm_label = QtWidgets.QLabel('%s:' % _("Clearance"))
- self.clearance_ppm_label.setToolTip(
- _("The distance between the possible copper thieving elements\n"
- "and/or robber bar and the actual openings in the mask.")
- )
- self.clearance_ppm_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.clearance_ppm_entry.set_range(-9999.9999, 9999.9999)
- self.clearance_ppm_entry.set_precision(self.decimals)
- self.clearance_ppm_entry.setSingleStep(0.1)
-
- grid_lay_1.addWidget(self.clearance_ppm_label, 9, 0)
- grid_lay_1.addWidget(self.clearance_ppm_entry, 9, 1, 1, 2)
-
- # Plated area
- self.plated_area_label = QtWidgets.QLabel('%s:' % _("Plated area"))
- self.plated_area_label.setToolTip(
- _("The area to be plated by pattern plating.\n"
- "Basically is made from the openings in the plating mask.\n\n"
- "<> - the calculated area is actually a bit larger\n"
- "due of the fact that the soldermask openings are by design\n"
- "a bit larger than the copper pads, and this area is\n"
- "calculated from the soldermask openings.")
- )
- self.plated_area_entry = FCEntry()
- self.plated_area_entry.setDisabled(True)
-
- if self.units.upper() == 'MM':
- self.units_area_label = QtWidgets.QLabel('%s2' % _("mm"))
- else:
- self.units_area_label = QtWidgets.QLabel('%s2' % _("in"))
-
- grid_lay_1.addWidget(self.plated_area_label, 10, 0)
- grid_lay_1.addWidget(self.plated_area_entry, 10, 1)
- grid_lay_1.addWidget(self.units_area_label, 10, 2)
-
- # ## Pattern Plating Mask
- self.ppm_button = QtWidgets.QPushButton(_("Generate pattern plating mask"))
- self.ppm_button.setToolTip(
- _("Will add to the soldermask gerber geometry\n"
- "the geometries of the copper thieving and/or\n"
- "the robber bar if those were generated.")
- )
- self.ppm_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
- grid_lay_1.addWidget(self.ppm_button, 11, 0, 1, 3)
-
- self.layout.addStretch()
-
- # ## 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)
+ # #############################################################################
+ # ######################### Tool GUI ##########################################
+ # #############################################################################
+ self.ui = ThievingUI(layout=self.layout, app=self.app)
+ self.toolName = self.ui.toolName
# Objects involved in Copper thieving
self.grb_object = None
@@ -533,14 +86,14 @@ class ToolCopperThieving(AppTool):
self.rb_thickness = None
# SIGNALS
- self.ref_combo_type.currentIndexChanged.connect(self.on_ref_combo_type_change)
- self.reference_radio.group_toggle_fn = self.on_toggle_reference
- self.fill_type_radio.activated_custom.connect(self.on_thieving_type)
+ self.ui.ref_combo_type.currentIndexChanged.connect(self.on_ref_combo_type_change)
+ self.ui.reference_radio.group_toggle_fn = self.on_toggle_reference
+ self.ui.fill_type_radio.activated_custom.connect(self.on_thieving_type)
- self.fill_button.clicked.connect(self.execute)
- self.rb_button.clicked.connect(self.add_robber_bar)
- self.ppm_button.clicked.connect(self.on_add_ppm)
- self.reset_button.clicked.connect(self.set_tool_ui)
+ self.ui.fill_button.clicked.connect(self.execute)
+ self.ui.rb_button.clicked.connect(self.add_robber_bar)
+ self.ui.ppm_button.clicked.connect(self.on_add_ppm)
+ self.ui.reset_button.clicked.connect(self.set_tool_ui)
self.work_finished.connect(self.on_new_pattern_plating_object)
@@ -577,23 +130,24 @@ class ToolCopperThieving(AppTool):
def set_tool_ui(self):
self.units = self.app.defaults['units']
- self.clearance_entry.set_value(float(self.app.defaults["tools_copper_thieving_clearance"]))
- self.margin_entry.set_value(float(self.app.defaults["tools_copper_thieving_margin"]))
- self.reference_radio.set_value(self.app.defaults["tools_copper_thieving_reference"])
- self.bbox_type_radio.set_value(self.app.defaults["tools_copper_thieving_box_type"])
- self.fill_type_radio.set_value(self.app.defaults["tools_copper_thieving_fill_type"])
self.geo_steps_per_circle = int(self.app.defaults["tools_copper_thieving_circle_steps"])
- self.dot_dia_entry.set_value(self.app.defaults["tools_copper_thieving_dots_dia"])
- self.dot_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_dots_spacing"])
- self.square_size_entry.set_value(self.app.defaults["tools_copper_thieving_squares_size"])
- self.squares_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_squares_spacing"])
- self.line_size_entry.set_value(self.app.defaults["tools_copper_thieving_lines_size"])
- self.lines_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_lines_spacing"])
+ self.ui.clearance_entry.set_value(float(self.app.defaults["tools_copper_thieving_clearance"]))
+ self.ui.margin_entry.set_value(float(self.app.defaults["tools_copper_thieving_margin"]))
+ self.ui.reference_radio.set_value(self.app.defaults["tools_copper_thieving_reference"])
+ self.ui.bbox_type_radio.set_value(self.app.defaults["tools_copper_thieving_box_type"])
+ self.ui.fill_type_radio.set_value(self.app.defaults["tools_copper_thieving_fill_type"])
- self.rb_margin_entry.set_value(self.app.defaults["tools_copper_thieving_rb_margin"])
- self.rb_thickness_entry.set_value(self.app.defaults["tools_copper_thieving_rb_thickness"])
- self.clearance_ppm_entry.set_value(self.app.defaults["tools_copper_thieving_mask_clearance"])
+ self.ui.dot_dia_entry.set_value(self.app.defaults["tools_copper_thieving_dots_dia"])
+ self.ui.dot_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_dots_spacing"])
+ self.ui.square_size_entry.set_value(self.app.defaults["tools_copper_thieving_squares_size"])
+ self.ui.squares_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_squares_spacing"])
+ self.ui.line_size_entry.set_value(self.app.defaults["tools_copper_thieving_lines_size"])
+ self.ui.lines_spacing_entry.set_value(self.app.defaults["tools_copper_thieving_lines_spacing"])
+
+ 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.clearance_ppm_entry.set_value(self.app.defaults["tools_copper_thieving_mask_clearance"])
# INIT SECTION
self.area_method = False
@@ -602,75 +156,75 @@ class ToolCopperThieving(AppTool):
self.new_solid_geometry = None
def on_ref_combo_type_change(self):
- obj_type = self.ref_combo_type.currentIndex()
- self.ref_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
- self.ref_combo.setCurrentIndex(0)
- self.ref_combo.obj_type = {
+ obj_type = self.ui.ref_combo_type.currentIndex()
+ self.ui.ref_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
+ self.ui.ref_combo.setCurrentIndex(0)
+ self.ui.ref_combo.obj_type = {
_("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
- }[self.ref_combo_type.get_value()]
+ }[self.ui.ref_combo_type.get_value()]
def on_toggle_reference(self):
- if self.reference_radio.get_value() == "itself" or self.reference_radio.get_value() == "area":
- self.ref_combo.hide()
- self.ref_combo_label.hide()
- self.ref_combo_type.hide()
- self.ref_combo_type_label.hide()
+ if self.ui.reference_radio.get_value() == "itself" or self.ui.reference_radio.get_value() == "area":
+ self.ui.ref_combo.hide()
+ self.ui.ref_combo_label.hide()
+ self.ui.ref_combo_type.hide()
+ self.ui.ref_combo_type_label.hide()
else:
- self.ref_combo.show()
- self.ref_combo_label.show()
- self.ref_combo_type.show()
- self.ref_combo_type_label.show()
+ self.ui.ref_combo.show()
+ self.ui.ref_combo_label.show()
+ self.ui.ref_combo_type.show()
+ self.ui.ref_combo_type_label.show()
- if self.reference_radio.get_value() == "itself":
- self.bbox_type_label.show()
- self.bbox_type_radio.show()
+ if self.ui.reference_radio.get_value() == "itself":
+ self.ui.bbox_type_label.show()
+ self.ui.bbox_type_radio.show()
else:
- if self.fill_type_radio.get_value() == 'line':
- self.reference_radio.set_value('itself')
+ if self.ui.fill_type_radio.get_value() == 'line':
+ self.ui.reference_radio.set_value('itself')
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Lines Grid works only for 'itself' reference ..."))
return
- self.bbox_type_label.hide()
- self.bbox_type_radio.hide()
+ self.ui.bbox_type_label.hide()
+ self.ui.bbox_type_radio.hide()
def on_thieving_type(self, choice):
if choice == 'solid':
- self.dots_frame.hide()
- self.squares_frame.hide()
- self.lines_frame.hide()
+ self.ui.dots_frame.hide()
+ self.ui.squares_frame.hide()
+ self.ui.lines_frame.hide()
self.app.inform.emit(_("Solid fill selected."))
elif choice == 'dot':
- self.dots_frame.show()
- self.squares_frame.hide()
- self.lines_frame.hide()
+ self.ui.dots_frame.show()
+ self.ui.squares_frame.hide()
+ self.ui.lines_frame.hide()
self.app.inform.emit(_("Dots grid fill selected."))
elif choice == 'square':
- self.dots_frame.hide()
- self.squares_frame.show()
- self.lines_frame.hide()
+ self.ui.dots_frame.hide()
+ self.ui.squares_frame.show()
+ self.ui.lines_frame.hide()
self.app.inform.emit(_("Squares grid fill selected."))
else:
- if self.reference_radio.get_value() != 'itself':
- self.reference_radio.set_value('itself')
+ if self.ui.reference_radio.get_value() != 'itself':
+ self.ui.reference_radio.set_value('itself')
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Lines Grid works only for 'itself' reference ..."))
- self.dots_frame.hide()
- self.squares_frame.hide()
- self.lines_frame.show()
+ self.ui.dots_frame.hide()
+ self.ui.squares_frame.hide()
+ self.ui.lines_frame.show()
def add_robber_bar(self):
- rb_margin = self.rb_margin_entry.get_value()
- self.rb_thickness = self.rb_thickness_entry.get_value()
+ rb_margin = self.ui.rb_margin_entry.get_value()
+ self.rb_thickness = self.ui.rb_thickness_entry.get_value()
# get the Gerber object on which the Robber bar will be inserted
- selection_index = self.grb_object_combo.currentIndex()
- model_index = self.app.collection.index(selection_index, 0, self.grb_object_combo.rootModelIndex())
+ selection_index = self.ui.grb_object_combo.currentIndex()
+ model_index = self.app.collection.index(selection_index, 0, self.ui.grb_object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
log.debug("ToolCopperThieving.add_robber_bar() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
- return 'fail'
+ return
try:
outline_pol = self.grb_object.solid_geometry.envelope
@@ -739,23 +293,23 @@ class ToolCopperThieving(AppTool):
def execute(self):
self.app.call_source = "copper_thieving_tool"
- self.clearance_val = self.clearance_entry.get_value()
- self.margin_val = self.margin_entry.get_value()
- reference_method = self.reference_radio.get_value()
+ self.clearance_val = self.ui.clearance_entry.get_value()
+ self.margin_val = self.ui.margin_entry.get_value()
+ reference_method = self.ui.reference_radio.get_value()
# get the Gerber object on which the Copper thieving will be inserted
- selection_index = self.grb_object_combo.currentIndex()
- model_index = self.app.collection.index(selection_index, 0, self.grb_object_combo.rootModelIndex())
+ selection_index = self.ui.grb_object_combo.currentIndex()
+ model_index = self.app.collection.index(selection_index, 0, self.ui.grb_object_combo.rootModelIndex())
try:
self.grb_object = model_index.internalPointer().obj
except Exception as e:
log.debug("ToolCopperThieving.execute() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
- return 'fail'
+ return
if reference_method == 'itself':
- bound_obj_name = self.grb_object_combo.currentText()
+ bound_obj_name = self.ui.grb_object_combo.currentText()
# Get reference object.
try:
@@ -788,14 +342,14 @@ class ToolCopperThieving(AppTool):
self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
elif reference_method == 'box':
- bound_obj_name = self.ref_combo.currentText()
+ bound_obj_name = self.ui.ref_combo.currentText()
# Get reference object.
try:
self.ref_obj = self.app.collection.get_by_name(bound_obj_name)
- except Exception as e:
+ except Exception:
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))
+ return
self.on_copper_thieving(
thieving_obj=self.grb_object,
@@ -950,7 +504,7 @@ class ToolCopperThieving(AppTool):
"""
if run_threaded:
- proc = self.app.proc_container.new('%s ...' % _("Thieving"))
+ self.app.proc_container.new('%s ...' % _("Thieving"))
else:
QtWidgets.QApplication.processEvents()
@@ -963,19 +517,19 @@ class ToolCopperThieving(AppTool):
log.debug("Copper Thieving Tool started. Reading parameters.")
self.app.inform.emit(_("Copper Thieving Tool started. Reading parameters."))
- ref_selected = self.reference_radio.get_value()
+ ref_selected = self.ui.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"])
- fill_type = self.fill_type_radio.get_value()
- dot_dia = self.dot_dia_entry.get_value()
- dot_spacing = self.dot_spacing_entry.get_value()
- square_size = self.square_size_entry.get_value()
- square_spacing = self.squares_spacing_entry.get_value()
- line_size = self.line_size_entry.get_value()
- line_spacing = self.lines_spacing_entry.get_value()
+ fill_type = self.ui.fill_type_radio.get_value()
+ dot_dia = self.ui.dot_dia_entry.get_value()
+ dot_spacing = self.ui.dot_spacing_entry.get_value()
+ square_size = self.ui.square_size_entry.get_value()
+ square_spacing = self.ui.squares_spacing_entry.get_value()
+ line_size = self.ui.line_size_entry.get_value()
+ line_spacing = self.ui.lines_spacing_entry.get_value()
# make sure that the source object solid geometry is an Iterable
if not isinstance(self.grb_object.solid_geometry, Iterable):
@@ -1047,7 +601,7 @@ class ToolCopperThieving(AppTool):
geo_n = working_obj.solid_geometry
try:
- if app_obj.bbox_type_radio.get_value() == 'min':
+ if app_obj.ui.bbox_type_radio.get_value() == 'min':
if isinstance(geo_n, MultiPolygon):
env_obj = geo_n.convex_hull
elif (isinstance(geo_n, MultiPolygon) and len(geo_n) == 1) or \
@@ -1333,7 +887,7 @@ class ToolCopperThieving(AppTool):
run_threaded = True
if run_threaded:
- proc = self.app.proc_container.new('%s ...' % _("P-Plating Mask"))
+ self.app.proc_container.new('%s ...' % _("P-Plating Mask"))
else:
QtWidgets.QApplication.processEvents()
@@ -1346,17 +900,17 @@ class ToolCopperThieving(AppTool):
def on_new_pattern_plating_object(self):
# get the Gerber object on which the Copper thieving will be inserted
- selection_index = self.sm_object_combo.currentIndex()
- model_index = self.app.collection.index(selection_index, 0, self.sm_object_combo.rootModelIndex())
+ selection_index = self.ui.sm_object_combo.currentIndex()
+ model_index = self.app.collection.index(selection_index, 0, self.ui.sm_object_combo.rootModelIndex())
try:
self.sm_object = model_index.internalPointer().obj
except Exception as e:
log.debug("ToolCopperThieving.on_add_ppm() --> %s" % str(e))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
- return 'fail'
+ return
- ppm_clearance = self.clearance_ppm_entry.get_value()
+ ppm_clearance = self.ui.clearance_ppm_entry.get_value()
rb_thickness = self.rb_thickness
self.app.proc_container.update_view_text(' %s' % _("Append PP-M geometry"))
@@ -1380,7 +934,7 @@ class ToolCopperThieving(AppTool):
plated_area += geo.area
if self.robber_geo:
plated_area += self.robber_geo.area
- self.plated_area_entry.set_value(plated_area)
+ self.ui.plated_area_entry.set_value(plated_area)
thieving_solid_geo = self.new_solid_geometry
robber_solid_geo = self.robber_geo
@@ -1394,14 +948,6 @@ class ToolCopperThieving(AppTool):
grb_obj.apertures = {}
grb_obj.solid_geometry = []
- # try:
- # grb_obj.options['xmin'] = 0
- # grb_obj.options['ymin'] = 0
- # grb_obj.options['xmax'] = 0
- # grb_obj.options['ymax'] = 0
- # except KeyError:
- # pass
-
# if we have copper thieving geometry, add it
if thieving_solid_geo:
if '0' not in grb_obj.apertures:
@@ -1578,3 +1124,485 @@ class ToolCopperThieving(AppTool):
self.flat_geometry.append(geometry)
return self.flat_geometry
+
+
+class ThievingUI:
+
+ toolName = _("Copper Thieving Tool")
+
+ def __init__(self, layout, app):
+ self.app = app
+ self.decimals = self.app.decimals
+ self.units = self.app.defaults['units']
+ self.layout = layout
+
+ # ## 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 = FCComboBox()
+ 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.is_last = True
+ self.grb_object_combo.obj_type = 'Gerber'
+
+ self.grbobj_label = QtWidgets.QLabel("%s:" % _("GERBER"))
+ self.grbobj_label.setToolTip(
+ _("Gerber Object to which will be added a copper thieving.")
+ )
+
+ i_grid_lay.addWidget(self.grbobj_label, 0, 0)
+ i_grid_lay.addWidget(self.grb_object_combo, 1, 0, 1, 2)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ i_grid_lay.addWidget(separator_line, 2, 0, 1, 2)
+
+ # ## 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('%s' % _('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 thieving components\n"
+ "(the polygon fill may be split in multiple polygons)\n"
+ "and the copper traces in the Gerber file.")
+ )
+ self.clearance_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.clearance_entry.set_range(0.00001, 9999.9999)
+ 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(callback=self.confirmation_message)
+ self.margin_entry.set_range(0.0, 9999.9999)
+ 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 thieving extent is based on the object extent.\n"
+ "- 'Area Selection' - left mouse click to start selection of the area to be filled.\n"
+ "- 'Reference Object' - will do copper thieving within the area specified by another object.")
+ )
+ grid_lay.addWidget(self.reference_label, 3, 0)
+ grid_lay.addWidget(self.reference_radio, 3, 1)
+
+ self.ref_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
+ self.ref_combo_type_label.setToolTip(
+ _("The type of FlatCAM object to be used as copper thieving reference.\n"
+ "It can be Gerber, Excellon or Geometry.")
+ )
+ self.ref_combo_type = FCComboBox()
+ self.ref_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
+
+ grid_lay.addWidget(self.ref_combo_type_label, 4, 0)
+ grid_lay.addWidget(self.ref_combo_type, 4, 1)
+
+ self.ref_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))
+ self.ref_combo_label.setToolTip(
+ _("The FlatCAM object to be used as non copper clearing reference.")
+ )
+ self.ref_combo = FCComboBox()
+ self.ref_combo.setModel(self.app.collection)
+ self.ref_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+ self.ref_combo.is_last = True
+ self.ref_combo.obj_type = {
+ _("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
+ }[self.ref_combo_type.get_value()]
+
+ grid_lay.addWidget(self.ref_combo_label, 5, 0)
+ grid_lay.addWidget(self.ref_combo, 5, 1)
+
+ self.ref_combo.hide()
+ self.ref_combo_label.hide()
+ self.ref_combo_type.hide()
+ self.ref_combo_type_label.hide()
+
+ # Bounding Box Type #
+ self.bbox_type_radio = RadioSet([
+ {'label': _('Rectangular'), 'value': 'rect'},
+ {"label": _("Minimal"), "value": "min"}
+ ], stretch=False)
+ self.bbox_type_label = QtWidgets.QLabel(_("Box Type:"))
+ self.bbox_type_label.setToolTip(
+ _("- 'Rectangular' - the bounding box will be of rectangular shape.\n"
+ "- 'Minimal' - the bounding box will be the convex hull shape.")
+ )
+ grid_lay.addWidget(self.bbox_type_label, 6, 0)
+ grid_lay.addWidget(self.bbox_type_radio, 6, 1)
+ self.bbox_type_label.hide()
+ self.bbox_type_radio.hide()
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ grid_lay.addWidget(separator_line, 7, 0, 1, 2)
+
+ # Fill Type
+ self.fill_type_radio = RadioSet([
+ {'label': _('Solid'), 'value': 'solid'},
+ {"label": _("Dots Grid"), "value": "dot"},
+ {"label": _("Squares Grid"), "value": "square"},
+ {"label": _("Lines Grid"), "value": "line"}
+ ], orientation='vertical', stretch=False)
+ self.fill_type_label = QtWidgets.QLabel(_("Fill Type:"))
+ self.fill_type_label.setToolTip(
+ _("- 'Solid' - copper thieving will be a solid polygon.\n"
+ "- 'Dots Grid' - the empty area will be filled with a pattern of dots.\n"
+ "- 'Squares Grid' - the empty area will be filled with a pattern of squares.\n"
+ "- 'Lines Grid' - the empty area will be filled with a pattern of lines.")
+ )
+ grid_lay.addWidget(self.fill_type_label, 8, 0)
+ grid_lay.addWidget(self.fill_type_radio, 8, 1)
+
+ # DOTS FRAME
+ self.dots_frame = QtWidgets.QFrame()
+ self.dots_frame.setContentsMargins(0, 0, 0, 0)
+ self.layout.addWidget(self.dots_frame)
+ dots_grid = QtWidgets.QGridLayout()
+ dots_grid.setColumnStretch(0, 0)
+ dots_grid.setColumnStretch(1, 1)
+ dots_grid.setContentsMargins(0, 0, 0, 0)
+ self.dots_frame.setLayout(dots_grid)
+ self.dots_frame.hide()
+
+ self.dots_label = QtWidgets.QLabel('%s:' % _("Dots Grid Parameters"))
+ dots_grid.addWidget(self.dots_label, 0, 0, 1, 2)
+
+ # Dot diameter #
+ self.dotdia_label = QtWidgets.QLabel('%s:' % _("Dia"))
+ self.dotdia_label.setToolTip(
+ _("Dot diameter in Dots Grid.")
+ )
+ self.dot_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.dot_dia_entry.set_range(0.0, 9999.9999)
+ self.dot_dia_entry.set_precision(self.decimals)
+ self.dot_dia_entry.setSingleStep(0.1)
+
+ dots_grid.addWidget(self.dotdia_label, 1, 0)
+ dots_grid.addWidget(self.dot_dia_entry, 1, 1)
+
+ # Dot spacing #
+ self.dotspacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+ self.dotspacing_label.setToolTip(
+ _("Distance between each two dots in Dots Grid.")
+ )
+ self.dot_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.dot_spacing_entry.set_range(0.0, 9999.9999)
+ self.dot_spacing_entry.set_precision(self.decimals)
+ self.dot_spacing_entry.setSingleStep(0.1)
+
+ dots_grid.addWidget(self.dotspacing_label, 2, 0)
+ dots_grid.addWidget(self.dot_spacing_entry, 2, 1)
+
+ # SQUARES FRAME
+ self.squares_frame = QtWidgets.QFrame()
+ self.squares_frame.setContentsMargins(0, 0, 0, 0)
+ self.layout.addWidget(self.squares_frame)
+ squares_grid = QtWidgets.QGridLayout()
+ squares_grid.setColumnStretch(0, 0)
+ squares_grid.setColumnStretch(1, 1)
+ squares_grid.setContentsMargins(0, 0, 0, 0)
+ self.squares_frame.setLayout(squares_grid)
+ self.squares_frame.hide()
+
+ self.squares_label = QtWidgets.QLabel('%s:' % _("Squares Grid Parameters"))
+ squares_grid.addWidget(self.squares_label, 0, 0, 1, 2)
+
+ # Square Size #
+ self.square_size_label = QtWidgets.QLabel('%s:' % _("Size"))
+ self.square_size_label.setToolTip(
+ _("Square side size in Squares Grid.")
+ )
+ self.square_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.square_size_entry.set_range(0.0, 9999.9999)
+ self.square_size_entry.set_precision(self.decimals)
+ self.square_size_entry.setSingleStep(0.1)
+
+ squares_grid.addWidget(self.square_size_label, 1, 0)
+ squares_grid.addWidget(self.square_size_entry, 1, 1)
+
+ # Squares spacing #
+ self.squares_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+ self.squares_spacing_label.setToolTip(
+ _("Distance between each two squares in Squares Grid.")
+ )
+ self.squares_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.squares_spacing_entry.set_range(0.0, 9999.9999)
+ self.squares_spacing_entry.set_precision(self.decimals)
+ self.squares_spacing_entry.setSingleStep(0.1)
+
+ squares_grid.addWidget(self.squares_spacing_label, 2, 0)
+ squares_grid.addWidget(self.squares_spacing_entry, 2, 1)
+
+ # LINES FRAME
+ self.lines_frame = QtWidgets.QFrame()
+ self.lines_frame.setContentsMargins(0, 0, 0, 0)
+ self.layout.addWidget(self.lines_frame)
+ lines_grid = QtWidgets.QGridLayout()
+ lines_grid.setColumnStretch(0, 0)
+ lines_grid.setColumnStretch(1, 1)
+ lines_grid.setContentsMargins(0, 0, 0, 0)
+ self.lines_frame.setLayout(lines_grid)
+ self.lines_frame.hide()
+
+ self.lines_label = QtWidgets.QLabel('%s:' % _("Lines Grid Parameters"))
+ lines_grid.addWidget(self.lines_label, 0, 0, 1, 2)
+
+ # Square Size #
+ self.line_size_label = QtWidgets.QLabel('%s:' % _("Size"))
+ self.line_size_label.setToolTip(
+ _("Line thickness size in Lines Grid.")
+ )
+ self.line_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.line_size_entry.set_range(0.0, 9999.9999)
+ self.line_size_entry.set_precision(self.decimals)
+ self.line_size_entry.setSingleStep(0.1)
+
+ lines_grid.addWidget(self.line_size_label, 1, 0)
+ lines_grid.addWidget(self.line_size_entry, 1, 1)
+
+ # Lines spacing #
+ self.lines_spacing_label = QtWidgets.QLabel('%s:' % _("Spacing"))
+ self.lines_spacing_label.setToolTip(
+ _("Distance between each two lines in Lines Grid.")
+ )
+ self.lines_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.lines_spacing_entry.set_range(0.0, 9999.9999)
+ self.lines_spacing_entry.set_precision(self.decimals)
+ self.lines_spacing_entry.setSingleStep(0.1)
+
+ lines_grid.addWidget(self.lines_spacing_label, 2, 0)
+ lines_grid.addWidget(self.lines_spacing_entry, 2, 1)
+
+ # ## Insert Copper Thieving
+ self.fill_button = QtWidgets.QPushButton(_("Insert Copper thieving"))
+ 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.fill_button.setStyleSheet("""
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
+ self.layout.addWidget(self.fill_button)
+
+ # ## Grid Layout
+ grid_lay_1 = QtWidgets.QGridLayout()
+ self.layout.addLayout(grid_lay_1)
+ grid_lay_1.setColumnStretch(0, 0)
+ grid_lay_1.setColumnStretch(1, 1)
+ grid_lay_1.setColumnStretch(2, 0)
+
+ separator_line_1 = QtWidgets.QFrame()
+ separator_line_1.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line_1.setFrameShadow(QtWidgets.QFrame.Sunken)
+ grid_lay_1.addWidget(separator_line_1, 0, 0, 1, 3)
+
+ grid_lay_1.addWidget(QtWidgets.QLabel(''))
+
+ self.robber_bar_label = QtWidgets.QLabel('%s' % _('Robber Bar Parameters'))
+ self.robber_bar_label.setToolTip(
+ _("Parameters used for the robber bar.\n"
+ "Robber bar = copper border to help in pattern hole plating.")
+ )
+ grid_lay_1.addWidget(self.robber_bar_label, 1, 0, 1, 3)
+
+ # ROBBER BAR MARGIN #
+ self.rb_margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+ self.rb_margin_label.setToolTip(
+ _("Bounding box margin for robber bar.")
+ )
+ self.rb_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.rb_margin_entry.set_range(-9999.9999, 9999.9999)
+ self.rb_margin_entry.set_precision(self.decimals)
+ self.rb_margin_entry.setSingleStep(0.1)
+
+ grid_lay_1.addWidget(self.rb_margin_label, 2, 0)
+ grid_lay_1.addWidget(self.rb_margin_entry, 2, 1, 1, 2)
+
+ # THICKNESS #
+ self.rb_thickness_label = QtWidgets.QLabel('%s:' % _("Thickness"))
+ self.rb_thickness_label.setToolTip(
+ _("The robber bar thickness.")
+ )
+ self.rb_thickness_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.rb_thickness_entry.set_range(0.0000, 9999.9999)
+ self.rb_thickness_entry.set_precision(self.decimals)
+ self.rb_thickness_entry.setSingleStep(0.1)
+
+ grid_lay_1.addWidget(self.rb_thickness_label, 3, 0)
+ grid_lay_1.addWidget(self.rb_thickness_entry, 3, 1, 1, 2)
+
+ # ## Insert Robber Bar
+ self.rb_button = QtWidgets.QPushButton(_("Insert Robber Bar"))
+ self.rb_button.setToolTip(
+ _("Will add a polygon with a defined thickness\n"
+ "that will surround the actual Gerber object\n"
+ "at a certain distance.\n"
+ "Required when doing holes pattern plating.")
+ )
+ self.rb_button.setStyleSheet("""
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
+ grid_lay_1.addWidget(self.rb_button, 4, 0, 1, 3)
+
+ separator_line_2 = QtWidgets.QFrame()
+ separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ grid_lay_1.addWidget(separator_line_2, 5, 0, 1, 3)
+
+ self.patern_mask_label = QtWidgets.QLabel('%s' % _('Pattern Plating Mask'))
+ self.patern_mask_label.setToolTip(
+ _("Generate a mask for pattern plating.")
+ )
+ grid_lay_1.addWidget(self.patern_mask_label, 6, 0, 1, 3)
+
+ self.sm_obj_label = QtWidgets.QLabel("%s:" % _("Select Soldermask object"))
+ self.sm_obj_label.setToolTip(
+ _("Gerber Object with the soldermask.\n"
+ "It will be used as a base for\n"
+ "the pattern plating mask.")
+ )
+
+ self.sm_object_combo = FCComboBox()
+ self.sm_object_combo.setModel(self.app.collection)
+ self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+ self.sm_object_combo.is_last = True
+ self.sm_object_combo.obj_type = 'Gerber'
+
+ grid_lay_1.addWidget(self.sm_obj_label, 7, 0, 1, 3)
+ grid_lay_1.addWidget(self.sm_object_combo, 8, 0, 1, 3)
+
+ # Openings CLEARANCE #
+ self.clearance_ppm_label = QtWidgets.QLabel('%s:' % _("Clearance"))
+ self.clearance_ppm_label.setToolTip(
+ _("The distance between the possible copper thieving elements\n"
+ "and/or robber bar and the actual openings in the mask.")
+ )
+ self.clearance_ppm_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.clearance_ppm_entry.set_range(-9999.9999, 9999.9999)
+ self.clearance_ppm_entry.set_precision(self.decimals)
+ self.clearance_ppm_entry.setSingleStep(0.1)
+
+ grid_lay_1.addWidget(self.clearance_ppm_label, 9, 0)
+ grid_lay_1.addWidget(self.clearance_ppm_entry, 9, 1, 1, 2)
+
+ # Plated area
+ self.plated_area_label = QtWidgets.QLabel('%s:' % _("Plated area"))
+ self.plated_area_label.setToolTip(
+ _("The area to be plated by pattern plating.\n"
+ "Basically is made from the openings in the plating mask.\n\n"
+ "<> - the calculated area is actually a bit larger\n"
+ "due of the fact that the soldermask openings are by design\n"
+ "a bit larger than the copper pads, and this area is\n"
+ "calculated from the soldermask openings.")
+ )
+ self.plated_area_entry = FCEntry()
+ self.plated_area_entry.setDisabled(True)
+
+ if self.units.upper() == 'MM':
+ self.units_area_label = QtWidgets.QLabel('%s2' % _("mm"))
+ else:
+ self.units_area_label = QtWidgets.QLabel('%s2' % _("in"))
+
+ grid_lay_1.addWidget(self.plated_area_label, 10, 0)
+ grid_lay_1.addWidget(self.plated_area_entry, 10, 1)
+ grid_lay_1.addWidget(self.units_area_label, 10, 2)
+
+ # ## Pattern Plating Mask
+ self.ppm_button = QtWidgets.QPushButton(_("Generate pattern plating mask"))
+ self.ppm_button.setToolTip(
+ _("Will add to the soldermask gerber geometry\n"
+ "the geometries of the copper thieving and/or\n"
+ "the robber bar if those were generated.")
+ )
+ self.ppm_button.setStyleSheet("""
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
+ grid_lay_1.addWidget(self.ppm_button, 11, 0, 1, 3)
+
+ self.layout.addStretch()
+
+ # ## 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 ###########################
+ # #############################################################################
+
+ 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)
diff --git a/appTools/ToolCorners.py b/appTools/ToolCorners.py
index 7887fc31..523263a6 100644
--- a/appTools/ToolCorners.py
+++ b/appTools/ToolCorners.py
@@ -28,8 +28,6 @@ log = logging.getLogger('base')
class ToolCorners(AppTool):
- toolName = _("Corner Markers Tool")
-
def __init__(self, app):
AppTool.__init__(self, app)
@@ -39,158 +37,11 @@ class ToolCorners(AppTool):
self.decimals = self.app.decimals
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(''))
-
- # Gerber object #
- self.object_label = QtWidgets.QLabel('%s:' % _("GERBER"))
- 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 = True
- self.object_combo.obj_type = "Gerber"
-
- self.layout.addWidget(self.object_label)
- self.layout.addWidget(self.object_combo)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
- self.layout.addWidget(separator_line)
-
- self.points_label = QtWidgets.QLabel('%s:' % _('Locations'))
- self.points_label.setToolTip(
- _("Locations where to place corner markers.")
- )
- self.layout.addWidget(self.points_label)
-
- # BOTTOM LEFT
- self.bl_cb = FCCheckBox(_("Bottom Left"))
- self.layout.addWidget(self.bl_cb)
-
- # BOTTOM RIGHT
- self.br_cb = FCCheckBox(_("Bottom Right"))
- self.layout.addWidget(self.br_cb)
-
- # TOP LEFT
- self.tl_cb = FCCheckBox(_("Top Left"))
- self.layout.addWidget(self.tl_cb)
-
- # TOP RIGHT
- self.tr_cb = FCCheckBox(_("Top Right"))
- self.layout.addWidget(self.tr_cb)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
- self.layout.addWidget(separator_line)
-
- # Toggle ALL
- self.toggle_all_cb = FCCheckBox(_("Toggle ALL"))
- self.layout.addWidget(self.toggle_all_cb)
-
- separator_line = QtWidgets.QFrame()
- separator_line.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
- self.layout.addWidget(separator_line)
-
- # ## Grid Layout
- grid_lay = QtWidgets.QGridLayout()
- self.layout.addLayout(grid_lay)
- grid_lay.setColumnStretch(0, 0)
- grid_lay.setColumnStretch(1, 1)
-
- self.param_label = QtWidgets.QLabel('%s:' % _('Parameters'))
- self.param_label.setToolTip(
- _("Parameters used for this tool.")
- )
- grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
-
- # Thickness #
- self.thick_label = QtWidgets.QLabel('%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, 9.9999)
- self.thick_entry.set_precision(self.decimals)
- self.thick_entry.setWrapping(True)
- self.thick_entry.setSingleStep(10 ** -self.decimals)
-
- grid_lay.addWidget(self.thick_label, 1, 0)
- grid_lay.addWidget(self.thick_entry, 1, 1)
-
- # Length #
- self.l_label = QtWidgets.QLabel('%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(-9999.9999, 9999.9999)
- self.l_entry.set_precision(self.decimals)
- self.l_entry.setSingleStep(10 ** -self.decimals)
-
- grid_lay.addWidget(self.l_label, 2, 0)
- grid_lay.addWidget(self.l_entry, 2, 1)
-
- # Margin #
- self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
- self.margin_label.setToolTip(
- _("Bounding box margin.")
- )
- self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
- self.margin_entry.set_range(-9999.9999, 9999.9999)
- self.margin_entry.set_precision(self.decimals)
- self.margin_entry.setSingleStep(0.1)
-
- grid_lay.addWidget(self.margin_label, 3, 0)
- grid_lay.addWidget(self.margin_entry, 3, 1)
-
- separator_line_2 = QtWidgets.QFrame()
- separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
- separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
- grid_lay.addWidget(separator_line_2, 4, 0, 1, 2)
-
- # ## Insert Corner Marker
- self.add_marker_button = FCButton(_("Add Marker"))
- self.add_marker_button.setToolTip(
- _("Will add corner markers to the selected Gerber file.")
- )
- self.add_marker_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
- grid_lay.addWidget(self.add_marker_button, 11, 0, 1, 2)
-
- self.layout.addStretch()
-
- # ## 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)
+ # #############################################################################
+ # ######################### Tool GUI ##########################################
+ # #############################################################################
+ self.ui = CornersUI(layout=self.layout, app=self.app)
+ self.toolName = self.ui.toolName
# Objects involved in Copper thieving
self.grb_object = None
@@ -204,8 +55,8 @@ class ToolCorners(AppTool):
self.grb_steps_per_circle = self.app.defaults["gerber_circle_steps"]
# SIGNALS
- self.add_marker_button.clicked.connect(self.add_markers)
- self.toggle_all_cb.toggled.connect(self.on_toggle_all)
+ self.ui.add_marker_button.clicked.connect(self.add_markers)
+ self.ui.toggle_all_cb.toggled.connect(self.on_toggle_all)
def run(self, toggle=True):
self.app.defaults.report_usage("ToolCorners()")
@@ -240,27 +91,27 @@ class ToolCorners(AppTool):
def set_tool_ui(self):
self.units = self.app.defaults['units']
- self.thick_entry.set_value(self.app.defaults["tools_corners_thickness"])
- self.l_entry.set_value(float(self.app.defaults["tools_corners_length"]))
- self.margin_entry.set_value(float(self.app.defaults["tools_corners_margin"]))
- self.toggle_all_cb.set_value(False)
+ self.ui.thick_entry.set_value(self.app.defaults["tools_corners_thickness"])
+ self.ui.l_entry.set_value(float(self.app.defaults["tools_corners_length"]))
+ self.ui.margin_entry.set_value(float(self.app.defaults["tools_corners_margin"]))
+ self.ui.toggle_all_cb.set_value(False)
def on_toggle_all(self, val):
- self.bl_cb.set_value(val)
- self.br_cb.set_value(val)
- self.tl_cb.set_value(val)
- self.tr_cb.set_value(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 add_markers(self):
self.app.call_source = "corners_tool"
- tl_state = self.tl_cb.get_value()
- tr_state = self.tr_cb.get_value()
- bl_state = self.bl_cb.get_value()
- br_state = self.br_cb.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.object_combo.currentIndex()
- model_index = self.app.collection.index(selection_index, 0, self.object_combo.rootModelIndex())
+ 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
@@ -296,9 +147,9 @@ class ToolCorners(AppTool):
:return: None
"""
- line_thickness = self.thick_entry.get_value()
- line_length = self.l_entry.get_value()
- margin = self.margin_entry.get_value()
+ line_thickness = self.ui.thick_entry.get_value()
+ line_length = self.ui.l_entry.get_value()
+ margin = self.ui.margin_entry.get_value()
geo_list = []
@@ -439,3 +290,186 @@ class ToolCorners(AppTool):
self.app.call_source = "app"
self.app.inform.emit('[success] %s' % _("Corners Tool exit."))
+
+
+class CornersUI:
+
+ toolName = _("Corner Markers Tool")
+
+ def __init__(self, layout, app):
+ self.app = app
+ self.decimals = self.app.decimals
+ self.layout = layout
+
+ # ## 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(""))
+
+ # Gerber object #
+ self.object_label = QtWidgets.QLabel('%s:' % _("GERBER"))
+ 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 = True
+ self.object_combo.obj_type = "Gerber"
+
+ self.layout.addWidget(self.object_label)
+ self.layout.addWidget(self.object_combo)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.layout.addWidget(separator_line)
+
+ self.points_label = QtWidgets.QLabel('%s:' % _('Locations'))
+ self.points_label.setToolTip(
+ _("Locations where to place corner markers.")
+ )
+ self.layout.addWidget(self.points_label)
+
+ # BOTTOM LEFT
+ self.bl_cb = FCCheckBox(_("Bottom Left"))
+ self.layout.addWidget(self.bl_cb)
+
+ # BOTTOM RIGHT
+ self.br_cb = FCCheckBox(_("Bottom Right"))
+ self.layout.addWidget(self.br_cb)
+
+ # TOP LEFT
+ self.tl_cb = FCCheckBox(_("Top Left"))
+ self.layout.addWidget(self.tl_cb)
+
+ # TOP RIGHT
+ self.tr_cb = FCCheckBox(_("Top Right"))
+ self.layout.addWidget(self.tr_cb)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.layout.addWidget(separator_line)
+
+ # Toggle ALL
+ self.toggle_all_cb = FCCheckBox(_("Toggle ALL"))
+ self.layout.addWidget(self.toggle_all_cb)
+
+ separator_line = QtWidgets.QFrame()
+ separator_line.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
+ self.layout.addWidget(separator_line)
+
+ # ## Grid Layout
+ grid_lay = QtWidgets.QGridLayout()
+ self.layout.addLayout(grid_lay)
+ grid_lay.setColumnStretch(0, 0)
+ grid_lay.setColumnStretch(1, 1)
+
+ self.param_label = QtWidgets.QLabel('%s:' % _('Parameters'))
+ self.param_label.setToolTip(
+ _("Parameters used for this tool.")
+ )
+ grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
+
+ # Thickness #
+ self.thick_label = QtWidgets.QLabel('%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, 9.9999)
+ self.thick_entry.set_precision(self.decimals)
+ self.thick_entry.setWrapping(True)
+ self.thick_entry.setSingleStep(10 ** -self.decimals)
+
+ grid_lay.addWidget(self.thick_label, 1, 0)
+ grid_lay.addWidget(self.thick_entry, 1, 1)
+
+ # Length #
+ self.l_label = QtWidgets.QLabel('%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(-9999.9999, 9999.9999)
+ self.l_entry.set_precision(self.decimals)
+ self.l_entry.setSingleStep(10 ** -self.decimals)
+
+ grid_lay.addWidget(self.l_label, 2, 0)
+ grid_lay.addWidget(self.l_entry, 2, 1)
+
+ # Margin #
+ self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
+ self.margin_label.setToolTip(
+ _("Bounding box margin.")
+ )
+ self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
+ self.margin_entry.set_range(-9999.9999, 9999.9999)
+ self.margin_entry.set_precision(self.decimals)
+ self.margin_entry.setSingleStep(0.1)
+
+ grid_lay.addWidget(self.margin_label, 3, 0)
+ grid_lay.addWidget(self.margin_entry, 3, 1)
+
+ separator_line_2 = QtWidgets.QFrame()
+ separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
+ separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
+ grid_lay.addWidget(separator_line_2, 4, 0, 1, 2)
+
+ # ## Insert Corner Marker
+ self.add_marker_button = FCButton(_("Add Marker"))
+ self.add_marker_button.setToolTip(
+ _("Will add corner markers to the selected Gerber file.")
+ )
+ self.add_marker_button.setStyleSheet("""
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
+ grid_lay.addWidget(self.add_marker_button, 11, 0, 1, 2)
+
+ self.layout.addStretch()
+
+ # ## 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 ###########################
+ # #############################################################################
+
+ 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)
\ No newline at end of file
diff --git a/appTools/ToolDblSided.py b/appTools/ToolDblSided.py
index f22b028e..4f6ffc9d 100644
--- a/appTools/ToolDblSided.py
+++ b/appTools/ToolDblSided.py
@@ -509,7 +509,6 @@ class DsidedUI:
}
""")
self.layout.addWidget(title_label)
-
self.layout.addWidget(QtWidgets.QLabel(""))
# ## Grid Layout
diff --git a/appTools/ToolFiducials.py b/appTools/ToolFiducials.py
index 86c99181..fd76b393 100644
--- a/appTools/ToolFiducials.py
+++ b/appTools/ToolFiducials.py
@@ -30,8 +30,6 @@ log = logging.getLogger('base')
class ToolFiducials(AppTool):
- toolName = _("Fiducials Tool")
-
def __init__(self, app):
AppTool.__init__(self, app)
@@ -41,17 +39,630 @@ class ToolFiducials(AppTool):
self.decimals = self.app.decimals
self.units = ''
+ # #############################################################################
+ # ######################### Tool GUI ##########################################
+ # #############################################################################
+ self.ui = FidoUI(layout=self.layout, app=self.app)
+ self.toolName = self.ui.toolName
+
+ # Objects involved in Copper thieving
+ self.grb_object = None
+ self.sm_object = None
+
+ self.copper_obj_set = set()
+ self.sm_obj_set = set()
+
+ # store the flattened geometry here:
+ self.flat_geometry = []
+
+ # Events ID
+ self.mr = None
+ self.mm = None
+
+ # Mouse cursor positions
+ self.cursor_pos = (0, 0)
+ self.first_click = False
+
+ self.mode_method = False
+
+ # Tool properties
+ self.fid_dia = None
+ self.sm_opening_dia = None
+
+ self.margin_val = None
+ self.sec_position = None
+
+ self.grb_steps_per_circle = self.app.defaults["gerber_circle_steps"]
+
+ self.click_points = []
+
+ # SIGNALS
+ self.ui.add_cfid_button.clicked.connect(self.add_fiducials)
+ self.ui.add_sm_opening_button.clicked.connect(self.add_soldermask_opening)
+
+ self.ui.fid_type_radio.activated_custom.connect(self.on_fiducial_type)
+ self.ui.pos_radio.activated_custom.connect(self.on_second_point)
+ self.ui.mode_radio.activated_custom.connect(self.on_method_change)
+ self.ui.reset_button.clicked.connect(self.set_tool_ui)
+
+ def run(self, toggle=True):
+ self.app.defaults.report_usage("ToolFiducials()")
+
+ 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])
+
+ AppTool.run(self)
+
+ self.set_tool_ui()
+
+ self.app.ui.notebook.setTabText(2, _("Fiducials Tool"))
+
+ def install(self, icon=None, separator=None, **kwargs):
+ AppTool.install(self, icon, separator, shortcut='Alt+F', **kwargs)
+
+ def set_tool_ui(self):
+ self.units = self.app.defaults['units']
+
+ self.ui.fid_size_entry.set_value(self.app.defaults["tools_fiducials_dia"])
+ self.ui.margin_entry.set_value(float(self.app.defaults["tools_fiducials_margin"]))
+ self.ui.mode_radio.set_value(self.app.defaults["tools_fiducials_mode"])
+ self.ui.pos_radio.set_value(self.app.defaults["tools_fiducials_second_pos"])
+ self.ui.fid_type_radio.set_value(self.app.defaults["tools_fiducials_type"])
+ self.ui.line_thickness_entry.set_value(float(self.app.defaults["tools_fiducials_line_thickness"]))
+
+ self.click_points = []
+ self.ui.bottom_left_coords_entry.set_value('')
+ self.ui.top_right_coords_entry.set_value('')
+ self.ui.sec_points_coords_entry.set_value('')
+
+ self.copper_obj_set = set()
+ self.sm_obj_set = set()
+
+ def on_second_point(self, val):
+ if val == 'no':
+ self.ui.id_item_3.setFlags(QtCore.Qt.NoItemFlags)
+ self.ui.sec_point_coords_lbl.setFlags(QtCore.Qt.NoItemFlags)
+ self.ui.sec_points_coords_entry.setDisabled(True)
+ else:
+ self.ui.id_item_3.setFlags(QtCore.Qt.ItemIsEnabled)
+ self.ui.sec_point_coords_lbl.setFlags(QtCore.Qt.ItemIsEnabled)
+ self.ui.sec_points_coords_entry.setDisabled(False)
+
+ def on_method_change(self, val):
+ """
+ Make sure that on method change we disconnect the event handlers and reset the points storage
+ :param val: value of the Radio button which trigger this method
+ :return: None
+ """
+ if val == 'auto':
+ self.click_points = []
+
+ try:
+ self.disconnect_event_handlers()
+ except TypeError:
+ pass
+
+ def on_fiducial_type(self, val):
+ if val == 'cross':
+ self.ui.line_thickness_label.setDisabled(False)
+ self.ui.line_thickness_entry.setDisabled(False)
+ else:
+ self.ui.line_thickness_label.setDisabled(True)
+ self.ui.line_thickness_entry.setDisabled(True)
+
+ def add_fiducials(self):
+ self.app.call_source = "fiducials_tool"
+
+ self.mode_method = self.ui.mode_radio.get_value()
+ self.margin_val = self.ui.margin_entry.get_value()
+ self.sec_position = self.ui.pos_radio.get_value()
+ fid_type = self.ui.fid_type_radio.get_value()
+
+ self.click_points = []
+
+ # get the Gerber object on which the Fiducial will be inserted
+ selection_index = self.ui.grb_object_combo.currentIndex()
+ model_index = self.app.collection.index(selection_index, 0, self.ui.grb_object_combo.rootModelIndex())
+
+ try:
+ self.grb_object = model_index.internalPointer().obj
+ except Exception as e:
+ log.debug("ToolFiducials.execute() --> %s" % str(e))
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
+ return
+
+ self.copper_obj_set.add(self.grb_object.options['name'])
+
+ if self.mode_method == 'auto':
+ xmin, ymin, xmax, ymax = self.grb_object.bounds()
+ bbox = box(xmin, ymin, xmax, ymax)
+ buf_bbox = bbox.buffer(self.margin_val, self.grb_steps_per_circle, join_style=2)
+ x0, y0, x1, y1 = buf_bbox.bounds
+
+ self.click_points.append(
+ (
+ float('%.*f' % (self.decimals, x0)),
+ float('%.*f' % (self.decimals, y0))
+ )
+ )
+ self.ui.bottom_left_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x0, self.decimals, y0))
+
+ self.click_points.append(
+ (
+ float('%.*f' % (self.decimals, x1)),
+ float('%.*f' % (self.decimals, y1))
+ )
+ )
+ self.ui.top_right_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y1))
+
+ if self.sec_position == 'up':
+ self.click_points.append(
+ (
+ float('%.*f' % (self.decimals, x0)),
+ float('%.*f' % (self.decimals, y1))
+ )
+ )
+ self.ui.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x0, self.decimals, y1))
+ elif self.sec_position == 'down':
+ self.click_points.append(
+ (
+ float('%.*f' % (self.decimals, x1)),
+ float('%.*f' % (self.decimals, y0))
+ )
+ )
+ self.ui.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y0))
+
+ self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
+ 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()
+ else:
+ self.app.inform.emit(_("Click to add first Fiducial. Bottom Left..."))
+ self.ui.bottom_left_coords_entry.set_value('')
+ self.ui.top_right_coords_entry.set_value('')
+ self.ui.sec_points_coords_entry.set_value('')
+
+ self.connect_event_handlers()
+
+ # To be called after clicking on the plot.
+
+ def add_fiducials_geo(self, points_list, g_obj, fid_size=None, fid_type=None, line_size=None):
+ """
+ Add geometry to the solid_geometry of the copper Gerber object
+ :param points_list: list of coordinates for the fiducials
+ :param g_obj: the Gerber object where to add the geometry
+ :param fid_size: the overall size of the fiducial or fiducial opening depending on the g_obj type
+ :param fid_type: the type of fiducial: circular or cross
+ :param line_size: the line thickenss when the fiducial type is cross
+ :return:
+ """
+ fid_size = self.ui.fid_size_entry.get_value() if fid_size is None else fid_size
+ fid_type = 'circular' if fid_type is None else fid_type
+ line_thickness = self.ui.line_thickness_entry.get_value() if line_size is None else line_size
+
+ radius = fid_size / 2.0
+
+ if fid_type == 'circular':
+ geo_list = [Point(pt).buffer(radius, self.grb_steps_per_circle) for pt in points_list]
+
+ aperture_found = None
+ for ap_id, ap_val in g_obj.apertures.items():
+ if ap_val['type'] == 'C' and ap_val['size'] == fid_size:
+ aperture_found = ap_id
+ break
+
+ if aperture_found:
+ for geo in geo_list:
+ dict_el = {}
+ dict_el['follow'] = geo.centroid
+ dict_el['solid'] = geo
+ g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+ else:
+ ap_keys = list(g_obj.apertures.keys())
+ if ap_keys:
+ new_apid = str(int(max(ap_keys)) + 1)
+ else:
+ new_apid = '10'
+
+ g_obj.apertures[new_apid] = {}
+ g_obj.apertures[new_apid]['type'] = 'C'
+ g_obj.apertures[new_apid]['size'] = fid_size
+ g_obj.apertures[new_apid]['geometry'] = []
+
+ for geo in geo_list:
+ dict_el = {}
+ dict_el['follow'] = geo.centroid
+ dict_el['solid'] = geo
+ g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+
+ s_list = []
+ if g_obj.solid_geometry:
+ try:
+ for poly in g_obj.solid_geometry:
+ s_list.append(poly)
+ except TypeError:
+ s_list.append(g_obj.solid_geometry)
+
+ s_list += geo_list
+ g_obj.solid_geometry = MultiPolygon(s_list)
+ elif fid_type == 'cross':
+ geo_list = []
+
+ for pt in points_list:
+ x = pt[0]
+ y = pt[1]
+ line_geo_hor = LineString([
+ (x - radius + (line_thickness / 2.0), y), (x + radius - (line_thickness / 2.0), y)
+ ])
+ line_geo_vert = LineString([
+ (x, y - radius + (line_thickness / 2.0)), (x, y + radius - (line_thickness / 2.0))
+ ])
+ geo_list.append([line_geo_hor, line_geo_vert])
+
+ aperture_found = None
+ for ap_id, ap_val in g_obj.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 geo_list:
+ geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
+ geo_buff_v = geo[1].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
+ geo_buff_list.append(geo_buff_h)
+ geo_buff_list.append(geo_buff_v)
+
+ dict_el = {}
+ dict_el['follow'] = geo_buff_h.centroid
+ dict_el['solid'] = geo_buff_h
+ g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+ dict_el['follow'] = geo_buff_v.centroid
+ dict_el['solid'] = geo_buff_v
+ g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+ else:
+ ap_keys = list(g_obj.apertures.keys())
+ if ap_keys:
+ new_apid = str(int(max(ap_keys)) + 1)
+ else:
+ new_apid = '10'
+
+ g_obj.apertures[new_apid] = {}
+ g_obj.apertures[new_apid]['type'] = 'C'
+ g_obj.apertures[new_apid]['size'] = line_thickness
+ g_obj.apertures[new_apid]['geometry'] = []
+
+ for geo in geo_list:
+ geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
+ geo_buff_v = geo[1].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
+ geo_buff_list.append(geo_buff_h)
+ geo_buff_list.append(geo_buff_v)
+
+ dict_el = {}
+ dict_el['follow'] = geo_buff_h.centroid
+ dict_el['solid'] = geo_buff_h
+ g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+ dict_el['follow'] = geo_buff_v.centroid
+ dict_el['solid'] = geo_buff_v
+ g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+
+ s_list = []
+ if g_obj.solid_geometry:
+ try:
+ for poly in g_obj.solid_geometry:
+ s_list.append(poly)
+ except TypeError:
+ s_list.append(g_obj.solid_geometry)
+
+ geo_buff_list = MultiPolygon(geo_buff_list)
+ geo_buff_list = geo_buff_list.buffer(0)
+ for poly in geo_buff_list:
+ s_list.append(poly)
+ g_obj.solid_geometry = MultiPolygon(s_list)
+ else:
+ # chess pattern fiducial type
+ geo_list = []
+
+ def make_square_poly(center_pt, side_size):
+ half_s = side_size / 2
+ x_center = center_pt[0]
+ y_center = center_pt[1]
+
+ pt1 = (x_center - half_s, y_center - half_s)
+ pt2 = (x_center + half_s, y_center - half_s)
+ pt3 = (x_center + half_s, y_center + half_s)
+ pt4 = (x_center - half_s, y_center + half_s)
+
+ return Polygon([pt1, pt2, pt3, pt4, pt1])
+
+ for pt in points_list:
+ x = pt[0]
+ y = pt[1]
+ first_square = make_square_poly(center_pt=(x-fid_size/4, y+fid_size/4), side_size=fid_size/2)
+ second_square = make_square_poly(center_pt=(x+fid_size/4, y-fid_size/4), side_size=fid_size/2)
+ geo_list += [first_square, second_square]
+
+ aperture_found = None
+ new_ap_size = math.sqrt(fid_size**2 + fid_size**2)
+ for ap_id, ap_val in g_obj.apertures.items():
+ if ap_val['type'] == 'R' and \
+ round(ap_val['size'], ndigits=self.decimals) == round(new_ap_size, ndigits=self.decimals):
+ aperture_found = ap_id
+ break
+
+ geo_buff_list = []
+ if aperture_found:
+ for geo in geo_list:
+ geo_buff_list.append(geo)
+
+ dict_el = {}
+ dict_el['follow'] = geo.centroid
+ dict_el['solid'] = geo
+ g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
+ else:
+ ap_keys = list(g_obj.apertures.keys())
+ if ap_keys:
+ new_apid = str(int(max(ap_keys)) + 1)
+ else:
+ new_apid = '10'
+
+ g_obj.apertures[new_apid] = {}
+ g_obj.apertures[new_apid]['type'] = 'R'
+ g_obj.apertures[new_apid]['size'] = new_ap_size
+ g_obj.apertures[new_apid]['width'] = fid_size
+ g_obj.apertures[new_apid]['height'] = fid_size
+ g_obj.apertures[new_apid]['geometry'] = []
+
+ for geo in geo_list:
+ geo_buff_list.append(geo)
+
+ dict_el = {}
+ dict_el['follow'] = geo.centroid
+ dict_el['solid'] = geo
+ g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
+
+ s_list = []
+ if g_obj.solid_geometry:
+ try:
+ for poly in g_obj.solid_geometry:
+ s_list.append(poly)
+ except TypeError:
+ s_list.append(g_obj.solid_geometry)
+
+ for poly in geo_buff_list:
+ s_list.append(poly)
+ g_obj.solid_geometry = MultiPolygon(s_list)
+
+ def add_soldermask_opening(self):
+ sm_opening_dia = self.ui.fid_size_entry.get_value() * 2.0
+
+ # get the Gerber object on which the Fiducial will be inserted
+ selection_index = self.ui.sm_object_combo.currentIndex()
+ model_index = self.app.collection.index(selection_index, 0, self.ui.sm_object_combo.rootModelIndex())
+
+ try:
+ self.sm_object = model_index.internalPointer().obj
+ except Exception as e:
+ log.debug("ToolFiducials.add_soldermask_opening() --> %s" % str(e))
+ self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
+ return
+
+ self.sm_obj_set.add(self.sm_object.options['name'])
+ self.add_fiducials_geo(self.click_points, g_obj=self.sm_object, fid_size=sm_opening_dia, fid_type='circular')
+
+ self.sm_object.source_file = self.app.export_gerber(obj_name=self.sm_object.options['name'], filename=None,
+ local_use=self.sm_object, use_thread=False)
+ self.on_exit()
+
+ def on_mouse_release(self, event):
+ if event.button == 1:
+ if self.app.is_legacy is False:
+ event_pos = event.pos
+ else:
+ event_pos = (event.xdata, event.ydata)
+
+ 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])
+ click_pt = Point([pos[0], pos[1]])
+
+ self.click_points.append(
+ (
+ float('%.*f' % (self.decimals, click_pt.x)),
+ float('%.*f' % (self.decimals, click_pt.y))
+ )
+ )
+ self.check_points()
+
+ def check_points(self):
+ fid_type = self.fid_type_radio.get_value()
+
+ if len(self.click_points) == 1:
+ self.ui.bottom_left_coords_entry.set_value(self.click_points[0])
+ self.app.inform.emit(_("Click to add the last fiducial. Top Right..."))
+
+ if self.sec_position != 'no':
+ if len(self.click_points) == 2:
+ self.ui.top_right_coords_entry.set_value(self.click_points[1])
+ self.app.inform.emit(_("Click to add the second fiducial. Top Left or Bottom Right..."))
+ elif len(self.click_points) == 3:
+ self.ui.sec_points_coords_entry.set_value(self.click_points[2])
+ self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
+ self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
+ 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()
+ else:
+ if len(self.click_points) == 2:
+ self.ui.top_right_coords_entry.set_value(self.click_points[1])
+ self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
+ self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
+ 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()
+
+ def on_mouse_move(self, event):
+ pass
+
+ def replot(self, obj, run_thread=True):
+ def worker_task():
+ with self.app.proc_container.new('%s...' % _("Plotting")):
+ obj.plot()
+
+ if run_thread:
+ self.app.worker_task.emit({'fcn': worker_task, 'params': []})
+ else:
+ worker_task()
+
+ def on_exit(self):
+ # plot the object
+ for ob_name in self.copper_obj_set:
+ try:
+ copper_obj = self.app.collection.get_by_name(name=ob_name)
+ if len(self.copper_obj_set) > 1:
+ self.replot(obj=copper_obj, run_thread=False)
+ else:
+ self.replot(obj=copper_obj)
+ except (AttributeError, TypeError):
+ continue
+
+ # update the bounding box values
+ try:
+ a, b, c, d = copper_obj.bounds()
+ copper_obj.options['xmin'] = a
+ copper_obj.options['ymin'] = b
+ copper_obj.options['xmax'] = c
+ copper_obj.options['ymax'] = d
+ except Exception as e:
+ log.debug("ToolFiducials.on_exit() copper_obj bounds error --> %s" % str(e))
+
+ for ob_name in self.sm_obj_set:
+ try:
+ sm_obj = self.app.collection.get_by_name(name=ob_name)
+ if len(self.sm_obj_set) > 1:
+ self.replot(obj=sm_obj, run_thread=False)
+ else:
+ self.replot(obj=sm_obj)
+ except (AttributeError, TypeError):
+ continue
+
+ # update the bounding box values
+ try:
+ a, b, c, d = sm_obj.bounds()
+ sm_obj.options['xmin'] = a
+ sm_obj.options['ymin'] = b
+ sm_obj.options['xmax'] = c
+ sm_obj.options['ymax'] = d
+ except Exception as e:
+ log.debug("ToolFiducials.on_exit() sm_obj bounds error --> %s" % str(e))
+
+ # reset the variables
+ self.grb_object = None
+ self.sm_object = None
+
+ # Events ID
+ self.mr = None
+ # self.mm = None
+
+ # Mouse cursor positions
+ self.cursor_pos = (0, 0)
+ self.first_click = False
+
+ self.disconnect_event_handlers()
+
+ self.app.call_source = "app"
+ self.app.inform.emit('[success] %s' % _("Fiducials Tool exit."))
+
+ def connect_event_handlers(self):
+ 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)
+
+ def disconnect_event_handlers(self):
+ 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)
+
+ def flatten(self, geometry):
+ """
+ Creates a list of non-iterable linear geometry objects.
+ :param geometry: Shapely type or list or list of list of such.
+
+ Results are placed in self.flat_geometry
+ """
+
+ # ## If iterable, expand recursively.
+ try:
+ for geo in geometry:
+ if geo is not None:
+ self.flatten(geometry=geo)
+
+ # ## Not iterable, do the actual indexing and add.
+ except TypeError:
+ self.flat_geometry.append(geometry)
+
+ return self.flat_geometry
+
+
+class FidoUI:
+
+ toolName = _("Fiducials Tool")
+
+ def __init__(self, layout, app):
+ self.app = app
+ self.decimals = self.app.decimals
+ self.layout = layout
+
# ## Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
- QLabel
- {
- font-size: 16px;
- font-weight: bold;
- }
- """)
+ QLabel
+ {
+ font-size: 16px;
+ font-weight: bold;
+ }
+ """)
self.layout.addWidget(title_label)
- self.layout.addWidget(QtWidgets.QLabel(''))
+ self.layout.addWidget(QtWidgets.QLabel(""))
self.points_label = QtWidgets.QLabel('%s:' % _('Fiducials Coordinates'))
self.points_label.setToolTip(
@@ -273,11 +884,11 @@ class ToolFiducials(AppTool):
_("Will add a polygon on the copper layer to serve as fiducial.")
)
self.add_cfid_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.add_cfid_button, 11, 0, 1, 2)
separator_line_2 = QtWidgets.QFrame()
@@ -308,11 +919,11 @@ class ToolFiducials(AppTool):
"for the copper fiducial.")
)
self.add_sm_opening_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
grid_lay.addWidget(self.add_sm_opening_button, 15, 0, 1, 2)
self.layout.addStretch()
@@ -324,604 +935,29 @@ class ToolFiducials(AppTool):
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
- QPushButton
- {
- font-weight: bold;
- }
- """)
+ QPushButton
+ {
+ font-weight: bold;
+ }
+ """)
self.layout.addWidget(self.reset_button)
- # Objects involved in Copper thieving
- self.grb_object = None
- self.sm_object = None
+ # #################################### FINSIHED GUI ###########################
+ # #############################################################################
- self.copper_obj_set = set()
- self.sm_obj_set = set()
-
- # store the flattened geometry here:
- self.flat_geometry = []
-
- # Events ID
- self.mr = None
- self.mm = None
-
- # Mouse cursor positions
- self.cursor_pos = (0, 0)
- self.first_click = False
-
- self.mode_method = False
-
- # Tool properties
- self.fid_dia = None
- self.sm_opening_dia = None
-
- self.margin_val = None
- self.sec_position = None
-
- self.grb_steps_per_circle = self.app.defaults["gerber_circle_steps"]
-
- self.click_points = []
-
- # SIGNALS
- self.add_cfid_button.clicked.connect(self.add_fiducials)
- self.add_sm_opening_button.clicked.connect(self.add_soldermask_opening)
-
- self.fid_type_radio.activated_custom.connect(self.on_fiducial_type)
- self.pos_radio.activated_custom.connect(self.on_second_point)
- self.mode_radio.activated_custom.connect(self.on_method_change)
- self.reset_button.clicked.connect(self.set_tool_ui)
-
- def run(self, toggle=True):
- self.app.defaults.report_usage("ToolFiducials()")
-
- 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
+ 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:
- if self.app.ui.splitter.sizes()[0] == 0:
- self.app.ui.splitter.setSizes([1, 1])
+ self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
- AppTool.run(self)
-
- self.set_tool_ui()
-
- self.app.ui.notebook.setTabText(2, _("Fiducials Tool"))
-
- def install(self, icon=None, separator=None, **kwargs):
- AppTool.install(self, icon, separator, shortcut='Alt+F', **kwargs)
-
- def set_tool_ui(self):
- self.units = self.app.defaults['units']
- self.fid_size_entry.set_value(self.app.defaults["tools_fiducials_dia"])
- self.margin_entry.set_value(float(self.app.defaults["tools_fiducials_margin"]))
- self.mode_radio.set_value(self.app.defaults["tools_fiducials_mode"])
- self.pos_radio.set_value(self.app.defaults["tools_fiducials_second_pos"])
- self.fid_type_radio.set_value(self.app.defaults["tools_fiducials_type"])
- self.line_thickness_entry.set_value(float(self.app.defaults["tools_fiducials_line_thickness"]))
-
- self.click_points = []
- self.bottom_left_coords_entry.set_value('')
- self.top_right_coords_entry.set_value('')
- self.sec_points_coords_entry.set_value('')
-
- self.copper_obj_set = set()
- self.sm_obj_set = set()
-
- def on_second_point(self, val):
- if val == 'no':
- self.id_item_3.setFlags(QtCore.Qt.NoItemFlags)
- self.sec_point_coords_lbl.setFlags(QtCore.Qt.NoItemFlags)
- self.sec_points_coords_entry.setDisabled(True)
+ 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.id_item_3.setFlags(QtCore.Qt.ItemIsEnabled)
- self.sec_point_coords_lbl.setFlags(QtCore.Qt.ItemIsEnabled)
- self.sec_points_coords_entry.setDisabled(False)
-
- def on_method_change(self, val):
- """
- Make sure that on method change we disconnect the event handlers and reset the points storage
- :param val: value of the Radio button which trigger this method
- :return: None
- """
- if val == 'auto':
- self.click_points = []
-
- try:
- self.disconnect_event_handlers()
- except TypeError:
- pass
-
- def on_fiducial_type(self, val):
- if val == 'cross':
- self.line_thickness_label.setDisabled(False)
- self.line_thickness_entry.setDisabled(False)
- else:
- self.line_thickness_label.setDisabled(True)
- self.line_thickness_entry.setDisabled(True)
-
- def add_fiducials(self):
- self.app.call_source = "fiducials_tool"
- self.mode_method = self.mode_radio.get_value()
- self.margin_val = self.margin_entry.get_value()
- self.sec_position = self.pos_radio.get_value()
- fid_type = self.fid_type_radio.get_value()
-
- self.click_points = []
-
- # get the Gerber object on which the Fiducial 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("ToolFiducials.execute() --> %s" % str(e))
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
- return 'fail'
-
- self.copper_obj_set.add(self.grb_object.options['name'])
-
- if self.mode_method == 'auto':
- xmin, ymin, xmax, ymax = self.grb_object.bounds()
- bbox = box(xmin, ymin, xmax, ymax)
- buf_bbox = bbox.buffer(self.margin_val, self.grb_steps_per_circle, join_style=2)
- x0, y0, x1, y1 = buf_bbox.bounds
-
- self.click_points.append(
- (
- float('%.*f' % (self.decimals, x0)),
- float('%.*f' % (self.decimals, y0))
- )
- )
- self.bottom_left_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x0, self.decimals, y0))
-
- self.click_points.append(
- (
- float('%.*f' % (self.decimals, x1)),
- float('%.*f' % (self.decimals, y1))
- )
- )
- self.top_right_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y1))
-
- if self.sec_position == 'up':
- self.click_points.append(
- (
- float('%.*f' % (self.decimals, x0)),
- float('%.*f' % (self.decimals, y1))
- )
- )
- self.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x0, self.decimals, y1))
- elif self.sec_position == 'down':
- self.click_points.append(
- (
- float('%.*f' % (self.decimals, x1)),
- float('%.*f' % (self.decimals, y0))
- )
- )
- self.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y0))
-
- self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
- 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()
- else:
- self.app.inform.emit(_("Click to add first Fiducial. Bottom Left..."))
- self.bottom_left_coords_entry.set_value('')
- self.top_right_coords_entry.set_value('')
- self.sec_points_coords_entry.set_value('')
-
- self.connect_event_handlers()
-
- # To be called after clicking on the plot.
-
- def add_fiducials_geo(self, points_list, g_obj, fid_size=None, fid_type=None, line_size=None):
- """
- Add geometry to the solid_geometry of the copper Gerber object
- :param points_list: list of coordinates for the fiducials
- :param g_obj: the Gerber object where to add the geometry
- :param fid_size: the overall size of the fiducial or fiducial opening depending on the g_obj type
- :param fid_type: the type of fiducial: circular or cross
- :param line_size: the line thickenss when the fiducial type is cross
- :return:
- """
- fid_size = self.fid_size_entry.get_value() if fid_size is None else fid_size
- fid_type = 'circular' if fid_type is None else fid_type
- line_thickness = self.line_thickness_entry.get_value() if line_size is None else line_size
-
- radius = fid_size / 2.0
-
- if fid_type == 'circular':
- geo_list = [Point(pt).buffer(radius, self.grb_steps_per_circle) for pt in points_list]
-
- aperture_found = None
- for ap_id, ap_val in g_obj.apertures.items():
- if ap_val['type'] == 'C' and ap_val['size'] == fid_size:
- aperture_found = ap_id
- break
-
- if aperture_found:
- for geo in geo_list:
- dict_el = {}
- dict_el['follow'] = geo.centroid
- dict_el['solid'] = geo
- g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
- else:
- ap_keys = list(g_obj.apertures.keys())
- if ap_keys:
- new_apid = str(int(max(ap_keys)) + 1)
- else:
- new_apid = '10'
-
- g_obj.apertures[new_apid] = {}
- g_obj.apertures[new_apid]['type'] = 'C'
- g_obj.apertures[new_apid]['size'] = fid_size
- g_obj.apertures[new_apid]['geometry'] = []
-
- for geo in geo_list:
- dict_el = {}
- dict_el['follow'] = geo.centroid
- dict_el['solid'] = geo
- g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
-
- s_list = []
- if g_obj.solid_geometry:
- try:
- for poly in g_obj.solid_geometry:
- s_list.append(poly)
- except TypeError:
- s_list.append(g_obj.solid_geometry)
-
- s_list += geo_list
- g_obj.solid_geometry = MultiPolygon(s_list)
- elif fid_type == 'cross':
- geo_list = []
-
- for pt in points_list:
- x = pt[0]
- y = pt[1]
- line_geo_hor = LineString([
- (x - radius + (line_thickness / 2.0), y), (x + radius - (line_thickness / 2.0), y)
- ])
- line_geo_vert = LineString([
- (x, y - radius + (line_thickness / 2.0)), (x, y + radius - (line_thickness / 2.0))
- ])
- geo_list.append([line_geo_hor, line_geo_vert])
-
- aperture_found = None
- for ap_id, ap_val in g_obj.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 geo_list:
- geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
- geo_buff_v = geo[1].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
- geo_buff_list.append(geo_buff_h)
- geo_buff_list.append(geo_buff_v)
-
- dict_el = {}
- dict_el['follow'] = geo_buff_h.centroid
- dict_el['solid'] = geo_buff_h
- g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
- dict_el['follow'] = geo_buff_v.centroid
- dict_el['solid'] = geo_buff_v
- g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
- else:
- ap_keys = list(g_obj.apertures.keys())
- if ap_keys:
- new_apid = str(int(max(ap_keys)) + 1)
- else:
- new_apid = '10'
-
- g_obj.apertures[new_apid] = {}
- g_obj.apertures[new_apid]['type'] = 'C'
- g_obj.apertures[new_apid]['size'] = line_thickness
- g_obj.apertures[new_apid]['geometry'] = []
-
- for geo in geo_list:
- geo_buff_h = geo[0].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
- geo_buff_v = geo[1].buffer(line_thickness / 2.0, self.grb_steps_per_circle)
- geo_buff_list.append(geo_buff_h)
- geo_buff_list.append(geo_buff_v)
-
- dict_el = {}
- dict_el['follow'] = geo_buff_h.centroid
- dict_el['solid'] = geo_buff_h
- g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
- dict_el['follow'] = geo_buff_v.centroid
- dict_el['solid'] = geo_buff_v
- g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
-
- s_list = []
- if g_obj.solid_geometry:
- try:
- for poly in g_obj.solid_geometry:
- s_list.append(poly)
- except TypeError:
- s_list.append(g_obj.solid_geometry)
-
- geo_buff_list = MultiPolygon(geo_buff_list)
- geo_buff_list = geo_buff_list.buffer(0)
- for poly in geo_buff_list:
- s_list.append(poly)
- g_obj.solid_geometry = MultiPolygon(s_list)
- else:
- # chess pattern fiducial type
- geo_list = []
-
- def make_square_poly(center_pt, side_size):
- half_s = side_size / 2
- x_center = center_pt[0]
- y_center = center_pt[1]
-
- pt1 = (x_center - half_s, y_center - half_s)
- pt2 = (x_center + half_s, y_center - half_s)
- pt3 = (x_center + half_s, y_center + half_s)
- pt4 = (x_center - half_s, y_center + half_s)
-
- return Polygon([pt1, pt2, pt3, pt4, pt1])
-
- for pt in points_list:
- x = pt[0]
- y = pt[1]
- first_square = make_square_poly(center_pt=(x-fid_size/4, y+fid_size/4), side_size=fid_size/2)
- second_square = make_square_poly(center_pt=(x+fid_size/4, y-fid_size/4), side_size=fid_size/2)
- geo_list += [first_square, second_square]
-
- aperture_found = None
- new_ap_size = math.sqrt(fid_size**2 + fid_size**2)
- for ap_id, ap_val in g_obj.apertures.items():
- if ap_val['type'] == 'R' and \
- round(ap_val['size'], ndigits=self.decimals) == round(new_ap_size, ndigits=self.decimals):
- aperture_found = ap_id
- break
-
- geo_buff_list = []
- if aperture_found:
- for geo in geo_list:
- geo_buff_list.append(geo)
-
- dict_el = {}
- dict_el['follow'] = geo.centroid
- dict_el['solid'] = geo
- g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
- else:
- ap_keys = list(g_obj.apertures.keys())
- if ap_keys:
- new_apid = str(int(max(ap_keys)) + 1)
- else:
- new_apid = '10'
-
- g_obj.apertures[new_apid] = {}
- g_obj.apertures[new_apid]['type'] = 'R'
- g_obj.apertures[new_apid]['size'] = new_ap_size
- g_obj.apertures[new_apid]['width'] = fid_size
- g_obj.apertures[new_apid]['height'] = fid_size
- g_obj.apertures[new_apid]['geometry'] = []
-
- for geo in geo_list:
- geo_buff_list.append(geo)
-
- dict_el = {}
- dict_el['follow'] = geo.centroid
- dict_el['solid'] = geo
- g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
-
- s_list = []
- if g_obj.solid_geometry:
- try:
- for poly in g_obj.solid_geometry:
- s_list.append(poly)
- except TypeError:
- s_list.append(g_obj.solid_geometry)
-
- for poly in geo_buff_list:
- s_list.append(poly)
- g_obj.solid_geometry = MultiPolygon(s_list)
-
- def add_soldermask_opening(self):
- sm_opening_dia = self.fid_size_entry.get_value() * 2.0
-
- # get the Gerber object on which the Fiducial will be inserted
- selection_index = self.sm_object_combo.currentIndex()
- model_index = self.app.collection.index(selection_index, 0, self.sm_object_combo.rootModelIndex())
-
- try:
- self.sm_object = model_index.internalPointer().obj
- except Exception as e:
- log.debug("ToolFiducials.add_soldermask_opening() --> %s" % str(e))
- self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
- return 'fail'
-
- self.sm_obj_set.add(self.sm_object.options['name'])
- self.add_fiducials_geo(self.click_points, g_obj=self.sm_object, fid_size=sm_opening_dia, fid_type='circular')
-
- self.sm_object.source_file = self.app.export_gerber(obj_name=self.sm_object.options['name'], filename=None,
- local_use=self.sm_object, use_thread=False)
- self.on_exit()
-
- def on_mouse_release(self, event):
- if event.button == 1:
- if self.app.is_legacy is False:
- event_pos = event.pos
- else:
- event_pos = (event.xdata, event.ydata)
-
- 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])
- click_pt = Point([pos[0], pos[1]])
-
- self.click_points.append(
- (
- float('%.*f' % (self.decimals, click_pt.x)),
- float('%.*f' % (self.decimals, click_pt.y))
- )
- )
- self.check_points()
-
- def check_points(self):
- fid_type = self.fid_type_radio.get_value()
-
- if len(self.click_points) == 1:
- self.bottom_left_coords_entry.set_value(self.click_points[0])
- self.app.inform.emit(_("Click to add the last fiducial. Top Right..."))
-
- if self.sec_position != 'no':
- if len(self.click_points) == 2:
- self.top_right_coords_entry.set_value(self.click_points[1])
- self.app.inform.emit(_("Click to add the second fiducial. Top Left or Bottom Right..."))
- elif len(self.click_points) == 3:
- self.sec_points_coords_entry.set_value(self.click_points[2])
- self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
- self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
- 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()
- else:
- if len(self.click_points) == 2:
- self.top_right_coords_entry.set_value(self.click_points[1])
- self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
- self.add_fiducials_geo(self.click_points, g_obj=self.grb_object, fid_type=fid_type)
- 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()
-
- def on_mouse_move(self, event):
- pass
-
- def replot(self, obj, run_thread=True):
- def worker_task():
- with self.app.proc_container.new('%s...' % _("Plotting")):
- obj.plot()
-
- if run_thread:
- self.app.worker_task.emit({'fcn': worker_task, 'params': []})
- else:
- worker_task()
-
- def on_exit(self):
- # plot the object
- for ob_name in self.copper_obj_set:
- try:
- copper_obj = self.app.collection.get_by_name(name=ob_name)
- if len(self.copper_obj_set) > 1:
- self.replot(obj=copper_obj, run_thread=False)
- else:
- self.replot(obj=copper_obj)
- except (AttributeError, TypeError):
- continue
-
- # update the bounding box values
- try:
- a, b, c, d = copper_obj.bounds()
- copper_obj.options['xmin'] = a
- copper_obj.options['ymin'] = b
- copper_obj.options['xmax'] = c
- copper_obj.options['ymax'] = d
- except Exception as e:
- log.debug("ToolFiducials.on_exit() copper_obj bounds error --> %s" % str(e))
-
- for ob_name in self.sm_obj_set:
- try:
- sm_obj = self.app.collection.get_by_name(name=ob_name)
- if len(self.sm_obj_set) > 1:
- self.replot(obj=sm_obj, run_thread=False)
- else:
- self.replot(obj=sm_obj)
- except (AttributeError, TypeError):
- continue
-
- # update the bounding box values
- try:
- a, b, c, d = sm_obj.bounds()
- sm_obj.options['xmin'] = a
- sm_obj.options['ymin'] = b
- sm_obj.options['xmax'] = c
- sm_obj.options['ymax'] = d
- except Exception as e:
- log.debug("ToolFiducials.on_exit() sm_obj bounds error --> %s" % str(e))
-
- # reset the variables
- self.grb_object = None
- self.sm_object = None
-
- # Events ID
- self.mr = None
- # self.mm = None
-
- # Mouse cursor positions
- self.cursor_pos = (0, 0)
- self.first_click = False
-
- self.disconnect_event_handlers()
-
- self.app.call_source = "app"
- self.app.inform.emit('[success] %s' % _("Fiducials Tool exit."))
-
- def connect_event_handlers(self):
- 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)
-
- def disconnect_event_handlers(self):
- 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)
-
- def flatten(self, geometry):
- """
- Creates a list of non-iterable linear geometry objects.
- :param geometry: Shapely type or list or list of list of such.
-
- Results are placed in self.flat_geometry
- """
-
- # ## If iterable, expand recursively.
- try:
- for geo in geometry:
- if geo is not None:
- self.flatten(geometry=geo)
-
- # ## Not iterable, do the actual indexing and add.
- except TypeError:
- self.flat_geometry.append(geometry)
-
- return self.flat_geometry
+ self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)