diff --git a/CHANGELOG.md b/CHANGELOG.md index 81be307d..cb53ee4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ CHANGELOG for FlatCAM beta - in Legacy2D graphic engine fixed setting the Opacity functionality - fixed a recent issue where Gerber files made out of single polygons were not processed - more Shapely deprecations fixes +- fixed some leftovers from Qt6 port +- some code optimizations in the Gerber Editor +- Corners Plugin now generate Gerber objects with the solid geometry flattened +- fixed issue where Gerber objects created from importing SVG geometries could not be edited 19.09.2021 diff --git a/appEditors/AppGerberEditor.py b/appEditors/AppGerberEditor.py index 802f2ba4..6ae87dcf 100644 --- a/appEditors/AppGerberEditor.py +++ b/appEditors/AppGerberEditor.py @@ -2741,6 +2741,8 @@ class ImportEditorGrb(QtCore.QObject, DrawTool): self.storage = self.draw_app.storage_dict # self.selected = self.draw_app.selected + self.event_is_dragging = False + # here we store all shapes that were selected; each item in the list is a dict ''' { @@ -4141,6 +4143,7 @@ class AppGerberEditor(QtCore.QObject): rect = Rect(xmin, ymin, xmax, ymax) rect.left, rect.right = xmin, xmax rect.bottom, rect.top = ymin, ymax + # Lock updates in other threads self.shapes.lock_updates() @@ -4553,23 +4556,25 @@ class AppGerberEditor(QtCore.QObject): self.app.log.error("AppGerberEditor.edit_fcgerber() --> %s" % str(e)) # apply the conversion factor on the obj.tools - conv_apertures = deepcopy(self.gerber_obj.tools) for apcode in self.gerber_obj.tools: - for key in self.gerber_obj.tools[apcode]: - if key == 'width': - conv_apertures[apcode]['width'] = self.gerber_obj.tools[apcode]['width'] * \ - self.conversion_factor - elif key == 'height': - conv_apertures[apcode]['height'] = self.gerber_obj.tools[apcode]['height'] * \ - self.conversion_factor - elif key == 'diam': - conv_apertures[apcode]['diam'] = self.gerber_obj.tools[apcode]['diam'] * self.conversion_factor - elif key == 'size': - conv_apertures[apcode]['size'] = self.gerber_obj.tools[apcode]['size'] * self.conversion_factor - else: - conv_apertures[apcode][key] = self.gerber_obj.tools[apcode][key] + object_keys = list(self.gerber_obj.tools[apcode].keys()) + + for key in object_keys: + if key == 'width': + self.gerber_obj.tools[apcode]['width'] = self.gerber_obj.tools[apcode]['width'] * \ + self.conversion_factor + elif key == 'height': + self.gerber_obj.tools[apcode]['height'] = self.gerber_obj.tools[apcode]['height'] * \ + self.conversion_factor + elif key == 'diam': + self.gerber_obj.tools[apcode]['diam'] = self.gerber_obj.tools[apcode]['diam'] * \ + self.conversion_factor + elif key == 'size': + self.gerber_obj.tools[apcode]['size'] = self.gerber_obj.tools[apcode]['size'] * \ + self.conversion_factor + else: + self.gerber_obj.tools[apcode][key] = self.gerber_obj.tools[apcode][key] - self.gerber_obj.tools = conv_apertures self.gerber_obj.units = app_units # # and then add it to the storage elements (each storage elements is a member of a list @@ -4648,7 +4653,7 @@ class AppGerberEditor(QtCore.QObject): for elem in app_obj.gerber_obj.tools[aper_id]['geometry']: if 'clear' in elem: global_clear_geo.append(elem['clear']) - self.app.log.warning("Found %d clear polygons." % len(global_clear_geo)) + app_obj.app.log.warning("Found %d clear polygons." % len(global_clear_geo)) if global_clear_geo: global_clear_geo = unary_union(global_clear_geo) @@ -4707,13 +4712,16 @@ class AppGerberEditor(QtCore.QObject): new_elem['clear'] = elem['clear'] if 'follow' in elem: new_elem['follow'] = elem['follow'] - temp_solid_geometry.append(deepcopy(new_elem)) + temp_solid_geometry.append(new_elem) - app_obj.gerber_obj.tools[ap_code]['geometry'] = deepcopy(temp_solid_geometry) + app_obj.gerber_obj.tools[ap_code]['geometry'] = temp_solid_geometry - self.app.log.warning( + app_obj.app.log.warning( "Polygon difference done for %d apertures." % len(app_obj.gerber_obj.tools)) + # ################################################################################################# + # Multi-Processing + # ################################################################################################# try: # Loading the Geometry into Editor Storage for ap_code, ap_dict in app_obj.gerber_obj.tools.items(): @@ -4721,18 +4729,23 @@ class AppGerberEditor(QtCore.QObject): app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_code, ap_dict)) ) except Exception as ee: - self.app.log.error( + app_obj.app.log.error( "AppGerberEditor.edit_fcgerber.worker_job() Adding processes to pool --> %s" % str(ee)) traceback.print_exc() output = [] - for p in app_obj.results: - output.append(p.get()) + try: + for p in app_obj.results: + output.append(p.get()) + except Exception as err: + app_obj.app.log.error( + "AppGerberEditor.edit_fcgerber.Exxecute_Edit.worker_job(). " + "Multiprocessing error. %s" % str(err)) for elem in output: app_obj.storage_dict[elem[0]] = deepcopy(elem[1]) - app_obj.mp_finished.emit(output) + # ################################################################################################# def run(self): self.worker_job(self.app) @@ -4789,7 +4802,7 @@ class AppGerberEditor(QtCore.QObject): try: self.plot_thread.stop() except Exception as e: - self.app.log.error("AppGerberEditor.update_fcgerber() --> %s" % str(e)) + self.app.log.warning("AppGerberEditor.update_fcgerber() Timer malfunctioned --> %s" % str(e)) if "_edit" in self.edited_obj_name: try: @@ -4883,13 +4896,15 @@ class AppGerberEditor(QtCore.QObject): grb_obj.aperture_macros = deepcopy(self.gerber_obj.aperture_macros) + follow_buffer = flatten_shapely_geometry(follow_buffer) + new_poly = MultiPolygon(poly_buffer) new_poly = new_poly.buffer(0.00000001) new_poly = new_poly.buffer(-0.00000001) new_poly = flatten_shapely_geometry(new_poly) grb_obj.solid_geometry = deepcopy(new_poly) - grb_obj.follow_geometry = deepcopy(follow_buffer) + grb_obj.follow_geometry = follow_buffer for k, v in self.gerber_obj_options.items(): if k == 'name': diff --git a/appParsers/ParseGerber.py b/appParsers/ParseGerber.py index c5190dd1..6afefb1c 100644 --- a/appParsers/ParseGerber.py +++ b/appParsers/ParseGerber.py @@ -1995,13 +1995,13 @@ class Gerber(Geometry): self.solid_geometry = flatten_shapely_geometry(self.solid_geometry) if 0 not in self.tools: self.tools[0] = { - 'type': 'REG', - 'size': 0.0, - 'geometry': [] + 'type': 'REG', + 'size': 0.0, + 'geometry': [] } for pol in self.solid_geometry: - new_el = {'solid': pol, 'follow': pol.exterior} + new_el = {'solid': pol, 'follow': LineString(pol.exterior.coords)} self.tools[0]['geometry'].append(new_el) def import_dxf_as_gerber(self, filename, units='MM'): diff --git a/appParsers/ParseSVG.py b/appParsers/ParseSVG.py index b42fd805..f297e383 100644 --- a/appParsers/ParseSVG.py +++ b/appParsers/ParseSVG.py @@ -178,19 +178,20 @@ def path2shapely(path, object_type, res=1.0, units='MM', factor=1.0): log.error("ParseSVG.path2shapely() MString --> %s" % str(e)) return None - if len(rings) > 0: - if len(rings) == 1 and not isinstance(rings, MultiLineString): + rings_len = len(rings.geoms) + if rings_len > 0: + if rings_len == 1 and not isinstance(rings, MultiLineString): # Polygons are closed and require more than 2 points - if Point(rings[0][0]).almost_equals(Point(rings[0][-1])) and len(rings[0]) > 2: - geo_element = Polygon(rings[0]) + if Point(rings.geoms[0][0]).almost_equals(Point(rings.geoms[0][-1])) and len(rings.geoms[0]) > 2: + geo_element = Polygon(rings.geoms[0]) else: - geo_element = LineString(rings[0]) + geo_element = LineString(rings.geoms[0]) else: try: - geo_element = Polygon(rings[0], rings[1:]) + geo_element = Polygon(rings.geoms[0], rings.geoms[1:]) except Exception: coords = [] - for line in rings: + for line in rings.geoms: coords.append(line.coords[0]) coords.append(line.coords[1]) try: diff --git a/appPlugins/ToolCorners.py b/appPlugins/ToolCorners.py index a6aaa95a..f105589c 100644 --- a/appPlugins/ToolCorners.py +++ b/appPlugins/ToolCorners.py @@ -11,6 +11,8 @@ from appTool import AppTool from appCommon.Common import LoudDict from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCComboBox, FCButton, RadioSet, FCLabel, \ VerticalScrollArea, FCGridLayout, FCFrame +from camlib import flatten_shapely_geometry + from shapely.geometry import MultiPolygon, LineString, Point from shapely.ops import unary_union @@ -469,6 +471,8 @@ class ToolCorners(AppTool): geo_buff_list = MultiPolygon(geo_buff_list) geo_buff_list = geo_buff_list.buffer(0) + geo_buff_list = flatten_shapely_geometry(geo_buff_list) + try: for poly in geo_buff_list: s_list.append(poly) @@ -487,8 +491,8 @@ class ToolCorners(AppTool): grb_obj.multigeo = False grb_obj.follow = deepcopy(g_obj.follow) grb_obj.tools = new_apertures - grb_obj.solid_geometry = unary_union(s_list) - grb_obj.follow_geometry = deepcopy(g_obj.follow_geometry) + geo_list + grb_obj.solid_geometry = flatten_shapely_geometry(unary_union(s_list)) + grb_obj.follow_geometry = flatten_shapely_geometry(g_obj.follow_geometry + geo_list) grb_obj.source_file = app_obj.f_handlers.export_gerber(obj_name=outname, filename=None, local_use=grb_obj, use_thread=False) diff --git a/app_Main.py b/app_Main.py index f7f36cbe..6fd2d424 100644 --- a/app_Main.py +++ b/app_Main.py @@ -1814,7 +1814,7 @@ class App(QtCore.QObject): "global_grid_lines", "global_grid_snap", "global_axis", "global_workspace", "global_workspaceT", "global_workspace_orientation", "global_hud" ]: - self.on_properties_tab_click(index=None) + self.on_properties_tab_click() # TODO handle changing the units in the Preferences # if key_changed == "units": @@ -1962,7 +1962,8 @@ class App(QtCore.QObject): self.invert_tool.install(icon=QtGui.QIcon(self.resource_location + '/invert32.png'), pos=self.ui.menu_plugins) self.corners_tool = ToolCorners(self) - self.corners_tool.install(icon=QtGui.QIcon(self.resource_location + '/corners_32.png'), pos=self.ui.menu_plugins) + self.corners_tool.install(icon=QtGui.QIcon(self.resource_location + '/corners_32.png'), + pos=self.ui.menu_plugins) self.etch_tool = ToolEtchCompensation(self) self.etch_tool.install(icon=QtGui.QIcon(self.resource_location + '/etch_32.png'), pos=self.ui.menu_plugins) @@ -2031,8 +2032,8 @@ class App(QtCore.QObject): self.remove_tools() # re-add the TCL Shell action to the Tools menu and reconnect it to ist slot function - self.ui.menu_plugins_shell = self.ui.menu_plugins.addAction(QtGui.QIcon(self.resource_location + '/shell16.png'), - '&Command Line\tS') + self.ui.menu_plugins_shell = self.ui.menu_plugins.addAction( + QtGui.QIcon(self.resource_location + '/shell16.png'), '&Command Line\tS') self.ui.menu_plugins_shell.triggered.connect(self.ui.toggle_shell_ui) # third install all of them @@ -2358,44 +2359,44 @@ class App(QtCore.QObject): self.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar') self.ui.toolbarfile.setObjectName('File_TB') self.ui.toolbarfile.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.LeftToolBarArea, self.ui.toolbarfile) + self.ui.addToolBar(Qt.ToolBarArea.LeftToolBarArea, self.ui.toolbarfile) self.ui.toolbaredit = QtWidgets.QToolBar('Edit Toolbar') self.ui.toolbaredit.setObjectName('Edit_TB') self.ui.toolbaredit.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.LeftToolBarArea, self.ui.toolbaredit) + self.ui.addToolBar(Qt.ToolBarArea.LeftToolBarArea, self.ui.toolbaredit) self.ui.toolbarshell = QtWidgets.QToolBar('Shell Toolbar') self.ui.toolbarshell.setObjectName('Shell_TB') self.ui.toolbarshell.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.LeftToolBarArea, self.ui.toolbarshell) + self.ui.addToolBar(Qt.ToolBarArea.LeftToolBarArea, self.ui.toolbarshell) self.ui.toolbarplugins = QtWidgets.QToolBar('Plugin Toolbar') self.ui.toolbarplugins.setObjectName('Plugins_TB') self.ui.toolbarplugins.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.LeftToolBarArea, self.ui.toolbarplugins) + self.ui.addToolBar(Qt.ToolBarArea.LeftToolBarArea, self.ui.toolbarplugins) self.ui.geo_edit_toolbar = QtWidgets.QToolBar('Geometry Editor Toolbar') self.ui.geo_edit_toolbar.setObjectName('GeoEditor_TB') self.ui.geo_edit_toolbar.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.RightToolBarArea, self.ui.geo_edit_toolbar) + self.ui.addToolBar(Qt.ToolBarArea.RightToolBarArea, self.ui.geo_edit_toolbar) self.ui.toolbarview = QtWidgets.QToolBar('View Toolbar') self.ui.toolbarview.setObjectName('View_TB') self.ui.toolbarview.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.RightToolBarArea, self.ui.toolbarview) + self.ui.addToolBar(Qt.ToolBarArea.RightToolBarArea, self.ui.toolbarview) - self.ui.addToolBarBreak(area=Qt.RightToolBarArea) + self.ui.addToolBarBreak(area=Qt.ToolBarArea.RightToolBarArea) self.ui.grb_edit_toolbar = QtWidgets.QToolBar('Gerber Editor Toolbar') self.ui.grb_edit_toolbar.setObjectName('GrbEditor_TB') self.ui.grb_edit_toolbar.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.RightToolBarArea, self.ui.grb_edit_toolbar) + self.ui.addToolBar(Qt.ToolBarArea.RightToolBarArea, self.ui.grb_edit_toolbar) self.ui.exc_edit_toolbar = QtWidgets.QToolBar('Excellon Editor Toolbar') self.ui.exc_edit_toolbar.setObjectName('ExcEditor_TB') self.ui.exc_edit_toolbar.setStyleSheet("QToolBar{spacing:0px;}") - self.ui.addToolBar(Qt.RightToolBarArea, self.ui.exc_edit_toolbar) + self.ui.addToolBar(Qt.ToolBarArea.RightToolBarArea, self.ui.exc_edit_toolbar) else: # ## TOOLBAR INSTALLATION # ## self.ui.toolbarfile = QtWidgets.QToolBar('File Toolbar') @@ -5455,8 +5456,8 @@ class App(QtCore.QObject): location = (x1, y1) else: # center - cx = x0 + abs((x1 - x0 ) / 2) - cy = y0 + abs((y1 - y0 ) / 2) + cx = x0 + abs((x1 - x0) / 2) + cy = y0 + abs((y1 - y0) / 2) location = (cx, cy) for obj in obj_list: @@ -8215,7 +8216,7 @@ class App(QtCore.QObject): # Last action in Recent Files menu is one that Clear the content clear_action_proj = QtGui.QAction(QtGui.QIcon(self.resource_location + '/trash32.png'), - (_("Clear Recent projects")), self) + (_("Clear Recent projects")), self) clear_action_proj.triggered.connect(reset_recent_projects) self.ui.recent_projects.addSeparator() self.ui.recent_projects.addAction(clear_action_proj) @@ -8239,7 +8240,7 @@ class App(QtCore.QObject): # Last action in Recent Files menu is one that Clear the content clear_action = QtGui.QAction(QtGui.QIcon(self.resource_location + '/trash32.png'), - (_("Clear Recent files")), self) + (_("Clear Recent files")), self) clear_action.triggered.connect(reset_recent_files) self.ui.recent.addSeparator() self.ui.recent.addAction(clear_action) @@ -8251,7 +8252,7 @@ class App(QtCore.QObject): self.log.debug("Recent items list has been populated.") - def on_properties_tab_click(self, index): + def on_properties_tab_click(self): if self.ui.properties_scroll_area.widget().objectName() == 'default_properties': self.setup_default_properties_tab() @@ -8428,10 +8429,10 @@ class App(QtCore.QObject): :param container: QT Widget where to install the canvas :return: None """ - if container: - plot_container = container - else: - plot_container = self.ui.right_layout + # if container: + # plot_container = container + # else: + # plot_container = self.ui.right_layout modifier = QtWidgets.QApplication.queryKeyboardModifiers() if modifier == QtCore.Qt.KeyboardModifier.ControlModifier: @@ -8487,7 +8488,8 @@ class App(QtCore.QObject): return plotcanvas - def on_plotcanvas_add(self, plotcanvas_obj, container): + @staticmethod + def on_plotcanvas_add(plotcanvas_obj, container): """ :param plotcanvas_obj: the class that setup the canvas