appGCodeEditor.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788
  1. # ##########################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # File Author: Marius Adrian Stanciu (c) #
  4. # Date: 07/22/2020 #
  5. # MIT Licence #
  6. # ##########################################################
  7. from appEditors.AppTextEditor import AppTextEditor
  8. from appObjects.FlatCAMCNCJob import CNCJobObject
  9. from appGUI.GUIElements import FCTextArea, FCEntry, FCButton, FCTable
  10. from PyQt5 import QtWidgets, QtCore, QtGui
  11. # from io import StringIO
  12. import logging
  13. import gettext
  14. import appTranslation as fcTranslate
  15. import builtins
  16. fcTranslate.apply_language('strings')
  17. if '_' not in builtins.__dict__:
  18. _ = gettext.gettext
  19. log = logging.getLogger('base')
  20. class AppGCodeEditor(QtCore.QObject):
  21. def __init__(self, app, parent=None):
  22. super().__init__(parent=parent)
  23. self.app = app
  24. self.decimals = self.app.decimals
  25. self.plain_text = ''
  26. self.callback = lambda x: None
  27. self.ui = AppGCodeEditorUI(app=self.app)
  28. self.edited_obj_name = ""
  29. self.edit_area = None
  30. self.gcode_obj = None
  31. self.code_edited = ''
  32. # store the status of the editor so the Delete at object level will not work until the edit is finished
  33. self.editor_active = False
  34. log.debug("Initialization of the GCode Editor is finished ...")
  35. def set_ui(self):
  36. """
  37. :return:
  38. :rtype:
  39. """
  40. self.decimals = self.app.decimals
  41. # #############################################################################################################
  42. # ############# ADD a new TAB in the PLot Tab Area
  43. # #############################################################################################################
  44. self.ui.gcode_editor_tab = AppTextEditor(app=self.app, plain_text=True)
  45. self.edit_area = self.ui.gcode_editor_tab.code_editor
  46. # add the tab if it was closed
  47. self.app.ui.plot_tab_area.addTab(self.ui.gcode_editor_tab, '%s' % _("Code Editor"))
  48. self.ui.gcode_editor_tab.setObjectName('gcode_editor_tab')
  49. # protect the tab that was just added
  50. for idx in range(self.app.ui.plot_tab_area.count()):
  51. if self.app.ui.plot_tab_area.widget(idx).objectName() == self.ui.gcode_editor_tab.objectName():
  52. self.app.ui.plot_tab_area.protectTab(idx)
  53. # delete the absolute and relative position and messages in the infobar
  54. self.app.ui.position_label.setText("")
  55. self.app.ui.rel_position_label.setText("")
  56. self.ui.gcode_editor_tab.code_editor.completer_enable = False
  57. self.ui.gcode_editor_tab.buttonRun.hide()
  58. # Switch plot_area to CNCJob tab
  59. self.app.ui.plot_tab_area.setCurrentWidget(self.ui.gcode_editor_tab)
  60. self.ui.gcode_editor_tab.t_frame.hide()
  61. self.ui.gcode_editor_tab.t_frame.show()
  62. self.app.proc_container.view.set_idle()
  63. # #############################################################################################################
  64. # #############################################################################################################
  65. self.ui.append_text.set_value(self.app.defaults["cncjob_append"])
  66. self.ui.prepend_text.set_value(self.app.defaults["cncjob_prepend"])
  67. # Remove anything else in the GUI Properties Tab
  68. self.app.ui.properties_scroll_area.takeWidget()
  69. # Put ourselves in the GUI Properties Tab
  70. self.app.ui.properties_scroll_area.setWidget(self.ui.edit_widget)
  71. # Switch notebook to Properties page
  72. self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab)
  73. # make a new name for the new Excellon object (the one with edited content)
  74. self.edited_obj_name = self.gcode_obj.options['name']
  75. self.ui.name_entry.set_value(self.edited_obj_name)
  76. self.activate()
  77. # #################################################################################
  78. # ################### SIGNALS #####################################################
  79. # #################################################################################
  80. self.ui.name_entry.returnPressed.connect(self.on_name_activate)
  81. self.ui.update_gcode_button.clicked.connect(self.insert_gcode)
  82. self.ui.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
  83. def build_ui(self):
  84. """
  85. :return:
  86. :rtype:
  87. """
  88. self.ui_disconnect()
  89. # if the FlatCAM object is Excellon don't build the CNC Tools Table but hide it
  90. self.ui.cnc_tools_table.hide()
  91. if self.gcode_obj.cnc_tools:
  92. self.ui.cnc_tools_table.show()
  93. self.build_cnc_tools_table()
  94. self.ui.exc_cnc_tools_table.hide()
  95. if self.gcode_obj.exc_cnc_tools:
  96. self.ui.exc_cnc_tools_table.show()
  97. self.build_excellon_cnc_tools()
  98. self.ui_connect()
  99. def build_cnc_tools_table(self):
  100. tool_idx = 0
  101. row_no = 0
  102. n = len(self.gcode_obj.cnc_tools) + 3
  103. self.ui.cnc_tools_table.setRowCount(n)
  104. # add the All Gcode selection
  105. allgcode_item = QtWidgets.QTableWidgetItem('%s' % _("All GCode"))
  106. allgcode_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  107. self.ui.cnc_tools_table.setItem(row_no, 1, allgcode_item)
  108. row_no += 1
  109. # add the Header Gcode selection
  110. header_item = QtWidgets.QTableWidgetItem('%s' % _("Header GCode"))
  111. header_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  112. self.ui.cnc_tools_table.setItem(row_no, 1, header_item)
  113. row_no += 1
  114. # add the Start Gcode selection
  115. start_item = QtWidgets.QTableWidgetItem('%s' % _("Start GCode"))
  116. start_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  117. self.ui.cnc_tools_table.setItem(row_no, 1, start_item)
  118. for dia_key, dia_value in self.gcode_obj.cnc_tools.items():
  119. tool_idx += 1
  120. row_no += 1
  121. t_id = QtWidgets.QTableWidgetItem('%d' % int(tool_idx))
  122. # id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  123. self.ui.cnc_tools_table.setItem(row_no, 0, t_id) # Tool name/id
  124. dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(dia_value['tooldia'])))
  125. offset_txt = list(str(dia_value['offset']))
  126. offset_txt[0] = offset_txt[0].upper()
  127. offset_item = QtWidgets.QTableWidgetItem(''.join(offset_txt))
  128. type_item = QtWidgets.QTableWidgetItem(str(dia_value['type']))
  129. tool_type_item = QtWidgets.QTableWidgetItem(str(dia_value['tool_type']))
  130. t_id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  131. dia_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  132. offset_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  133. type_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  134. tool_type_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  135. self.ui.cnc_tools_table.setItem(row_no, 1, dia_item) # Diameter
  136. self.ui.cnc_tools_table.setItem(row_no, 2, offset_item) # Offset
  137. self.ui.cnc_tools_table.setItem(row_no, 3, type_item) # Toolpath Type
  138. self.ui.cnc_tools_table.setItem(row_no, 4, tool_type_item) # Tool Type
  139. tool_uid_item = QtWidgets.QTableWidgetItem(str(dia_key))
  140. # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
  141. self.ui.cnc_tools_table.setItem(row_no, 5, tool_uid_item) # Tool unique ID)
  142. self.ui.cnc_tools_table.resizeColumnsToContents()
  143. self.ui.cnc_tools_table.resizeRowsToContents()
  144. vertical_header = self.ui.cnc_tools_table.verticalHeader()
  145. # vertical_header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
  146. vertical_header.hide()
  147. self.ui.cnc_tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
  148. horizontal_header = self.ui.cnc_tools_table.horizontalHeader()
  149. horizontal_header.setMinimumSectionSize(10)
  150. horizontal_header.setDefaultSectionSize(70)
  151. horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
  152. horizontal_header.resizeSection(0, 20)
  153. horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
  154. horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
  155. horizontal_header.setSectionResizeMode(4, QtWidgets.QHeaderView.Fixed)
  156. horizontal_header.resizeSection(4, 40)
  157. # horizontal_header.setStretchLastSection(True)
  158. self.ui.cnc_tools_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
  159. self.ui.cnc_tools_table.setColumnWidth(0, 20)
  160. self.ui.cnc_tools_table.setColumnWidth(4, 40)
  161. self.ui.cnc_tools_table.setColumnWidth(6, 17)
  162. # self.ui.geo_tools_table.setSortingEnabled(True)
  163. self.ui.cnc_tools_table.setMinimumHeight(self.ui.cnc_tools_table.getHeight())
  164. self.ui.cnc_tools_table.setMaximumHeight(self.ui.cnc_tools_table.getHeight())
  165. def build_excellon_cnc_tools(self):
  166. """
  167. :return:
  168. :rtype:
  169. """
  170. tool_idx = 0
  171. row_no = 0
  172. n = len(self.gcode_obj.exc_cnc_tools) + 3
  173. self.ui.exc_cnc_tools_table.setRowCount(n)
  174. # add the All Gcode selection
  175. allgcode_item = QtWidgets.QTableWidgetItem('%s' % _("All GCode"))
  176. allgcode_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  177. self.ui.exc_cnc_tools_table.setItem(row_no, 1, allgcode_item)
  178. row_no += 1
  179. # add the Header Gcode selection
  180. header_item = QtWidgets.QTableWidgetItem('%s' % _("Header GCode"))
  181. header_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  182. self.ui.exc_cnc_tools_table.setItem(row_no, 1, header_item)
  183. row_no += 1
  184. # add the Start Gcode selection
  185. start_item = QtWidgets.QTableWidgetItem('%s' % _("Start GCode"))
  186. start_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  187. self.ui.exc_cnc_tools_table.setItem(row_no, 1, start_item)
  188. for tooldia_key, dia_value in self.gcode_obj.exc_cnc_tools.items():
  189. tool_idx += 1
  190. row_no += 1
  191. t_id = QtWidgets.QTableWidgetItem('%d' % int(tool_idx))
  192. dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia_key)))
  193. nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_drills']))
  194. nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(dia_value['nr_slots']))
  195. cutz_item = QtWidgets.QTableWidgetItem('%.*f' % (
  196. self.decimals, float(dia_value['offset']) + float(dia_value['data']['tools_drill_cutz'])))
  197. t_id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  198. dia_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  199. nr_drills_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  200. nr_slots_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  201. cutz_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
  202. self.ui.exc_cnc_tools_table.setItem(row_no, 0, t_id) # Tool name/id
  203. self.ui.exc_cnc_tools_table.setItem(row_no, 1, dia_item) # Diameter
  204. self.ui.exc_cnc_tools_table.setItem(row_no, 2, nr_drills_item) # Nr of drills
  205. self.ui.exc_cnc_tools_table.setItem(row_no, 3, nr_slots_item) # Nr of slots
  206. tool_uid_item = QtWidgets.QTableWidgetItem(str(dia_value['tool']))
  207. # ## REMEMBER: THIS COLUMN IS HIDDEN IN OBJECTUI.PY # ##
  208. self.ui.exc_cnc_tools_table.setItem(row_no, 4, tool_uid_item) # Tool unique ID)
  209. self.ui.exc_cnc_tools_table.setItem(row_no, 5, cutz_item)
  210. self.ui.exc_cnc_tools_table.resizeColumnsToContents()
  211. self.ui.exc_cnc_tools_table.resizeRowsToContents()
  212. vertical_header = self.ui.exc_cnc_tools_table.verticalHeader()
  213. vertical_header.hide()
  214. self.ui.exc_cnc_tools_table.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
  215. horizontal_header = self.ui.exc_cnc_tools_table.horizontalHeader()
  216. horizontal_header.setMinimumSectionSize(10)
  217. horizontal_header.setDefaultSectionSize(70)
  218. horizontal_header.setSectionResizeMode(0, QtWidgets.QHeaderView.Fixed)
  219. horizontal_header.resizeSection(0, 20)
  220. horizontal_header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
  221. horizontal_header.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeToContents)
  222. horizontal_header.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeToContents)
  223. horizontal_header.setSectionResizeMode(5, QtWidgets.QHeaderView.ResizeToContents)
  224. # horizontal_header.setStretchLastSection(True)
  225. self.ui.exc_cnc_tools_table.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
  226. self.ui.exc_cnc_tools_table.setColumnWidth(0, 20)
  227. self.ui.exc_cnc_tools_table.setColumnWidth(6, 17)
  228. self.ui.exc_cnc_tools_table.setMinimumHeight(self.ui.exc_cnc_tools_table.getHeight())
  229. self.ui.exc_cnc_tools_table.setMaximumHeight(self.ui.exc_cnc_tools_table.getHeight())
  230. def ui_connect(self):
  231. """
  232. :return:
  233. :rtype:
  234. """
  235. # rows selected
  236. if self.gcode_obj.cnc_tools:
  237. self.ui.cnc_tools_table.clicked.connect(self.on_row_selection_change)
  238. self.ui.cnc_tools_table.horizontalHeader().sectionClicked.connect(self.on_toggle_all_rows)
  239. if self.gcode_obj.exc_cnc_tools:
  240. self.ui.exc_cnc_tools_table.clicked.connect(self.on_row_selection_change)
  241. self.ui.exc_cnc_tools_table.horizontalHeader().sectionClicked.connect(self.on_toggle_all_rows)
  242. def ui_disconnect(self):
  243. """
  244. :return:
  245. :rtype:
  246. """
  247. # rows selected
  248. if self.gcode_obj.cnc_tools:
  249. try:
  250. self.ui.cnc_tools_table.clicked.disconnect(self.on_row_selection_change)
  251. except (TypeError, AttributeError):
  252. pass
  253. try:
  254. self.ui.cnc_tools_table.horizontalHeader().sectionClicked.disconnect(self.on_toggle_all_rows)
  255. except (TypeError, AttributeError):
  256. pass
  257. if self.gcode_obj.exc_cnc_tools:
  258. try:
  259. self.ui.exc_cnc_tools_table.clicked.disconnect(self.on_row_selection_change)
  260. except (TypeError, AttributeError):
  261. pass
  262. try:
  263. self.ui.exc_cnc_tools_table.horizontalHeader().sectionClicked.disconnect(self.on_toggle_all_rows)
  264. except (TypeError, AttributeError):
  265. pass
  266. def on_row_selection_change(self):
  267. """
  268. :return:
  269. :rtype:
  270. """
  271. flags = QtGui.QTextDocument.FindCaseSensitively
  272. self.edit_area.moveCursor(QtGui.QTextCursor.Start)
  273. if self.gcode_obj.cnc_tools:
  274. t_table = self.ui.cnc_tools_table
  275. elif self.gcode_obj.exc_cnc_tools:
  276. t_table = self.ui.exc_cnc_tools_table
  277. else:
  278. return
  279. sel_model = t_table.selectionModel()
  280. sel_indexes = sel_model.selectedIndexes()
  281. # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
  282. sel_rows = set()
  283. for idx in sel_indexes:
  284. sel_rows.add(idx.row())
  285. if 0 in sel_rows:
  286. self.edit_area.selectAll()
  287. return
  288. if 1 in sel_rows:
  289. text_to_be_found = self.gcode_obj.gc_header
  290. text_list = [x for x in text_to_be_found.split("\n") if x != '']
  291. self.edit_area.find(str(text_list[0]), flags)
  292. my_text_cursor = self.edit_area.textCursor()
  293. start_sel = my_text_cursor.selectionStart()
  294. end_sel = 0
  295. while True:
  296. f = self.edit_area.find(str(text_list[-1]), flags)
  297. if f is False:
  298. break
  299. my_text_cursor = self.edit_area.textCursor()
  300. end_sel = my_text_cursor.selectionEnd()
  301. my_text_cursor.setPosition(start_sel)
  302. my_text_cursor.setPosition(end_sel, QtGui.QTextCursor.KeepAnchor)
  303. self.edit_area.setTextCursor(my_text_cursor)
  304. if 2 in sel_rows:
  305. text_to_be_found = self.gcode_obj.gc_start
  306. text_list = [x for x in text_to_be_found.split("\n") if x != '']
  307. self.edit_area.find(str(text_list[0]), flags)
  308. my_text_cursor = self.edit_area.textCursor()
  309. start_sel = my_text_cursor.selectionStart()
  310. end_sel = 0
  311. while True:
  312. f = self.edit_area.find(str(text_list[-1]), flags)
  313. if f is False:
  314. break
  315. my_text_cursor = self.edit_area.textCursor()
  316. end_sel = my_text_cursor.selectionEnd()
  317. my_text_cursor.setPosition(start_sel)
  318. my_text_cursor.setPosition(end_sel, QtGui.QTextCursor.KeepAnchor)
  319. self.edit_area.setTextCursor(my_text_cursor)
  320. sel_list = []
  321. for row in sel_rows:
  322. # those are special rows treated before so we except them
  323. if row not in [0, 1, 2]:
  324. tool_no = int(t_table.item(row, 0).text())
  325. text_to_be_found = None
  326. if self.gcode_obj.cnc_tools:
  327. text_to_be_found = self.gcode_obj.cnc_tools[tool_no]['gcode']
  328. elif self.gcode_obj.exc_cnc_tools:
  329. tool_dia = self.app.dec_format(float(t_table.item(row, 1).text()), dec=self.decimals)
  330. for tool_d in self.gcode_obj.exc_cnc_tools:
  331. if self.app.dec_format(tool_d, dec=self.decimals) == tool_dia:
  332. text_to_be_found = self.gcode_obj.exc_cnc_tools[tool_d]['gcode']
  333. if text_to_be_found is None:
  334. continue
  335. else:
  336. continue
  337. text_list = [x for x in text_to_be_found.split("\n") if x != '']
  338. # self.edit_area.find(str(text_list[0]), flags)
  339. # my_text_cursor = self.edit_area.textCursor()
  340. # start_sel = my_text_cursor.selectionStart()
  341. # first I search for the tool
  342. found_tool = self.edit_area.find('T%d' % tool_no, flags)
  343. if found_tool is False:
  344. continue
  345. # once the tool found then I set the text Cursor position to the tool Tx position
  346. my_text_cursor = self.edit_area.textCursor()
  347. tool_pos = my_text_cursor.selectionStart()
  348. my_text_cursor.setPosition(tool_pos)
  349. # I search for the first finding of the first line in the Tool GCode
  350. f = self.edit_area.find(str(text_list[0]), flags)
  351. if f is False:
  352. continue
  353. # once found I set the text Cursor position here
  354. my_text_cursor = self.edit_area.textCursor()
  355. start_sel = my_text_cursor.selectionStart()
  356. # I search for the next find of M6 (which belong to the next tool
  357. m6 = self.edit_area.find('M6', flags)
  358. if m6 is False:
  359. # this mean that we are in the last tool, we take all to the end
  360. self.edit_area.moveCursor(QtGui.QTextCursor.End)
  361. my_text_cursor = self.edit_area.textCursor()
  362. end_sel = my_text_cursor.selectionEnd()
  363. else:
  364. pos_list = []
  365. end_sel = 0
  366. my_text_cursor = self.edit_area.textCursor()
  367. m6_pos = my_text_cursor.selectionEnd()
  368. # move cursor back to the start of the tool gcode so the find method will work on the tool gcode
  369. t_curs = self.edit_area.textCursor()
  370. t_curs.setPosition(start_sel)
  371. self.edit_area.setTextCursor(t_curs)
  372. # search for all findings of the last line in the tool gcode
  373. # yet, we may find in multiple locations or in the gcode that belong to other tools
  374. while True:
  375. f = self.edit_area.find(str(text_list[-1]), flags)
  376. if f is False:
  377. break
  378. my_text_cursor = self.edit_area.textCursor()
  379. pos_list.append(my_text_cursor.selectionEnd())
  380. # now we find a position that is less than the m6_pos but also the closest (maximum)
  381. belong_to_tool_list = []
  382. for last_line_pos in pos_list:
  383. if last_line_pos < m6_pos:
  384. belong_to_tool_list.append(last_line_pos)
  385. if belong_to_tool_list:
  386. end_sel = max(belong_to_tool_list)
  387. else:
  388. # this mean that we are in the last tool, we take all to the end
  389. self.edit_area.moveCursor(QtGui.QTextCursor.End)
  390. my_text_cursor = self.edit_area.textCursor()
  391. end_sel = my_text_cursor.selectionEnd()
  392. my_text_cursor.setPosition(start_sel)
  393. my_text_cursor.setPosition(end_sel, QtGui.QTextCursor.KeepAnchor)
  394. self.edit_area.setTextCursor(my_text_cursor)
  395. tool_selection = QtWidgets.QTextEdit.ExtraSelection()
  396. tool_selection.cursor = self.edit_area.textCursor()
  397. tool_selection.format.setFontUnderline(True)
  398. sel_list.append(tool_selection)
  399. self.edit_area.setExtraSelections(sel_list)
  400. def on_toggle_all_rows(self):
  401. """
  402. :return:
  403. :rtype:
  404. """
  405. if self.gcode_obj.cnc_tools:
  406. t_table = self.ui.cnc_tools_table
  407. elif self.gcode_obj.exc_cnc_tools:
  408. t_table = self.ui.exc_cnc_tools_table
  409. else:
  410. return
  411. sel_model = t_table.selectionModel()
  412. sel_indexes = sel_model.selectedIndexes()
  413. # it will iterate over all indexes which means all items in all columns too but I'm interested only on rows
  414. sel_rows = set()
  415. for idx in sel_indexes:
  416. sel_rows.add(idx.row())
  417. if len(sel_rows) == t_table.rowCount():
  418. t_table.clearSelection()
  419. my_text_cursor = self.edit_area.textCursor()
  420. my_text_cursor.clearSelection()
  421. else:
  422. t_table.selectAll()
  423. def handleTextChanged(self):
  424. """
  425. :return:
  426. :rtype:
  427. """
  428. # enable = not self.ui.code_editor.document().isEmpty()
  429. # self.ui.buttonPrint.setEnabled(enable)
  430. # self.ui.buttonPreview.setEnabled(enable)
  431. self.buttonSave.setStyleSheet("QPushButton {color: red;}")
  432. self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as_red.png'))
  433. def insert_gcode(self):
  434. """
  435. :return:
  436. :rtype:
  437. """
  438. pass
  439. def edit_fcgcode(self, cnc_obj):
  440. """
  441. :param cnc_obj:
  442. :type cnc_obj:
  443. :return:
  444. :rtype:
  445. """
  446. assert isinstance(cnc_obj, CNCJobObject)
  447. self.gcode_obj = cnc_obj
  448. gcode_text = self.gcode_obj.source_file
  449. self.set_ui()
  450. self.build_ui()
  451. # then append the text from GCode to the text editor
  452. self.ui.gcode_editor_tab.load_text(gcode_text, move_to_start=True, clear_text=True)
  453. self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
  454. def update_fcgcode(self, edited_obj):
  455. """
  456. :return:
  457. :rtype:
  458. """
  459. my_gcode = self.ui.gcode_editor_tab.code_editor.toPlainText()
  460. self.gcode_obj.source_file = my_gcode
  461. self.deactivate()
  462. self.ui.gcode_editor_tab.buttonSave.setStyleSheet("")
  463. self.ui.gcode_editor_tab.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
  464. def on_open_gcode(self):
  465. """
  466. :return:
  467. :rtype:
  468. """
  469. _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \
  470. "All Files (*.*)"
  471. path, _f = QtWidgets.QFileDialog.getOpenFileName(
  472. caption=_('Open file'), directory=self.app.get_last_folder(), filter=_filter_)
  473. if path:
  474. file = QtCore.QFile(path)
  475. if file.open(QtCore.QIODevice.ReadOnly):
  476. stream = QtCore.QTextStream(file)
  477. self.code_edited = stream.readAll()
  478. self.ui.gcode_editor_tab.load_text(self.code_edited, move_to_start=True, clear_text=True)
  479. file.close()
  480. def activate(self):
  481. self.editor_active = True
  482. self.app.call_source = 'gcode_editor'
  483. def deactivate(self):
  484. self.editor_active = False
  485. self.app.call_source = 'app'
  486. def on_name_activate(self):
  487. self.edited_obj_name = self.ui.name_entry.get_value()
  488. class AppGCodeEditorUI:
  489. def __init__(self, app):
  490. self.app = app
  491. # Number of decimals used by tools in this class
  492. self.decimals = self.app.decimals
  493. # ## Current application units in Upper Case
  494. self.units = self.app.defaults['units'].upper()
  495. # self.setSizePolicy(
  496. # QtWidgets.QSizePolicy.MinimumExpanding,
  497. # QtWidgets.QSizePolicy.MinimumExpanding
  498. # )
  499. self.gcode_editor_tab = None
  500. self.edit_widget = QtWidgets.QWidget()
  501. # ## Box for custom widgets
  502. # This gets populated in offspring implementations.
  503. layout = QtWidgets.QVBoxLayout()
  504. self.edit_widget.setLayout(layout)
  505. # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
  506. # this way I can hide/show the frame
  507. self.edit_frame = QtWidgets.QFrame()
  508. self.edit_frame.setContentsMargins(0, 0, 0, 0)
  509. layout.addWidget(self.edit_frame)
  510. self.edit_box = QtWidgets.QVBoxLayout()
  511. self.edit_box.setContentsMargins(0, 0, 0, 0)
  512. self.edit_frame.setLayout(self.edit_box)
  513. # ## Page Title box (spacing between children)
  514. self.title_box = QtWidgets.QHBoxLayout()
  515. self.edit_box.addLayout(self.title_box)
  516. # ## Page Title icon
  517. pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
  518. self.icon = QtWidgets.QLabel()
  519. self.icon.setPixmap(pixmap)
  520. self.title_box.addWidget(self.icon, stretch=0)
  521. # ## Title label
  522. self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('GCode Editor'))
  523. self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
  524. self.title_box.addWidget(self.title_label, stretch=1)
  525. # ## Object name
  526. self.name_box = QtWidgets.QHBoxLayout()
  527. self.edit_box.addLayout(self.name_box)
  528. name_label = QtWidgets.QLabel(_("Name:"))
  529. self.name_box.addWidget(name_label)
  530. self.name_entry = FCEntry()
  531. self.name_box.addWidget(self.name_entry)
  532. separator_line = QtWidgets.QFrame()
  533. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  534. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  535. self.edit_box.addWidget(separator_line)
  536. # CNC Tools Table when made out of Geometry
  537. self.cnc_tools_table = FCTable()
  538. self.cnc_tools_table.setSortingEnabled(False)
  539. self.cnc_tools_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
  540. self.edit_box.addWidget(self.cnc_tools_table)
  541. self.cnc_tools_table.setColumnCount(6)
  542. self.cnc_tools_table.setColumnWidth(0, 20)
  543. self.cnc_tools_table.setHorizontalHeaderLabels(['#', _('Dia'), _('Offset'), _('Type'), _('TT'), ''])
  544. self.cnc_tools_table.setColumnHidden(5, True)
  545. # CNC Tools Table when made out of Excellon
  546. self.exc_cnc_tools_table = FCTable()
  547. self.exc_cnc_tools_table.setSortingEnabled(False)
  548. self.exc_cnc_tools_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
  549. self.edit_box.addWidget(self.exc_cnc_tools_table)
  550. self.exc_cnc_tools_table.setColumnCount(6)
  551. self.exc_cnc_tools_table.setColumnWidth(0, 20)
  552. self.exc_cnc_tools_table.setHorizontalHeaderLabels(['#', _('Dia'), _('Drills'), _('Slots'), '', _("Cut Z")])
  553. self.exc_cnc_tools_table.setColumnHidden(4, True)
  554. separator_line = QtWidgets.QFrame()
  555. separator_line.setFrameShape(QtWidgets.QFrame.HLine)
  556. separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
  557. self.edit_box.addWidget(separator_line)
  558. # Prepend text to GCode
  559. prependlabel = QtWidgets.QLabel('%s 1:' % _('CNC Code Snippet'))
  560. prependlabel.setToolTip(
  561. _("Code snippet defined in Preferences.")
  562. )
  563. self.edit_box.addWidget(prependlabel)
  564. self.prepend_text = FCTextArea()
  565. self.prepend_text.setPlaceholderText(
  566. _("Type here any G-Code commands you would\n"
  567. "like to insert at the cursor location.")
  568. )
  569. self.edit_box.addWidget(self.prepend_text)
  570. # Insert Button
  571. self.update_gcode_button = FCButton(_('Insert Code'))
  572. # self.update_gcode_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
  573. self.update_gcode_button.setToolTip(
  574. _("Insert the code above at the cursor location.")
  575. )
  576. self.edit_box.addWidget(self.update_gcode_button)
  577. # Append text to GCode
  578. appendlabel = QtWidgets.QLabel('%s 2:' % _('CNC Code Snippet'))
  579. appendlabel.setToolTip(
  580. _("Code snippet defined in Preferences.")
  581. )
  582. self.edit_box.addWidget(appendlabel)
  583. self.append_text = FCTextArea()
  584. self.append_text.setPlaceholderText(
  585. _("Type here any G-Code commands you would\n"
  586. "like to insert at the cursor location.")
  587. )
  588. self.edit_box.addWidget(self.append_text)
  589. # Insert Button
  590. self.update_gcode_sec_button = FCButton(_('Insert Code'))
  591. # self.update_gcode_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
  592. self.update_gcode_sec_button.setToolTip(
  593. _("Insert the code above at the cursor location.")
  594. )
  595. self.edit_box.addWidget(self.update_gcode_sec_button)
  596. layout.addStretch()
  597. # Editor
  598. self.exit_editor_button = FCButton(_('Exit Editor'))
  599. self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
  600. self.exit_editor_button.setToolTip(
  601. _("Exit from Editor.")
  602. )
  603. self.exit_editor_button.setStyleSheet("""
  604. QPushButton
  605. {
  606. font-weight: bold;
  607. }
  608. """)
  609. layout.addWidget(self.exit_editor_button)
  610. # ############################ FINSIHED GUI ##################################################################
  611. # #############################################################################################################
  612. def confirmation_message(self, accepted, minval, maxval):
  613. if accepted is False:
  614. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
  615. self.decimals,
  616. minval,
  617. self.decimals,
  618. maxval), False)
  619. else:
  620. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  621. def confirmation_message_int(self, accepted, minval, maxval):
  622. if accepted is False:
  623. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
  624. (_("Edited value is out of range"), minval, maxval), False)
  625. else:
  626. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)