# ########################################################## # FlatCAM: 2D Post-processing for Manufacturing # # http://flatcam.org # # Author: Juan Pablo Caram (c) # # Date: 2/5/2014 # # MIT Licence # # ########################################################## # ########################################################## # File modified by: Marius Stanciu # # ########################################################## from appEditors.AppTextEditor import AppTextEditor from appObjects.FlatCAMObj import * from appGUI.ObjectUI import * import tkinter as tk import sys import gettext import appTranslation as fcTranslate import builtins fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext class ScriptObject(FlatCAMObj): """ Represents a TCL script object. """ optionChanged = QtCore.pyqtSignal(str) ui_type = ScriptObjectUI def __init__(self, name): self.decimals = self.app.decimals log.debug("Creating a ScriptObject object...") FlatCAMObj.__init__(self, name) self.kind = "script" self.options.update({ "plot": True, "type": 'Script', "source_file": '', }) self.units = '' self.script_editor_tab = None self.ser_attrs = ['options', 'kind', 'source_file'] self.source_file = '' self.script_code = '' self.units_found = self.app.defaults['units'] def set_ui(self, ui): """ Sets the Object UI in Selected Tab for the FlatCAM Script type of object. :param ui: :return: """ FlatCAMObj.set_ui(self, ui) log.debug("ScriptObject.set_ui()") assert isinstance(self.ui, ScriptObjectUI), \ "Expected a ScriptObjectUI, got %s" % type(self.ui) self.units = self.app.defaults['units'].upper() self.units_found = self.app.defaults['units'] # Fill form fields only on object create self.to_form() # Show/Hide Advanced Options if self.app.defaults["global_app_level"] == 'b': self.ui.level.setText('%s' % _("Basic")) else: self.ui.level.setText('%s' % _("Advanced")) self.script_editor_tab = AppTextEditor(app=self.app, plain_text=True, parent=self.app.ui) # tab_here = False # # try to not add too many times a tab that it is already installed # for idx in range(self.app.ui.plot_tab_area.count()): # if self.app.ui.plot_tab_area.widget(idx).objectName() == self.options['name']: # tab_here = True # break # # # add the tab if it is not already added # if tab_here is False: # self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor")) # self.script_editor_tab.setObjectName(self.options['name']) # self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor")) # self.script_editor_tab.setObjectName(self.options['name']) # first clear previous text in text editor (if any) # self.script_editor_tab.code_editor.clear() # self.script_editor_tab.code_editor.setReadOnly(False) self.ui.autocomplete_cb.set_value(self.app.defaults['script_autocompleter']) self.on_autocomplete_changed(state=self.app.defaults['script_autocompleter']) self.script_editor_tab.buttonRun.show() # Switch plot_area to Script Editor tab self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab) flt = "FlatCAM Scripts (*.FlatScript);;All Files (*.*)" self.script_editor_tab.buttonOpen.clicked.disconnect() self.script_editor_tab.buttonOpen.clicked.connect(lambda: self.script_editor_tab.handleOpen(filt=flt)) self.script_editor_tab.buttonSave.clicked.disconnect() self.script_editor_tab.buttonSave.clicked.connect(lambda: self.script_editor_tab.handleSaveGCode(filt=flt)) self.script_editor_tab.buttonRun.clicked.connect(self.handle_run_code) self.script_editor_tab.handleTextChanged() self.ui.autocomplete_cb.stateChanged.connect(self.on_autocomplete_changed) self.ser_attrs = ['options', 'kind', 'source_file'] # ---------------------------------------------------- # # ----------- LOAD THE TEXT SOURCE FILE -------------- # # ---------------------------------------------------- # self.app.proc_container.view.set_busy(_("Loading...")) self.script_editor_tab.t_frame.hide() try: # self.script_editor_tab.code_editor.setPlainText(self.source_file) self.script_editor_tab.load_text(self.source_file, move_to_end=True) except Exception as e: log.debug("ScriptObject.set_ui() --> %s" % str(e)) self.script_editor_tab.t_frame.show() self.app.proc_container.view.set_idle() self.build_ui() def build_ui(self): FlatCAMObj.build_ui(self) tab_here = False # try to not add too many times a tab that it is already installed for idx in range(self.app.ui.plot_tab_area.count()): if self.app.ui.plot_tab_area.widget(idx).objectName() == self.options['name']: tab_here = True break # add the tab if it is not already added if tab_here is False: self.app.ui.plot_tab_area.addTab(self.script_editor_tab, '%s' % _("Script Editor")) self.script_editor_tab.setObjectName(self.options['name']) self.app.ui.plot_tab_area.setCurrentWidget(self.script_editor_tab) def parse_file(self, filename): """ Will set an attribute of the object, self.source_file, with the parsed data. :param filename: Tcl Script file to parse :return: None """ with open(filename, "r") as opened_script: script_content = opened_script.readlines() script_content = ''.join(script_content) self.source_file = script_content def handle_run_code(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.app.shell.open_processing() # Disables input box. # make sure that the pixmaps are not updated when running this as they will crash # TODO find why the pixmaps load crash when run from this object (perhaps another thread?) self.app.ui.fcinfo.lock_pmaps = True self.script_code = self.script_editor_tab.code_editor.toPlainText() old_line = '' for tcl_command_line in self.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: result = self.app.shell.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("ScriptObject.handleRunCode() --> %s" % str(e)) if old_line != '': # it means that the script finished with an error result = self.app.shell.tcl.eval("set errorInfo") log.error("Exec command Exception: %s\n" % result) self.app.shell.append_error('ERROR: %s\n' % result) self.app.ui.fcinfo.lock_pmaps = False self.app.shell.close_processing() def on_autocomplete_changed(self, state): if state: self.script_editor_tab.code_editor.completer_enable = True else: self.script_editor_tab.code_editor.completer_enable = False def mirror(self, axis, point): pass def offset(self, vect): pass def rotate(self, angle, point): pass def scale(self, xfactor, yfactor=None, point=None): pass def skew(self, angle_x, angle_y, point): pass def buffer(self, distance, join, factor=None): pass def bounds(self, flatten=False): return None, None, None, None def to_dict(self): """ Returns a representation of the object as a dictionary. Attributes to include are listed in ``self.ser_attrs``. :return: A dictionary-encoded copy of the object. :rtype: dict """ d = {} for attr in self.ser_attrs: d[attr] = getattr(self, attr) return d def from_dict(self, d): """ Sets object's attributes from a dictionary. Attributes to include are listed in ``self.ser_attrs``. This method will look only for only and all the attributes in ``self.ser_attrs``. They must all be present. Use only for deserializing saved objects. :param d: Dictionary of attributes to set in the object. :type d: dict :return: None """ for attr in self.ser_attrs: setattr(self, attr, d[attr])