from flatcamGUI.GUIElements import * from PyQt5 import QtPrintSupport import tkinter as tk from copy import deepcopy import sys import gettext import FlatCAMTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext class TextEditor(QtWidgets.QWidget): def __init__(self, app, text=None): super().__init__() self.app = app self.setSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding ) self.main_editor_layout = QtWidgets.QVBoxLayout(self) self.main_editor_layout.setContentsMargins(0, 0, 0, 0) self.t_frame = QtWidgets.QFrame() self.t_frame.setContentsMargins(0, 0, 0, 0) self.main_editor_layout.addWidget(self.t_frame) self.work_editor_layout = QtWidgets.QGridLayout(self.t_frame) self.work_editor_layout.setContentsMargins(2, 2, 2, 2) self.t_frame.setLayout(self.work_editor_layout) self.code_editor = FCTextAreaExtended() stylesheet = """ QTextEdit { selection-background-color:yellow; selection-color:black; } """ self.code_editor.setStyleSheet(stylesheet) if text: self.code_editor.setPlainText(text) self.buttonPreview = QtWidgets.QPushButton(_('Print Preview')) self.buttonPreview.setToolTip(_("Open a OS standard Preview Print window.")) self.buttonPreview.setMinimumWidth(100) self.buttonPrint = QtWidgets.QPushButton(_('Print Code')) self.buttonPrint.setToolTip(_("Open a OS standard Print window.")) self.buttonFind = QtWidgets.QPushButton(_('Find in Code')) self.buttonFind.setToolTip(_("Will search and highlight in yellow the string in the Find box.")) self.buttonFind.setMinimumWidth(100) self.entryFind = FCEntry() self.entryFind.setToolTip(_("Find box. Enter here the strings to be searched in the text.")) self.buttonReplace = QtWidgets.QPushButton(_('Replace With')) self.buttonReplace.setToolTip(_("Will replace the string from the Find box with the one in the Replace box.")) self.buttonReplace.setMinimumWidth(100) self.entryReplace = FCEntry() self.entryReplace.setToolTip(_("String to replace the one in the Find box throughout the text.")) self.sel_all_cb = QtWidgets.QCheckBox(_('All')) self.sel_all_cb.setToolTip(_("When checked it will replace all instances in the 'Find' box\n" "with the text in the 'Replace' box..")) self.button_copy_all = QtWidgets.QPushButton(_('Copy All')) self.button_copy_all.setToolTip(_("Will copy all the text in the Code Editor to the clipboard.")) self.button_copy_all.setMinimumWidth(100) self.buttonOpen = QtWidgets.QPushButton(_('Open Code')) self.buttonOpen.setToolTip(_("Will open a text file in the editor.")) self.buttonSave = QtWidgets.QPushButton(_('Save Code')) self.buttonSave.setToolTip(_("Will save the text in the editor into a file.")) self.buttonRun = QtWidgets.QPushButton(_('Run Code')) self.buttonRun.setToolTip(_("Will run the TCL commands found in the text file, one by one.")) self.buttonRun.hide() self.work_editor_layout.addWidget(self.code_editor, 0, 0, 1, 5) editor_hlay_1 = QtWidgets.QHBoxLayout() # cnc_tab_lay_1.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) editor_hlay_1.addWidget(self.buttonFind) editor_hlay_1.addWidget(self.entryFind) editor_hlay_1.addWidget(self.buttonReplace) editor_hlay_1.addWidget(self.entryReplace) editor_hlay_1.addWidget(self.sel_all_cb) editor_hlay_1.addWidget(self.button_copy_all) self.work_editor_layout.addLayout(editor_hlay_1, 1, 0, 1, 5) editor_hlay_2 = QtWidgets.QHBoxLayout() editor_hlay_2.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) editor_hlay_2.addWidget(self.buttonPreview) editor_hlay_2.addWidget(self.buttonPrint) self.work_editor_layout.addLayout(editor_hlay_2, 2, 0, 1, 1, QtCore.Qt.AlignLeft) cnc_tab_lay_4 = QtWidgets.QHBoxLayout() cnc_tab_lay_4.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) cnc_tab_lay_4.addWidget(self.buttonOpen) cnc_tab_lay_4.addWidget(self.buttonSave) cnc_tab_lay_4.addWidget(self.buttonRun) self.work_editor_layout.addLayout(cnc_tab_lay_4, 2, 4, 1, 1) # ################################################################################# # ################### SIGNALS ##################################################### # ################################################################################# self.code_editor.textChanged.connect(self.handleTextChanged) self.buttonOpen.clicked.connect(self.handleOpen) self.buttonSave.clicked.connect(self.handleSaveGCode) self.buttonPrint.clicked.connect(self.handlePrint) self.buttonPreview.clicked.connect(self.handlePreview) self.buttonFind.clicked.connect(self.handleFindGCode) self.buttonReplace.clicked.connect(self.handleReplaceGCode) self.button_copy_all.clicked.connect(self.handleCopyAll) self.code_editor.set_model_data(self.app.myKeywords) self.gcode_edited = '' self.script_code = '' def handlePrint(self): self.app.report_usage("handlePrint()") dialog = QtPrintSupport.QPrintDialog() if dialog.exec_() == QtWidgets.QDialog.Accepted: self.code_editor.document().print_(dialog.printer()) def handlePreview(self): self.app.report_usage("handlePreview()") dialog = QtPrintSupport.QPrintPreviewDialog() dialog.paintRequested.connect(self.code_editor.print_) dialog.exec_() def handleTextChanged(self): # enable = not self.ui.code_editor.document().isEmpty() # self.ui.buttonPrint.setEnabled(enable) # self.ui.buttonPreview.setEnabled(enable) pass def handleOpen(self, filt=None): self.app.report_usage("handleOpen()") if filt: _filter_ = filt else: _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \ "All Files (*.*)" path, _f = QtWidgets.QFileDialog.getOpenFileName( caption=_('Open file'), directory=self.app.get_last_folder(), filter=_filter_) if path: file = QtCore.QFile(path) if file.open(QtCore.QIODevice.ReadOnly): stream = QtCore.QTextStream(file) self.gcode_edited = stream.readAll() self.code_editor.setPlainText(self.gcode_edited) file.close() def handleSaveGCode(self, name=None, filt=None): self.app.report_usage("handleSaveGCode()") if filt: _filter_ = filt else: _filter_ = "G-Code Files (*.nc);; G-Code Files (*.txt);; G-Code Files (*.tap);; G-Code Files (*.cnc);; " \ "All Files (*.*)" if name: obj_name = name else: try: obj_name = self.app.collection.get_active().options['name'] except AttributeError: obj_name = 'file' if filt is None: _filter_ = "FlatConfig Files (*.FlatConfig);;All Files (*.*)" try: filename = str(QtWidgets.QFileDialog.getSaveFileName( caption=_("Export G-Code ..."), directory=self.app.defaults["global_last_folder"] + '/' + str(obj_name), filter=_filter_ )[0]) except TypeError: filename = str(QtWidgets.QFileDialog.getSaveFileName(caption=_("Export G-Code ..."), filter=_filter_)[0]) if filename == "": self.app.inform.emit('[WARNING_NOTCL] %s' % _("Export Code cancelled.")) return else: try: my_gcode = self.code_editor.toPlainText() with open(filename, 'w') as f: for line in my_gcode: f.write(line) except FileNotFoundError: self.app.inform.emit('[WARNING] %s' % _("No such file or directory")) return except PermissionError: self.app.inform.emit('[WARNING] %s' % _("Permission denied, saving not possible.\n" "Most likely another app is holding the file open and not accessible.")) return # Just for adding it to the recent files list. if self.app.defaults["global_open_style"] is False: self.app.file_opened.emit("cncjob", filename) self.app.file_saved.emit("cncjob", filename) self.app.inform.emit('%s: %s' % (_("Saved to"), str(filename))) def handleFindGCode(self): self.app.report_usage("handleFindGCode()") flags = QtGui.QTextDocument.FindCaseSensitively text_to_be_found = self.entryFind.get_value() r = self.code_editor.find(str(text_to_be_found), flags) if r is False: self.code_editor.moveCursor(QtGui.QTextCursor.Start) def handleReplaceGCode(self): self.app.report_usage("handleReplaceGCode()") old = self.entryFind.get_value() new = self.entryReplace.get_value() if self.sel_all_cb.isChecked(): while True: cursor = self.code_editor.textCursor() cursor.beginEditBlock() flags = QtGui.QTextDocument.FindCaseSensitively # self.ui.editor is the QPlainTextEdit r = self.code_editor.find(str(old), flags) if r: qc = self.code_editor.textCursor() if qc.hasSelection(): qc.insertText(new) else: self.ui.code_editor.moveCursor(QtGui.QTextCursor.Start) break # Mark end of undo block cursor.endEditBlock() else: cursor = self.code_editor.textCursor() cursor.beginEditBlock() qc = self.code_editor.textCursor() if qc.hasSelection(): qc.insertText(new) # Mark end of undo block cursor.endEditBlock() def handleCopyAll(self): text = self.code_editor.toPlainText() self.app.clipboard.setText(text) self.app.inform.emit(_("Code Editor content copied to clipboard ...")) def handleRunCode(self): # trying to run a Tcl command without having the Shell open will create some warnings because the Tcl Shell # tries to print on a hidden widget, therefore show the dock if hidden if self.app.ui.shell_dock.isHidden(): self.app.ui.shell_dock.show() self.script_code = deepcopy(self.code_editor.toPlainText()) old_line = '' for tcl_command_line in self.app.script_code.splitlines(): # do not process lines starting with '#' = comment and empty lines if not tcl_command_line.startswith('#') and tcl_command_line != '': # id FlatCAM is run in Windows then replace all the slashes with # the UNIX style slash that TCL understands if sys.platform == 'win32': if "open" in tcl_command_line: tcl_command_line = tcl_command_line.replace('\\', '/') if old_line != '': new_command = old_line + tcl_command_line + '\n' else: new_command = tcl_command_line # execute the actual Tcl command try: self.app.shell.open_proccessing() # Disables input box. result = self.app.tcl.eval(str(new_command)) if result != 'None': self.app.shell.append_output(result + '\n') old_line = '' except tk.TclError: old_line = old_line + tcl_command_line + '\n' except Exception as e: log.debug("App.handleRunCode() --> %s" % str(e)) if old_line != '': # it means that the script finished with an error result = self.app.tcl.eval("set errorInfo") log.error("Exec command Exception: %s" % (result + '\n')) self.app.shell.append_error('ERROR: ' + result + '\n') self.app.shell.close_proccessing() def closeEvent(self, QCloseEvent): try: self.code_editor.textChanged.disconnect() except TypeError: pass try: self.buttonOpen.clicked.disconnect() except TypeError: pass try: self.buttonPrint.clicked.disconnect() except TypeError: pass try: self.buttonPreview.clicked.disconnect() except TypeError: pass try: self.buttonFind.clicked.disconnect() except TypeError: pass try: self.buttonReplace.clicked.disconnect() except TypeError: pass try: self.button_copy_all.clicked.disconnect() except TypeError: pass super().closeEvent(QCloseEvent)