ToolCorners.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. # ##########################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # File Author: Marius Adrian Stanciu (c) #
  4. # Date: 5/17/2020 #
  5. # MIT Licence #
  6. # ##########################################################
  7. from PyQt5 import QtWidgets, QtCore, QtGui
  8. from appTool import AppTool
  9. from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, FCButton, RadioSet, FCLabel
  10. from shapely.geometry import MultiPolygon, LineString, Point
  11. from shapely.ops import unary_union
  12. from copy import deepcopy
  13. import logging
  14. import gettext
  15. import appTranslation as fcTranslate
  16. import builtins
  17. fcTranslate.apply_language('strings')
  18. if '_' not in builtins.__dict__:
  19. _ = gettext.gettext
  20. log = logging.getLogger('base')
  21. class ToolCorners(AppTool):
  22. def __init__(self, app):
  23. AppTool.__init__(self, app)
  24. self.app = app
  25. self.canvas = self.app.plotcanvas
  26. self.decimals = self.app.decimals
  27. self.units = ''
  28. # here we store the locations of the selected corners
  29. self.points = {}
  30. # #############################################################################
  31. # ######################### Tool GUI ##########################################
  32. # #############################################################################
  33. self.ui = CornersUI(layout=self.layout, app=self.app)
  34. self.toolName = self.ui.toolName
  35. # Objects involved in Copper thieving
  36. self.grb_object = None
  37. # store the flattened geometry here:
  38. self.flat_geometry = []
  39. # Tool properties
  40. self.fid_dia = None
  41. self.grb_steps_per_circle = self.app.defaults["gerber_circle_steps"]
  42. # SIGNALS
  43. self.ui.add_marker_button.clicked.connect(self.add_markers)
  44. self.ui.toggle_all_cb.toggled.connect(self.on_toggle_all)
  45. self.ui.drill_button.clicked.connect(self.on_create_drill_object)
  46. def run(self, toggle=True):
  47. self.app.defaults.report_usage("ToolCorners()")
  48. if toggle:
  49. # if the splitter is hidden, display it, else hide it but only if the current widget is the same
  50. if self.app.ui.splitter.sizes()[0] == 0:
  51. self.app.ui.splitter.setSizes([1, 1])
  52. else:
  53. try:
  54. if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
  55. # if tab is populated with the tool but it does not have the focus, focus on it
  56. if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
  57. # focus on Tool Tab
  58. self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
  59. else:
  60. self.app.ui.splitter.setSizes([0, 1])
  61. except AttributeError:
  62. pass
  63. else:
  64. if self.app.ui.splitter.sizes()[0] == 0:
  65. self.app.ui.splitter.setSizes([1, 1])
  66. AppTool.run(self)
  67. self.set_tool_ui()
  68. self.app.ui.notebook.setTabText(2, _("Corners Tool"))
  69. def install(self, icon=None, separator=None, **kwargs):
  70. AppTool.install(self, icon, separator, shortcut='Alt+M', **kwargs)
  71. def set_tool_ui(self):
  72. self.units = self.app.defaults['units']
  73. self.ui.thick_entry.set_value(self.app.defaults["tools_corners_thickness"])
  74. self.ui.l_entry.set_value(float(self.app.defaults["tools_corners_length"]))
  75. self.ui.margin_entry.set_value(float(self.app.defaults["tools_corners_margin"]))
  76. self.ui.toggle_all_cb.set_value(False)
  77. self.ui.type_radio.set_value(self.app.defaults["tools_corners_type"])
  78. self.ui.drill_dia_entry.set_value(self.app.defaults["tools_corners_drill_dia"])
  79. def on_toggle_all(self, val):
  80. self.ui.bl_cb.set_value(val)
  81. self.ui.br_cb.set_value(val)
  82. self.ui.tl_cb.set_value(val)
  83. self.ui.tr_cb.set_value(val)
  84. def add_markers(self):
  85. self.app.call_source = "corners_tool"
  86. tl_state = self.ui.tl_cb.get_value()
  87. tr_state = self.ui.tr_cb.get_value()
  88. bl_state = self.ui.bl_cb.get_value()
  89. br_state = self.ui.br_cb.get_value()
  90. # get the Gerber object on which the corner marker will be inserted
  91. selection_index = self.ui.object_combo.currentIndex()
  92. model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
  93. try:
  94. self.grb_object = model_index.internalPointer().obj
  95. except Exception as e:
  96. log.debug("ToolCorners.add_markers() --> %s" % str(e))
  97. self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
  98. self.app.call_source = "app"
  99. return
  100. xmin, ymin, xmax, ymax = self.grb_object.bounds()
  101. self.points = {}
  102. if tl_state:
  103. self.points['tl'] = (xmin, ymax)
  104. if tr_state:
  105. self.points['tr'] = (xmax, ymax)
  106. if bl_state:
  107. self.points['bl'] = (xmin, ymin)
  108. if br_state:
  109. self.points['br'] = (xmax, ymin)
  110. ret_val = self.add_corners_geo(self.points, g_obj=self.grb_object)
  111. self.app.call_source = "app"
  112. if ret_val == 'fail':
  113. self.app.call_source = "app"
  114. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
  115. return
  116. self.on_exit(ret_val)
  117. def add_corners_geo(self, points_storage, g_obj):
  118. """
  119. Add geometry to the solid_geometry of the copper Gerber object
  120. :param points_storage: a dictionary holding the points where to add corners
  121. :param g_obj: the Gerber object where to add the geometry
  122. :return: None
  123. """
  124. marker_type = self.ui.type_radio.get_value()
  125. line_thickness = self.ui.thick_entry.get_value()
  126. margin = self.ui.margin_entry.get_value()
  127. line_length = self.ui.l_entry.get_value() / 2.0
  128. geo_list = []
  129. if not points_storage:
  130. self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
  131. return 'fail'
  132. for key in points_storage:
  133. if key == 'tl':
  134. pt = points_storage[key]
  135. x = pt[0] - margin - line_thickness / 2.0
  136. y = pt[1] + margin + line_thickness / 2.0
  137. if marker_type == 's':
  138. line_geo_hor = LineString([
  139. (x, y), (x + line_length, y)
  140. ])
  141. line_geo_vert = LineString([
  142. (x, y), (x, y - line_length)
  143. ])
  144. else:
  145. line_geo_hor = LineString([
  146. (x - line_length, y), (x + line_length, y)
  147. ])
  148. line_geo_vert = LineString([
  149. (x, y + line_length), (x, y - line_length)
  150. ])
  151. geo_list.append(line_geo_hor)
  152. geo_list.append(line_geo_vert)
  153. if key == 'tr':
  154. pt = points_storage[key]
  155. x = pt[0] + margin + line_thickness / 2.0
  156. y = pt[1] + margin + line_thickness / 2.0
  157. if marker_type == 's':
  158. line_geo_hor = LineString([
  159. (x, y), (x - line_length, y)
  160. ])
  161. line_geo_vert = LineString([
  162. (x, y), (x, y - line_length)
  163. ])
  164. else:
  165. line_geo_hor = LineString([
  166. (x + line_length, y), (x - line_length, y)
  167. ])
  168. line_geo_vert = LineString([
  169. (x, y + line_length), (x, y - line_length)
  170. ])
  171. geo_list.append(line_geo_hor)
  172. geo_list.append(line_geo_vert)
  173. if key == 'bl':
  174. pt = points_storage[key]
  175. x = pt[0] - margin - line_thickness / 2.0
  176. y = pt[1] - margin - line_thickness / 2.0
  177. if marker_type == 's':
  178. line_geo_hor = LineString([
  179. (x, y), (x + line_length, y)
  180. ])
  181. line_geo_vert = LineString([
  182. (x, y), (x, y + line_length)
  183. ])
  184. else:
  185. line_geo_hor = LineString([
  186. (x - line_length, y), (x + line_length, y)
  187. ])
  188. line_geo_vert = LineString([
  189. (x, y - line_length), (x, y + line_length)
  190. ])
  191. geo_list.append(line_geo_hor)
  192. geo_list.append(line_geo_vert)
  193. if key == 'br':
  194. pt = points_storage[key]
  195. x = pt[0] + margin + line_thickness / 2.0
  196. y = pt[1] - margin - line_thickness / 2.0
  197. if marker_type == 's':
  198. line_geo_hor = LineString([
  199. (x, y), (x - line_length, y)
  200. ])
  201. line_geo_vert = LineString([
  202. (x, y), (x, y + line_length)
  203. ])
  204. else:
  205. line_geo_hor = LineString([
  206. (x + line_length, y), (x - line_length, y)
  207. ])
  208. line_geo_vert = LineString([
  209. (x, y - line_length), (x, y + line_length)
  210. ])
  211. geo_list.append(line_geo_hor)
  212. geo_list.append(line_geo_vert)
  213. new_apertures = deepcopy(g_obj.apertures)
  214. aperture_found = None
  215. for ap_id, ap_val in new_apertures.items():
  216. if ap_val['type'] == 'C' and ap_val['size'] == line_thickness:
  217. aperture_found = ap_id
  218. break
  219. geo_buff_list = []
  220. if aperture_found:
  221. for geo in geo_list:
  222. geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=2)
  223. geo_buff_list.append(geo_buff)
  224. dict_el = {'follow': geo, 'solid': geo_buff}
  225. new_apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
  226. else:
  227. ap_keys = list(new_apertures.keys())
  228. if ap_keys:
  229. new_apid = str(int(max(ap_keys)) + 1)
  230. else:
  231. new_apid = '10'
  232. new_apertures[new_apid] = {}
  233. new_apertures[new_apid]['type'] = 'C'
  234. new_apertures[new_apid]['size'] = line_thickness
  235. new_apertures[new_apid]['geometry'] = []
  236. for geo in geo_list:
  237. geo_buff = geo.buffer(line_thickness / 2.0, resolution=self.grb_steps_per_circle, join_style=3)
  238. geo_buff_list.append(geo_buff)
  239. dict_el = {'follow': geo, 'solid': geo_buff}
  240. new_apertures[new_apid]['geometry'].append(deepcopy(dict_el))
  241. s_list = []
  242. if g_obj.solid_geometry:
  243. try:
  244. for poly in g_obj.solid_geometry:
  245. s_list.append(poly)
  246. except TypeError:
  247. s_list.append(g_obj.solid_geometry)
  248. geo_buff_list = MultiPolygon(geo_buff_list)
  249. geo_buff_list = geo_buff_list.buffer(0)
  250. try:
  251. for poly in geo_buff_list:
  252. s_list.append(poly)
  253. except TypeError:
  254. s_list.append(geo_buff_list)
  255. outname = '%s_%s' % (str(self.grb_object.options['name']), 'corners')
  256. def initialize(grb_obj, app_obj):
  257. grb_obj.options = {}
  258. for opt in g_obj.options:
  259. if opt != 'name':
  260. grb_obj.options[opt] = deepcopy(g_obj.options[opt])
  261. grb_obj.options['name'] = outname
  262. grb_obj.multitool = False
  263. grb_obj.multigeo = False
  264. grb_obj.follow = deepcopy(g_obj.follow)
  265. grb_obj.apertures = new_apertures
  266. grb_obj.solid_geometry = unary_union(s_list)
  267. grb_obj.follow_geometry = deepcopy(g_obj.follow_geometry) + geo_list
  268. grb_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None, local_use=grb_obj,
  269. use_thread=False)
  270. ret = self.app.app_obj.new_object('gerber', outname, initialize, plot=True)
  271. return ret
  272. def on_create_drill_object(self):
  273. self.app.call_source = "corners_tool"
  274. tooldia = self.ui.drill_dia_entry.get_value()
  275. if tooldia == 0:
  276. self.app.inform.emit('[WARNING_NOTCL] %s %s' % (_("Cancelled."), _("The tool diameter is zero.")))
  277. return
  278. line_thickness = self.ui.thick_entry.get_value()
  279. margin = self.ui.margin_entry.get_value()
  280. tl_state = self.ui.tl_cb.get_value()
  281. tr_state = self.ui.tr_cb.get_value()
  282. bl_state = self.ui.bl_cb.get_value()
  283. br_state = self.ui.br_cb.get_value()
  284. if not tl_state and not tr_state and not bl_state and not br_state:
  285. self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
  286. # get the Gerber object on which the corner marker will be inserted
  287. selection_index = self.ui.object_combo.currentIndex()
  288. model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
  289. try:
  290. self.grb_object = model_index.internalPointer().obj
  291. except Exception as e:
  292. log.debug("ToolCorners.add_markers() --> %s" % str(e))
  293. self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
  294. self.app.call_source = "app"
  295. return
  296. xmin, ymin, xmax, ymax = self.grb_object.bounds()
  297. # list of (x,y) tuples. Store here the drill coordinates
  298. drill_list = []
  299. if tl_state:
  300. x = xmin - margin - line_thickness / 2.0
  301. y = ymax + margin + line_thickness / 2.0
  302. drill_list.append(
  303. Point((x, y))
  304. )
  305. if tr_state:
  306. x = xmax + margin + line_thickness / 2.0
  307. y = ymax + margin + line_thickness / 2.0
  308. drill_list.append(
  309. Point((x, y))
  310. )
  311. if bl_state:
  312. x = xmin - margin - line_thickness / 2.0
  313. y = ymin - margin - line_thickness / 2.0
  314. drill_list.append(
  315. Point((x, y))
  316. )
  317. if br_state:
  318. x = xmax + margin + line_thickness / 2.0
  319. y = ymin - margin - line_thickness / 2.0
  320. drill_list.append(
  321. Point((x, y))
  322. )
  323. tools = {1: {}}
  324. tools[1]["tooldia"] = tooldia
  325. tools[1]['drills'] = drill_list
  326. tools[1]['solid_geometry'] = []
  327. def obj_init(obj_inst, app_inst):
  328. obj_inst.tools = deepcopy(tools)
  329. obj_inst.create_geometry()
  330. obj_inst.source_file = app_inst.f_handlers.export_excellon(obj_name=obj_inst.options['name'],
  331. local_use=obj_inst,
  332. filename=None,
  333. use_thread=False)
  334. outname = '%s_%s' % (str(self.grb_object.options['name']), 'corner_drills')
  335. ret_val = self.app.app_obj.new_object("excellon", outname, obj_init)
  336. self.app.call_source = "app"
  337. if not ret_val == 'fail':
  338. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
  339. else:
  340. self.app.inform.emit('[success] %s' % _("Excellon object with corner drills created."))
  341. def replot(self, obj, run_thread=True):
  342. def worker_task():
  343. with self.app.proc_container.new('%s...' % _("Plotting")):
  344. obj.plot()
  345. self.app.app_obj.object_plotted.emit(obj)
  346. if run_thread:
  347. self.app.worker_task.emit({'fcn': worker_task, 'params': []})
  348. else:
  349. worker_task()
  350. def on_exit(self, corner_gerber_obj=None):
  351. # plot the object
  352. if corner_gerber_obj:
  353. try:
  354. for ob in corner_gerber_obj:
  355. self.replot(obj=ob)
  356. except (AttributeError, TypeError):
  357. self.replot(obj=corner_gerber_obj)
  358. except Exception:
  359. return
  360. # update the bounding box values
  361. try:
  362. a, b, c, d = self.grb_object.bounds()
  363. self.grb_object.options['xmin'] = a
  364. self.grb_object.options['ymin'] = b
  365. self.grb_object.options['xmax'] = c
  366. self.grb_object.options['ymax'] = d
  367. except Exception as e:
  368. log.debug("ToolCorners.on_exit() copper_obj bounds error --> %s" % str(e))
  369. self.app.call_source = "app"
  370. self.app.inform.emit('[success] %s' % _("A Gerber object with corner markers was created."))
  371. class CornersUI:
  372. toolName = _("Corner Markers Tool")
  373. def __init__(self, layout, app):
  374. self.app = app
  375. self.decimals = self.app.decimals
  376. self.layout = layout
  377. # ## Title
  378. title_label = FCLabel("%s" % self.toolName)
  379. title_label.setStyleSheet("""
  380. QLabel
  381. {
  382. font-size: 16px;
  383. font-weight: bold;
  384. }
  385. """)
  386. self.layout.addWidget(title_label)
  387. self.layout.addWidget(FCLabel(""))
  388. # Gerber object #
  389. self.object_label = FCLabel('<b>%s:</b>' % _("GERBER"))
  390. self.object_label.setToolTip(
  391. _("The Gerber object to which will be added corner markers.")
  392. )
  393. self.object_combo = FCComboBox()
  394. self.object_combo.setModel(self.app.collection)
  395. self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  396. self.object_combo.is_last = True
  397. self.object_combo.obj_type = "Gerber"
  398. self.layout.addWidget(self.object_label)
  399. self.layout.addWidget(self.object_combo)
  400. separator_line = QtWidgets.QFrame()
  401. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  402. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  403. self.layout.addWidget(separator_line)
  404. self.points_label = FCLabel('<b>%s:</b>' % _('Locations'))
  405. self.points_label.setToolTip(
  406. _("Locations where to place corner markers.")
  407. )
  408. self.layout.addWidget(self.points_label)
  409. # ## Grid Layout
  410. grid_loc = QtWidgets.QGridLayout()
  411. self.layout.addLayout(grid_loc)
  412. # TOP LEFT
  413. self.tl_cb = FCCheckBox(_("Top Left"))
  414. grid_loc.addWidget(self.tl_cb, 0, 0)
  415. # TOP RIGHT
  416. self.tr_cb = FCCheckBox(_("Top Right"))
  417. grid_loc.addWidget(self.tr_cb, 0, 1)
  418. # BOTTOM LEFT
  419. self.bl_cb = FCCheckBox(_("Bottom Left"))
  420. grid_loc.addWidget(self.bl_cb, 1, 0)
  421. # BOTTOM RIGHT
  422. self.br_cb = FCCheckBox(_("Bottom Right"))
  423. grid_loc.addWidget(self.br_cb, 1, 1)
  424. separator_line = QtWidgets.QFrame()
  425. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  426. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  427. self.layout.addWidget(separator_line)
  428. # Toggle ALL
  429. self.toggle_all_cb = FCCheckBox(_("Toggle ALL"))
  430. self.layout.addWidget(self.toggle_all_cb)
  431. separator_line = QtWidgets.QFrame()
  432. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  433. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  434. self.layout.addWidget(separator_line)
  435. # ## Grid Layout
  436. grid_lay = QtWidgets.QGridLayout()
  437. self.layout.addLayout(grid_lay)
  438. grid_lay.setColumnStretch(0, 0)
  439. grid_lay.setColumnStretch(1, 1)
  440. self.param_label = FCLabel('<b>%s:</b>' % _('Parameters'))
  441. self.param_label.setToolTip(
  442. _("Parameters used for this tool.")
  443. )
  444. grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
  445. # Type of Marker
  446. self.type_label = FCLabel('%s:' % _("Type"))
  447. self.type_label.setToolTip(
  448. _("Shape of the marker.")
  449. )
  450. self.type_radio = RadioSet([
  451. {"label": _("Semi-Cross"), "value": "s"},
  452. {"label": _("Cross"), "value": "c"},
  453. ])
  454. grid_lay.addWidget(self.type_label, 2, 0)
  455. grid_lay.addWidget(self.type_radio, 2, 1)
  456. # Thickness #
  457. self.thick_label = FCLabel('%s:' % _("Thickness"))
  458. self.thick_label.setToolTip(
  459. _("The thickness of the line that makes the corner marker.")
  460. )
  461. self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
  462. self.thick_entry.set_range(0.0000, 10.0000)
  463. self.thick_entry.set_precision(self.decimals)
  464. self.thick_entry.setWrapping(True)
  465. self.thick_entry.setSingleStep(10 ** -self.decimals)
  466. grid_lay.addWidget(self.thick_label, 4, 0)
  467. grid_lay.addWidget(self.thick_entry, 4, 1)
  468. # Length #
  469. self.l_label = FCLabel('%s:' % _("Length"))
  470. self.l_label.setToolTip(
  471. _("The length of the line that makes the corner marker.")
  472. )
  473. self.l_entry = FCDoubleSpinner(callback=self.confirmation_message)
  474. self.l_entry.set_range(-10000.0000, 10000.0000)
  475. self.l_entry.set_precision(self.decimals)
  476. self.l_entry.setSingleStep(10 ** -self.decimals)
  477. grid_lay.addWidget(self.l_label, 6, 0)
  478. grid_lay.addWidget(self.l_entry, 6, 1)
  479. # Margin #
  480. self.margin_label = FCLabel('%s:' % _("Margin"))
  481. self.margin_label.setToolTip(
  482. _("Bounding box margin.")
  483. )
  484. self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
  485. self.margin_entry.set_range(-10000.0000, 10000.0000)
  486. self.margin_entry.set_precision(self.decimals)
  487. self.margin_entry.setSingleStep(0.1)
  488. grid_lay.addWidget(self.margin_label, 8, 0)
  489. grid_lay.addWidget(self.margin_entry, 8, 1)
  490. # separator_line_2 = QtWidgets.QFrame()
  491. # separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
  492. # separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
  493. # grid_lay.addWidget(separator_line_2, 10, 0, 1, 2)
  494. # ## Insert Corner Marker
  495. self.add_marker_button = FCButton(_("Add Marker"))
  496. self.add_marker_button.setIcon(QtGui.QIcon(self.app.resource_location + '/corners_32.png'))
  497. self.add_marker_button.setToolTip(
  498. _("Will add corner markers to the selected Gerber file.")
  499. )
  500. self.add_marker_button.setStyleSheet("""
  501. QPushButton
  502. {
  503. font-weight: bold;
  504. }
  505. """)
  506. grid_lay.addWidget(self.add_marker_button, 12, 0, 1, 2)
  507. separator_line_2 = QtWidgets.QFrame()
  508. separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
  509. separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
  510. grid_lay.addWidget(separator_line_2, 14, 0, 1, 2)
  511. # Drill is corners
  512. self.drills_label = FCLabel('<b>%s:</b>' % _('Drills in Corners'))
  513. grid_lay.addWidget(self.drills_label, 16, 0, 1, 2)
  514. # Drill Tooldia #
  515. self.drill_dia_label = FCLabel('%s:' % _("Drill Dia"))
  516. self.drill_dia_label.setToolTip(
  517. '%s.' % _("Drill Diameter")
  518. )
  519. self.drill_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
  520. self.drill_dia_entry.set_range(0.0000, 100.0000)
  521. self.drill_dia_entry.set_precision(self.decimals)
  522. self.drill_dia_entry.setWrapping(True)
  523. grid_lay.addWidget(self.drill_dia_label, 18, 0)
  524. grid_lay.addWidget(self.drill_dia_entry, 18, 1)
  525. # ## Create an Excellon object
  526. self.drill_button = FCButton(_("Create Excellon Object"))
  527. self.drill_button.setIcon(QtGui.QIcon(self.app.resource_location + '/drill32.png'))
  528. self.drill_button.setToolTip(
  529. _("Will add drill holes in the center of the markers.")
  530. )
  531. self.drill_button.setStyleSheet("""
  532. QPushButton
  533. {
  534. font-weight: bold;
  535. }
  536. """)
  537. grid_lay.addWidget(self.drill_button, 20, 0, 1, 2)
  538. self.layout.addStretch()
  539. # ## Reset Tool
  540. self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
  541. self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
  542. self.reset_button.setToolTip(
  543. _("Will reset the tool parameters.")
  544. )
  545. self.reset_button.setStyleSheet("""
  546. QPushButton
  547. {
  548. font-weight: bold;
  549. }
  550. """)
  551. self.layout.addWidget(self.reset_button)
  552. # #################################### FINSIHED GUI ###########################
  553. # #############################################################################
  554. def confirmation_message(self, accepted, minval, maxval):
  555. if accepted is False:
  556. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
  557. self.decimals,
  558. minval,
  559. self.decimals,
  560. maxval), False)
  561. else:
  562. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  563. def confirmation_message_int(self, accepted, minval, maxval):
  564. if accepted is False:
  565. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
  566. (_("Edited value is out of range"), minval, maxval), False)
  567. else:
  568. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)