- finished the moving of all Tcl Shell stuff out of the FlatCAAMApp class to flatcamTools.ToolShell class
- updated the requirements.txt file to request that the Shapely package needs to be at least version 1.7.0 as it is needed in the latest versions of FlatCAM beta - some TOOD cleanups - minor changes
This commit is contained in:
379
FlatCAMApp.py
379
FlatCAMApp.py
@@ -73,8 +73,6 @@ from vispy.io import write_png
|
||||
|
||||
from flatcamTools import *
|
||||
|
||||
import tclCommands
|
||||
|
||||
import gettext
|
||||
import FlatCAMTranslation as fcTranslate
|
||||
import builtins
|
||||
@@ -1173,6 +1171,15 @@ class App(QtCore.QObject):
|
||||
# set FlatCAM units in the Status bar
|
||||
self.set_screen_units(self.defaults['units'])
|
||||
|
||||
# #############################################################################
|
||||
# ################################ AUTOSAVE SETUP #############################
|
||||
# #############################################################################
|
||||
|
||||
self.block_autosave = False
|
||||
self.autosave_timer = QtCore.QTimer(self)
|
||||
self.save_project_auto_update()
|
||||
self.autosave_timer.timeout.connect(self.save_project_auto)
|
||||
|
||||
# #############################################################################
|
||||
# ######################## UPDATE PREFERENCES GUI FORMS #######################
|
||||
# #############################################################################
|
||||
@@ -1960,15 +1967,6 @@ class App(QtCore.QObject):
|
||||
self.worker_task.connect(self.workers.add_task)
|
||||
self.log.debug("Finished creating Workers crew.")
|
||||
|
||||
# #############################################################################
|
||||
# ################################ AUTOSAVE SETUP #############################
|
||||
# #############################################################################
|
||||
|
||||
self.block_autosave = False
|
||||
self.autosave_timer = QtCore.QTimer(self)
|
||||
self.save_project_auto_update()
|
||||
self.autosave_timer.timeout.connect(self.save_project_auto)
|
||||
|
||||
# #############################################################################
|
||||
# ################################# Activity Monitor ##########################
|
||||
# #############################################################################
|
||||
@@ -2553,23 +2551,11 @@ class App(QtCore.QObject):
|
||||
# ####################################################################################
|
||||
# ####################### Shell SETUP ################################################
|
||||
# ####################################################################################
|
||||
# this will hold the TCL instance
|
||||
self.tcl = None
|
||||
|
||||
# the actual variable will be redeclared in setup_tcl()
|
||||
self.tcl_commands_storage = None
|
||||
|
||||
self.init_tcl()
|
||||
|
||||
self.shell = FCShell(self, version=self.version)
|
||||
self.shell._edit.set_model_data(self.myKeywords)
|
||||
self.shell.setWindowIcon(self.ui.app_icon)
|
||||
self.shell.setWindowTitle("FlatCAM Shell")
|
||||
self.shell.resize(*self.defaults["global_shell_shape"])
|
||||
self.shell._append_to_browser('in', "FlatCAM %s - " % self.version)
|
||||
self.shell.append_output('%s\n\n' % _("Type >help< to get started"))
|
||||
self.shell = FCShell(app=self, version=self.version)
|
||||
|
||||
self.ui.shell_dock.setWidget(self.shell)
|
||||
self.log.debug("TCL Shell has been initialized.")
|
||||
|
||||
# show TCL shell at start-up based on the Menu -? Edit -> Preferences setting.
|
||||
if self.defaults["global_shell_at_startup"]:
|
||||
@@ -4251,7 +4237,7 @@ class App(QtCore.QObject):
|
||||
# Object creation/instantiation
|
||||
obj = classdict[kind](name)
|
||||
|
||||
obj.units = self.options["units"] # TODO: The constructor should look at defaults.
|
||||
obj.units = self.options["units"]
|
||||
|
||||
# IMPORTANT
|
||||
# The key names in defaults and options dictionary's are not random:
|
||||
@@ -6128,9 +6114,10 @@ class App(QtCore.QObject):
|
||||
def on_fullscreen(self, disable=False):
|
||||
self.report_usage("on_fullscreen()")
|
||||
|
||||
flags = self.ui.windowFlags()
|
||||
if self.toggle_fscreen is False and disable is False:
|
||||
# self.ui.showFullScreen()
|
||||
self.ui.setWindowFlags(self.ui.windowFlags() | Qt.FramelessWindowHint)
|
||||
self.ui.setWindowFlags(flags | Qt.FramelessWindowHint)
|
||||
a = self.ui.geometry()
|
||||
self.x_pos = a.x()
|
||||
self.y_pos = a.y()
|
||||
@@ -6158,7 +6145,7 @@ class App(QtCore.QObject):
|
||||
self.ui.splitter_left.setVisible(False)
|
||||
self.toggle_fscreen = True
|
||||
elif self.toggle_fscreen is True or disable is True:
|
||||
self.ui.setWindowFlags(self.ui.windowFlags() & ~Qt.FramelessWindowHint)
|
||||
self.ui.setWindowFlags(flags & ~Qt.FramelessWindowHint)
|
||||
self.ui.setGeometry(self.x_pos, self.y_pos, self.width, self.height)
|
||||
self.ui.showNormal()
|
||||
self.restore_toolbar_view()
|
||||
@@ -8312,7 +8299,7 @@ class App(QtCore.QObject):
|
||||
:return: None
|
||||
"""
|
||||
if self.is_legacy is False:
|
||||
self.plotcanvas.update() # TODO: Need update canvas?
|
||||
self.plotcanvas.update()
|
||||
else:
|
||||
self.plotcanvas.auto_adjust_axes()
|
||||
|
||||
@@ -8320,7 +8307,6 @@ class App(QtCore.QObject):
|
||||
self.collection.update_view()
|
||||
# self.inform.emit(_("Plots updated ..."))
|
||||
|
||||
# TODO: Rework toolbar 'clear', 'replot' functions
|
||||
def on_toolbar_replot(self):
|
||||
"""
|
||||
Callback for toolbar button. Re-plots all objects.
|
||||
@@ -8372,7 +8358,6 @@ class App(QtCore.QObject):
|
||||
def on_collection_updated(self, obj, state, old_name):
|
||||
"""
|
||||
Create a menu from the object loaded in the collection.
|
||||
TODO: should use the collection model to do this
|
||||
|
||||
:param obj: object that was changed (added, deleted, renamed)
|
||||
:param state: what was done with the object. Can be: added, deleted, delete_all, renamed
|
||||
@@ -8572,8 +8557,11 @@ class App(QtCore.QObject):
|
||||
grid_toggle.triggered.connect(lambda: self.ui.grid_snap_btn.trigger())
|
||||
|
||||
def set_grid(self):
|
||||
self.ui.grid_gap_x_entry.setText(self.sender().text())
|
||||
self.ui.grid_gap_y_entry.setText(self.sender().text())
|
||||
menu_action = self.sender()
|
||||
assert isinstance(menu_action, QtWidgets.QAction), "Expected QAction got %s" % type(menu_action)
|
||||
|
||||
self.ui.grid_gap_x_entry.setText(menu_action.text())
|
||||
self.ui.grid_gap_y_entry.setText(menu_action.text())
|
||||
|
||||
def on_grid_add(self):
|
||||
# ## Current application units in lower Case
|
||||
@@ -9324,7 +9312,8 @@ class App(QtCore.QObject):
|
||||
"""
|
||||
Returns the application to its startup state. This method is thread-safe.
|
||||
|
||||
:return: None
|
||||
:param cli: Boolean. If True this method was run from command line
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.report_usage("on_file_new")
|
||||
@@ -9440,7 +9429,7 @@ class App(QtCore.QObject):
|
||||
self.report_usage("obj_move()")
|
||||
self.move_tool.run(toggle=False)
|
||||
|
||||
def on_fileopengerber(self, signal: bool = None, name=None):
|
||||
def on_fileopengerber(self, signal, name=None):
|
||||
"""
|
||||
File menu callback for opening a Gerber.
|
||||
|
||||
@@ -9487,7 +9476,7 @@ class App(QtCore.QObject):
|
||||
if filename != '':
|
||||
self.worker_task.emit({'fcn': self.open_gerber, 'params': [filename]})
|
||||
|
||||
def on_fileopenexcellon(self, signal: bool = None, name=None):
|
||||
def on_fileopenexcellon(self, signal, name=None):
|
||||
"""
|
||||
File menu callback for opening an Excellon file.
|
||||
|
||||
@@ -9524,7 +9513,7 @@ class App(QtCore.QObject):
|
||||
if filename != '':
|
||||
self.worker_task.emit({'fcn': self.open_excellon, 'params': [filename]})
|
||||
|
||||
def on_fileopengcode(self, signal: bool = None, name=None):
|
||||
def on_fileopengcode(self, signal, name=None):
|
||||
"""
|
||||
|
||||
File menu call back for opening gcode.
|
||||
@@ -9566,7 +9555,7 @@ class App(QtCore.QObject):
|
||||
if filename != '':
|
||||
self.worker_task.emit({'fcn': self.open_gcode, 'params': [filename, None, True]})
|
||||
|
||||
def on_file_openproject(self, signal: bool = None):
|
||||
def on_file_openproject(self, signal):
|
||||
"""
|
||||
File menu callback for opening a project.
|
||||
|
||||
@@ -9597,12 +9586,13 @@ class App(QtCore.QObject):
|
||||
# thread safe. The new_project()
|
||||
self.open_project(filename)
|
||||
|
||||
def on_fileopenhpgl2(self, signal: bool = None, name=None):
|
||||
def on_fileopenhpgl2(self, signal, name=None):
|
||||
"""
|
||||
File menu callback for opening a HPGL2.
|
||||
|
||||
:param signal: required because clicking the entry will generate a checked signal which needs a container
|
||||
:return: None
|
||||
:param signal: required because clicking the entry will generate a checked signal which needs a container
|
||||
:param name:
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.report_usage("on_fileopenhpgl2")
|
||||
@@ -9635,12 +9625,12 @@ class App(QtCore.QObject):
|
||||
if filename != '':
|
||||
self.worker_task.emit({'fcn': self.open_hpgl2, 'params': [filename]})
|
||||
|
||||
def on_file_openconfig(self, signal: bool = None):
|
||||
def on_file_openconfig(self, signal):
|
||||
"""
|
||||
File menu callback for opening a config file.
|
||||
|
||||
:param signal: required because clicking the entry will generate a checked signal which needs a container
|
||||
:return: None
|
||||
:param signal: required because clicking the entry will generate a checked signal which needs a container
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.report_usage("on_file_openconfig")
|
||||
@@ -9726,8 +9716,7 @@ class App(QtCore.QObject):
|
||||
image = _screenshot()
|
||||
data = np.asarray(image)
|
||||
if not data.ndim == 3 and data.shape[-1] in (3, 4):
|
||||
self.inform.emit('[[WARNING_NOTCL]] %s' %
|
||||
_('Data must be a 3D array with last dimension 3 or 4'))
|
||||
self.inform.emit('[[WARNING_NOTCL]] %s' % _('Data must be a 3D array with last dimension 3 or 4'))
|
||||
return
|
||||
|
||||
filter_ = "PNG File (*.png);;All Files (*.*)"
|
||||
@@ -9770,8 +9759,7 @@ class App(QtCore.QObject):
|
||||
|
||||
# Check for more compatible types and add as required
|
||||
if not isinstance(obj, FlatCAMGerber):
|
||||
self.inform.emit('[ERROR_NOTCL] %s' %
|
||||
_("Failed. Only Gerber objects can be saved as Gerber files..."))
|
||||
self.inform.emit('[ERROR_NOTCL] %s' % _("Failed. Only Gerber objects can be saved as Gerber files..."))
|
||||
return
|
||||
|
||||
name = self.collection.get_active().options["name"]
|
||||
@@ -10392,7 +10380,6 @@ class App(QtCore.QObject):
|
||||
|
||||
# The Qt methods above will return a QString which can cause problems later.
|
||||
# So far json.dump() will fail to serialize it.
|
||||
# TODO: Improve the serialization methods and remove this fix.
|
||||
filename = str(filename)
|
||||
|
||||
if filename == "":
|
||||
@@ -10853,7 +10840,6 @@ class App(QtCore.QObject):
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(obj_name))
|
||||
except Exception:
|
||||
# TODO: The return behavior has not been established... should raise exception?
|
||||
return "Could not retrieve object: %s" % obj_name
|
||||
else:
|
||||
obj = local_use
|
||||
@@ -11003,7 +10989,6 @@ class App(QtCore.QObject):
|
||||
try:
|
||||
obj = self.collection.get_by_name(str(obj_name))
|
||||
except Exception:
|
||||
# TODO: The return behavior has not been established... should raise exception?
|
||||
return "Could not retrieve object: %s" % obj_name
|
||||
else:
|
||||
obj = local_use
|
||||
@@ -11806,7 +11791,6 @@ class App(QtCore.QObject):
|
||||
|
||||
:return:
|
||||
"""
|
||||
# TODO: Move this to constructor
|
||||
icons = {
|
||||
"gerber": self.resource_location + "/flatcam_icon16.png",
|
||||
"excellon": self.resource_location + "/drill16.png",
|
||||
@@ -11990,32 +11974,38 @@ class App(QtCore.QObject):
|
||||
tsize = fsize + int(fsize / 2)
|
||||
|
||||
# selected_text = (_('''
|
||||
# <p><span style="font-size:{tsize}px"><strong>Selected Tab - Choose an Item from Project Tab</strong></span></p>
|
||||
# <p><span style="font-size:{tsize}px"><strong>Selected Tab - Choose an Item from Project Tab</strong></span>
|
||||
# </p>
|
||||
#
|
||||
# <p><span style="font-size:{fsize}px"><strong>Details</strong>:<br />
|
||||
# The normal flow when working in FlatCAM is the following:</span></p>
|
||||
#
|
||||
# <ol>
|
||||
# <li><span style="font-size:{fsize}px">Loat/Import a Gerber, Excellon, Gcode, DXF, Raster Image or SVG file into
|
||||
# <li><span style="font-size:{fsize}px">Loat/Import a Gerber, Excellon, Gcode, DXF, Raster Image or SVG
|
||||
# file into
|
||||
# FlatCAM using either the menu's, toolbars, key shortcuts or
|
||||
# even dragging and dropping the files on the GUI.<br />
|
||||
# <br />
|
||||
# You can also load a <strong>FlatCAM project</strong> by double clicking on the project file, drag & drop of the
|
||||
# You can also load a <strong>FlatCAM project</strong> by double clicking on the project file, drag &
|
||||
# drop of the
|
||||
# file into the FLATCAM GUI or through the menu/toolbar links offered within the app.</span><br />
|
||||
# </li>
|
||||
# <li><span style="font-size:{fsize}px">Once an object is available in the Project Tab, by selecting it and then
|
||||
# <li><span style="font-size:{fsize}px">Once an object is available in the Project Tab, by selecting it
|
||||
# and then
|
||||
# focusing on <strong>SELECTED TAB </strong>(more simpler is to double click the object name in the
|
||||
# Project Tab), <strong>SELECTED TAB </strong>will be updated with the object properties according to
|
||||
# it's kind: Gerber, Excellon, Geometry or CNCJob object.<br />
|
||||
# <br />
|
||||
# If the selection of the object is done on the canvas by single click instead, and the <strong>SELECTED TAB</strong>
|
||||
# If the selection of the object is done on the canvas by single click instead, and the
|
||||
# <strong>SELECTED TAB</strong>
|
||||
# is in focus, again the object properties will be displayed into the Selected Tab. Alternatively,
|
||||
# double clicking on the object on the canvas will bring the <strong>SELECTED TAB</strong> and populate
|
||||
# it even if it was out of focus.<br />
|
||||
# <br />
|
||||
# You can change the parameters in this screen and the flow direction is like this:<br />
|
||||
# <br />
|
||||
# <strong>Gerber/Excellon Object</strong> -> Change Param -> Generate Geometry -><strong> Geometry Object
|
||||
# <strong>Gerber/Excellon Object</strong> -> Change Param -> Generate Geometry ->
|
||||
# <strong> Geometry Object
|
||||
# </strong>-> Add tools (change param in Selected Tab) -> Generate CNCJob -><strong> CNCJob Object
|
||||
# </strong>-> Verify GCode (through Edit CNC Code) and/or append/prepend to GCode (again, done in
|
||||
# <strong>SELECTED TAB) </strong>-> Save GCode</span></li>
|
||||
@@ -12128,7 +12118,7 @@ class App(QtCore.QObject):
|
||||
# no_stats dict; just so it won't break things on website
|
||||
no_ststs_dict = {}
|
||||
no_ststs_dict["global_ststs"] = {}
|
||||
full_url = App.version_url + "?s=" + str(self.defaults['global_serial']) + "&v=" + str(self.version) + \
|
||||
full_url = App.version_url + "?s=" + str(self.defaults['global_serial']) + "&v=" + str(self.version) +\
|
||||
"&os=" + str(self.os) + "&" + urllib.parse.urlencode(no_ststs_dict["global_ststs"])
|
||||
|
||||
App.log.debug("Checking for updates @ %s" % full_url)
|
||||
@@ -12441,7 +12431,7 @@ class App(QtCore.QObject):
|
||||
new_color = self.defaults['gerber_plot_fill']
|
||||
clicked_action = self.sender()
|
||||
|
||||
assert isinstance(clicked_action, QAction), "Expected a QAction, got %s" % isinstance(clicked_action, QAction)
|
||||
assert isinstance(clicked_action, QAction), "Expected a QAction, got %s" % type(clicked_action)
|
||||
act_name = clicked_action.text()
|
||||
sel_obj_list = self.collection.get_selected()
|
||||
|
||||
@@ -12717,8 +12707,12 @@ class App(QtCore.QObject):
|
||||
:return:
|
||||
"""
|
||||
log.debug("App.save_project_auto_update() --> updated the interval timeout.")
|
||||
if self.autosave_timer.isActive():
|
||||
self.autosave_timer.stop()
|
||||
try:
|
||||
if self.autosave_timer.isActive():
|
||||
self.autosave_timer.stop()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if self.defaults['global_autosave'] is True:
|
||||
self.autosave_timer.setInterval(int(self.defaults['global_autosave_timeout']))
|
||||
self.autosave_timer.start()
|
||||
@@ -12749,211 +12743,6 @@ class App(QtCore.QObject):
|
||||
self.options.update(self.defaults)
|
||||
# self.options_write_form()
|
||||
|
||||
def init_tcl(self):
|
||||
"""
|
||||
Initialize the TCL Shell. A dock widget that holds the GUI interface to the FlatCAM command line.
|
||||
:return: None
|
||||
"""
|
||||
if hasattr(self, 'tcl') and self.tcl is not None:
|
||||
# self.tcl = None
|
||||
# TODO we need to clean non default variables and procedures here
|
||||
# new object cannot be used here as it will not remember values created for next passes,
|
||||
# because tcl was executed in old instance of TCL
|
||||
pass
|
||||
else:
|
||||
self.tcl = tk.Tcl()
|
||||
self.setup_shell()
|
||||
self.log.debug("TCL Shell has been initialized.")
|
||||
|
||||
def setup_shell(self):
|
||||
"""
|
||||
Creates shell functions. Runs once at startup.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.log.debug("setup_shell()")
|
||||
|
||||
# def shelp(p=None):
|
||||
# pass
|
||||
|
||||
# --- Migrated to new architecture ---
|
||||
# def options(name):
|
||||
# ops = self.collection.get_by_name(str(name)).options
|
||||
# return '\n'.join(["%s: %s" % (o, ops[o]) for o in ops])
|
||||
|
||||
# def h(*args):
|
||||
# """
|
||||
# Pre-processes arguments to detect '-keyword value' pairs into dictionary
|
||||
# and standalone parameters into list.
|
||||
# """
|
||||
#
|
||||
# kwa = {}
|
||||
# a = []
|
||||
# n = len(args)
|
||||
# name = None
|
||||
# for i in range(n):
|
||||
# match = re.search(r'^-([a-zA-Z].*)', args[i])
|
||||
# if match:
|
||||
# assert name is None
|
||||
# name = match.group(1)
|
||||
# continue
|
||||
#
|
||||
# if name is None:
|
||||
# a.append(args[i])
|
||||
# else:
|
||||
# kwa[name] = args[i]
|
||||
# name = None
|
||||
#
|
||||
# return a, kwa
|
||||
|
||||
# @contextmanager
|
||||
# def wait_signal(signal, timeout=10000):
|
||||
# """
|
||||
# Block loop until signal emitted, timeout (ms) elapses
|
||||
# or unhandled exception happens in a thread.
|
||||
#
|
||||
# :param timeout: time after which the loop is exited
|
||||
# :param signal: Signal to wait for.
|
||||
# """
|
||||
# loop = QtCore.QEventLoop()
|
||||
#
|
||||
# # Normal termination
|
||||
# signal.connect(loop.quit)
|
||||
#
|
||||
# # Termination by exception in thread
|
||||
# self.thread_exception.connect(loop.quit)
|
||||
#
|
||||
# status = {'timed_out': False}
|
||||
#
|
||||
# def report_quit():
|
||||
# status['timed_out'] = True
|
||||
# loop.quit()
|
||||
#
|
||||
# yield
|
||||
#
|
||||
# # Temporarily change how exceptions are managed.
|
||||
# oeh = sys.excepthook
|
||||
# ex = []
|
||||
#
|
||||
# def except_hook(type_, value, traceback_):
|
||||
# ex.append(value)
|
||||
# oeh(type_, value, traceback_)
|
||||
#
|
||||
# sys.excepthook = except_hook
|
||||
#
|
||||
# # Terminate on timeout
|
||||
# if timeout is not None:
|
||||
# QtCore.QTimer.singleShot(timeout, report_quit)
|
||||
#
|
||||
# # # ## Block ## ##
|
||||
# loop.exec_()
|
||||
#
|
||||
# # Restore exception management
|
||||
# sys.excepthook = oeh
|
||||
# if ex:
|
||||
# self.raise_tcl_error(str(ex[0]))
|
||||
#
|
||||
# if status['timed_out']:
|
||||
# raise Exception('Timed out!')
|
||||
#
|
||||
# def make_docs():
|
||||
# output = ''
|
||||
# import collections
|
||||
# od = collections.OrderedDict(sorted(self.tcl_commands_storage.items()))
|
||||
# for cmd_, val in od.items():
|
||||
# output += cmd_ + ' \n' + ''.join(['~'] * len(cmd_)) + '\n'
|
||||
#
|
||||
# t = val['help']
|
||||
# usage_i = t.find('>')
|
||||
# if usage_i < 0:
|
||||
# expl = t
|
||||
# output += expl + '\n\n'
|
||||
# continue
|
||||
#
|
||||
# expl = t[:usage_i - 1]
|
||||
# output += expl + '\n\n'
|
||||
#
|
||||
# end_usage_i = t[usage_i:].find('\n')
|
||||
#
|
||||
# if end_usage_i < 0:
|
||||
# end_usage_i = len(t[usage_i:])
|
||||
# output += ' ' + t[usage_i:] + '\n No parameters.\n'
|
||||
# else:
|
||||
# extras = t[usage_i + end_usage_i + 1:]
|
||||
# parts = [s.strip() for s in extras.split('\n')]
|
||||
#
|
||||
# output += ' ' + t[usage_i:usage_i + end_usage_i] + '\n'
|
||||
# for p in parts:
|
||||
# output += ' ' + p + '\n\n'
|
||||
#
|
||||
# return output
|
||||
|
||||
'''
|
||||
Howto implement TCL shell commands:
|
||||
|
||||
All parameters passed to command should be possible to set as None and test it afterwards.
|
||||
This is because we need to see error caused in tcl,
|
||||
if None value as default parameter is not allowed TCL will return empty error.
|
||||
Use:
|
||||
def mycommand(name=None,...):
|
||||
|
||||
Test it like this:
|
||||
if name is None:
|
||||
|
||||
self.raise_tcl_error('Argument name is missing.')
|
||||
|
||||
When error ocurre, always use raise_tcl_error, never return "sometext" on error,
|
||||
otherwise we will miss it and processing will silently continue.
|
||||
Method raise_tcl_error pass error into TCL interpreter, then raise python exception,
|
||||
which is catched in exec_command and displayed in TCL shell console with red background.
|
||||
Error in console is displayed with TCL trace.
|
||||
|
||||
This behavior works only within main thread,
|
||||
errors with promissed tasks can be catched and detected only with log.
|
||||
TODO: this problem have to be addressed somehow, maybe rewrite promissing to be blocking somehow for
|
||||
TCL shell.
|
||||
|
||||
Kamil's comment: I will rewrite existing TCL commands from time to time to follow this rules.
|
||||
|
||||
'''
|
||||
|
||||
self.tcl_commands_storage = {}
|
||||
# commands = {
|
||||
# 'help': {
|
||||
# 'fcn': shelp,
|
||||
# 'help': _("Shows list of commands."),
|
||||
# 'description': ''
|
||||
# },
|
||||
# }
|
||||
|
||||
# Import/overwrite tcl commands as objects of TclCommand descendants
|
||||
# This modifies the variable 'commands'.
|
||||
tclCommands.register_all_commands(self, self.tcl_commands_storage)
|
||||
|
||||
# Add commands to the tcl interpreter
|
||||
for cmd in self.tcl_commands_storage:
|
||||
self.tcl.createcommand(cmd, self.tcl_commands_storage[cmd]['fcn'])
|
||||
|
||||
# Make the tcl puts function return instead of print to stdout
|
||||
self.tcl.eval('''
|
||||
rename puts original_puts
|
||||
proc puts {args} {
|
||||
if {[llength $args] == 1} {
|
||||
return "[lindex $args 0]"
|
||||
} else {
|
||||
eval original_puts $args
|
||||
}
|
||||
}
|
||||
''')
|
||||
|
||||
# TODO: This shouldn't be here.
|
||||
class TclErrorException(Exception):
|
||||
"""
|
||||
this exception is defined here, to be able catch it if we successfully handle all errors from shell command
|
||||
"""
|
||||
pass
|
||||
|
||||
def toggle_shell(self):
|
||||
"""
|
||||
Toggle shell: if is visible close it, if it is closed then open it
|
||||
@@ -13025,62 +12814,6 @@ class App(QtCore.QObject):
|
||||
except AttributeError:
|
||||
log.debug("shell_message() is called before Shell Class is instantiated. The message is: %s", str(msg))
|
||||
|
||||
def raise_tcl_unknown_error(self, unknownException):
|
||||
"""
|
||||
Raise exception if is different type than TclErrorException
|
||||
this is here mainly to show unknown errors inside TCL shell console.
|
||||
|
||||
:param unknownException:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if not isinstance(unknownException, self.TclErrorException):
|
||||
self.raise_tcl_error("Unknown error: %s" % str(unknownException))
|
||||
else:
|
||||
raise unknownException
|
||||
|
||||
def display_tcl_error(self, error, error_info=None):
|
||||
"""
|
||||
Escape bracket [ with '\' otherwise there is error
|
||||
"ERROR: missing close-bracket" instead of real error
|
||||
|
||||
:param error: it may be text or exception
|
||||
:param error_info: Some informations about the error
|
||||
:return: None
|
||||
"""
|
||||
|
||||
if isinstance(error, Exception):
|
||||
exc_type, exc_value, exc_traceback = error_info
|
||||
if not isinstance(error, self.TclErrorException):
|
||||
show_trace = 1
|
||||
else:
|
||||
show_trace = int(self.defaults['global_verbose_error_level'])
|
||||
|
||||
if show_trace > 0:
|
||||
trc = traceback.format_list(traceback.extract_tb(exc_traceback))
|
||||
trc_formated = []
|
||||
for a in reversed(trc):
|
||||
trc_formated.append(a.replace(" ", " > ").replace("\n", ""))
|
||||
text = "%s\nPython traceback: %s\n%s" % (exc_value, exc_type, "\n".join(trc_formated))
|
||||
else:
|
||||
text = "%s" % error
|
||||
else:
|
||||
text = error
|
||||
|
||||
text = text.replace('[', '\\[').replace('"', '\\"')
|
||||
self.tcl.eval('return -code error "%s"' % text)
|
||||
|
||||
def raise_tcl_error(self, text):
|
||||
"""
|
||||
This method pass exception from python into TCL as error, so we get stacktrace and reason
|
||||
|
||||
:param text: text of error
|
||||
:return: raise exception
|
||||
"""
|
||||
|
||||
self.display_tcl_error(text)
|
||||
raise self.TclErrorException(text)
|
||||
|
||||
|
||||
class ArgsThread(QtCore.QObject):
|
||||
open_signal = pyqtSignal(list)
|
||||
|
||||
Reference in New Issue
Block a user