ToolFiducials.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. # ##########################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # File Author: Marius Adrian Stanciu (c) #
  4. # Date: 10/25/2019 #
  5. # MIT Licence #
  6. # ##########################################################
  7. from PyQt5 import QtWidgets, QtCore
  8. from FlatCAMTool import FlatCAMTool
  9. from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable
  10. import shapely.geometry.base as base
  11. from shapely.ops import unary_union
  12. from shapely.geometry import Point
  13. from shapely.geometry import box as box
  14. import logging
  15. from copy import deepcopy
  16. import gettext
  17. import FlatCAMTranslation as fcTranslate
  18. import builtins
  19. fcTranslate.apply_language('strings')
  20. if '_' not in builtins.__dict__:
  21. _ = gettext.gettext
  22. log = logging.getLogger('base')
  23. class ToolFiducials(FlatCAMTool):
  24. toolName = _("Fiducials Tool")
  25. def __init__(self, app):
  26. FlatCAMTool.__init__(self, app)
  27. self.app = app
  28. self.canvas = self.app.plotcanvas
  29. self.decimals = 4
  30. self.units = ''
  31. # ## Title
  32. title_label = QtWidgets.QLabel("%s" % self.toolName)
  33. title_label.setStyleSheet("""
  34. QLabel
  35. {
  36. font-size: 16px;
  37. font-weight: bold;
  38. }
  39. """)
  40. self.layout.addWidget(title_label)
  41. self.layout.addWidget(QtWidgets.QLabel(''))
  42. self.points_label = QtWidgets.QLabel('<b>%s:</b>' % _('Fiducials Coordinates'))
  43. self.points_label.setToolTip(
  44. _("A table with the fiducial points coordinates,\n"
  45. "in the format (x, y).")
  46. )
  47. self.layout.addWidget(self.points_label)
  48. self.points_table = FCTable()
  49. self.points_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
  50. self.layout.addWidget(self.points_table)
  51. self.layout.addWidget(QtWidgets.QLabel(''))
  52. self.points_table.setColumnCount(3)
  53. self.points_table.setHorizontalHeaderLabels(
  54. [
  55. '#',
  56. _("Name"),
  57. _("Coordinates"),
  58. ]
  59. )
  60. self.points_table.setRowCount(3)
  61. row = 0
  62. flags = QtCore.Qt.ItemIsEnabled
  63. # BOTTOM LEFT
  64. id_item_1 = QtWidgets.QTableWidgetItem('%d' % 1)
  65. id_item_1.setFlags(flags)
  66. self.points_table.setItem(row, 0, id_item_1) # Tool name/id
  67. self.bottom_left_coords_lbl = QtWidgets.QTableWidgetItem('%s' % _('Bottom Left'))
  68. self.bottom_left_coords_lbl.setFlags(flags)
  69. self.points_table.setItem(row, 1, self.bottom_left_coords_lbl)
  70. self.bottom_left_coords_entry = EvalEntry()
  71. self.points_table.setCellWidget(row, 2, self.bottom_left_coords_entry)
  72. row += 1
  73. # TOP RIGHT
  74. id_item_2 = QtWidgets.QTableWidgetItem('%d' % 2)
  75. id_item_2.setFlags(flags)
  76. self.points_table.setItem(row, 0, id_item_2) # Tool name/id
  77. self.top_right_coords_lbl = QtWidgets.QTableWidgetItem('%s' % _('Top Right'))
  78. self.top_right_coords_lbl.setFlags(flags)
  79. self.points_table.setItem(row, 1, self.top_right_coords_lbl)
  80. self.top_right_coords_entry = EvalEntry()
  81. self.points_table.setCellWidget(row, 2, self.top_right_coords_entry)
  82. row += 1
  83. # Second Point
  84. self.id_item_3 = QtWidgets.QTableWidgetItem('%d' % 3)
  85. self.id_item_3.setFlags(flags)
  86. self.points_table.setItem(row, 0, self.id_item_3) # Tool name/id
  87. self.sec_point_coords_lbl = QtWidgets.QTableWidgetItem('%s' % _('Second Point'))
  88. self.sec_point_coords_lbl.setFlags(flags)
  89. self.points_table.setItem(row, 1, self.sec_point_coords_lbl)
  90. self.sec_points_coords_entry = EvalEntry()
  91. self.points_table.setCellWidget(row, 2, self.sec_points_coords_entry)
  92. vertical_header = self.points_table.verticalHeader()
  93. vertical_header.hide()
  94. self.points_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
  95. horizontal_header = self.points_table.horizontalHeader()
  96. horizontal_header.setMinimumSectionSize(10)
  97. horizontal_header.setDefaultSectionSize(70)
  98. self.points_table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
  99. # for x in range(4):
  100. # self.points_table.resizeColumnToContents(x)
  101. self.points_table.resizeColumnsToContents()
  102. self.points_table.resizeRowsToContents()
  103. horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
  104. horizontal_header.resizeSection(0, 20)
  105. horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed)
  106. horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
  107. self.points_table.setMinimumHeight(self.points_table.getHeight() + 2)
  108. self.points_table.setMaximumHeight(self.points_table.getHeight() + 2)
  109. # remove the frame on the QLineEdit childrens of the table
  110. for row in range(self.points_table.rowCount()):
  111. self.points_table.cellWidget(row, 2).setFrame(False)
  112. # ## Grid Layout
  113. grid_lay = QtWidgets.QGridLayout()
  114. self.layout.addLayout(grid_lay)
  115. grid_lay.setColumnStretch(0, 0)
  116. grid_lay.setColumnStretch(1, 1)
  117. self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _('Parameters'))
  118. self.param_label.setToolTip(
  119. _("Parameters used for this tool.")
  120. )
  121. grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
  122. # DIAMETER #
  123. self.dia_label = QtWidgets.QLabel('%s:' % _("Diameter"))
  124. self.dia_label.setToolTip(
  125. _("This set the fiducial diameter.\n"
  126. "The soldermask opening is double than that.")
  127. )
  128. self.dia_entry = FCDoubleSpinner()
  129. self.dia_entry.set_range(1.0000, 3.0000)
  130. self.dia_entry.set_precision(self.decimals)
  131. self.dia_entry.setWrapping(True)
  132. self.dia_entry.setSingleStep(0.1)
  133. grid_lay.addWidget(self.dia_label, 1, 0)
  134. grid_lay.addWidget(self.dia_entry, 1, 1)
  135. # MARGIN #
  136. self.margin_label = QtWidgets.QLabel('%s:' % _("Margin"))
  137. self.margin_label.setToolTip(
  138. _("Bounding box margin.")
  139. )
  140. self.margin_entry = FCDoubleSpinner()
  141. self.margin_entry.set_range(-9999.9999, 9999.9999)
  142. self.margin_entry.set_precision(self.decimals)
  143. self.margin_entry.setSingleStep(0.1)
  144. grid_lay.addWidget(self.margin_label, 2, 0)
  145. grid_lay.addWidget(self.margin_entry, 2, 1)
  146. # Mode #
  147. self.mode_radio = RadioSet([
  148. {'label': _('Auto'), 'value': 'auto'},
  149. {"label": _("Manual"), "value": "manual"}
  150. ], stretch=False)
  151. self.mode_label = QtWidgets.QLabel(_("Mode:"))
  152. self.mode_label.setToolTip(
  153. _("- 'Auto' - automatic placement of fiducials in the corners of the bounding box.\n "
  154. "- 'Manual' - manual placement of fiducials.")
  155. )
  156. grid_lay.addWidget(self.mode_label, 3, 0)
  157. grid_lay.addWidget(self.mode_radio, 3, 1)
  158. # Position for second fiducial #
  159. self.pos_radio = RadioSet([
  160. {'label': _('Up'), 'value': 'up'},
  161. {"label": _("Down"), "value": "down"},
  162. {"label": _("None"), "value": "no"}
  163. ], stretch=False)
  164. self.pos_label = QtWidgets.QLabel('%s:' % _("Second fiducial"))
  165. self.pos_label.setToolTip(
  166. _("The position for the second fiducial.\n"
  167. "- 'Up' - the order is: bottom-left, top-left, top-right.\n "
  168. "- 'Down' - the order is: bottom-left, bottom-right, top-right.\n"
  169. "- 'None' - there is no second fiducial. The order is: bottom-left, top-right.")
  170. )
  171. grid_lay.addWidget(self.pos_label, 4, 0)
  172. grid_lay.addWidget(self.pos_radio, 4, 1)
  173. separator_line = QtWidgets.QFrame()
  174. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  175. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  176. grid_lay.addWidget(separator_line, 5, 0, 1, 2)
  177. # Copper Gerber object
  178. self.grb_object_combo = QtWidgets.QComboBox()
  179. self.grb_object_combo.setModel(self.app.collection)
  180. self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  181. self.grb_object_combo.setCurrentIndex(1)
  182. self.grbobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("Copper Gerber"))
  183. self.grbobj_label.setToolTip(
  184. _("Gerber Object to which will be added a copper thieving.")
  185. )
  186. grid_lay.addWidget(self.grbobj_label, 6, 0, 1, 2)
  187. grid_lay.addWidget(self.grb_object_combo, 7, 0, 1, 2)
  188. # ## Insert Copper Fiducial
  189. self.add_cfid_button = QtWidgets.QPushButton(_("Add Fiducial"))
  190. self.add_cfid_button.setToolTip(
  191. _("Will add a polygon on the copper layer to serve as fiducial.")
  192. )
  193. grid_lay.addWidget(self.add_cfid_button, 8, 0, 1, 2)
  194. separator_line_1 = QtWidgets.QFrame()
  195. separator_line_1.setFrameShape(QtWidgets.QFrame.HLine)
  196. separator_line_1.setFrameShadow(QtWidgets.QFrame.Sunken)
  197. grid_lay.addWidget(separator_line_1, 9, 0, 1, 2)
  198. # Soldermask Gerber object #
  199. self.sm_object_label = QtWidgets.QLabel('<b>%s:</b>' % _("Soldermask Gerber"))
  200. self.sm_object_label.setToolTip(
  201. _("The Soldermask Gerber object.")
  202. )
  203. self.sm_object_combo = QtWidgets.QComboBox()
  204. self.sm_object_combo.setModel(self.app.collection)
  205. self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  206. self.sm_object_combo.setCurrentIndex(1)
  207. grid_lay.addWidget(self.sm_object_label, 10, 0, 1, 2)
  208. grid_lay.addWidget(self.sm_object_combo, 11, 0, 1, 2)
  209. # ## Insert Soldermask opening for Fiducial
  210. self.add_sm_opening_button = QtWidgets.QPushButton(_("Add Soldermask Opening"))
  211. self.add_sm_opening_button.setToolTip(
  212. _("Will add a polygon on the soldermask layer\n"
  213. "to serve as fiducial opening.\n"
  214. "The diameter is always double of the diameter\n"
  215. "for the copper fiducial.")
  216. )
  217. grid_lay.addWidget(self.add_sm_opening_button, 12, 0, 1, 2)
  218. self.layout.addStretch()
  219. # Objects involved in Copper thieving
  220. self.grb_object = None
  221. self.sm_object = None
  222. self.copper_obj_set = set()
  223. self.sm_obj_set = set()
  224. # store the flattened geometry here:
  225. self.flat_geometry = list()
  226. # Events ID
  227. self.mr = None
  228. self.mm = None
  229. # Mouse cursor positions
  230. self.cursor_pos = (0, 0)
  231. self.first_click = False
  232. self.mode_method = False
  233. # Tool properties
  234. self.fid_dia = None
  235. self.sm_opening_dia = None
  236. self.margin_val = None
  237. self.sec_position = None
  238. self.geo_steps_per_circle = 128
  239. self.click_points = list()
  240. # SIGNALS
  241. self.add_cfid_button.clicked.connect(self.add_fiducials)
  242. self.add_sm_opening_button.clicked.connect(self.add_soldermask_opening)
  243. # self.reference_radio.group_toggle_fn = self.on_toggle_reference
  244. self.pos_radio.activated_custom.connect(self.on_second_point)
  245. self.mode_radio.activated_custom.connect(self.on_method_change)
  246. def run(self, toggle=True):
  247. self.app.report_usage("ToolFiducials()")
  248. if toggle:
  249. # if the splitter is hidden, display it, else hide it but only if the current widget is the same
  250. if self.app.ui.splitter.sizes()[0] == 0:
  251. self.app.ui.splitter.setSizes([1, 1])
  252. else:
  253. try:
  254. if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
  255. # if tab is populated with the tool but it does not have the focus, focus on it
  256. if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
  257. # focus on Tool Tab
  258. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  259. else:
  260. self.app.ui.splitter.setSizes([0, 1])
  261. except AttributeError:
  262. pass
  263. else:
  264. if self.app.ui.splitter.sizes()[0] == 0:
  265. self.app.ui.splitter.setSizes([1, 1])
  266. FlatCAMTool.run(self)
  267. self.set_tool_ui()
  268. self.app.ui.notebook.setTabText(2, _("Fiducials Tool"))
  269. def install(self, icon=None, separator=None, **kwargs):
  270. FlatCAMTool.install(self, icon, separator, shortcut='ALT+J', **kwargs)
  271. def set_tool_ui(self):
  272. self.units = self.app.ui.general_defaults_form.general_app_group.units_radio.get_value()
  273. # self.mode_radio.set_value(float(self.app.defaults["tools_fiducials_mode"]))
  274. # self.margin_entry.set_value(float(self.app.defaults["tools_fiducials_margin"]))
  275. # self.dia_entry.set_value(self.app.defaults["tools_fiducials_dia"])
  276. self.click_points = list()
  277. self.bottom_left_coords_entry.set_value('')
  278. self.top_right_coords_entry.set_value('')
  279. self.sec_points_coords_entry.set_value('')
  280. self.copper_obj_set = set()
  281. self.sm_obj_set = set()
  282. def on_second_point(self, val):
  283. if val == 'no':
  284. self.id_item_3.setFlags(QtCore.Qt.NoItemFlags)
  285. self.sec_point_coords_lbl.setFlags(QtCore.Qt.NoItemFlags)
  286. self.sec_points_coords_entry.setDisabled(True)
  287. else:
  288. self.id_item_3.setFlags(QtCore.Qt.ItemIsEnabled)
  289. self.sec_point_coords_lbl.setFlags(QtCore.Qt.ItemIsEnabled)
  290. self.sec_points_coords_entry.setDisabled(False)
  291. def on_method_change(self, val):
  292. """
  293. Make sure that on method change we disconnect the event handlers and reset the points storage
  294. :param val: value of the Radio button which trigger this method
  295. :return: None
  296. """
  297. if val == 'auto':
  298. self.click_points = list()
  299. try:
  300. self.disconnect_event_handlers()
  301. except TypeError:
  302. pass
  303. def add_fiducials(self):
  304. self.app.call_source = "fiducials_tool"
  305. self.mode_method = self.mode_radio.get_value()
  306. self.margin_val = self.margin_entry.get_value()
  307. self.sec_position = self.pos_radio.get_value()
  308. # get the Gerber object on which the Fiducial will be inserted
  309. selection_index = self.grb_object_combo.currentIndex()
  310. model_index = self.app.collection.index(selection_index, 0, self.grb_object_combo.rootModelIndex())
  311. try:
  312. self.grb_object = model_index.internalPointer().obj
  313. except Exception as e:
  314. log.debug("ToolFiducials.execute() --> %s" % str(e))
  315. self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
  316. return 'fail'
  317. self.copper_obj_set.add(self.grb_object.options['name'])
  318. if self.mode_method == 'auto':
  319. xmin, ymin, xmax, ymax = self.grb_object.bounds()
  320. bbox = box(xmin, ymin, xmax, ymax)
  321. buf_bbox = bbox.buffer(self.margin_val, join_style=2)
  322. x0, y0, x1, y1 = buf_bbox.bounds
  323. self.click_points.append(
  324. (
  325. float('%.*f' % (self.decimals, x0)),
  326. float('%.*f' % (self.decimals, y0))
  327. )
  328. )
  329. self.bottom_left_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x0, self.decimals, y0))
  330. self.click_points.append(
  331. (
  332. float('%.*f' % (self.decimals, x1)),
  333. float('%.*f' % (self.decimals, y1))
  334. )
  335. )
  336. self.top_right_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y1))
  337. if self.sec_position == 'up':
  338. self.click_points.append(
  339. (
  340. float('%.*f' % (self.decimals, x0)),
  341. float('%.*f' % (self.decimals, y1))
  342. )
  343. )
  344. self.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x0, self.decimals, y1))
  345. elif self.sec_position == 'down':
  346. self.click_points.append(
  347. (
  348. float('%.*f' % (self.decimals, x1)),
  349. float('%.*f' % (self.decimals, y0))
  350. )
  351. )
  352. self.sec_points_coords_entry.set_value('(%.*f, %.*f)' % (self.decimals, x1, self.decimals, y0))
  353. self.add_fiducials_geo(self.click_points)
  354. self.on_exit()
  355. else:
  356. self.app.inform.emit(_("Click to add first Fiducial. Bottom Left..."))
  357. self.bottom_left_coords_entry.set_value('')
  358. self.top_right_coords_entry.set_value('')
  359. self.sec_points_coords_entry.set_value('')
  360. if self.app.is_legacy is False:
  361. self.app.plotcanvas.graph_event_disconnect('mouse_press', self.app.on_mouse_click_over_plot)
  362. # self.app.plotcanvas.graph_event_disconnect('mouse_move', self.app.on_mouse_move_over_plot)
  363. self.app.plotcanvas.graph_event_disconnect('mouse_release', self.app.on_mouse_click_release_over_plot)
  364. else:
  365. self.app.plotcanvas.graph_event_disconnect(self.app.mp)
  366. self.app.plotcanvas.graph_event_disconnect(self.app.mm)
  367. self.app.plotcanvas.graph_event_disconnect(self.app.mr)
  368. self.mr = self.app.plotcanvas.graph_event_connect('mouse_release', self.on_mouse_release)
  369. # self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
  370. # To be called after clicking on the plot.
  371. def add_fiducials_geo(self, points_list):
  372. """
  373. Add geometry to the solid_geometry of the copper Gerber object
  374. :param points_list: list of coordinates for the fiducials
  375. :return:
  376. """
  377. self.fid_dia = self.dia_entry.get_value()
  378. radius = self.fid_dia / 2.0
  379. geo_list = [Point(pt).buffer(radius) for pt in points_list]
  380. aperture_found = None
  381. for ap_id, ap_val in self.grb_object.apertures.items():
  382. if ap_val['type'] == 'C' and ap_val['size'] == self.fid_dia:
  383. aperture_found = ap_id
  384. break
  385. if aperture_found:
  386. for geo in geo_list:
  387. dict_el = dict()
  388. dict_el['follow'] = geo.centroid
  389. dict_el['solid'] = geo
  390. self.grb_object.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
  391. else:
  392. new_apid = int(max(list(self.grb_object.apertures.keys()))) + 1
  393. self.grb_object.apertures[new_apid] = dict()
  394. self.grb_object.apertures[new_apid]['type'] = 'C'
  395. self.grb_object.apertures[new_apid]['size'] = self.fid_dia
  396. self.grb_object.apertures[new_apid]['geometry'] = list()
  397. for geo in geo_list:
  398. dict_el = dict()
  399. dict_el['follow'] = geo.centroid
  400. dict_el['solid'] = geo
  401. self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
  402. if self.grb_object.solid_geometry:
  403. self.grb_object.solid_geometry = [self.grb_object.solid_geometry, geo_list]
  404. def add_soldermask_opening(self):
  405. self.sm_opening_dia = self.dia_entry.get_value() * 2.0
  406. # get the Gerber object on which the Fiducial will be inserted
  407. selection_index = self.grb_object_combo.currentIndex()
  408. model_index = self.app.collection.index(selection_index, 0, self.grb_object_combo.rootModelIndex())
  409. try:
  410. self.sm_object = model_index.internalPointer().obj
  411. except Exception as e:
  412. log.debug("ToolFiducials.add_soldermask_opening() --> %s" % str(e))
  413. self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
  414. return 'fail'
  415. self.sm_obj_set.add(self.sm_object.options['name'])
  416. def on_mouse_release(self, event):
  417. if event.button == 1:
  418. if self.app.is_legacy is False:
  419. event_pos = event.pos
  420. else:
  421. event_pos = (event.xdata, event.ydata)
  422. pos_canvas = self.canvas.translate_coords(event_pos)
  423. if self.app.grid_status():
  424. pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
  425. else:
  426. pos = (pos_canvas[0], pos_canvas[1])
  427. click_pt = Point([pos[0], pos[1]])
  428. self.click_points.append(
  429. (
  430. float('%.*f' % (self.decimals, click_pt.x)),
  431. float('%.*f' % (self.decimals, click_pt.y))
  432. )
  433. )
  434. self.check_points()
  435. def check_points(self):
  436. if len(self.click_points) == 1:
  437. self.bottom_left_coords_entry.set_value(self.click_points[0])
  438. self.app.inform.emit(_("Click to add the last fiducial. Top Right..."))
  439. if self.sec_position != 'no':
  440. if len(self.click_points) == 2:
  441. self.top_right_coords_entry.set_value(self.click_points[1])
  442. self.app.inform.emit(_("Click to add the second fiducial. Top Left or Bottom Right..."))
  443. elif len(self.click_points) == 3:
  444. self.sec_points_coords_entry.set_value(self.click_points[2])
  445. self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
  446. self.add_fiducials_geo(self.click_points)
  447. self.on_exit()
  448. else:
  449. if len(self.click_points) == 2:
  450. self.sec_points_coords_entry.set_value(self.click_points[2])
  451. self.app.inform.emit('[success] %s' % _("Done. All fiducials have been added."))
  452. self.add_fiducials_geo(self.click_points)
  453. self.on_exit()
  454. def on_mouse_move(self, event):
  455. pass
  456. def replot(self, obj):
  457. def worker_task():
  458. with self.app.proc_container.new('%s...' % _("Plotting")):
  459. obj.plot()
  460. self.app.worker_task.emit({'fcn': worker_task, 'params': []})
  461. def on_exit(self):
  462. # plot the object
  463. for ob_name in self.copper_obj_set:
  464. try:
  465. copper_obj = self.app.collection.get_by_name(name=ob_name)
  466. self.replot(obj=copper_obj)
  467. except (AttributeError, TypeError):
  468. continue
  469. # update the bounding box values
  470. try:
  471. a, b, c, d = copper_obj.bounds()
  472. copper_obj.options['xmin'] = a
  473. copper_obj.options['ymin'] = b
  474. copper_obj.options['xmax'] = c
  475. copper_obj.options['ymax'] = d
  476. except Exception as e:
  477. log.debug("ToolFiducials.on_exit() copper_obj bounds error --> %s" % str(e))
  478. for ob_name in self.sm_obj_set:
  479. try:
  480. sm_obj = self.app.collection.get_by_name(name=ob_name)
  481. self.replot(obj=sm_obj)
  482. except (AttributeError, TypeError):
  483. continue
  484. # update the bounding box values
  485. try:
  486. a, b, c, d = sm_obj.bounds()
  487. sm_obj.options['xmin'] = a
  488. sm_obj.options['ymin'] = b
  489. sm_obj.options['xmax'] = c
  490. sm_obj.options['ymax'] = d
  491. except Exception as e:
  492. log.debug("ToolFiducials.on_exit() sm_obj bounds error --> %s" % str(e))
  493. # reset the variables
  494. self.grb_object = None
  495. self.sm_object = None
  496. # Events ID
  497. self.mr = None
  498. # self.mm = None
  499. # Mouse cursor positions
  500. self.cursor_pos = (0, 0)
  501. self.first_click = False
  502. # if True it means we exited from tool in the middle of fiducials adding
  503. if len(self.click_points) not in [0, 3]:
  504. self.click_points = list()
  505. if self.mode_method == 'manual' and len(self.click_points) not in [0, 3]:
  506. self.disconnect_event_handlers()
  507. self.app.call_source = "app"
  508. self.app.inform.emit('[success] %s' % _("Fiducials Tool exit."))
  509. def disconnect_event_handlers(self):
  510. if self.app.is_legacy is False:
  511. self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release)
  512. self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move)
  513. else:
  514. self.app.plotcanvas.graph_event_disconnect(self.mr)
  515. self.app.plotcanvas.graph_event_disconnect(self.mm)
  516. self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press',
  517. self.app.on_mouse_click_over_plot)
  518. # self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move',
  519. # self.app.on_mouse_move_over_plot)
  520. self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release',
  521. self.app.on_mouse_click_release_over_plot)
  522. def flatten(self, geometry):
  523. """
  524. Creates a list of non-iterable linear geometry objects.
  525. :param geometry: Shapely type or list or list of list of such.
  526. Results are placed in self.flat_geometry
  527. """
  528. # ## If iterable, expand recursively.
  529. try:
  530. for geo in geometry:
  531. if geo is not None:
  532. self.flatten(geometry=geo)
  533. # ## Not iterable, do the actual indexing and add.
  534. except TypeError:
  535. self.flat_geometry.append(geometry)
  536. return self.flat_geometry