|
|
@@ -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)
|