FlatCAMCommon.py 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. # ##########################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # http://flatcam.org #
  4. # Author: Juan Pablo Caram (c) #
  5. # Date: 2/5/2014 #
  6. # MIT Licence #
  7. # ##########################################################
  8. # ##########################################################
  9. # File Modified (major mod): Marius Adrian Stanciu #
  10. # Date: 11/4/2019 #
  11. # ##########################################################
  12. from PyQt5 import QtGui, QtCore, QtWidgets
  13. from flatcamGUI.GUIElements import FCTable, FCEntry, FCButton, FCDoubleSpinner, FCComboBox, FCCheckBox
  14. from camlib import to_dict
  15. import sys
  16. import webbrowser
  17. import json
  18. from copy import deepcopy
  19. from datetime import datetime
  20. import gettext
  21. import FlatCAMTranslation as fcTranslate
  22. import builtins
  23. fcTranslate.apply_language('strings')
  24. if '_' not in builtins.__dict__:
  25. _ = gettext.gettext
  26. class LoudDict(dict):
  27. """
  28. A Dictionary with a callback for
  29. item changes.
  30. """
  31. def __init__(self, *args, **kwargs):
  32. dict.__init__(self, *args, **kwargs)
  33. self.callback = lambda x: None
  34. def __setitem__(self, key, value):
  35. """
  36. Overridden __setitem__ method. Will emit 'changed(QString)'
  37. if the item was changed, with key as parameter.
  38. """
  39. if key in self and self.__getitem__(key) == value:
  40. return
  41. dict.__setitem__(self, key, value)
  42. self.callback(key)
  43. def update(self, *args, **kwargs):
  44. if len(args) > 1:
  45. raise TypeError("update expected at most 1 arguments, got %d" % len(args))
  46. other = dict(*args, **kwargs)
  47. for key in other:
  48. self[key] = other[key]
  49. def set_change_callback(self, callback):
  50. """
  51. Assigns a function as callback on item change. The callback
  52. will receive the key of the object that was changed.
  53. :param callback: Function to call on item change.
  54. :type callback: func
  55. :return: None
  56. """
  57. self.callback = callback
  58. class FCSignal:
  59. """
  60. Taken from here: https://blog.abstractfactory.io/dynamic-signals-in-pyqt/
  61. """
  62. def __init__(self):
  63. self.__subscribers = []
  64. def emit(self, *args, **kwargs):
  65. for subs in self.__subscribers:
  66. subs(*args, **kwargs)
  67. def connect(self, func):
  68. self.__subscribers.append(func)
  69. def disconnect(self, func):
  70. try:
  71. self.__subscribers.remove(func)
  72. except ValueError:
  73. print('Warning: function %s not removed '
  74. 'from signal %s' % (func, self))
  75. class BookmarkManager(QtWidgets.QWidget):
  76. mark_rows = QtCore.pyqtSignal()
  77. def __init__(self, app, storage, parent=None):
  78. super(BookmarkManager, self).__init__(parent)
  79. self.app = app
  80. assert isinstance(storage, dict), "Storage argument is not a dictionary"
  81. self.bm_dict = deepcopy(storage)
  82. # Icon and title
  83. # self.setWindowIcon(parent.app_icon)
  84. # self.setWindowTitle(_("Bookmark Manager"))
  85. # self.resize(600, 400)
  86. # title = QtWidgets.QLabel(
  87. # "<font size=8><B>FlatCAM</B></font><BR>"
  88. # )
  89. # title.setOpenExternalLinks(True)
  90. # layouts
  91. layout = QtWidgets.QVBoxLayout()
  92. self.setLayout(layout)
  93. table_hlay = QtWidgets.QHBoxLayout()
  94. layout.addLayout(table_hlay)
  95. self.table_widget = FCTable(drag_drop=True, protected_rows=[0, 1])
  96. self.table_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
  97. table_hlay.addWidget(self.table_widget)
  98. self.table_widget.setColumnCount(3)
  99. self.table_widget.setColumnWidth(0, 20)
  100. self.table_widget.setHorizontalHeaderLabels(
  101. [
  102. '#',
  103. _('Title'),
  104. _('Web Link')
  105. ]
  106. )
  107. self.table_widget.horizontalHeaderItem(0).setToolTip(
  108. _("Index.\n"
  109. "The rows in gray color will populate the Bookmarks menu.\n"
  110. "The number of gray colored rows is set in Preferences."))
  111. self.table_widget.horizontalHeaderItem(1).setToolTip(
  112. _("Description of the link that is set as an menu action.\n"
  113. "Try to keep it short because it is installed as a menu item."))
  114. self.table_widget.horizontalHeaderItem(2).setToolTip(
  115. _("Web Link. E.g: https://your_website.org "))
  116. # pal = QtGui.QPalette()
  117. # pal.setColor(QtGui.QPalette.Background, Qt.white)
  118. # New Bookmark
  119. new_vlay = QtWidgets.QVBoxLayout()
  120. layout.addLayout(new_vlay)
  121. new_title_lbl = QtWidgets.QLabel('<b>%s</b>' % _("New Bookmark"))
  122. new_vlay.addWidget(new_title_lbl)
  123. form0 = QtWidgets.QFormLayout()
  124. new_vlay.addLayout(form0)
  125. title_lbl = QtWidgets.QLabel('%s:' % _("Title"))
  126. self.title_entry = FCEntry()
  127. form0.addRow(title_lbl, self.title_entry)
  128. link_lbl = QtWidgets.QLabel('%s:' % _("Web Link"))
  129. self.link_entry = FCEntry()
  130. self.link_entry.set_value('http://')
  131. form0.addRow(link_lbl, self.link_entry)
  132. # Buttons Layout
  133. button_hlay = QtWidgets.QHBoxLayout()
  134. layout.addLayout(button_hlay)
  135. add_entry_btn = FCButton(_("Add Entry"))
  136. remove_entry_btn = FCButton(_("Remove Entry"))
  137. export_list_btn = FCButton(_("Export List"))
  138. import_list_btn = FCButton(_("Import List"))
  139. # closebtn = QtWidgets.QPushButton(_("Close"))
  140. # button_hlay.addStretch()
  141. button_hlay.addWidget(add_entry_btn)
  142. button_hlay.addWidget(remove_entry_btn)
  143. button_hlay.addWidget(export_list_btn)
  144. button_hlay.addWidget(import_list_btn)
  145. # button_hlay.addWidget(closebtn)
  146. # ##############################################################################
  147. # ######################## SIGNALS #############################################
  148. # ##############################################################################
  149. add_entry_btn.clicked.connect(self.on_add_entry)
  150. remove_entry_btn.clicked.connect(self.on_remove_entry)
  151. export_list_btn.clicked.connect(self.on_export_bookmarks)
  152. import_list_btn.clicked.connect(self.on_import_bookmarks)
  153. self.title_entry.returnPressed.connect(self.on_add_entry)
  154. self.link_entry.returnPressed.connect(self.on_add_entry)
  155. # closebtn.clicked.connect(self.accept)
  156. self.table_widget.drag_drop_sig.connect(self.mark_table_rows_for_actions)
  157. self.build_bm_ui()
  158. def build_bm_ui(self):
  159. self.table_widget.setRowCount(len(self.bm_dict))
  160. nr_crt = 0
  161. sorted_bookmarks = sorted(list(self.bm_dict.items()), key=lambda x: int(x[0]))
  162. for entry, bookmark in sorted_bookmarks:
  163. row = nr_crt
  164. nr_crt += 1
  165. title = bookmark[0]
  166. weblink = bookmark[1]
  167. id_item = QtWidgets.QTableWidgetItem('%d' % int(nr_crt))
  168. # id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  169. self.table_widget.setItem(row, 0, id_item) # Tool name/id
  170. title_item = QtWidgets.QTableWidgetItem(title)
  171. self.table_widget.setItem(row, 1, title_item)
  172. weblink_txt = QtWidgets.QTextBrowser()
  173. weblink_txt.setOpenExternalLinks(True)
  174. weblink_txt.setFrameStyle(QtWidgets.QFrame.NoFrame)
  175. weblink_txt.document().setDefaultStyleSheet("a{ text-decoration: none; }")
  176. weblink_txt.setHtml('<a href=%s>%s</a>' % (weblink, weblink))
  177. self.table_widget.setCellWidget(row, 2, weblink_txt)
  178. vertical_header = self.table_widget.verticalHeader()
  179. vertical_header.hide()
  180. horizontal_header = self.table_widget.horizontalHeader()
  181. horizontal_header.setMinimumSectionSize(10)
  182. horizontal_header.setDefaultSectionSize(70)
  183. horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
  184. horizontal_header.resizeSection(0, 20)
  185. horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
  186. horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
  187. self.mark_table_rows_for_actions()
  188. self.app.defaults["global_bookmarks"].clear()
  189. for key, val in self.bm_dict.items():
  190. self.app.defaults["global_bookmarks"][key] = deepcopy(val)
  191. def on_add_entry(self, **kwargs):
  192. """
  193. Add a entry in the Bookmark Table and in the menu actions
  194. :return: None
  195. """
  196. if 'title' in kwargs:
  197. title = kwargs['title']
  198. else:
  199. title = self.title_entry.get_value()
  200. if title == '':
  201. self.app.inform.emit(f'[ERROR_NOTCL] {_("Title entry is empty.")}')
  202. return 'fail'
  203. if 'link' is kwargs:
  204. link = kwargs['link']
  205. else:
  206. link = self.link_entry.get_value()
  207. if link == 'http://':
  208. self.app.inform.emit(f'[ERROR_NOTCL] {_("Web link entry is empty.")}')
  209. return 'fail'
  210. # if 'http' not in link or 'https' not in link:
  211. # link = 'http://' + link
  212. for bookmark in self.bm_dict.values():
  213. if title == bookmark[0] or link == bookmark[1]:
  214. self.app.inform.emit(f'[ERROR_NOTCL] {_("Either the Title or the Weblink already in the table.")}')
  215. return 'fail'
  216. # for some reason if the last char in the weblink is a slash it does not make the link clickable
  217. # so I remove it
  218. if link[-1] == '/':
  219. link = link[:-1]
  220. # add the new entry to storage
  221. new_entry = len(self.bm_dict) + 1
  222. self.bm_dict[str(new_entry)] = [title, link]
  223. # add the link to the menu but only if it is within the set limit
  224. bm_limit = int(self.app.defaults["global_bookmarks_limit"])
  225. if len(self.bm_dict) < bm_limit:
  226. act = QtWidgets.QAction(parent=self.app.ui.menuhelp_bookmarks)
  227. act.setText(title)
  228. act.setIcon(QtGui.QIcon('share/link16.png'))
  229. act.triggered.connect(lambda: webbrowser.open(link))
  230. self.app.ui.menuhelp_bookmarks.insertAction(self.app.ui.menuhelp_bookmarks_manager, act)
  231. self.app.inform.emit(f'[success] {_("Bookmark added.")}')
  232. # add the new entry to the bookmark manager table
  233. self.build_bm_ui()
  234. def on_remove_entry(self):
  235. """
  236. Remove an Entry in the Bookmark table and from the menu actions
  237. :return:
  238. """
  239. index_list = []
  240. for model_index in self.table_widget.selectionModel().selectedRows():
  241. index = QtCore.QPersistentModelIndex(model_index)
  242. index_list.append(index)
  243. title_to_remove = self.table_widget.item(model_index.row(), 1).text()
  244. if title_to_remove == 'FlatCAM' or title_to_remove == 'Backup Site':
  245. self.app.inform.emit('[WARNING_NOTCL] %s.' % _("This bookmark can not be removed"))
  246. self.build_bm_ui()
  247. return
  248. else:
  249. for k, bookmark in list(self.bm_dict.items()):
  250. if title_to_remove == bookmark[0]:
  251. # remove from the storage
  252. self.bm_dict.pop(k, None)
  253. for act in self.app.ui.menuhelp_bookmarks.actions():
  254. if act.text() == title_to_remove:
  255. # disconnect the signal
  256. try:
  257. act.triggered.disconnect()
  258. except TypeError:
  259. pass
  260. # remove the action from the menu
  261. self.app.ui.menuhelp_bookmarks.removeAction(act)
  262. # house keeping: it pays to have keys increased by one
  263. new_key = 0
  264. new_dict = dict()
  265. for k, v in self.bm_dict.items():
  266. # we start with key 1 so we can use the len(self.bm_dict)
  267. # when adding bookmarks (keys in bm_dict)
  268. new_key += 1
  269. new_dict[str(new_key)] = v
  270. self.bm_dict = deepcopy(new_dict)
  271. new_dict.clear()
  272. self.app.inform.emit(f'[success] {_("Bookmark removed.")}')
  273. # for index in index_list:
  274. # self.table_widget.model().removeRow(index.row())
  275. self.build_bm_ui()
  276. def on_export_bookmarks(self):
  277. self.app.report_usage("on_export_bookmarks")
  278. self.app.log.debug("on_export_bookmarks()")
  279. date = str(datetime.today()).rpartition('.')[0]
  280. date = ''.join(c for c in date if c not in ':-')
  281. date = date.replace(' ', '_')
  282. filter__ = "Text File (*.TXT);;All Files (*.*)"
  283. filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export FlatCAM Bookmarks"),
  284. directory='{l_save}/FlatCAM_{n}_{date}'.format(
  285. l_save=str(self.app.get_last_save_folder()),
  286. n=_("Bookmarks"),
  287. date=date),
  288. filter=filter__)
  289. filename = str(filename)
  290. if filename == "":
  291. self.app.inform.emit('[WARNING_NOTCL] %s' % _("FlatCAM bookmarks export cancelled."))
  292. return
  293. else:
  294. try:
  295. f = open(filename, 'w')
  296. f.close()
  297. except PermissionError:
  298. self.app.inform.emit('[WARNING] %s' %
  299. _("Permission denied, saving not possible.\n"
  300. "Most likely another app is holding the file open and not accessible."))
  301. return
  302. except IOError:
  303. self.app.log.debug('Creating a new bookmarks file ...')
  304. f = open(filename, 'w')
  305. f.close()
  306. except:
  307. e = sys.exc_info()[0]
  308. self.app.log.error("Could not load defaults file.")
  309. self.app.log.error(str(e))
  310. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load bookmarks file."))
  311. return
  312. # Save Bookmarks to a file
  313. try:
  314. with open(filename, "w") as f:
  315. for title, link in self.bm_dict.items():
  316. line2write = str(title) + ':' + str(link) + '\n'
  317. f.write(line2write)
  318. except:
  319. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write bookmarks to file."))
  320. return
  321. self.app.inform.emit('[success] %s: %s' % (_("Exported bookmarks to"), filename))
  322. def on_import_bookmarks(self):
  323. self.app.log.debug("on_import_bookmarks()")
  324. filter_ = "Text File (*.txt);;All Files (*.*)"
  325. filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import FlatCAM Bookmarks"), filter=filter_)
  326. filename = str(filename)
  327. if filename == "":
  328. self.app.inform.emit('[WARNING_NOTCL] %s' % _("FlatCAM bookmarks import cancelled."))
  329. else:
  330. try:
  331. with open(filename) as f:
  332. bookmarks = f.readlines()
  333. except IOError:
  334. self.app.log.error("Could not load bookmarks file.")
  335. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load bookmarks file."))
  336. return
  337. for line in bookmarks:
  338. proc_line = line.replace(' ', '').partition(':')
  339. self.on_add_entry(title=proc_line[0], link=proc_line[2])
  340. self.app.inform.emit('[success] %s: %s' % (_("Imported Bookmarks from"), filename))
  341. def mark_table_rows_for_actions(self):
  342. for row in range(self.table_widget.rowCount()):
  343. item_to_paint = self.table_widget.item(row, 0)
  344. if row < self.app.defaults["global_bookmarks_limit"]:
  345. item_to_paint.setBackground(QtGui.QColor('gray'))
  346. # item_to_paint.setForeground(QtGui.QColor('black'))
  347. else:
  348. item_to_paint.setBackground(QtGui.QColor('white'))
  349. # item_to_paint.setForeground(QtGui.QColor('black'))
  350. def rebuild_actions(self):
  351. # rebuild the storage to reflect the order of the lines
  352. self.bm_dict.clear()
  353. for row in range(self.table_widget.rowCount()):
  354. title = self.table_widget.item(row, 1).text()
  355. wlink = self.table_widget.cellWidget(row, 2).toPlainText()
  356. entry = int(row) + 1
  357. self.bm_dict.update(
  358. {
  359. str(entry): [title, wlink]
  360. }
  361. )
  362. self.app.install_bookmarks(book_dict=self.bm_dict)
  363. # def accept(self):
  364. # self.rebuild_actions()
  365. # super().accept()
  366. def closeEvent(self, QCloseEvent):
  367. self.rebuild_actions()
  368. super().closeEvent(QCloseEvent)
  369. class ToolsDB(QtWidgets.QWidget):
  370. mark_tools_rows = QtCore.pyqtSignal()
  371. def __init__(self, app, callback_on_edited, callback_on_tool_request, parent=None):
  372. super(ToolsDB, self).__init__(parent)
  373. self.app = app
  374. self.decimals = 4
  375. self.callback_app = callback_on_edited
  376. self.on_tool_request = callback_on_tool_request
  377. self.offset_item_options = ["Path", "In", "Out", "Custom"]
  378. self.type_item_options = [_("Iso"), _("Rough"), _("Finish")]
  379. self.tool_type_item_options = ["C1", "C2", "C3", "C4", "B", "V"]
  380. '''
  381. dict to hold all the tools in the Tools DB
  382. format:
  383. {
  384. tool_id: {
  385. 'name': 'new_tool'
  386. 'tooldia': self.app.defaults["geometry_cnctooldia"]
  387. 'offset': 'Path'
  388. 'offset_value': 0.0
  389. 'type': _('Rough'),
  390. 'tool_type': 'C1'
  391. 'data': dict()
  392. }
  393. }
  394. '''
  395. self.db_tool_dict = dict()
  396. # layouts
  397. layout = QtWidgets.QVBoxLayout()
  398. self.setLayout(layout)
  399. table_hlay = QtWidgets.QHBoxLayout()
  400. layout.addLayout(table_hlay)
  401. self.table_widget = FCTable(drag_drop=True)
  402. self.table_widget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
  403. table_hlay.addWidget(self.table_widget)
  404. self.table_widget.setColumnCount(26)
  405. # self.table_widget.setColumnWidth(0, 20)
  406. self.table_widget.setHorizontalHeaderLabels(
  407. [
  408. '#',
  409. _("Tool Name"),
  410. _("Tool Dia"),
  411. _("Tool Offset"),
  412. _("Custom Offset"),
  413. _("Tool Type"),
  414. _("Tool Shape"),
  415. _("Cut Z"),
  416. _("MultiDepth"),
  417. _("DPP"),
  418. _("V-Dia"),
  419. _("V-Angle"),
  420. _("Travel Z"),
  421. _("FR"),
  422. _("FR Z"),
  423. _("FR Rapids"),
  424. _("Spindle Speed"),
  425. _("Dwell"),
  426. _("Dwelltime"),
  427. _("Postprocessor"),
  428. _("ExtraCut"),
  429. _("Toolchange"),
  430. _("Toolchange XY"),
  431. _("Toolchange Z"),
  432. _("Start Z"),
  433. _("End Z"),
  434. ]
  435. )
  436. self.table_widget.horizontalHeaderItem(0).setToolTip(
  437. _("Index.\n"
  438. "The rows in gray color will populate the Bookmarks menu.\n"
  439. "The number of gray colored rows is set in Preferences."))
  440. # pal = QtGui.QPalette()
  441. # pal.setColor(QtGui.QPalette.Background, Qt.white)
  442. # New Bookmark
  443. new_vlay = QtWidgets.QVBoxLayout()
  444. layout.addLayout(new_vlay)
  445. new_tool_lbl = QtWidgets.QLabel('<b>%s</b>' % _("New Tool"))
  446. new_vlay.addWidget(new_tool_lbl, alignment=QtCore.Qt.AlignBottom)
  447. self.buttons_frame = QtWidgets.QFrame()
  448. self.buttons_frame.setContentsMargins(0, 0, 0, 0)
  449. layout.addWidget(self.buttons_frame)
  450. self.buttons_box = QtWidgets.QHBoxLayout()
  451. self.buttons_box.setContentsMargins(0, 0, 0, 0)
  452. self.buttons_frame.setLayout(self.buttons_box)
  453. self.buttons_frame.show()
  454. add_entry_btn = FCButton(_("Add Tool to Tools DB"))
  455. remove_entry_btn = FCButton(_("Remove Tool from Tools DB"))
  456. export_db_btn = FCButton(_("Export Tool DB"))
  457. import_db_btn = FCButton(_("Import Tool DB"))
  458. # button_hlay.addStretch()
  459. self.buttons_box.addWidget(add_entry_btn)
  460. self.buttons_box.addWidget(remove_entry_btn)
  461. self.buttons_box.addWidget(export_db_btn)
  462. self.buttons_box.addWidget(import_db_btn)
  463. # self.buttons_box.addWidget(closebtn)
  464. self.add_tool_from_db = FCButton(_("Add Tool from Tools DB"))
  465. self.add_tool_from_db.hide()
  466. hlay = QtWidgets.QHBoxLayout()
  467. layout.addLayout(hlay)
  468. hlay.addWidget(self.add_tool_from_db)
  469. hlay.addStretch()
  470. # ##############################################################################
  471. # ######################## SIGNALS #############################################
  472. # ##############################################################################
  473. add_entry_btn.clicked.connect(self.on_add_entry)
  474. remove_entry_btn.clicked.connect(self.on_remove_entry)
  475. export_db_btn.clicked.connect(self.on_export_tools_db_file)
  476. import_db_btn.clicked.connect(self.on_import_tools_db_file)
  477. # closebtn.clicked.connect(self.accept)
  478. self.add_tool_from_db.clicked.connect(self.on_tool_requested_from_app)
  479. self.setup_db_ui()
  480. def setup_db_ui(self):
  481. filename = self.app.data_path + '/tools_db.FlatConfig'
  482. # load the database tools from the file
  483. try:
  484. with open(filename) as f:
  485. tools = f.read()
  486. except IOError:
  487. self.app.log.error("Could not load tools DB file.")
  488. self.app.inform.emit('[ERROR] %s' % _("Could not load Tools DB file."))
  489. return
  490. try:
  491. self.db_tool_dict = json.loads(tools)
  492. except:
  493. e = sys.exc_info()[0]
  494. self.app.log.error(str(e))
  495. self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
  496. return
  497. self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
  498. self.build_db_ui()
  499. def build_db_ui(self):
  500. self.ui_disconnect()
  501. self.table_widget.setRowCount(len(self.db_tool_dict))
  502. nr_crt = 0
  503. for toolid, dict_val in self.db_tool_dict.items():
  504. row = nr_crt
  505. nr_crt += 1
  506. t_name = dict_val['name']
  507. self.add_tool_table_line(row, name=t_name, widget=self.table_widget, tooldict=dict_val)
  508. vertical_header = self.table_widget.verticalHeader()
  509. vertical_header.hide()
  510. horizontal_header = self.table_widget.horizontalHeader()
  511. horizontal_header.setMinimumSectionSize(10)
  512. horizontal_header.setDefaultSectionSize(70)
  513. self.table_widget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
  514. for x in range(27):
  515. self.table_widget.resizeColumnToContents(x)
  516. horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
  517. # horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
  518. # horizontal_header.setSectionResizeMode(13, QtWidgets.QHeaderView.Fixed)
  519. horizontal_header.resizeSection(0, 20)
  520. # horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.ResizeToContents)
  521. # horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
  522. self.ui_connect()
  523. def add_tool_table_line(self, row, name, widget, tooldict):
  524. data = tooldict['data']
  525. nr_crt = row + 1
  526. id_item = QtWidgets.QTableWidgetItem('%d' % int(nr_crt))
  527. id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  528. widget.setItem(row, 0, id_item) # Tool name/id
  529. tool_name_item = QtWidgets.QTableWidgetItem(name)
  530. widget.setItem(row, 1, tool_name_item)
  531. dia_item = FCDoubleSpinner()
  532. dia_item.set_precision(self.decimals)
  533. dia_item.setSingleStep(0.1)
  534. dia_item.set_range(0.0, 9999.9999)
  535. dia_item.set_value(float(tooldict['tooldia']))
  536. widget.setCellWidget(row, 2, dia_item)
  537. tool_offset_item = FCComboBox()
  538. for item in self.offset_item_options:
  539. tool_offset_item.addItem(item)
  540. tool_offset_item.set_value(tooldict['offset'])
  541. widget.setCellWidget(row, 3, tool_offset_item)
  542. c_offset_item = FCDoubleSpinner()
  543. c_offset_item.set_precision(self.decimals)
  544. c_offset_item.setSingleStep(0.1)
  545. c_offset_item.set_range(-9999.9999, 9999.9999)
  546. c_offset_item.set_value(float(tooldict['offset_value']))
  547. widget.setCellWidget(row, 4, c_offset_item)
  548. tt_item = FCComboBox()
  549. for item in self.type_item_options:
  550. tt_item.addItem(item)
  551. tt_item.set_value(tooldict['type'])
  552. widget.setCellWidget(row, 5, tt_item)
  553. tshape_item = FCComboBox()
  554. for item in self.tool_type_item_options:
  555. tshape_item.addItem(item)
  556. tshape_item.set_value(tooldict['tool_type'])
  557. widget.setCellWidget(row, 6, tshape_item)
  558. cutz_item = FCDoubleSpinner()
  559. cutz_item.set_precision(self.decimals)
  560. cutz_item.setSingleStep(0.1)
  561. if self.app.defaults['global_machinist_setting']:
  562. cutz_item.set_range(-9999.9999, 9999.9999)
  563. else:
  564. cutz_item.set_range(-9999.9999, -0.0000)
  565. cutz_item.set_value(float(data['cutz']))
  566. widget.setCellWidget(row, 7, cutz_item)
  567. multidepth_item = FCCheckBox()
  568. multidepth_item.set_value(data['multidepth'])
  569. widget.setCellWidget(row, 8, multidepth_item)
  570. depth_per_pass_item = FCDoubleSpinner()
  571. depth_per_pass_item.set_precision(self.decimals)
  572. depth_per_pass_item.setSingleStep(0.1)
  573. depth_per_pass_item.set_range(0.0, 9999.9999)
  574. depth_per_pass_item.set_value(float(data['depthperpass']))
  575. widget.setCellWidget(row, 9, depth_per_pass_item)
  576. vtip_dia_item = FCDoubleSpinner()
  577. vtip_dia_item.set_precision(self.decimals)
  578. vtip_dia_item.setSingleStep(0.1)
  579. vtip_dia_item.set_range(0.0, 9999.9999)
  580. vtip_dia_item.set_value(float(data['vtipdia']))
  581. widget.setCellWidget(row, 10, vtip_dia_item)
  582. vtip_angle_item = FCDoubleSpinner()
  583. vtip_angle_item.set_precision(self.decimals)
  584. vtip_angle_item.setSingleStep(0.1)
  585. vtip_angle_item.set_range(-360.0, 360.0)
  586. vtip_angle_item.set_value(float(data['vtipangle']))
  587. widget.setCellWidget(row, 11, vtip_angle_item)
  588. travelz_item = FCDoubleSpinner()
  589. travelz_item.set_precision(self.decimals)
  590. travelz_item.setSingleStep(0.1)
  591. if self.app.defaults['global_machinist_setting']:
  592. travelz_item.set_range(-9999.9999, 9999.9999)
  593. else:
  594. travelz_item.set_range(0.0000, 9999.9999)
  595. travelz_item.set_value(float(data['travelz']))
  596. widget.setCellWidget(row, 12, travelz_item)
  597. fr_item = FCDoubleSpinner()
  598. fr_item.set_precision(self.decimals)
  599. fr_item.set_range(0.0, 9999.9999)
  600. fr_item.set_value(float(data['feedrate']))
  601. widget.setCellWidget(row, 13, fr_item)
  602. frz_item = FCDoubleSpinner()
  603. frz_item.set_precision(self.decimals)
  604. frz_item.set_range(0.0, 9999.9999)
  605. frz_item.set_value(float(data['feedrate_z']))
  606. widget.setCellWidget(row, 14, frz_item)
  607. frrapids_item = FCDoubleSpinner()
  608. frrapids_item.set_precision(self.decimals)
  609. frrapids_item.set_range(0.0, 9999.9999)
  610. frrapids_item.set_value(float(data['feedrate_rapid']))
  611. widget.setCellWidget(row, 15, frrapids_item)
  612. spindlespeed_item = QtWidgets.QTableWidgetItem(str(data['spindlespeed']) if data['spindlespeed'] else '')
  613. widget.setItem(row, 16, spindlespeed_item)
  614. dwell_item = FCCheckBox()
  615. dwell_item.set_value(data['dwell'])
  616. widget.setCellWidget(row, 17, dwell_item)
  617. dwelltime_item = FCDoubleSpinner()
  618. dwelltime_item.set_precision(self.decimals)
  619. dwelltime_item.set_range(0.0, 9999.9999)
  620. dwelltime_item.set_value(float(data['dwelltime']))
  621. widget.setCellWidget(row, 18, dwelltime_item)
  622. pp_item = FCComboBox()
  623. for item in self.app.postprocessors:
  624. pp_item.addItem(item)
  625. pp_item.set_value(data['ppname_g'])
  626. widget.setCellWidget(row, 19, pp_item)
  627. ecut_item = FCCheckBox()
  628. ecut_item.set_value(data['extracut'])
  629. widget.setCellWidget(row, 20, ecut_item)
  630. toolchange_item = FCCheckBox()
  631. toolchange_item.set_value(data['toolchange'])
  632. widget.setCellWidget(row, 21, toolchange_item)
  633. toolchangexy_item = QtWidgets.QTableWidgetItem(str(data['toolchangexy']) if data['toolchangexy'] else '')
  634. widget.setItem(row, 22, toolchangexy_item)
  635. toolchangez_item = FCDoubleSpinner()
  636. toolchangez_item.set_precision(self.decimals)
  637. toolchangez_item.setSingleStep(0.1)
  638. if self.app.defaults['global_machinist_setting']:
  639. toolchangez_item.set_range(-9999.9999, 9999.9999)
  640. else:
  641. toolchangez_item.set_range(0.0000, 9999.9999)
  642. toolchangez_item.set_value(float(data['toolchangez']))
  643. widget.setCellWidget(row, 23, toolchangez_item)
  644. startz_item = QtWidgets.QTableWidgetItem(str(data['startz']) if data['startz'] else '')
  645. widget.setItem(row, 24, startz_item)
  646. endz_item = FCDoubleSpinner()
  647. endz_item.set_precision(self.decimals)
  648. endz_item.setSingleStep(0.1)
  649. if self.app.defaults['global_machinist_setting']:
  650. endz_item.set_range(-9999.9999, 9999.9999)
  651. else:
  652. endz_item.set_range(0.0000, 9999.9999)
  653. endz_item.set_value(float(data['endz']))
  654. widget.setCellWidget(row, 25, endz_item)
  655. def on_add_entry(self):
  656. """
  657. Add a tool in the DB Tool Table
  658. :return: None
  659. """
  660. new_toolid = len(self.db_tool_dict) + 1
  661. dict_elem = dict()
  662. default_data = dict()
  663. default_data.update({
  664. "cutz": float(self.app.defaults["geometry_cutz"]),
  665. "multidepth": self.app.defaults["geometry_multidepth"],
  666. "depthperpass": float(self.app.defaults["geometry_depthperpass"]),
  667. "vtipdia": float(self.app.defaults["geometry_vtipdia"]),
  668. "vtipangle": float(self.app.defaults["geometry_vtipangle"]),
  669. "travelz": float(self.app.defaults["geometry_travelz"]),
  670. "feedrate": float(self.app.defaults["geometry_feedrate"]),
  671. "feedrate_z": float(self.app.defaults["geometry_feedrate_z"]),
  672. "feedrate_rapid": float(self.app.defaults["geometry_feedrate_rapid"]),
  673. "spindlespeed": self.app.defaults["geometry_spindlespeed"],
  674. "dwell": self.app.defaults["geometry_dwell"],
  675. "dwelltime": float(self.app.defaults["geometry_dwelltime"]),
  676. "ppname_g": self.app.defaults["geometry_ppname_g"],
  677. "extracut": self.app.defaults["geometry_extracut"],
  678. "toolchange": self.app.defaults["geometry_toolchange"],
  679. "toolchangexy": self.app.defaults["geometry_toolchangexy"],
  680. "toolchangez": float(self.app.defaults["geometry_toolchangez"]),
  681. "startz": self.app.defaults["geometry_startz"],
  682. "endz": float(self.app.defaults["geometry_endz"])
  683. })
  684. dict_elem['name'] = 'new_tool'
  685. dict_elem['tooldia'] = self.app.defaults["geometry_cnctooldia"]
  686. dict_elem['offset'] = 'Path'
  687. dict_elem['offset_value'] = 0.0
  688. dict_elem['type'] = _('Rough')
  689. dict_elem['tool_type'] = 'C1'
  690. dict_elem['data'] = default_data
  691. self.db_tool_dict.update(
  692. {
  693. new_toolid: deepcopy(dict_elem)
  694. }
  695. )
  696. self.app.inform.emit(f'[success] {_("Tool added to DB.")}')
  697. # add the new entry to the Tools DB table
  698. self.build_db_ui()
  699. self.callback_on_edited()
  700. def on_remove_entry(self):
  701. """
  702. Remove a Tool in the Tools DB table
  703. :return:
  704. """
  705. index_list = []
  706. for model_index in self.table_widget.selectionModel().selectedRows():
  707. index = QtCore.QPersistentModelIndex(model_index)
  708. index_list.append(index)
  709. toolname_to_remove = self.table_widget.item(model_index.row(), 0).text()
  710. for toolid, dict_val in list(self.db_tool_dict.items()):
  711. if int(toolname_to_remove) == int(toolid):
  712. # remove from the storage
  713. self.db_tool_dict.pop(toolid, None)
  714. self.app.inform.emit(f'[success] {_("Tool removed from Tools DB.")}')
  715. self.build_db_ui()
  716. self.callback_on_edited()
  717. def on_export_tools_db_file(self):
  718. self.app.report_usage("on_export_tools_db_file")
  719. self.app.log.debug("on_export_tools_db_file()")
  720. date = str(datetime.today()).rpartition('.')[0]
  721. date = ''.join(c for c in date if c not in ':-')
  722. date = date.replace(' ', '_')
  723. filter__ = "Text File (*.TXT);;All Files (*.*)"
  724. filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export Tools Database"),
  725. directory='{l_save}/FlatCAM_{n}_{date}'.format(
  726. l_save=str(self.app.get_last_save_folder()),
  727. n=_("Tools_Database"),
  728. date=date),
  729. filter=filter__)
  730. filename = str(filename)
  731. if filename == "":
  732. self.app.inform.emit('[WARNING_NOTCL] %s' % _("FlatCAM Tools DB export cancelled."))
  733. return
  734. else:
  735. try:
  736. f = open(filename, 'w')
  737. f.close()
  738. except PermissionError:
  739. self.app.inform.emit('[WARNING] %s' %
  740. _("Permission denied, saving not possible.\n"
  741. "Most likely another app is holding the file open and not accessible."))
  742. return
  743. except IOError:
  744. self.app.log.debug('Creating a new Tools DB file ...')
  745. f = open(filename, 'w')
  746. f.close()
  747. except:
  748. e = sys.exc_info()[0]
  749. self.app.log.error("Could not load Tools DB file.")
  750. self.app.log.error(str(e))
  751. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
  752. return
  753. # Save update options
  754. try:
  755. # Save Tools DB in a file
  756. try:
  757. with open(filename, "w") as f:
  758. json.dump(self.db_tool_dict, f, default=to_dict, indent=2)
  759. except Exception as e:
  760. self.app.log.debug("App.on_save_tools_db() --> %s" % str(e))
  761. self.inform.emit(f'[ERROR_NOTCL] {_("Failed to write Tools DB to file.")}')
  762. return
  763. except:
  764. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Failed to write Tools DB to file."))
  765. return
  766. self.app.inform.emit('[success] %s: %s' % (_("Exported Tools DB to"), filename))
  767. def on_import_tools_db_file(self):
  768. self.app.report_usage("on_import_tools_db_file")
  769. self.app.log.debug("on_import_tools_db_file()")
  770. filter__ = "Text File (*.TXT);;All Files (*.*)"
  771. filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import FlatCAM Tools DB"), filter=filter__)
  772. if filename == "":
  773. self.app.inform.emit('[WARNING_NOTCL] %s' % _("FlatCAM Tools DB import cancelled."))
  774. else:
  775. try:
  776. with open(filename) as f:
  777. tools_in_db = f.read()
  778. except IOError:
  779. self.app.log.error("Could not load Tools DB file.")
  780. self.app.inform.emit('[ERROR_NOTCL] %s' % _("Could not load Tools DB file."))
  781. return
  782. try:
  783. self.db_tool_dict = json.loads(tools_in_db)
  784. except:
  785. e = sys.exc_info()[0]
  786. self.app.log.error(str(e))
  787. self.app.inform.emit('[ERROR] %s' % _("Failed to parse Tools DB file."))
  788. return
  789. self.app.inform.emit('[success] %s: %s' % (_("Loaded FlatCAM Tools DB from"), filename))
  790. self.build_db_ui()
  791. self.callback_on_edited()
  792. def on_save_tools_db(self, silent=False):
  793. self.app.log.debug("ToolsDB.on_save_button() --> Saving Tools Database to file.")
  794. filename = self.app.data_path + "/tools_db.FlatConfig"
  795. # Preferences save, update the color of the Tools DB Tab text
  796. for idx in range(self.app.ui.plot_tab_area.count()):
  797. if self.app.ui.plot_tab_area.tabText(idx) == _("Tools Database"):
  798. self.app.ui.plot_tab_area.tabBar.setTabTextColor(idx, QtGui.QColor('black'))
  799. # Save Tools DB in a file
  800. try:
  801. f = open(filename, "w")
  802. json.dump(self.db_tool_dict, f, default=to_dict, indent=2)
  803. f.close()
  804. except Exception as e:
  805. self.app.log.debug("ToolsDB.on_save_tools_db() --> %s" % str(e))
  806. self.app.inform.emit(f'[ERROR_NOTCL] {_("Failed to write Tools DB to file.")}')
  807. return
  808. if not silent:
  809. self.app.inform.emit('[success] %s: %s' % (_("Exported Tools DB to"), filename))
  810. def ui_connect(self):
  811. try:
  812. try:
  813. self.table_widget.itemChanged.disconnect(self.callback_on_edited)
  814. except (TypeError, AttributeError):
  815. pass
  816. self.table_widget.itemChanged.connect(self.callback_on_edited)
  817. except AttributeError:
  818. pass
  819. for row in range(self.table_widget.rowCount()):
  820. for col in range(self.table_widget.columnCount()):
  821. # ComboBox
  822. try:
  823. try:
  824. self.table_widget.cellWidget(row, col).currentIndexChanged.disconnect(self.callback_on_edited)
  825. except (TypeError, AttributeError):
  826. pass
  827. self.table_widget.cellWidget(row, col).currentIndexChanged.connect(self.callback_on_edited)
  828. except AttributeError:
  829. pass
  830. # CheckBox
  831. try:
  832. try:
  833. self.table_widget.cellWidget(row, col).toggled.disconnect(self.callback_on_edited)
  834. except (TypeError, AttributeError):
  835. pass
  836. self.table_widget.cellWidget(row, col).toggled.connect(self.callback_on_edited)
  837. except AttributeError:
  838. pass
  839. # SpinBox, DoubleSpinBox
  840. try:
  841. try:
  842. self.table_widget.cellWidget(row, col).valueChanged.disconnect(self.callback_on_edited)
  843. except (TypeError, AttributeError):
  844. pass
  845. self.table_widget.cellWidget(row, col).valueChanged.connect(self.callback_on_edited)
  846. except AttributeError:
  847. pass
  848. def ui_disconnect(self):
  849. try:
  850. self.table_widget.itemChanged.disconnect(self.callback_on_edited)
  851. except (TypeError, AttributeError):
  852. pass
  853. for row in range(self.table_widget.rowCount()):
  854. for col in range(self.table_widget.columnCount()):
  855. # ComboBox
  856. try:
  857. self.table_widget.cellWidget(row, col).currentIndexChanged.disconnect(self.callback_on_edited)
  858. except (TypeError, AttributeError):
  859. pass
  860. # CheckBox
  861. try:
  862. self.table_widget.cellWidget(row, col).toggled.disconnect(self.callback_on_edited)
  863. except (TypeError, AttributeError):
  864. pass
  865. # SpinBox, DoubleSpinBox
  866. try:
  867. self.table_widget.cellWidget(row, col).valueChanged.disconnect(self.callback_on_edited)
  868. except (TypeError, AttributeError):
  869. pass
  870. def callback_on_edited(self):
  871. # update the dictionary storage self.db_tool_dict
  872. self.db_tool_dict.clear()
  873. dict_elem = dict()
  874. default_data = dict()
  875. for row in range(self.table_widget.rowCount()):
  876. new_toolid = row + 1
  877. for col in range(self.table_widget.columnCount()):
  878. column_header_text = self.table_widget.horizontalHeaderItem(col).text()
  879. if column_header_text == 'Tool Name':
  880. dict_elem['name'] = self.table_widget.item(row, col).text()
  881. elif column_header_text == 'Tool Dia':
  882. dict_elem['tooldia'] = self.table_widget.cellWidget(row, col).get_value()
  883. elif column_header_text == 'Tool Offset':
  884. dict_elem['offset'] = self.table_widget.cellWidget(row, col).get_value()
  885. elif column_header_text == 'Custom Offset':
  886. dict_elem['offset_value'] = self.table_widget.cellWidget(row, col).get_value()
  887. elif column_header_text == 'Tool Type':
  888. dict_elem['type'] = self.table_widget.cellWidget(row, col).get_value()
  889. elif column_header_text == 'Tool Shape':
  890. dict_elem['tool_type'] = self.table_widget.cellWidget(row, col).get_value()
  891. else:
  892. if column_header_text == 'Cut Z':
  893. default_data['cutz'] = self.table_widget.cellWidget(row, col).get_value()
  894. elif column_header_text == 'MultiDepth':
  895. default_data['multidepth'] = self.table_widget.cellWidget(row, col).get_value()
  896. elif column_header_text == 'DPP':
  897. default_data['depthperpass'] = self.table_widget.cellWidget(row, col).get_value()
  898. elif column_header_text == 'V-Dia':
  899. default_data['vtipdia'] = self.table_widget.cellWidget(row, col).get_value()
  900. elif column_header_text == 'V-Angle':
  901. default_data['vtipangle'] = self.table_widget.cellWidget(row, col).get_value()
  902. elif column_header_text == 'Travel Z':
  903. default_data['travelz'] = self.table_widget.cellWidget(row, col).get_value()
  904. elif column_header_text == 'FR':
  905. default_data['feedrate'] = self.table_widget.cellWidget(row, col).get_value()
  906. elif column_header_text == 'FR Z':
  907. default_data['feedrate_z'] = self.table_widget.cellWidget(row, col).get_value()
  908. elif column_header_text == 'FR Rapids':
  909. default_data['feedrate_rapid'] = self.table_widget.cellWidget(row, col).get_value()
  910. elif column_header_text == 'Spindle Speed':
  911. default_data['spindlespeed'] = float(self.table_widget.item(row, col).text()) \
  912. if self.table_widget.item(row, col).text() is not '' else None
  913. elif column_header_text == 'Dwell':
  914. default_data['dwell'] = self.table_widget.cellWidget(row, col).get_value()
  915. elif column_header_text == 'Dwelltime':
  916. default_data['dwelltime'] = self.table_widget.cellWidget(row, col).get_value()
  917. elif column_header_text == 'Postprocessor':
  918. default_data['ppname_g'] = self.table_widget.cellWidget(row, col).get_value()
  919. elif column_header_text == 'ExtraCut':
  920. default_data['extracut'] = self.table_widget.cellWidget(row, col).get_value()
  921. elif column_header_text == 'Toolchange':
  922. default_data['toolchange'] = self.table_widget.cellWidget(row, col).get_value()
  923. elif column_header_text == 'Toolchange XY':
  924. default_data['toolchangexy'] = self.table_widget.item(row, col).text()
  925. elif column_header_text == 'Toolchange Z':
  926. default_data['toolchangez'] = self.table_widget.cellWidget(row, col).get_value()
  927. elif column_header_text == 'Start Z':
  928. default_data['startz'] = float(self.table_widget.item(row, col).text()) \
  929. if self.table_widget.item(row, col).text() is not '' else None
  930. elif column_header_text == 'End Z':
  931. default_data['endz'] = self.table_widget.cellWidget(row, col).get_value()
  932. dict_elem['data'] = default_data
  933. self.db_tool_dict.update(
  934. {
  935. new_toolid: deepcopy(dict_elem)
  936. }
  937. )
  938. self.callback_app()
  939. def on_tool_requested_from_app(self):
  940. if not self.table_widget.selectionModel().selectedRows():
  941. self.app.inform.emit('[WARNING_NOTCL] %s...' % _("No Tool/row selected in the Tools Database table"))
  942. return
  943. elif len(self.table_widget.selectionModel().selectedRows()) > 1:
  944. self.app.inform.emit('[WARNING_NOTCL] %s...' %
  945. _("Only one tool can be selected in the Tools Database table"))
  946. return
  947. # only one model in list since the conditions above assure this
  948. model_index = self.table_widget.selectionModel().selectedRows()[0]
  949. selected_row = model_index.row()
  950. tool_uid = selected_row + 1
  951. for key in self.db_tool_dict.keys():
  952. if str(key) == str(tool_uid):
  953. selected_tool = self.db_tool_dict[key]
  954. self.on_tool_request(tool=selected_tool)
  955. def resize_new_tool_table_widget(self, min_size, max_size):
  956. """
  957. Resize the table widget responsible for adding new tool in the Tool Database
  958. :param min_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
  959. :param max_size: passed by rangeChanged signal or the self.new_tool_table_widget.horizontalScrollBar()
  960. :return:
  961. """
  962. t_height = self.t_height
  963. if max_size > min_size:
  964. t_height = self.t_height + self.new_tool_table_widget.verticalScrollBar().height()
  965. self.new_tool_table_widget.setMaximumHeight(t_height)
  966. def closeEvent(self, QCloseEvent):
  967. super().closeEvent(QCloseEvent)