diff --git a/FlatCAMApp.py b/FlatCAMApp.py index ad163d94..8d50fb69 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2107,11 +2107,11 @@ class App(QtCore.QObject): 'join_geometry', 'list_sys', 'listsys', 'milld', 'mills', 'milldrills', 'millslots', 'mirror', 'ncc', 'ncc_clear', 'ncr', 'new', 'new_geometry', 'non_copper_regions', 'offset', - 'open_excellon', 'open_gcode', 'open_gerber', 'open_project', 'options', 'paint', - 'pan', 'panel', 'panelize', 'plot_all', 'plot_objects', 'quit_flatcam', + 'open_excellon', 'open_gcode', 'open_gerber', 'open_project', 'options', 'origin', + 'paint', 'pan', 'panel', 'panelize', 'plot_all', 'plot_objects', 'quit_flatcam', 'save', 'save_project', - 'save_sys', 'scale', - 'set_active', 'set_sys', 'setsys', 'skew', 'subtract_poly', 'subtract_rectangle', + 'save_sys', 'scale', 'set_active', 'set_origin', 'set_sys', + 'setsys', 'skew', 'subtract_poly', 'subtract_rectangle', 'version', 'write_gcode' ] @@ -2488,6 +2488,7 @@ class App(QtCore.QObject): self.mp = None self.mm = None self.mr = None + self.mp_zc = None # when True, the app has to return from any thread self.abort_flag = False @@ -6781,16 +6782,56 @@ class App(QtCore.QObject): pass self.replot_signal[list].connect(origin_replot) - def on_set_zero_click(self, event): - # this function will be available only for mouse left click + def on_set_zero_click(self, event, location=None, noplot=False, use_thread=True): + """ - if self.is_legacy is False: - event_pos = event.pos - else: - event_pos = (event.xdata, event.ydata) + :param event: + :param location: + :param noplot: + :param use_thread: + :return: + """ + noplot_sig = noplot + + def worker_task(): + with self.proc_container.new(_("Setting Origin...")): + for obj in self.collection.get_list(): + obj.offset((x, y)) + self.object_changed.emit(obj) + + # Update the object bounding box options + a, b, c, d = obj.bounds() + obj.options['xmin'] = a + obj.options['ymin'] = b + obj.options['xmax'] = c + obj.options['ymax'] = d + self.inform.emit('[success] %s...' % + _('Origin set')) + if noplot_sig is False: + self.replot_signal.emit([]) + + if location is not None: + if len(location) != 2: + self.inform.emit('[ERROR_NOTCL] %s...' % + _("Origin coordinates specified but incomplete.")) + return 'fail' + + x, y = location + + if use_thread is True: + self.worker_task.emit({'fcn': worker_task, 'params': []}) + else: + worker_task() + self.should_we_save = True + return - pos_canvas = self.plotcanvas.translate_coords(event_pos) if event.button == 1: + if self.is_legacy is False: + event_pos = event.pos + else: + event_pos = (event.xdata, event.ydata) + pos_canvas = self.plotcanvas.translate_coords(event_pos) + if self.grid_status() == True: pos = self.geo_editor.snap(pos_canvas[0], pos_canvas[1]) else: @@ -6799,23 +6840,10 @@ class App(QtCore.QObject): x = 0 - pos[0] y = 0 - pos[1] - def worker_task(): - with self.proc_container.new(_("Setting Origin...")): - for obj in self.collection.get_list(): - obj.offset((x, y)) - self.object_changed.emit(obj) - - # Update the object bounding box options - a, b, c, d = obj.bounds() - obj.options['xmin'] = a - obj.options['ymin'] = b - obj.options['xmax'] = c - obj.options['ymax'] = d - self.inform.emit('[success]%s...' % - _('Origin set')) - self.replot_signal.emit([]) - - self.worker_task.emit({'fcn': worker_task, 'params': []}) + if use_thread is True: + self.worker_task.emit({'fcn': worker_task, 'params': []}) + else: + worker_task() self.should_we_save = True def on_jump_to(self, custom_location=None, fit_center=True): diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 48a7861e..2d332165 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -85,8 +85,7 @@ class FlatCAMObj(QtCore.QObject): if self.app.is_legacy is False: self.shapes = self.app.plotcanvas.new_shape_group() else: - # dont't give an axis name to this one or it will delete the old object when loading a new one - self.shapes = ShapeCollectionLegacy(obj=self, app=self.app) + self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name=name) # self.mark_shapes = self.app.plotcanvas.new_shape_collection(layers=2) self.mark_shapes = {} diff --git a/README.md b/README.md index 02b9f288..02ea4bf9 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ CAD program, and create G-Code for Isolation routing. - in legacy graphic engine, fixed bug that made the old object disappear when a new object was loaded - in legacy graphic engine, fixed bug that crashed the app when creating a new project +- in legacy graphic engine, fixed a bug that when deleting an object all objects where deleted +- added a new TclCommand named set_origin which will set the origin for all loaded objects to zero if the -auto True argument is used and to a certain x,y location if the format is: set_origin 5,7 22.09.2019 diff --git a/camlib.py b/camlib.py index 8730a6cf..08f76b8b 100644 --- a/camlib.py +++ b/camlib.py @@ -5041,7 +5041,7 @@ class Excellon(Geometry): def bounds(self): """ Returns coordinates of rectangular bounds - of Gerber geometry: (xmin, ymin, xmax, ymax). + of Excellon geometry: (xmin, ymin, xmax, ymax). """ # fixed issue of getting bounds only for one level lists of objects # now it can get bounds for nested lists of objects diff --git a/tclCommands/TclCommandSetOrigin.py b/tclCommands/TclCommandSetOrigin.py new file mode 100644 index 00000000..01cfe944 --- /dev/null +++ b/tclCommands/TclCommandSetOrigin.py @@ -0,0 +1,89 @@ +from tclCommands.TclCommand import TclCommand +from ObjectCollection import * + +from camlib import get_bounds + +import gettext +import FlatCAMTranslation as fcTranslate +import builtins + +fcTranslate.apply_language('strings') +if '_' not in builtins.__dict__: + _ = gettext.gettext + + +class TclCommandSetOrigin(TclCommand): + """ + Tcl shell command to clear the text in the Tcl Shell browser. + + example: + + """ + + # List of all command aliases, to be able use old names for backward compatibility (add_poly, add_polygon) + aliases = ['set_origin', 'origin'] + + # Dictionary of types from Tcl command, needs to be ordered + arg_names = collections.OrderedDict([ + ('loc', str) + ]) + + # Dictionary of types from Tcl command, needs to be ordered , this is for options like -optionname value + option_types = collections.OrderedDict([ + ('auto', bool) + ]) + + # array of mandatory options for current Tcl command: required = {'name','outname'} + required = [] + + # structured help for current command, args needs to be ordered + help = { + 'main': "Will set the origin at the specified x,y location.", + 'args': collections.OrderedDict([ + ('loc', 'Location to offset all the selected objects. No spaces between x and y pair. Use like this: 2,3'), + ('auto', 'If set to 1 it will set the origin to the minimum x, y of the object selection bounding box.' + '-auto=1 is not correct but -auto 1 or -auto True is correct.') + ]), + 'examples': ['set_origin 3,2', 'set_origin -auto 1'] + } + + def execute(self, args, unnamed_args): + """ + + :param args: + :param unnamed_args: + :return: + """ + + print(args) + loc = list() + if 'auto' in args: + if args['auto'] == 1: + objs = self.app.collection.get_list() + minx, miny, __, ___ = get_bounds(objs) + + loc.append(0 - minx) + loc.append(0 - miny) + else: + loc = [0, 0] + elif 'loc' in args: + try: + location = [float(eval(coord)) for coord in str(args['loc']).split(",") if coord != ''] + except AttributeError as e: + log.debug("TclCommandSetOrigin.execute --> %s" % str(e)) + location = (0, 0) + + loc.append(location[0]) + loc.append(location[1]) + + if len(location) != 2: + self.raise_tcl_error('%s: %s' % ( + _("Expected a pair of (x, y) coordinates. Got"), str(len(location)))) + return 'fail' + else: + loc = [0, 0] + + self.app.on_set_zero_click(event=None, location=loc, noplot=True, use_thread=False) + self.app.inform.emit('[success] Tcl %s: %s' % + (_('Origin set by offsetting all loaded objects with '), + '{0:.4f}, {0:.4f}'.format(loc[0], loc[1]))) diff --git a/tclCommands/__init__.py b/tclCommands/__init__.py index 1c7ac5b9..9899e8b4 100644 --- a/tclCommands/__init__.py +++ b/tclCommands/__init__.py @@ -53,6 +53,7 @@ import tclCommands.TclCommandSaveProject import tclCommands.TclCommandSaveSys import tclCommands.TclCommandScale import tclCommands.TclCommandSetActive +import tclCommands.TclCommandSetOrigin import tclCommands.TclCommandSetSys import tclCommands.TclCommandSkew import tclCommands.TclCommandSubtractPoly