- added a new Tcl Command named SetPath which will set a path to be used by the Tcl commands. Once set will serve as a fallback path in case that the files fail to be opened first time. It will be persistent, saved in preferences. - added the GUI for the new Open Example in the FIle -> Scripting menu. - I am modifying all the open ... handlers to add a parameter that will flag if the method was launched from Tcl Shell. This way if the method will fail to open the filename (which include the path) it will try to open from a set fallback path. - fixed issue #406, bug introduced recently (leftover changes). - modified the ImportSVG Tcl command name to OpenSVG (open_svg alias) - added a new Tcl command named OpenDXF (open_dxf alias) - fixed some errors in Scripting features - added a new Tcl command named GetPath as a convenient way to get the current default path stored in App.defaults['global_tcl_path']
232 lines
8.5 KiB
Python
232 lines
8.5 KiB
Python
# ##########################################################
|
|
# 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 flatcamEditors.FlatCAMTextEditor import TextEditor
|
|
from flatcamObjects.FlatCAMObj import *
|
|
from flatcamGUI.ObjectUI import *
|
|
|
|
import tkinter as tk
|
|
import sys
|
|
from copy import deepcopy
|
|
|
|
import gettext
|
|
import FlatCAMTranslation 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.ser_attrs = ['options', 'kind', 'source_file']
|
|
self.source_file = ''
|
|
self.script_code = ''
|
|
|
|
self.units_found = self.app.defaults['units']
|
|
|
|
# self.script_editor_tab = TextEditor(app=self.app, plain_text=True)
|
|
self.script_editor_tab = TextEditor(app=self.app, plain_text=True)
|
|
|
|
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(_(
|
|
'<span style="color:green;"><b>Basic</b></span>'
|
|
))
|
|
else:
|
|
self.ui.level.setText(_(
|
|
'<span style="color:red;"><b>Advanced</b></span>'
|
|
))
|
|
|
|
# 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 CNCJob 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)
|
|
# for line in self.source_file.splitlines():
|
|
# QtWidgets.QApplication.processEvents()
|
|
# self.script_editor_tab.code_editor.append(line)
|
|
except Exception as e:
|
|
log.debug("ScriptObject.set_ui() --> %s" % str(e))
|
|
|
|
self.script_editor_tab.code_editor.moveCursor(QtGui.QTextCursor.End)
|
|
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)
|
|
|
|
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.script_code = deepcopy(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:
|
|
self.app.shell.open_processing() # Disables input box.
|
|
|
|
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.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 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])
|