ToolCorners.py 26 KB

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