FlatCAMTextEditor.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. # ##########################################################
  2. # FlatCAM: 2D Post-processing for Manufacturing #
  3. # File Author: Marius Adrian Stanciu (c) #
  4. # Date: 10/10/2019 #
  5. # MIT Licence #
  6. # ##########################################################
  7. from flatcamGUI.GUIElements import *
  8. from PyQt5 import QtPrintSupport
  9. import gettext
  10. import FlatCAMTranslation as fcTranslate
  11. import builtins
  12. fcTranslate.apply_language('strings')
  13. if '_' not in builtins.__dict__:
  14. _ = gettext.gettext
  15. class TextEditor(QtWidgets.QWidget):
  16. def __init__(self, app, text=None, plain_text=None):
  17. super().__init__()
  18. self.app = app
  19. self.setSizePolicy(
  20. QtWidgets.QSizePolicy.MinimumExpanding,
  21. QtWidgets.QSizePolicy.MinimumExpanding
  22. )
  23. self.main_editor_layout = QtWidgets.QVBoxLayout(self)
  24. self.main_editor_layout.setContentsMargins(0, 0, 0, 0)
  25. self.t_frame = QtWidgets.QFrame()
  26. self.t_frame.setContentsMargins(0, 0, 0, 0)
  27. self.main_editor_layout.addWidget(self.t_frame)
  28. self.work_editor_layout = QtWidgets.QGridLayout(self.t_frame)
  29. self.work_editor_layout.setContentsMargins(2, 2, 2, 2)
  30. self.t_frame.setLayout(self.work_editor_layout)
  31. if plain_text:
  32. self.editor_class = FCTextAreaLineNumber()
  33. self.code_editor = self.editor_class.edit
  34. stylesheet = """
  35. QPlainTextEdit { selection-background-color:yellow;
  36. selection-color:black;
  37. }
  38. """
  39. self.work_editor_layout.addWidget(self.editor_class, 0, 0, 1, 5)
  40. else:
  41. self.code_editor = FCTextAreaExtended()
  42. stylesheet = """
  43. QTextEdit { selection-background-color:yellow;
  44. selection-color:black;
  45. }
  46. """
  47. self.work_editor_layout.addWidget(self.code_editor, 0, 0, 1, 5)
  48. self.code_editor.setStyleSheet(stylesheet)
  49. if text:
  50. self.code_editor.setPlainText(text)
  51. self.buttonPreview = QtWidgets.QPushButton(_('Print Preview'))
  52. self.buttonPreview.setToolTip(_("Open a OS standard Preview Print window."))
  53. self.buttonPreview.setMinimumWidth(100)
  54. self.buttonPrint = QtWidgets.QPushButton(_('Print Code'))
  55. self.buttonPrint.setToolTip(_("Open a OS standard Print window."))
  56. self.buttonFind = QtWidgets.QPushButton(_('Find in Code'))
  57. self.buttonFind.setToolTip(_("Will search and highlight in yellow the string in the Find box."))
  58. self.buttonFind.setMinimumWidth(100)
  59. self.entryFind = FCEntry()
  60. self.entryFind.setToolTip(_("Find box. Enter here the strings to be searched in the text."))
  61. self.buttonReplace = QtWidgets.QPushButton(_('Replace With'))
  62. self.buttonReplace.setToolTip(_("Will replace the string from the Find box with the one in the Replace box."))
  63. self.buttonReplace.setMinimumWidth(100)
  64. self.entryReplace = FCEntry()
  65. self.entryReplace.setToolTip(_("String to replace the one in the Find box throughout the text."))
  66. self.sel_all_cb = QtWidgets.QCheckBox(_('All'))
  67. self.sel_all_cb.setToolTip(_("When checked it will replace all instances in the 'Find' box\n"
  68. "with the text in the 'Replace' box.."))
  69. self.button_copy_all = QtWidgets.QPushButton(_('Copy All'))
  70. self.button_copy_all.setToolTip(_("Will copy all the text in the Code Editor to the clipboard."))
  71. self.button_copy_all.setMinimumWidth(100)
  72. self.buttonOpen = QtWidgets.QPushButton(_('Open Code'))
  73. self.buttonOpen.setToolTip(_("Will open a text file in the editor."))
  74. self.buttonSave = QtWidgets.QPushButton(_('Save Code'))
  75. self.buttonSave.setToolTip(_("Will save the text in the editor into a file."))
  76. self.buttonRun = QtWidgets.QPushButton(_('Run Code'))
  77. self.buttonRun.setToolTip(_("Will run the TCL commands found in the text file, one by one."))
  78. self.buttonRun.hide()
  79. editor_hlay_1 = QtWidgets.QHBoxLayout()
  80. # cnc_tab_lay_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
  81. editor_hlay_1.addWidget(self.buttonFind)
  82. editor_hlay_1.addWidget(self.entryFind)
  83. editor_hlay_1.addWidget(self.buttonReplace)
  84. editor_hlay_1.addWidget(self.entryReplace)
  85. editor_hlay_1.addWidget(self.sel_all_cb)
  86. editor_hlay_1.addWidget(self.button_copy_all)
  87. self.work_editor_layout.addLayout(editor_hlay_1, 1, 0, 1, 5)
  88. editor_hlay_2 = QtWidgets.QHBoxLayout()
  89. editor_hlay_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
  90. editor_hlay_2.addWidget(self.buttonPreview)
  91. editor_hlay_2.addWidget(self.buttonPrint)
  92. self.work_editor_layout.addLayout(editor_hlay_2, 2, 0, 1, 1, QtCore.Qt.AlignLeft)
  93. cnc_tab_lay_4 = QtWidgets.QHBoxLayout()
  94. cnc_tab_lay_4.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
  95. cnc_tab_lay_4.addWidget(self.buttonOpen)
  96. cnc_tab_lay_4.addWidget(self.buttonSave)
  97. cnc_tab_lay_4.addWidget(self.buttonRun)
  98. self.work_editor_layout.addLayout(cnc_tab_lay_4, 2, 4, 1, 1)
  99. # #################################################################################
  100. # ################### SIGNALS #####################################################
  101. # #################################################################################
  102. self.code_editor.textChanged.connect(self.handleTextChanged)
  103. self.buttonOpen.clicked.connect(self.handleOpen)
  104. self.buttonSave.clicked.connect(self.handleSaveGCode)
  105. self.buttonPrint.clicked.connect(self.handlePrint)
  106. self.buttonPreview.clicked.connect(self.handlePreview)
  107. self.buttonFind.clicked.connect(self.handleFindGCode)
  108. self.buttonReplace.clicked.connect(self.handleReplaceGCode)
  109. self.button_copy_all.clicked.connect(self.handleCopyAll)
  110. self.code_editor.set_model_data(self.app.myKeywords)
  111. self.code_edited = ''
  112. def handlePrint(self):
  113. self.app.report_usage("handlePrint()")
  114. dialog = QtPrintSupport.QPrintDialog()
  115. if dialog.exec_() == QtWidgets.QDialog.Accepted:
  116. self.code_editor.document().print_(dialog.printer())
  117. def handlePreview(self):
  118. self.app.report_usage("handlePreview()")
  119. dialog = QtPrintSupport.QPrintPreviewDialog()
  120. dialog.paintRequested.connect(self.code_editor.print_)
  121. dialog.exec_()
  122. def handleTextChanged(self):
  123. # enable = not self.ui.code_editor.document().isEmpty()
  124. # self.ui.buttonPrint.setEnabled(enable)
  125. # self.ui.buttonPreview.setEnabled(enable)
  126. pass
  127. def handleOpen(self, filt=None):
  128. self.app.report_usage("handleOpen()")
  129. if filt:
  130. _filter_ = filt
  131. else:
  132. _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \
  133. "All Files (*.*)"
  134. path, _f = QtWidgets.QFileDialog.getOpenFileName(
  135. caption=_('Open file'), directory=self.app.get_last_folder(), filter=_filter_)
  136. if path:
  137. file = QtCore.QFile(path)
  138. if file.open(QtCore.QIODevice.ReadOnly):
  139. stream = QtCore.QTextStream(file)
  140. self.code_edited = stream.readAll()
  141. self.code_editor.setPlainText(self.code_edited)
  142. file.close()
  143. def handleSaveGCode(self, name=None, filt=None):
  144. self.app.report_usage("handleSaveGCode()")
  145. if filt:
  146. _filter_ = filt
  147. else:
  148. _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \
  149. "All Files (*.*)"
  150. if name:
  151. obj_name = name
  152. else:
  153. try:
  154. obj_name = self.app.collection.get_active().options['name']
  155. except AttributeError:
  156. obj_name = 'file'
  157. if filt is None:
  158. _filter_ = "FlatConfig Files (*.FlatConfig);;All Files (*.*)"
  159. try:
  160. filename = str(QtWidgets.QFileDialog.getSaveFileName(
  161. caption=_("Export Code ..."),
  162. directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name),
  163. filter=_filter_
  164. )[0])
  165. except TypeError:
  166. filename = str(QtWidgets.QFileDialog.getSaveFileName(caption=_("Export Code ..."), filter=_filter_)[0])
  167. if filename == "":
  168. self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export Code cancelled."))
  169. return
  170. else:
  171. try:
  172. my_gcode = self.code_editor.toPlainText()
  173. with open(filename, 'w') as f:
  174. for line in my_gcode:
  175. f.write(line)
  176. except FileNotFoundError:
  177. self.app.inform.emit('[WARNING] %s' % _("No such file or directory"))
  178. return
  179. except PermissionError:
  180. self.app.inform.emit('[WARNING] %s' %
  181. _("Permission denied, saving not possible.\n"
  182. "Most likely another app is holding the file open and not accessible."))
  183. return
  184. # Just for adding it to the recent files list.
  185. if self.app.defaults["global_open_style"] is False:
  186. self.app.file_opened.emit("cncjob", filename)
  187. self.app.file_saved.emit("cncjob", filename)
  188. self.app.inform.emit('%s: %s' % (_("Saved to"), str(filename)))
  189. def handleFindGCode(self):
  190. self.app.report_usage("handleFindGCode()")
  191. flags = QtGui.QTextDocument.FindCaseSensitively
  192. text_to_be_found = self.entryFind.get_value()
  193. r = self.code_editor.find(str(text_to_be_found), flags)
  194. if r is False:
  195. self.code_editor.moveCursor(QtGui.QTextCursor.Start)
  196. r = self.code_editor.find(str(text_to_be_found), flags)
  197. def handleReplaceGCode(self):
  198. self.app.report_usage("handleReplaceGCode()")
  199. old = self.entryFind.get_value()
  200. new = self.entryReplace.get_value()
  201. if self.sel_all_cb.isChecked():
  202. while True:
  203. cursor = self.code_editor.textCursor()
  204. cursor.beginEditBlock()
  205. flags = QtGui.QTextDocument.FindCaseSensitively
  206. # self.ui.editor is the QPlainTextEdit
  207. r = self.code_editor.find(str(old), flags)
  208. if r:
  209. qc = self.code_editor.textCursor()
  210. if qc.hasSelection():
  211. qc.insertText(new)
  212. else:
  213. self.ui.code_editor.moveCursor(QtGui.QTextCursor.Start)
  214. break
  215. # Mark end of undo block
  216. cursor.endEditBlock()
  217. else:
  218. cursor = self.code_editor.textCursor()
  219. cursor.beginEditBlock()
  220. qc = self.code_editor.textCursor()
  221. if qc.hasSelection():
  222. qc.insertText(new)
  223. # Mark end of undo block
  224. cursor.endEditBlock()
  225. def handleCopyAll(self):
  226. text = self.code_editor.toPlainText()
  227. self.app.clipboard.setText(text)
  228. self.app.inform.emit(_("Code Editor content copied to clipboard ..."))
  229. # def closeEvent(self, QCloseEvent):
  230. # super().closeEvent(QCloseEvent)