From f9cbd78cd152b5c932f9c4fe5c8094f932f5c360 Mon Sep 17 00:00:00 2001 From: Juan Pablo Caram Date: Sat, 11 Jun 2016 21:33:38 -0400 Subject: [PATCH] Show messages and errors in TCL shell. Better exception handling and reporting when opening files. --- FlatCAMApp.py | 97 +++++++++++++++++++++++++++++++++++++++------------ camlib.py | 19 +++++----- 2 files changed, 85 insertions(+), 31 deletions(-) diff --git a/FlatCAMApp.py b/FlatCAMApp.py index dafc5448..fcdfc560 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -691,16 +691,35 @@ class App(QtCore.QObject): else: self.defaults['stats'][resource] = 1 + # TODO: This shouldn't be here. class TclErrorException(Exception): """ this exception is deffined here, to be able catch it if we sucessfully handle all errors from shell command """ pass + def shell_message(self, msg, show=False, error=False): + """ + Shows a message on the FlatCAM Shell + + :param msg: Message to display. + :param show: Opens the shell. + :param error: Shows the message as an error. + :return: None + """ + if show: + self.ui.shell_dock.show() + + if error: + self.shell.append_error(msg + "\n") + else: + self.shell.append_output(msg + "\n") + 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 + Raise exception if is different type than TclErrorException + this is here mainly to show unknown errors inside TCL shell console. + :param unknownException: :return: """ @@ -727,20 +746,20 @@ class App(QtCore.QObject): show_trace = int(self.defaults['verbose_error_level']) if show_trace > 0: - trc=traceback.format_list(traceback.extract_tb(exc_traceback)) - trc_formated=[] + 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, + 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 + text = "%s" % error else: - text=error + text = error - text = text.replace('[', '\\[').replace('"','\\"') + text = text.replace('[', '\\[').replace('"', '\\"') self.tcl.eval('return -code error "%s"' % text) @@ -782,7 +801,7 @@ class App(QtCore.QObject): try: self.shell.open_proccessing() result = self.tcl.eval(str(text)) - if result!='None': + if result != 'None': self.shell.append_output(result + '\n') except Tkinter.TclError, e: #this will display more precise answer if something in TCL shell fail @@ -836,16 +855,27 @@ class App(QtCore.QObject): def info(self, msg): """ - Writes on the status bar. + Informs the user. Normally on the status bar, optionally + also on the shell. :param msg: Text to write. + :param toshell: Forward the :return: None """ + + # Type of message in brackets at the begining of the message. match = re.search("\[([^\]]+)\](.*)", msg) if match: - self.ui.fcinfo.set_status(QtCore.QString(match.group(2)), level=match.group(1)) + level = match.group(1) + msg_ = match.group(2) + self.ui.fcinfo.set_status(QtCore.QString(msg_), level=level) + + error = level == "error" or level == "warning" + self.shell_message(msg, error=error, show=True) + else: self.ui.fcinfo.set_status(QtCore.QString(msg), level="info") + self.shell_message(msg) def load_defaults(self): """ @@ -1953,6 +1983,17 @@ class App(QtCore.QObject): self.log.error(str(e)) raise + except: + msg = "[error] An internal error has ocurred. See shell.\n" + msg += traceback.format_exc() + app_obj.inform.emit(msg) + raise + + if gerber_obj.is_empty(): + app_obj.inform.emit("[error] No geometry found in file: " + filename) + self.collection.set_active(gerber_obj.options["name"]) + self.collection.delete_active() + # Further parsing self.progress.emit(70) # TODO: Note the mixture of self and app_obj used here @@ -1997,18 +2038,31 @@ class App(QtCore.QObject): try: excellon_obj.parse_file(filename) + except IOError: app_obj.inform.emit("[error] Cannot open file: " + filename) self.progress.emit(0) # TODO: self and app_bjj mixed raise IOError("Cannot open file: " + filename) + except: + msg = "[error] An internal error has ocurred. See shell.\n" + msg += traceback.format_exc() + app_obj.inform.emit(msg) + raise + try: excellon_obj.create_geometry() - except Exception as e: - app_obj.inform.emit("[error] Failed to create geometry after parsing: " + filename) - self.progress.emit(0) - raise e + except: + msg = "[error] An internal error has ocurred. See shell.\n" + msg += traceback.format_exc() + app_obj.inform.emit(msg) + raise + + if excellon_obj.is_empty(): + app_obj.inform.emit("[error] No geometry found in file: " + filename) + self.collection.set_active(excellon_obj.options["name"]) + self.collection.delete_active() #self.progress.emit(70) with self.proc_container.new("Opening Excellon."): @@ -2479,8 +2533,8 @@ class App(QtCore.QObject): return "Could not retrieve object: %s" % name def geo_init_me(geo_obj, app_obj): - margin = kwa['margin']+kwa['dia']/2 - gap_size = kwa['dia']+kwa['gapsize'] + margin = kwa['margin'] + kwa['dia'] / 2 + gap_size = kwa['dia'] + kwa['gapsize'] minx, miny, maxx, maxy = obj.bounds() minx -= margin maxx += margin @@ -2519,9 +2573,8 @@ class App(QtCore.QObject): return 'Ok' - def geocutout(name=None, *args): - ''' + """ TCL shell command - see help section Subtract gaps from geometry, this will not create new object @@ -2529,7 +2582,7 @@ class App(QtCore.QObject): :param name: name of object :param args: array of arguments :return: "Ok" if completed without errors - ''' + """ try: a, kwa = h(*args) @@ -2569,7 +2622,7 @@ class App(QtCore.QObject): lenghty = (ymax - ymin) gapsize = kwa['gapsize'] + kwa['dia'] / 2 - if kwa['gaps'] == '8' or kwa['gaps']=='2lr': + if kwa['gaps'] == '8' or kwa['gaps'] == '2lr': subtract_rectangle(name, xmin - gapsize, diff --git a/camlib.py b/camlib.py index 89d99af4..2e7a82d9 100644 --- a/camlib.py +++ b/camlib.py @@ -158,6 +158,16 @@ class Geometry(object): log.error("Failed to run union on polylines.") raise + def is_empty(self): + + if isinstance(self.solid_geometry, BaseGeometry): + return self.solid_geometry.is_empty + + if isinstance(self.solid_geometry, list): + return len(self.solid_geometry) == 0 + + raise Exception("self.solid_geometry is neither BaseGeometry or list.") + def subtract_polygon(self, points): """ Subtract polygon from the given object. This only operates on the paths in the original geometry, i.e. it converts polygons into paths. @@ -390,15 +400,6 @@ class Geometry(object): """ return self.solid_geometry.buffer(offset) - def is_empty(self): - if self.solid_geometry is None: - return True - - if type(self.solid_geometry) is list and len(self.solid_geometry) == 0: - return True - - return False - def import_svg(self, filename, flip=True): """ Imports shapes from an SVG file into the object's geometry.