diff --git a/CHANGELOG.md b/CHANGELOG.md index a1cab36b..d6fc2717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,15 @@ CHANGELOG for FlatCAM beta ================================================= +5.05.2020 + +- fixed an issue that made the preprocessors comboxes in Preferences not to load and display the saved value fro the file +- some PEP8 corrections + 4.05.2020 - in detachable tabs, Linux loose the reference of the detached tab and on close of the detachable tabs will gave a 'segmantation fault' error. Solved it by not deleting the reference in case of Unix-like systems -- some strigns added to translation strings +- some strings added to translation strings 3.05.2020 diff --git a/FlatCAMApp.py b/FlatCAMApp.py index dc26b5de..923a599e 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -543,52 +543,15 @@ class App(QtCore.QObject): self.save_project_auto_update() self.autosave_timer.timeout.connect(self.save_project_auto) - # ########################################################################################################### - # ##################################### UPDATE PREFERENCES GUI FORMS ######################################## - # ########################################################################################################### - - self.preferencesUiManager = PreferencesUIManager(defaults=self.defaults, data_path=self.data_path, ui=self.ui, - inform=self.inform) - self.preferencesUiManager.defaults_write_form() - - # When the self.defaults dictionary changes will update the Preferences GUI forms - self.defaults.set_change_callback(self.on_defaults_dict_change) - - # ########################################################################################################### - # ##################################### FIRST RUN SECTION ################################################### - # ################################ It's done only once after install ##################################### - # ########################################################################################################### - - if self.defaults["first_run"] is True: - - # ONLY AT FIRST STARTUP INIT THE GUI LAYOUT TO 'COMPACT' - initial_lay = 'minimal' - self.ui.general_defaults_form.general_gui_group.on_layout(lay=initial_lay) - - # Set the combobox in Preferences to the current layout - idx = self.ui.general_defaults_form.general_gui_group.layout_combo.findText(initial_lay) - self.ui.general_defaults_form.general_gui_group.layout_combo.setCurrentIndex(idx) - - # after the first run, this object should be False - self.defaults["first_run"] = False - self.preferencesUiManager.save_defaults(silent=True) - - # ########################################################################################################### - # ############################################ Data ######################################################### - # ########################################################################################################### - - self.recent = [] - self.recent_projects = [] - - self.clipboard = QtWidgets.QApplication.clipboard() - - self.project_filename = None - self.toggle_units_ignore = False - # ########################################################################################################### # #################################### LOAD PREPROCESSORS ################################################### # ########################################################################################################### + # ----------------------------------------- WARNING -------------------------------------------------------- + # Preprocessors need to be loaded before the Preferences Manager builds the Preferences + # That's because the number of preprocessors can vary and here the comboboxes are populated + # ----------------------------------------------------------------------------------------------------------- + # a dictionary that have as keys the name of the preprocessor files and the value is the class from # the preprocessor file self.preprocessors = load_preprocessors(self) @@ -621,6 +584,46 @@ class App(QtCore.QObject): self.ui.excellon_defaults_form.excellon_opt_group.pp_excellon_name_cb.addItem(name) + # ########################################################################################################### + # ##################################### UPDATE PREFERENCES GUI FORMS ######################################## + # ########################################################################################################### + + self.preferencesUiManager = PreferencesUIManager(defaults=self.defaults, data_path=self.data_path, ui=self.ui, + inform=self.inform) + self.preferencesUiManager.defaults_write_form() + + # When the self.defaults dictionary changes will update the Preferences GUI forms + self.defaults.set_change_callback(self.on_defaults_dict_change) + + # ########################################################################################################### + # ##################################### FIRST RUN SECTION ################################################### + # ################################ It's done only once after install ##################################### + # ########################################################################################################### + if self.defaults["first_run"] is True: + # ONLY AT FIRST STARTUP INIT THE GUI LAYOUT TO 'COMPACT' + initial_lay = 'minimal' + self.ui.general_defaults_form.general_gui_group.on_layout(lay=initial_lay) + + # Set the combobox in Preferences to the current layout + idx = self.ui.general_defaults_form.general_gui_group.layout_combo.findText(initial_lay) + self.ui.general_defaults_form.general_gui_group.layout_combo.setCurrentIndex(idx) + + # after the first run, this object should be False + self.defaults["first_run"] = False + self.preferencesUiManager.save_defaults(silent=True) + + # ########################################################################################################### + # ############################################ Data ######################################################### + # ########################################################################################################### + + self.recent = [] + self.recent_projects = [] + + self.clipboard = QtWidgets.QApplication.clipboard() + + self.project_filename = None + self.toggle_units_ignore = False + # ########################################################################################################### # ########################################## LOAD LANGUAGES ################################################ # ########################################################################################################### @@ -2577,7 +2580,7 @@ class App(QtCore.QObject): self.date = self.date.replace(' ', '_') filter__ = "HTML File .html (*.html);;TXT File .txt (*.txt);;All Files (*.*)" - path_to_save = self.defaults["global_last_save_folder"] if\ + path_to_save = self.defaults["global_last_save_folder"] if \ self.defaults["global_last_save_folder"] is not None else self.data_path try: filename, _f = FCFileSaveDialog.get_saved_filename( @@ -3613,7 +3616,6 @@ class App(QtCore.QObject): sys.exit(0) else: os._exit(0) # fix to work with Python 3.8 - @staticmethod def kill_app(): diff --git a/camlib.py b/camlib.py index f4dff415..6e5bdcf2 100644 --- a/camlib.py +++ b/camlib.py @@ -249,7 +249,7 @@ class ApertureMacro: pol, dia, x, y = ApertureMacro.default2zero(4, mods) - return {"pol": int(pol), "geometry": Point(x, y).buffer(dia/2)} + return {"pol": int(pol), "geometry": Point(x, y).buffer(dia / 2)} @staticmethod def make_vectorline(mods): @@ -262,7 +262,7 @@ class ApertureMacro: pol, width, xs, ys, xe, ye, angle = ApertureMacro.default2zero(7, mods) line = LineString([(xs, ys), (xe, ye)]) - box = line.buffer(width/2, cap_style=2) + box = line.buffer(width / 2, cap_style=2) box_rotated = affinity.rotate(box, angle, origin=(0, 0)) return {"pol": int(pol), "geometry": box_rotated} @@ -278,7 +278,7 @@ class ApertureMacro: pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - box = shply_box(x-width/2, y-height/2, x+width/2, y+height/2) + box = shply_box(x - width / 2, y - height / 2, x + width / 2, y + height / 2) box_rotated = affinity.rotate(box, angle, origin=(0, 0)) return {"pol": int(pol), "geometry": box_rotated} @@ -294,7 +294,7 @@ class ApertureMacro: pol, width, height, x, y, angle = ApertureMacro.default2zero(6, mods) - box = shply_box(x, y, x+width, y+height) + box = shply_box(x, y, x + width, y + height) box_rotated = affinity.rotate(box, angle, origin=(0, 0)) return {"pol": int(pol), "geometry": box_rotated} @@ -309,12 +309,12 @@ class ApertureMacro: pol = mods[0] n = mods[1] - points = [(0, 0)]*(n+1) + points = [(0, 0)] * (n + 1) - for i in range(n+1): - points[i] = mods[2*i + 2:2*i + 4] + for i in range(n + 1): + points[i] = mods[2 * i + 2:2 * i + 4] - angle = mods[2*n + 4] + angle = mods[2 * n + 4] poly = Polygon(points) poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) @@ -333,11 +333,11 @@ class ApertureMacro: """ pol, nverts, x, y, dia, angle = ApertureMacro.default2zero(6, mods) - points = [(0, 0)]*nverts + points = [(0, 0)] * nverts for i in range(nverts): - points[i] = (x + 0.5 * dia * np.cos(2*np.pi * i/nverts), - y + 0.5 * dia * np.sin(2*np.pi * i/nverts)) + points[i] = (x + 0.5 * dia * np.cos(2 * np.pi * i / nverts), + y + 0.5 * dia * np.sin(2 * np.pi * i / nverts)) poly = Polygon(points) poly_rotated = affinity.rotate(poly, angle, origin=(0, 0)) @@ -358,9 +358,9 @@ class ApertureMacro: x, y, dia, thickness, gap, nrings, cross_th, cross_len, angle = ApertureMacro.default2zero(9, mods) - r = dia/2 - thickness/2 - result = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) # Need a copy! + r = dia / 2 - thickness / 2 + result = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0) + ring = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0) # Need a copy! i = 1 # Number of rings created so far @@ -370,13 +370,13 @@ class ApertureMacro: r -= thickness + gap if r <= 0: break - ring = Point((x, y)).buffer(r).exterior.buffer(thickness/2.0) + ring = Point((x, y)).buffer(r).exterior.buffer(thickness / 2.0) result = cascaded_union([result, ring]) i += 1 # ## Crosshair - hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th/2.0, cap_style=2) - ver = LineString([(x, y-cross_len), (x, y + cross_len)]).buffer(cross_th/2.0, cap_style=2) + hor = LineString([(x - cross_len, y), (x + cross_len, y)]).buffer(cross_th / 2.0, cap_style=2) + ver = LineString([(x, y - cross_len), (x, y + cross_len)]).buffer(cross_th / 2.0, cap_style=2) result = cascaded_union([result, hor, ver]) return {"pol": 1, "geometry": result} @@ -394,9 +394,9 @@ class ApertureMacro: x, y, dout, din, t, angle = ApertureMacro.default2zero(6, mods) - ring = Point((x, y)).buffer(dout/2.0).difference(Point((x, y)).buffer(din/2.0)) - hline = LineString([(x - dout/2.0, y), (x + dout/2.0, y)]).buffer(t/2.0, cap_style=3) - vline = LineString([(x, y - dout/2.0), (x, y + dout/2.0)]).buffer(t/2.0, cap_style=3) + ring = Point((x, y)).buffer(dout / 2.0).difference(Point((x, y)).buffer(din / 2.0)) + hline = LineString([(x - dout / 2.0, y), (x + dout / 2.0, y)]).buffer(t / 2.0, cap_style=3) + vline = LineString([(x, y - dout / 2.0), (x, y + dout / 2.0)]).buffer(t / 2.0, cap_style=3) thermal = ring.difference(hline.union(vline)) return {"pol": 1, "geometry": thermal} @@ -920,14 +920,16 @@ class Geometry(object): Creates contours around geometry at a given offset distance. - :param offset: Offset distance. - :type offset: float - :param iso_type: type of isolation, can be 0 = exteriors or 1 = interiors or 2 = both (complete) - :param corner: type of corner for the isolation: 0 = round; 1 = square; 2= beveled (line that connects the ends) - :param follow: whether the geometry to be isolated is a follow_geometry - :param passes: current pass out of possible multiple passes for which the isolation is done - :return: The buffered geometry. - :rtype: Shapely.MultiPolygon or Shapely.Polygon + :param offset: Offset distance. + :type offset: float + :param geometry The geometry to work with + :param iso_type: type of isolation, can be 0 = exteriors or 1 = interiors or 2 = both (complete) + :param corner: type of corner for the isolation: + 0 = round; 1 = square; 2= beveled (line that connects the ends) + :param follow: whether the geometry to be isolated is a follow_geometry + :param passes: current pass out of possible multiple passes for which the isolation is done + :return: The buffered geometry. + :rtype: Shapely.MultiPolygon or Shapely.Polygon """ if self.app.abort_flag: @@ -1194,7 +1196,7 @@ class Geometry(object): return 0 bounds = self.bounds() return bounds[2] - bounds[0], bounds[3] - bounds[1] - + def get_empty_area(self, boundary=None): """ Returns the complement of self.solid_geometry within @@ -1886,6 +1888,7 @@ class Geometry(object): # ## Index first and last points in paths def get_pts(o): return [o.coords[0], o.coords[-1]] + # # storage = FlatCAMRTreeStorage() # storage.get_points = get_pts @@ -1982,10 +1985,10 @@ class Geometry(object): the geometry appropriately. This call ``scale()``. Don't call it again in descendents. - :param units: "IN" or "MM" - :type units: str - :return: Scaling factor resulting from unit change. - :rtype: float + :param obj_units: "IN" or "MM" + :type units: str + :return: Scaling factor resulting from unit change. + :rtype: float """ if obj_units.upper() == self.units.upper(): @@ -2013,8 +2016,8 @@ class Geometry(object): 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 + :return: A dictionary-encoded copy of the object. + :rtype: dict """ d = {} for attr in self.ser_attrs: @@ -2030,9 +2033,9 @@ class Geometry(object): be present. Use only for deserializing saved objects. - :param d: Dictionary of attributes to set in the object. - :type d: dict - :return: None + :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]) @@ -2432,7 +2435,7 @@ class CNCjob(Geometry): pp_geometry_name='default', pp_excellon_name='default', depthpercut=0.1, z_pdepth=-0.02, spindlespeed=None, spindledir='CW', dwell=True, dwelltime=1000, - toolchangez=0.787402, toolchange_xy=[0.0, 0.0], + toolchangez=0.787402, toolchange_xy='0.0,0.0', endz=2.0, endxy='', segx=None, segy=None, @@ -2441,7 +2444,8 @@ class CNCjob(Geometry): self.decimals = self.app.decimals # Used when parsing G-code arcs - self.steps_per_circle = int(self.app.defaults['cncjob_steps_per_circle']) + self.steps_per_circle = steps_per_circle if steps_per_circle is not None else \ + int(self.app.defaults['cncjob_steps_per_circle']) Geometry.__init__(self, geo_steps_per_circle=self.steps_per_circle) @@ -2667,6 +2671,7 @@ class CNCjob(Geometry): if self.xy_toolchange == '': self.xy_toolchange = None else: + self.xy_toolchange = re.sub('[()\[\]]', '', str(self.xy_toolchange)) self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",") if self.xy_toolchange != ''] if self.xy_toolchange and len(self.xy_toolchange) < 2: self.app.inform.emit('[ERROR]%s' % @@ -2677,6 +2682,7 @@ class CNCjob(Geometry): log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> %s" % str(e)) pass + self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) self.xy_end = [float(eval(a)) for a in self.xy_end.split(",") if self.xy_end != ''] if self.xy_end and len(self.xy_end) < 2: self.app.inform.emit('[ERROR] %s' % _("The End Move X,Y field in Edit -> Preferences has to be " @@ -2689,7 +2695,7 @@ class CNCjob(Geometry): log.debug("Creating CNC Job from Excellon...") # Tools - + # sort the tools list by the second item in tuple (here we have a dict with diameter of the tool) # so we actually are sorting the tools by diameter # sorted_tools = sorted(exobj.tools.items(), key=lambda t1: t1['C']) @@ -2700,7 +2706,7 @@ class CNCjob(Geometry): sorted_tools = sorted(sort, key=lambda t1: t1[1]) if tools == "all": - tools = [i[0] for i in sorted_tools] # we get a array of ordered tools + tools = [i[0] for i in sorted_tools] # we get a array of ordered tools log.debug("Tools 'all' and sorted are: %s" % str(tools)) else: selected_tools = [x.strip() for x in tools.split(",")] # we strip spaces and also separate the tools by ',' @@ -3101,7 +3107,7 @@ class CNCjob(Geometry): raise grace self.tool = tool - self.postdata['toolC']=exobj.tools[tool]["C"] + self.postdata['toolC'] = exobj.tools[tool]["C"] self.tooldia = exobj.tools[tool]["C"] if self.use_ui: @@ -3577,7 +3583,8 @@ class CNCjob(Geometry): self.startz = float(startz) if startz is not None else None self.z_end = float(endz) if endz is not None else None - self.xy_end = [float(eval(a)) for a in endxy.split(",") if endxy != ''] + self.xy_end = re.sub('[()\[\]]', '', str(endxy)) + self.xy_end = [float(eval(a)) for a in self.xy_end.split(",") if endxy != ''] if self.xy_end and len(self.xy_end) < 2: self.app.inform.emit('[ERROR] %s' % _("The End Move X,Y field in Edit -> Preferences has to be " "in the format (x, y) but now there is only one value, not two.")) @@ -3595,7 +3602,8 @@ class CNCjob(Geometry): if toolchangexy == '': self.xy_toolchange = None else: - self.xy_toolchange = [float(eval(a)) for a in toolchangexy.split(",")] + self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) + self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")] if len(self.xy_toolchange) < 2: self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y field in Edit -> Preferences has to be " "in the format (x, y) \n" @@ -3693,7 +3701,7 @@ class CNCjob(Geometry): self.gcode = self.doformat(p.start_code) - self.gcode += self.doformat(p.feedrate_code) # sets the feed rate + self.gcode += self.doformat(p.feedrate_code) # sets the feed rate if toolchange is False: self.gcode += self.doformat(p.lift_code, x=0, y=0) # Move (up) to travel height @@ -3707,19 +3715,19 @@ class CNCjob(Geometry): self.gcode += self.doformat(p.toolchange_code) if 'laser' not in self.pp_geometry_name: - self.gcode += self.doformat(p.spindle_code) # Spindle start + self.gcode += self.doformat(p.spindle_code) # Spindle start else: # for laser this will disable the laser self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time else: if 'laser' not in self.pp_geometry_name: self.gcode += self.doformat(p.spindle_code) # Spindle start if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time total_travel = 0.0 total_cut = 0.0 @@ -3788,7 +3796,7 @@ class CNCjob(Geometry): total_travel = total_travel + abs(distance(pt1=current_pt, pt2=pt)) current_pt = geo.coords[-1] - pt, geo = storage.nearest(current_pt) # Next + pt, geo = storage.nearest(current_pt) # Next disp_number = int(np.interp(path_count, [0, geo_len], [0, 100])) if old_disp_number < disp_number <= 100: @@ -3961,7 +3969,9 @@ class CNCjob(Geometry): self.startz = float(startz) if startz is not None else self.app.defaults["geometry_startz"] self.z_end = float(endz) if endz is not None else self.app.defaults["geometry_endz"] + self.xy_end = endxy if endxy != '' else self.app.defaults["geometry_endxy"] + self.xy_end = re.sub('[()\[\]]', '', str(self.xy_end)) self.xy_end = [float(eval(a)) for a in self.xy_end.split(",") if self.xy_end != ''] if self.xy_end and len(self.xy_end) < 2: self.app.inform.emit('[ERROR] %s' % _("The End Move X,Y field in Edit -> Preferences has to be " @@ -3978,7 +3988,8 @@ class CNCjob(Geometry): if toolchangexy == '': self.xy_toolchange = None else: - self.xy_toolchange = [float(eval(a)) for a in toolchangexy.split(",")] + self.xy_toolchange = re.sub('[()\[\]]', '', str(toolchangexy)) + self.xy_toolchange = [float(eval(a)) for a in self.xy_toolchange.split(",")] if len(self.xy_toolchange) < 2: self.app.inform.emit( '[ERROR] %s' % @@ -4085,7 +4096,7 @@ class CNCjob(Geometry): self.gcode = self.doformat(p.start_code) - self.gcode += self.doformat(p.feedrate_code) # sets the feed rate + self.gcode += self.doformat(p.feedrate_code) # sets the feed rate if toolchange is False: self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height @@ -4099,19 +4110,19 @@ class CNCjob(Geometry): self.gcode += self.doformat(p.toolchange_code) if 'laser' not in self.pp_geometry_name: - self.gcode += self.doformat(p.spindle_code) # Spindle start + self.gcode += self.doformat(p.spindle_code) # Spindle start else: # for laser this will disable the laser self.gcode += self.doformat(p.lift_code, x=self.oldx, y=self.oldy) # Move (up) to travel height if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time else: if 'laser' not in self.pp_geometry_name: self.gcode += self.doformat(p.spindle_code) # Spindle start if self.dwell is True: - self.gcode += self.doformat(p.dwell_code) # Dwell time + self.gcode += self.doformat(p.dwell_code) # Dwell time total_travel = 0.0 total_cut = 0.0 @@ -4553,7 +4564,7 @@ class CNCjob(Geometry): kind = ["C", "F"] # T=travel, C=cut, F=fast, S=slow # Results go here - geometry = [] + geometry = [] # Last known instruction current = {'X': 0.0, 'Y': 0.0, 'Z': 0.0, 'G': 0} @@ -4636,7 +4647,7 @@ class CNCjob(Geometry): kind = ['C', 'F'] geometry.append( { - "geom": Point(current_drill_point_coords).buffer(dia/2.0).exterior, + "geom": Point(current_drill_point_coords).buffer(dia / 2.0).exterior, "kind": kind } ) @@ -4644,14 +4655,14 @@ class CNCjob(Geometry): if 'G' in gobj: current['G'] = int(gobj['G']) - + if 'X' in gobj or 'Y' in gobj: if 'X' in gobj: x = gobj['X'] # current['X'] = x else: x = current['X'] - + if 'Y' in gobj: y = gobj['Y'] else: @@ -4670,7 +4681,7 @@ class CNCjob(Geometry): arcdir = [None, None, "cw", "ccw"] if current['G'] in [2, 3]: # arc center = [gobj['I'] + current['X'], gobj['J'] + current['Y']] - radius = np.sqrt(gobj['I']**2 + gobj['J']**2) + radius = np.sqrt(gobj['I'] ** 2 + gobj['J'] ** 2) start = np.arctan2(-gobj['J'], -gobj['I']) stop = np.arctan2(-center[1] + y, -center[0] + x) path += arc(center, radius, start, stop, arcdir[current['G']], int(self.steps_per_circle)) @@ -5592,7 +5603,7 @@ class CNCjob(Geometry): new_nr = float(nr) * xfactor # replace the updated string line = line.replace(nr, ('%.*f' % (self.app.defaults["cncjob_coords_decimals"], new_nr)) - ) + ) # this scales all the X and Y and Z and F values and also the Tool Dia in the toolchange message if header_stop is True: @@ -5993,9 +6004,9 @@ def arc(center, radius, start, stop, direction, steps_per_circ): stop += 2 * np.pi if direction == "cw" and stop >= start: stop -= 2 * np.pi - + angle = abs(stop - start) - + # angle = stop-start steps = max([int(np.ceil(angle / (2 * np.pi) * steps_per_circ)), 2]) delta_angle = da_sign[direction] * angle * 1.0 / steps @@ -6578,7 +6589,6 @@ class FlatCAMRTreeStorage(FlatCAMRTree): tidx = super(FlatCAMRTreeStorage, self).nearest(pt) return (tidx.bbox[0], tidx.bbox[1]), self.objects[tidx.object] - # class myO: # def __init__(self, coords): # self.coords = coords diff --git a/flatcamGUI/GUIElements.py b/flatcamGUI/GUIElements.py index 78cc8d1a..bc283456 100644 --- a/flatcamGUI/GUIElements.py +++ b/flatcamGUI/GUIElements.py @@ -1337,7 +1337,8 @@ class FCComboBox(QtWidgets.QComboBox): return str(self.currentText()) def set_value(self, val): - self.setCurrentIndex(self.findText(str(val))) + idx = self.findText(str(val)) + self.setCurrentIndex(idx) @property def is_last(self): diff --git a/flatcamObjects/FlatCAMExcellon.py b/flatcamObjects/FlatCAMExcellon.py index 79020ab1..691fb897 100644 --- a/flatcamObjects/FlatCAMExcellon.py +++ b/flatcamObjects/FlatCAMExcellon.py @@ -1115,6 +1115,7 @@ class ExcellonObject(FlatCAMObj, Excellon): else: geo_obj.solid_geometry.append( Point(hole['point']).buffer(buffer_value).exterior) + if use_thread: def geo_thread(app_obj): app_obj.new_object("geometry", outname, geo_init, plot=plot)