ToolCorners.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  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. # get the Gerber object on which the corner marker will be inserted
  285. selection_index = self.ui.object_combo.currentIndex()
  286. model_index = self.app.collection.index(selection_index, 0, self.ui.object_combo.rootModelIndex())
  287. try:
  288. self.grb_object = model_index.internalPointer().obj
  289. except Exception as e:
  290. log.debug("ToolCorners.add_markers() --> %s" % str(e))
  291. self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
  292. self.app.call_source = "app"
  293. return
  294. if tl_state is False and tr_state is False and bl_state is False and br_state is False:
  295. self.app.inform.emit("[ERROR_NOTCL] %s." % _("Please select at least a location"))
  296. self.app.call_source = "app"
  297. return
  298. xmin, ymin, xmax, ymax = self.grb_object.bounds()
  299. # list of (x,y) tuples. Store here the drill coordinates
  300. drill_list = []
  301. if tl_state:
  302. x = xmin - margin - line_thickness / 2.0
  303. y = ymax + margin + line_thickness / 2.0
  304. drill_list.append(
  305. Point((x, y))
  306. )
  307. if tr_state:
  308. x = xmax + margin + line_thickness / 2.0
  309. y = ymax + margin + line_thickness / 2.0
  310. drill_list.append(
  311. Point((x, y))
  312. )
  313. if bl_state:
  314. x = xmin - margin - line_thickness / 2.0
  315. y = ymin - margin - line_thickness / 2.0
  316. drill_list.append(
  317. Point((x, y))
  318. )
  319. if br_state:
  320. x = xmax + margin + line_thickness / 2.0
  321. y = ymin - margin - line_thickness / 2.0
  322. drill_list.append(
  323. Point((x, y))
  324. )
  325. tools = {1: {}}
  326. tools[1]["tooldia"] = tooldia
  327. tools[1]['drills'] = drill_list
  328. tools[1]['solid_geometry'] = []
  329. def obj_init(obj_inst, app_inst):
  330. obj_inst.options.update({
  331. 'name': outname
  332. })
  333. obj_inst.tools = deepcopy(tools)
  334. obj_inst.create_geometry()
  335. obj_inst.source_file = app_inst.f_handlers.export_excellon(obj_name=obj_inst.options['name'],
  336. local_use=obj_inst,
  337. filename=None,
  338. use_thread=False)
  339. outname = '%s_%s' % (str(self.grb_object.options['name']), 'corner_drills')
  340. ret_val = self.app.app_obj.new_object("excellon", outname, obj_init)
  341. self.app.call_source = "app"
  342. if ret_val == 'fail':
  343. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed."))
  344. else:
  345. self.app.inform.emit('[success] %s' % _("Excellon object with corner drills created."))
  346. def replot(self, obj, run_thread=True):
  347. def worker_task():
  348. with self.app.proc_container.new('%s ...' % _("Plotting")):
  349. obj.plot()
  350. self.app.app_obj.object_plotted.emit(obj)
  351. if run_thread:
  352. self.app.worker_task.emit({'fcn': worker_task, 'params': []})
  353. else:
  354. worker_task()
  355. def on_exit(self, corner_gerber_obj=None):
  356. # plot the object
  357. if corner_gerber_obj:
  358. try:
  359. for ob in corner_gerber_obj:
  360. self.replot(obj=ob)
  361. except (AttributeError, TypeError):
  362. self.replot(obj=corner_gerber_obj)
  363. except Exception:
  364. return
  365. # update the bounding box values
  366. try:
  367. a, b, c, d = self.grb_object.bounds()
  368. self.grb_object.options['xmin'] = a
  369. self.grb_object.options['ymin'] = b
  370. self.grb_object.options['xmax'] = c
  371. self.grb_object.options['ymax'] = d
  372. except Exception as e:
  373. log.debug("ToolCorners.on_exit() copper_obj bounds error --> %s" % str(e))
  374. self.app.call_source = "app"
  375. self.app.inform.emit('[success] %s' % _("A Gerber object with corner markers was created."))
  376. class CornersUI:
  377. toolName = _("Corner Markers Tool")
  378. def __init__(self, layout, app):
  379. self.app = app
  380. self.decimals = self.app.decimals
  381. self.layout = layout
  382. # ## Title
  383. title_label = FCLabel("%s" % self.toolName)
  384. title_label.setStyleSheet("""
  385. QLabel
  386. {
  387. font-size: 16px;
  388. font-weight: bold;
  389. }
  390. """)
  391. self.layout.addWidget(title_label)
  392. self.layout.addWidget(FCLabel(""))
  393. # Gerber object #
  394. self.object_label = FCLabel('<b>%s:</b>' % _("GERBER"))
  395. self.object_label.setToolTip(
  396. _("The Gerber object to which will be added corner markers.")
  397. )
  398. self.object_combo = FCComboBox()
  399. self.object_combo.setModel(self.app.collection)
  400. self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
  401. self.object_combo.is_last = True
  402. self.object_combo.obj_type = "Gerber"
  403. self.layout.addWidget(self.object_label)
  404. self.layout.addWidget(self.object_combo)
  405. separator_line = QtWidgets.QFrame()
  406. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  407. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  408. self.layout.addWidget(separator_line)
  409. self.points_label = FCLabel('<b>%s:</b>' % _('Locations'))
  410. self.points_label.setToolTip(
  411. _("Locations where to place corner markers.")
  412. )
  413. self.layout.addWidget(self.points_label)
  414. # ## Grid Layout
  415. grid_loc = QtWidgets.QGridLayout()
  416. self.layout.addLayout(grid_loc)
  417. # TOP LEFT
  418. self.tl_cb = FCCheckBox(_("Top Left"))
  419. grid_loc.addWidget(self.tl_cb, 0, 0)
  420. # TOP RIGHT
  421. self.tr_cb = FCCheckBox(_("Top Right"))
  422. grid_loc.addWidget(self.tr_cb, 0, 1)
  423. # BOTTOM LEFT
  424. self.bl_cb = FCCheckBox(_("Bottom Left"))
  425. grid_loc.addWidget(self.bl_cb, 1, 0)
  426. # BOTTOM RIGHT
  427. self.br_cb = FCCheckBox(_("Bottom Right"))
  428. grid_loc.addWidget(self.br_cb, 1, 1)
  429. separator_line = QtWidgets.QFrame()
  430. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  431. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  432. self.layout.addWidget(separator_line)
  433. # Toggle ALL
  434. self.toggle_all_cb = FCCheckBox(_("Toggle ALL"))
  435. self.layout.addWidget(self.toggle_all_cb)
  436. separator_line = QtWidgets.QFrame()
  437. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  438. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  439. self.layout.addWidget(separator_line)
  440. # ## Grid Layout
  441. grid_lay = QtWidgets.QGridLayout()
  442. self.layout.addLayout(grid_lay)
  443. grid_lay.setColumnStretch(0, 0)
  444. grid_lay.setColumnStretch(1, 1)
  445. self.param_label = FCLabel('<b>%s:</b>' % _('Parameters'))
  446. self.param_label.setToolTip(
  447. _("Parameters used for this tool.")
  448. )
  449. grid_lay.addWidget(self.param_label, 0, 0, 1, 2)
  450. # Type of Marker
  451. self.type_label = FCLabel('%s:' % _("Type"))
  452. self.type_label.setToolTip(
  453. _("Shape of the marker.")
  454. )
  455. self.type_radio = RadioSet([
  456. {"label": _("Semi-Cross"), "value": "s"},
  457. {"label": _("Cross"), "value": "c"},
  458. ])
  459. grid_lay.addWidget(self.type_label, 2, 0)
  460. grid_lay.addWidget(self.type_radio, 2, 1)
  461. # Thickness #
  462. self.thick_label = FCLabel('%s:' % _("Thickness"))
  463. self.thick_label.setToolTip(
  464. _("The thickness of the line that makes the corner marker.")
  465. )
  466. self.thick_entry = FCDoubleSpinner(callback=self.confirmation_message)
  467. self.thick_entry.set_range(0.0000, 10.0000)
  468. self.thick_entry.set_precision(self.decimals)
  469. self.thick_entry.setWrapping(True)
  470. self.thick_entry.setSingleStep(10 ** -self.decimals)
  471. grid_lay.addWidget(self.thick_label, 4, 0)
  472. grid_lay.addWidget(self.thick_entry, 4, 1)
  473. # Length #
  474. self.l_label = FCLabel('%s:' % _("Length"))
  475. self.l_label.setToolTip(
  476. _("The length of the line that makes the corner marker.")
  477. )
  478. self.l_entry = FCDoubleSpinner(callback=self.confirmation_message)
  479. self.l_entry.set_range(-10000.0000, 10000.0000)
  480. self.l_entry.set_precision(self.decimals)
  481. self.l_entry.setSingleStep(10 ** -self.decimals)
  482. grid_lay.addWidget(self.l_label, 6, 0)
  483. grid_lay.addWidget(self.l_entry, 6, 1)
  484. # Margin #
  485. self.margin_label = FCLabel('%s:' % _("Margin"))
  486. self.margin_label.setToolTip(
  487. _("Bounding box margin.")
  488. )
  489. self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
  490. self.margin_entry.set_range(-10000.0000, 10000.0000)
  491. self.margin_entry.set_precision(self.decimals)
  492. self.margin_entry.setSingleStep(0.1)
  493. grid_lay.addWidget(self.margin_label, 8, 0)
  494. grid_lay.addWidget(self.margin_entry, 8, 1)
  495. # separator_line_2 = QtWidgets.QFrame()
  496. # separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
  497. # separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
  498. # grid_lay.addWidget(separator_line_2, 10, 0, 1, 2)
  499. # ## Insert Corner Marker
  500. self.add_marker_button = FCButton(_("Add Marker"))
  501. self.add_marker_button.setIcon(QtGui.QIcon(self.app.resource_location + '/corners_32.png'))
  502. self.add_marker_button.setToolTip(
  503. _("Will add corner markers to the selected Gerber file.")
  504. )
  505. self.add_marker_button.setStyleSheet("""
  506. QPushButton
  507. {
  508. font-weight: bold;
  509. }
  510. """)
  511. grid_lay.addWidget(self.add_marker_button, 12, 0, 1, 2)
  512. separator_line_2 = QtWidgets.QFrame()
  513. separator_line_2.setFrameShape(QtWidgets.QFrame.HLine)
  514. separator_line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
  515. grid_lay.addWidget(separator_line_2, 14, 0, 1, 2)
  516. # Drill is corners
  517. self.drills_label = FCLabel('<b>%s:</b>' % _('Drills in Corners'))
  518. grid_lay.addWidget(self.drills_label, 16, 0, 1, 2)
  519. # Drill Tooldia #
  520. self.drill_dia_label = FCLabel('%s:' % _("Drill Dia"))
  521. self.drill_dia_label.setToolTip(
  522. '%s.' % _("Drill Diameter")
  523. )
  524. self.drill_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
  525. self.drill_dia_entry.set_range(0.0000, 100.0000)
  526. self.drill_dia_entry.set_precision(self.decimals)
  527. self.drill_dia_entry.setWrapping(True)
  528. grid_lay.addWidget(self.drill_dia_label, 18, 0)
  529. grid_lay.addWidget(self.drill_dia_entry, 18, 1)
  530. # ## Create an Excellon object
  531. self.drill_button = FCButton(_("Create Excellon Object"))
  532. self.drill_button.setIcon(QtGui.QIcon(self.app.resource_location + '/drill32.png'))
  533. self.drill_button.setToolTip(
  534. _("Will add drill holes in the center of the markers.")
  535. )
  536. self.drill_button.setStyleSheet("""
  537. QPushButton
  538. {
  539. font-weight: bold;
  540. }
  541. """)
  542. grid_lay.addWidget(self.drill_button, 20, 0, 1, 2)
  543. self.layout.addStretch()
  544. # ## Reset Tool
  545. self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
  546. self.reset_button.setIcon(QtGui.QIcon(self.app.resource_location + '/reset32.png'))
  547. self.reset_button.setToolTip(
  548. _("Will reset the tool parameters.")
  549. )
  550. self.reset_button.setStyleSheet("""
  551. QPushButton
  552. {
  553. font-weight: bold;
  554. }
  555. """)
  556. self.layout.addWidget(self.reset_button)
  557. # #################################### FINSIHED GUI ###########################
  558. # #############################################################################
  559. def confirmation_message(self, accepted, minval, maxval):
  560. if accepted is False:
  561. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
  562. self.decimals,
  563. minval,
  564. self.decimals,
  565. maxval), False)
  566. else:
  567. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  568. def confirmation_message_int(self, accepted, minval, maxval):
  569. if accepted is False:
  570. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
  571. (_("Edited value is out of range"), minval, maxval), False)
  572. else:
  573. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)