ToolCorners.py 26 KB

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