appGCodeEditor.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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
  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.plain_text = ''
  25. self.callback = lambda x: None
  26. self.ui = AppGCodeEditorUI(app=self.app)
  27. self.edited_obj_name = ""
  28. self.gcode_obj = None
  29. self.code_edited = ''
  30. # store the status of the editor so the Delete at object level will not work until the edit is finished
  31. self.editor_active = False
  32. log.debug("Initialization of the GCode Editor is finished ...")
  33. def set_ui(self):
  34. """
  35. :return:
  36. :rtype:
  37. """
  38. # #############################################################################################################
  39. # ############# ADD a new TAB in the PLot Tab Area
  40. # #############################################################################################################
  41. self.ui.gcode_editor_tab = AppTextEditor(app=self.app, plain_text=True)
  42. # add the tab if it was closed
  43. self.app.ui.plot_tab_area.addTab(self.ui.gcode_editor_tab, '%s' % _("Code Editor"))
  44. self.ui.gcode_editor_tab.setObjectName('code_editor_tab')
  45. # delete the absolute and relative position and messages in the infobar
  46. self.app.ui.position_label.setText("")
  47. self.app.ui.rel_position_label.setText("")
  48. self.ui.gcode_editor_tab.code_editor.completer_enable = False
  49. self.ui.gcode_editor_tab.buttonRun.hide()
  50. # Switch plot_area to CNCJob tab
  51. self.app.ui.plot_tab_area.setCurrentWidget(self.ui.gcode_editor_tab)
  52. self.ui.gcode_editor_tab.t_frame.hide()
  53. self.ui.gcode_editor_tab.t_frame.show()
  54. self.app.proc_container.view.set_idle()
  55. # #############################################################################################################
  56. # #############################################################################################################
  57. self.ui.append_text.set_value(self.app.defaults["cncjob_append"])
  58. self.ui.prepend_text.set_value(self.app.defaults["cncjob_prepend"])
  59. # #################################################################################
  60. # ################### SIGNALS #####################################################
  61. # #################################################################################
  62. self.ui.name_entry.returnPressed.connect(self.on_name_activate)
  63. self.ui.update_gcode_button.clicked.connect(self.insert_gcode)
  64. self.ui.exit_editor_button.clicked.connect(lambda: self.app.editor2object())
  65. def build_ui(self):
  66. """
  67. :return:
  68. :rtype:
  69. """
  70. # Remove anything else in the GUI Selected Tab
  71. self.app.ui.selected_scroll_area.takeWidget()
  72. # Put ourselves in the GUI Selected Tab
  73. self.app.ui.selected_scroll_area.setWidget(self.ui.edit_widget)
  74. # Switch notebook to Selected page
  75. self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
  76. # make a new name for the new Excellon object (the one with edited content)
  77. self.edited_obj_name = self.gcode_obj.options['name']
  78. self.ui.name_entry.set_value(self.edited_obj_name)
  79. def ui_connect(self):
  80. """
  81. :return:
  82. :rtype:
  83. """
  84. pass
  85. def ui_disconnect(self):
  86. """
  87. :return:
  88. :rtype:
  89. """
  90. pass
  91. def handleTextChanged(self):
  92. """
  93. :return:
  94. :rtype:
  95. """
  96. # enable = not self.ui.code_editor.document().isEmpty()
  97. # self.ui.buttonPrint.setEnabled(enable)
  98. # self.ui.buttonPreview.setEnabled(enable)
  99. self.buttonSave.setStyleSheet("QPushButton {color: red;}")
  100. self.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as_red.png'))
  101. def insert_gcode(self):
  102. """
  103. :return:
  104. :rtype:
  105. """
  106. pass
  107. def edit_fcgcode(self, cnc_obj):
  108. """
  109. :param cnc_obj:
  110. :type cnc_obj:
  111. :return:
  112. :rtype:
  113. """
  114. assert isinstance(cnc_obj, CNCJobObject)
  115. self.gcode_obj = cnc_obj
  116. gcode_text = self.gcode_obj.source_file
  117. self.set_ui()
  118. self.build_ui()
  119. # then append the text from GCode to the text editor
  120. self.ui.gcode_editor_tab.load_text(gcode_text, move_to_start=True, clear_text=True)
  121. self.app.inform.emit('[success] %s...' % _('Loaded Machine Code into Code Editor'))
  122. def update_fcgcode(self, edited_obj):
  123. """
  124. :return:
  125. :rtype:
  126. """
  127. preamble = str(self.ui.prepend_text.get_value())
  128. postamble = str(self.ui.append_text.get_value())
  129. my_gcode = self.ui.gcode_editor_tab.code_editor.toPlainText()
  130. self.gcode_obj.source_file = my_gcode
  131. self.ui.gcode_editor_tab.buttonSave.setStyleSheet("")
  132. self.ui.gcode_editor_tab.buttonSave.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
  133. def on_open_gcode(self):
  134. """
  135. :return:
  136. :rtype:
  137. """
  138. _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \
  139. "All Files (*.*)"
  140. path, _f = QtWidgets.QFileDialog.getOpenFileName(
  141. caption=_('Open file'), directory=self.app.get_last_folder(), filter=_filter_)
  142. if path:
  143. file = QtCore.QFile(path)
  144. if file.open(QtCore.QIODevice.ReadOnly):
  145. stream = QtCore.QTextStream(file)
  146. self.code_edited = stream.readAll()
  147. self.ui.gcode_editor_tab.load_text(self.code_edited, move_to_start=True, clear_text=True)
  148. file.close()
  149. def on_name_activate(self):
  150. self.edited_obj_name = self.ui.name_entry.get_value()
  151. class AppGCodeEditorUI:
  152. def __init__(self, app):
  153. self.app = app
  154. # Number of decimals used by tools in this class
  155. self.decimals = self.app.decimals
  156. # ## Current application units in Upper Case
  157. self.units = self.app.defaults['units'].upper()
  158. # self.setSizePolicy(
  159. # QtWidgets.QSizePolicy.MinimumExpanding,
  160. # QtWidgets.QSizePolicy.MinimumExpanding
  161. # )
  162. self.gcode_editor_tab = None
  163. self.edit_widget = QtWidgets.QWidget()
  164. # ## Box for custom widgets
  165. # This gets populated in offspring implementations.
  166. layout = QtWidgets.QVBoxLayout()
  167. self.edit_widget.setLayout(layout)
  168. # add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
  169. # this way I can hide/show the frame
  170. self.edit_frame = QtWidgets.QFrame()
  171. self.edit_frame.setContentsMargins(0, 0, 0, 0)
  172. layout.addWidget(self.edit_frame)
  173. self.edit_box = QtWidgets.QVBoxLayout()
  174. self.edit_box.setContentsMargins(0, 0, 0, 0)
  175. self.edit_frame.setLayout(self.edit_box)
  176. # ## Page Title box (spacing between children)
  177. self.title_box = QtWidgets.QHBoxLayout()
  178. self.edit_box.addLayout(self.title_box)
  179. # ## Page Title icon
  180. pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
  181. self.icon = QtWidgets.QLabel()
  182. self.icon.setPixmap(pixmap)
  183. self.title_box.addWidget(self.icon, stretch=0)
  184. # ## Title label
  185. self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('GCode Editor'))
  186. self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
  187. self.title_box.addWidget(self.title_label, stretch=1)
  188. # ## Object name
  189. self.name_box = QtWidgets.QHBoxLayout()
  190. self.edit_box.addLayout(self.name_box)
  191. name_label = QtWidgets.QLabel(_("Name:"))
  192. self.name_box.addWidget(name_label)
  193. self.name_entry = FCEntry()
  194. self.name_box.addWidget(self.name_entry)
  195. # Prepend text to GCode
  196. prependlabel = QtWidgets.QLabel('%s:' % _('Prepend to CNC Code'))
  197. prependlabel.setToolTip(
  198. _("Type here any G-Code commands you would\n"
  199. "like to add at the beginning of the G-Code file.")
  200. )
  201. self.edit_box.addWidget(prependlabel)
  202. self.prepend_text = FCTextArea()
  203. self.prepend_text.setPlaceholderText(
  204. _("Type here any G-Code commands you would\n"
  205. "like to add at the beginning of the G-Code file.")
  206. )
  207. self.edit_box.addWidget(self.prepend_text)
  208. # Append text to GCode
  209. appendlabel = QtWidgets.QLabel('%s:' % _('Append to CNC Code'))
  210. appendlabel.setToolTip(
  211. _("Type here any G-Code commands you would\n"
  212. "like to append to the generated file.\n"
  213. "I.e.: M2 (End of program)")
  214. )
  215. self.edit_box.addWidget(appendlabel)
  216. self.append_text = FCTextArea()
  217. self.append_text.setPlaceholderText(
  218. _("Type here any G-Code commands you would\n"
  219. "like to append to the generated file.\n"
  220. "I.e.: M2 (End of program)")
  221. )
  222. self.edit_box.addWidget(self.append_text)
  223. h_lay = QtWidgets.QHBoxLayout()
  224. h_lay.setAlignment(QtCore.Qt.AlignVCenter)
  225. self.edit_box.addLayout(h_lay)
  226. # GO Button
  227. self.update_gcode_button = FCButton(_('Update Code'))
  228. # self.update_gcode_button.setIcon(QtGui.QIcon(self.app.resource_location + '/save_as.png'))
  229. self.update_gcode_button.setToolTip(
  230. _("Update the Gcode in the Editor with the values\n"
  231. "in the 'Prepend' and 'Append' text boxes.")
  232. )
  233. h_lay.addWidget(self.update_gcode_button)
  234. layout.addStretch()
  235. # Editor
  236. self.exit_editor_button = FCButton(_('Exit Editor'))
  237. self.exit_editor_button.setIcon(QtGui.QIcon(self.app.resource_location + '/power16.png'))
  238. self.exit_editor_button.setToolTip(
  239. _("Exit from Editor.")
  240. )
  241. self.exit_editor_button.setStyleSheet("""
  242. QPushButton
  243. {
  244. font-weight: bold;
  245. }
  246. """)
  247. layout.addWidget(self.exit_editor_button)
  248. # ############################ FINSIHED GUI ##################################################################
  249. # #############################################################################################################
  250. def confirmation_message(self, accepted, minval, maxval):
  251. if accepted is False:
  252. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' % (_("Edited value is out of range"),
  253. self.decimals,
  254. minval,
  255. self.decimals,
  256. maxval), False)
  257. else:
  258. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)
  259. def confirmation_message_int(self, accepted, minval, maxval):
  260. if accepted is False:
  261. self.app.inform[str, bool].emit('[WARNING_NOTCL] %s: [%d, %d]' %
  262. (_("Edited value is out of range"), minval, maxval), False)
  263. else:
  264. self.app.inform[str, bool].emit('[success] %s' % _("Edited value is within limits."), False)