diff --git a/CHANGELOG.md b/CHANGELOG.md index 08eb0e63..330bb833 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ CHANGELOG for FlatCAM Evo beta - changed the app main icons, hopefully they are more visible on black backgrounds than the ones that FlatCAM has - make sure that the state of the workspace is saved correctly on change +- refactored the imports in the Plugins, moved almost all imports in the AppTool file +- fixed a number of issues, mostly leftovers from moving the UI of a Plugin in its own class +- fixed some bugs in the Punch Gerber plugin +- fixed some bugs where the 'pool' parameter was not passed when creating shapes collections (in 3D graphic mode); I wonder how it worked until now +- added a new feature in the Isolation Plugin: now for all the isolation Geometry objects this plugin can do a supplementary simplification of the geometry using the tolerance parameter defined in the General Parameters. This should lead to a reduced number of tool lifts when doing corners 30.03.2022 diff --git a/appCommon/Common.py b/appCommon/Common.py index 06b67fb2..379a11ec 100644 --- a/appCommon/Common.py +++ b/appCommon/Common.py @@ -243,7 +243,8 @@ class ExclusionAreas(QtCore.QObject): # VisPy visuals if self.app.use_3d_engine: try: - self.exclusion_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) + self.exclusion_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1, + pool=self.app.pool) except AttributeError: self.exclusion_shapes = None else: diff --git a/appGUI/preferences/PreferencesUIManager.py b/appGUI/preferences/PreferencesUIManager.py index 7802e127..a4118a29 100644 --- a/appGUI/preferences/PreferencesUIManager.py +++ b/appGUI/preferences/PreferencesUIManager.py @@ -316,6 +316,8 @@ class PreferencesUIManager(QtCore.QObject): "tools_iso_poly_ints": self.ui.plugin_eng_pref_form.tools_iso_group.poly_int_cb, "tools_iso_force": self.ui.plugin_eng_pref_form.tools_iso_group.force_iso_cb, "tools_iso_area_shape": self.ui.plugin_eng_pref_form.tools_iso_group.area_shape_radio, + "tools_iso_simplification": self.ui.plugin_eng_pref_form.tools_iso_group.simplify_cb, + "tools_iso_simplification_tol": self.ui.plugin_eng_pref_form.tools_iso_group.sim_tol_entry, "tools_iso_plotting": self.ui.plugin_eng_pref_form.tools_iso_group.plotting_radio, # ######################################################################################################### diff --git a/appGUI/preferences/tools/ToolsISOPrefGroupUI.py b/appGUI/preferences/tools/ToolsISOPrefGroupUI.py index 238ed8be..1e029906 100644 --- a/appGUI/preferences/tools/ToolsISOPrefGroupUI.py +++ b/appGUI/preferences/tools/ToolsISOPrefGroupUI.py @@ -295,6 +295,20 @@ class ToolsISOPrefGroupUI(OptionsGroupUI): gen_grid.addWidget(self.valid_cb, 2, 0, 1, 3) + # Simplification Tolerance + self.simplify_cb = FCCheckBox('%s' % _("Simplify")) + self.simplify_cb.setToolTip( + _("All points in the simplified object will be\n" + "within the tolerance distance of the original geometry.") + ) + self.sim_tol_entry = FCDoubleSpinner() + self.sim_tol_entry.set_precision(self.decimals) + self.sim_tol_entry.setSingleStep(10 ** -self.decimals) + self.sim_tol_entry.set_range(0.0000, 10000.0000) + + gen_grid.addWidget(self.simplify_cb, 4, 0) + gen_grid.addWidget(self.sim_tol_entry, 4, 1) + # Isolation Scope self.select_label = FCLabel('%s:' % _("Selection")) self.select_label.setToolTip( @@ -309,8 +323,8 @@ class ToolsISOPrefGroupUI(OptionsGroupUI): [_("All"), _("Area Selection"), _("Polygon Selection"), _("Reference Object")] ) - gen_grid.addWidget(self.select_label, 4, 0) - gen_grid.addWidget(self.select_combo, 4, 1, 1, 2) + gen_grid.addWidget(self.select_label, 6, 0) + gen_grid.addWidget(self.select_combo, 6, 1, 1, 2) # Area Shape self.area_shape_label = FCLabel('%s:' % _("Shape")) @@ -321,8 +335,8 @@ class ToolsISOPrefGroupUI(OptionsGroupUI): self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, {'label': _("Polygon"), 'value': 'polygon'}]) - gen_grid.addWidget(self.area_shape_label, 6, 0) - gen_grid.addWidget(self.area_shape_radio, 6, 1, 1, 2) + gen_grid.addWidget(self.area_shape_label, 8, 0) + gen_grid.addWidget(self.area_shape_radio, 8, 1, 1, 2) # Polygon interiors selection self.poly_int_cb = FCCheckBox(_("Interiors")) @@ -338,13 +352,13 @@ class ToolsISOPrefGroupUI(OptionsGroupUI): "interiors of a polygon (holes in the polygon) could not be isolated.\n" "Works when 'rest machining' is used.") ) - gen_grid.addWidget(self.poly_int_cb, 8, 0) - gen_grid.addWidget(self.force_iso_cb, 8, 1) + gen_grid.addWidget(self.poly_int_cb, 10, 0) + gen_grid.addWidget(self.force_iso_cb, 10, 1) separator_line = QtWidgets.QFrame() separator_line.setFrameShape(QtWidgets.QFrame.Shape.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken) - gen_grid.addWidget(separator_line, 10, 0, 1, 3) + gen_grid.addWidget(separator_line, 12, 0, 1, 3) # ## Plotting type self.plotting_radio = RadioSet([{'label': _('Normal'), 'value': 'normal'}, @@ -354,8 +368,8 @@ class ToolsISOPrefGroupUI(OptionsGroupUI): _("- 'Normal' - normal plotting, done at the end of the job\n" "- 'Progressive' - each shape is plotted after it is generated") ) - gen_grid.addWidget(plotting_label, 12, 0) - gen_grid.addWidget(self.plotting_radio, 12, 1, 1, 2) + gen_grid.addWidget(plotting_label, 14, 0) + gen_grid.addWidget(self.plotting_radio, 14, 1, 1, 2) FCGridLayout.set_common_column_size([par_grid, tool_grid, gen_grid], 0) diff --git a/appMain.py b/appMain.py index 7c1ad13b..b6bd1338 100644 --- a/appMain.py +++ b/appMain.py @@ -1158,12 +1158,12 @@ class App(QtCore.QObject): # VisPy visuals if self.use_3d_engine: try: - self.tool_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1) + self.tool_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1, pool=self.pool) except AttributeError: self.tool_shapes = None # Storage for Hover Shapes - self.hover_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1) + self.hover_shapes = ShapeCollection(parent=self.plotcanvas.view.scene, layers=1, pool=self.pool) else: from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.tool_shapes = ShapeCollectionLegacy(obj=self, app=self, name="tool") @@ -5133,7 +5133,7 @@ class App(QtCore.QObject): _("Please enter a tool diameter with non-zero value, in Float format.")) return try: - self.collection.get_active().on_tool_add(clicked_state=False, dia=float(val)) + self.collection.get_active().on_tool_add(dia=float(val)) except Exception as tadd_err: self.log.debug("App.on_tool_add_keypress() --> %s" % str(tadd_err)) else: diff --git a/appObjects/AppObjectTemplate.py b/appObjects/AppObjectTemplate.py index 2928440b..192bb636 100644 --- a/appObjects/AppObjectTemplate.py +++ b/appObjects/AppObjectTemplate.py @@ -94,7 +94,7 @@ class FlatCAMObj(QtCore.QObject): if self.app.use_3d_engine: self.shapes = self.app.plotcanvas.new_shape_group() - self.mark_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) + self.mark_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1, pool=self.app.pool) else: self.shapes = ShapeCollectionLegacy(obj=self, app=self.app, name=name) self.mark_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name=name + "_mark_shapes") diff --git a/appPlugins/ToolAlignObjects.py b/appPlugins/ToolAlignObjects.py index 24f6ce73..428599f2 100644 --- a/appPlugins/ToolAlignObjects.py +++ b/appPlugins/ToolAlignObjects.py @@ -5,20 +5,7 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui -from appTool import AppTool - -from appGUI.GUIElements import FCComboBox, RadioSet, FCLabel, FCButton, VerticalScrollArea, FCGridLayout, FCFrame - -import math - -from shapely.geometry import Point -from shapely.affinity import translate - -import gettext -import appTranslation as fcTranslate -import builtins -import logging +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: diff --git a/appPlugins/ToolCalculators.py b/appPlugins/ToolCalculators.py index da0e60cf..499e7d5a 100644 --- a/appPlugins/ToolCalculators.py +++ b/appPlugins/ToolCalculators.py @@ -4,16 +4,8 @@ # Date: 3/10/2019 # # MIT Licence # # ########################################################## -import numpy as np -from PyQt6 import QtWidgets, QtGui -from appTool import AppTool -from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, NumericalEvalEntry, FCLabel, RadioSet, FCButton, \ - VerticalScrollArea, FCGridLayout, FCFrame -import math -import gettext -import appTranslation as fcTranslate -import builtins +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -337,9 +329,9 @@ class ToolCalculator(AppTool): def on_calculate_growth(self): self.ui_disconnect() density = self.ui.cdensity_entry.get_value() - time = self.ui.time_entry.get_value() + g_time = self.ui.time_entry.get_value() - growth = time / (2.142857142857143 * float(20 / density)) + growth = g_time / (2.142857142857143 * float(20 / density)) self.ui.growth_entry.set_value(self.app.dec_format(growth, self.decimals)) self.app.inform.emit('[success] %s' % _("Done.")) diff --git a/appPlugins/ToolCalibration.py b/appPlugins/ToolCalibration.py index c579f530..c8d0e51e 100644 --- a/appPlugins/ToolCalibration.py +++ b/appPlugins/ToolCalibration.py @@ -5,26 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCDoubleSpinner, EvalEntry, FCCheckBox, OptionalInputSection, FCEntry, \ - VerticalScrollArea, FCTable, FCComboBox, RadioSet, FCGridLayout, FCLabel +from appTool import * from appEditors.AppTextEditor import AppTextEditor -from shapely.geometry import Point -from shapely.geometry.base import * -from shapely.affinity import scale, skew - -import math -from datetime import datetime -import logging -from copy import deepcopy - -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext diff --git a/appPlugins/ToolCopperThieving.py b/appPlugins/ToolCopperThieving.py index a126e03d..0e59a369 100644 --- a/appPlugins/ToolCopperThieving.py +++ b/appPlugins/ToolCopperThieving.py @@ -5,28 +5,11 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from camlib import grace, flatten_shapely_geometry -from appTool import AppTool -from appGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox, FCLabel, FCCheckBox, \ - VerticalScrollArea, FCGridLayout, FCFrame, FCComboBox2 +from appTool import * from appCommon.Common import LoudDict - +from appCommon.Common import GracefulException as grace +from camlib import flatten_shapely_geometry import shapely.geometry.base as base -from shapely.ops import unary_union -from shapely.geometry import Polygon, MultiPolygon, Point, LineString -from shapely.geometry import box as box -import shapely.affinity as affinity - -import logging -from copy import deepcopy -import numpy as np -from collections.abc import Iterable - -import gettext -import appTranslation as fcTranslate -import builtins fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -383,7 +366,7 @@ class ToolCopperThieving(AppTool): self.ref_obj = self.app.collection.get_by_name(bound_obj_name) except Exception as e: self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(e))) - return "Could not retrieve object: %s" % self.obj_name + return "Could not retrieve object: %s" % self.grb_object.obj_options['name'] self.copper_thieving( thieving_obj=self.grb_object, @@ -787,7 +770,7 @@ class ToolCopperThieving(AppTool): dx = bounding_box.centroid.x - thieving_box_geo.centroid.x dy = bounding_box.centroid.y - thieving_box_geo.centroid.y - thieving_box_geo = affinity.translate(thieving_box_geo, xoff=dx, yoff=dy) + thieving_box_geo = translate(thieving_box_geo, xoff=dx, yoff=dy) thieving_box_geo = flatten_shapely_geometry(thieving_box_geo) thieving_geo = [] @@ -916,7 +899,7 @@ class ToolCopperThieving(AppTool): geo_list.append(tool_obj.thief_solid_geometry) # append into the 0 aperture - geo_elem = {'solid': tool_obj.new_solid_geometry, 'follow': tool_obj.new_solid_geometry.exterior} + geo_elem = {'solid': tool_obj.thief_solid_geometry, 'follow': tool_obj.thief_solid_geometry.exterior} new_apertures[0]['geometry'].append(deepcopy(geo_elem)) # prepare also the solid_geometry for the new object having the thieving geometry diff --git a/appPlugins/ToolCutOut.py b/appPlugins/ToolCutOut.py index 48c1b7ab..185f2c96 100644 --- a/appPlugins/ToolCutOut.py +++ b/appPlugins/ToolCutOut.py @@ -5,28 +5,10 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtGui, QtCore -from appTool import AppTool -from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, OptionalInputSection, FCButton, \ - FCLabel, VerticalScrollArea, FCGridLayout, FCFrame, FCComboBox2 - -from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing, MultiLineString, Point -from shapely.ops import unary_union, linemerge -import shapely.affinity as affinity -from camlib import flatten_shapely_geometry - +from appTool import * +from camlib import grace, flatten_shapely_geometry from matplotlib.backend_bases import KeyEvent as mpl_key_event - from numpy import Inf -from copy import deepcopy -import math -import logging -import gettext -import sys -import simplejson as json - -import appTranslation as fcTranslate -import builtins fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -1168,8 +1150,8 @@ class CutOut(AppTool): except TypeError: gaps_solid_geo.append(self_c.subtract_geo(geom_struct, c_geo)) elif cutout_obj.kind == 'gerber' and margin < 0: - msg = '[WARNING_NOTCL] %s' % _("Rectangular cutout with negative margin is not possible.") - app_obj.inform.emit(msg) + mesg = '[WARNING_NOTCL] %s' % _("Rectangular cutout with negative margin is not possible.") + app_obj.inform.emit(mesg) return "fail" if not solid_geo: @@ -1982,7 +1964,7 @@ class CutOut(AppTool): # rotate only if there is an angle to rotate to if rot_angle != 0: - cut_geo = affinity.rotate(cut_geo, -rot_angle) + cut_geo = rotate(cut_geo, -rot_angle) # Remove any previous utility shape self.app.geo_editor.tool_shape.clear(update=True) @@ -2157,7 +2139,7 @@ class CutOut(AppTool): maxx = -Inf maxy = -Inf - work_geo = obj.geoms if isinstance(obj , (MultiPolygon, MultiLineString)) else obj + work_geo = obj.geoms if isinstance(obj, (MultiPolygon, MultiLineString)) else obj for k in work_geo: minx_, miny_, maxx_, maxy_ = bounds_rec(k) minx = min(minx, minx_) diff --git a/appPlugins/ToolDblSided.py b/appPlugins/ToolDblSided.py index b5c8f443..3d723c7a 100644 --- a/appPlugins/ToolDblSided.py +++ b/appPlugins/ToolDblSided.py @@ -1,20 +1,5 @@ -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCButton, FCComboBox, NumericalEvalTupleEntry, FCLabel, \ - VerticalScrollArea, FCGridLayout, FCComboBox2, FCFrame - -from numpy import Inf -from copy import deepcopy - -from shapely.geometry import Point -from shapely import affinity - -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -297,6 +282,7 @@ class DblSidedTool(AppTool): align_type = self.ui.align_type_radio.get_value() mode = self.ui.axis_location.get_value() + px = py = 0.0 if align_type in ["X", "Y"]: if mode == "point": try: @@ -350,7 +336,7 @@ class DblSidedTool(AppTool): for hole in holes: point = Point(hole) - point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py)) + point_mirror = scale(point, xscale, yscale, origin=(px, py)) tools[1]['drills'] += [point, point_mirror] tools[1]['solid_geometry'] += [point, point_mirror] @@ -1169,8 +1155,7 @@ class DsidedUI: } """) self.tools_box.addWidget(self.reset_button) - - + self.align_type_radio.activated_custom.connect(self.on_align_type_changed) # #################################### FINSIHED GUI ########################### # ############################################################################# diff --git a/appPlugins/ToolDistance.py b/appPlugins/ToolDistance.py index 51d1c38e..21536310 100644 --- a/appPlugins/ToolDistance.py +++ b/appPlugins/ToolDistance.py @@ -7,18 +7,9 @@ from appTool import * from appGUI.VisPyVisuals import * -from appGUI.GUIElements import FCEntry, FCButton, FCCheckBox, FCLabel, VerticalScrollArea, FCGridLayout, FCFrame - -import appTranslation as fcTranslate from camlib import AppRTreeStorage from appEditors.AppGeoEditor import DrawToolShape -from copy import copy -import math -import logging -import gettext -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -83,7 +74,7 @@ class Distance(AppTool): # VisPy visuals if self.app.use_3d_engine: - self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) + self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1, pool=self.app.pool) else: from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.sel_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='measurement') diff --git a/appPlugins/ToolDrilling.py b/appPlugins/ToolDrilling.py index 73ff7e88..5bf0dccd 100644 --- a/appPlugins/ToolDrilling.py +++ b/appPlugins/ToolDrilling.py @@ -5,32 +5,10 @@ # License: MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton, \ - FCComboBox, OptionalInputSection, FCSpinner, NumericalEvalEntry, OptionalHideInputSection, FCLabel, \ - NumericalEvalTupleEntry, FCComboBox2, VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from appParsers.ParseExcellon import Excellon - -from copy import deepcopy - -import numpy as np - -from shapely.geometry import LineString - -import json -import sys -import re - from matplotlib.backend_bases import KeyEvent as mpl_key_event -import logging -import gettext -import appTranslation as fcTranslate -import builtins -import platform - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -2080,7 +2058,8 @@ class ToolDrilling(AppTool, Excellon): drill_no = len(points[selected_id]) for drill in points[selected_id]: sol_geo.append( - drill.buffer((selected_tooldia / 2.0), resolution=cnc_job_obj.geo_steps_per_circle) + drill.buffer((selected_tooldia / 2.0), + resolution=cnc_job_obj.geo_steps_per_circle) ) slot_no = 0 @@ -2131,7 +2110,8 @@ class ToolDrilling(AppTool, Excellon): drill_no += len(points[selected_id]) for drill in points[selected_id]: sol_geo.append( - drill.buffer((selected_tooldia / 2.0), resolution=cnc_job_obj.geo_steps_per_circle) + drill.buffer((selected_tooldia / 2.0), + resolution=cnc_job_obj.geo_steps_per_circle) ) convert_slots = self.excellon_tools[selected_id]['data']['tools_drill_drill_slots'] diff --git a/appPlugins/ToolEtchCompensation.py b/appPlugins/ToolEtchCompensation.py index 7ced8579..595fd5f7 100644 --- a/appPlugins/ToolEtchCompensation.py +++ b/appPlugins/ToolEtchCompensation.py @@ -5,23 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox, NumericalEvalEntry, FCEntry, \ - VerticalScrollArea, FCGridLayout, FCLabel, FCFrame +from appTool import * from camlib import flatten_shapely_geometry -from shapely.ops import unary_union - -from copy import deepcopy -import math - -import logging -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext diff --git a/appPlugins/ToolExtract.py b/appPlugins/ToolExtract.py index 94e515d0..f0d8ba45 100644 --- a/appPlugins/ToolExtract.py +++ b/appPlugins/ToolExtract.py @@ -5,20 +5,7 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox, FCLabel, FCTable, \ - VerticalScrollArea, FCGridLayout, FCFrame - -from shapely.geometry import Point, MultiPolygon, Polygon, box - -from copy import deepcopy - -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -1098,7 +1085,8 @@ class ExtractUI: self.apertures_table.horizontalHeaderItem(3).setToolTip( _("Mark the aperture instances on canvas.")) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.MinimumExpanding, + QtWidgets.QSizePolicy.Policy.Preferred) self.apertures_table.setSizePolicy(sizePolicy) self.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.MultiSelection) diff --git a/appPlugins/ToolFiducials.py b/appPlugins/ToolFiducials.py index 3365ccee..b8f8a6ad 100644 --- a/appPlugins/ToolFiducials.py +++ b/appPlugins/ToolFiducials.py @@ -5,25 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable, FCComboBox, FCButton, FCLabel, \ - VerticalScrollArea, FCGridLayout, FCFrame, FCComboBox2 +from appTool import * from appCommon.Common import LoudDict -from shapely.geometry import Point, Polygon, MultiPolygon, LineString -from shapely.geometry import box as box -from shapely.ops import unary_union - -import math -import logging -from copy import deepcopy - -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -490,8 +474,7 @@ class ToolFiducials(AppTool): :param center_pt: :param side_size: - :return: - :type return: Polygon + :return: Polygon """ half_s = side_size / 2 x_center = center_pt[0] @@ -947,7 +930,7 @@ class FidoUI: self.points_table.setMinimumHeight(self.points_table.getHeight() + 2) self.points_table.setMaximumHeight(self.points_table.getHeight() + 2) - # remove the frame on the QLineEdit childrens of the table + # remove the frame on the QLineEdit children of the table for row in range(self.points_table.rowCount()): self.points_table.cellWidget(row, 2).setFrame(False) diff --git a/appPlugins/ToolFilm.py b/appPlugins/ToolFilm.py index e010ed7d..4a64f39d 100644 --- a/appPlugins/ToolFilm.py +++ b/appPlugins/ToolFilm.py @@ -4,20 +4,8 @@ # Date: 3/10/2019 # # MIT Licence # # ########################################################## -import math -from PyQt6 import QtCore, QtWidgets, QtGui - -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \ - OptionalHideInputSection, FCComboBox, FCFileSaveDialog, FCButton, FCLabel, FCSpinner, \ - VerticalScrollArea, FCGridLayout, FCFrame, FCComboBox2 - -from copy import deepcopy -import logging -from shapely.geometry import Polygon, MultiPolygon, Point, LineString, LinearRing -import shapely.affinity as affinity -from shapely.ops import unary_union +from appTool import * from reportlab.graphics import renderPDF from reportlab.pdfgen import canvas @@ -30,10 +18,6 @@ from xml.dom.minidom import parseString as parse_xml_string from lxml import etree as ET from io import StringIO -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -640,13 +624,13 @@ class Film(AppTool): return "Could not retrieve object: %s" % obj_name try: - box = self.app.collection.get_by_name(str(box_name)) + box_obj = self.app.collection.get_by_name(str(box_name)) except Exception: return "Could not retrieve object: %s" % box_name - if box is None: + if box_obj is None: self.app.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj)) - box = obj + box_obj = obj scale_factor_x = scale_factor_x scale_factor_y = scale_factor_y @@ -668,7 +652,7 @@ class Film(AppTool): scale_factor_x += dpi_rate scale_factor_y += dpi_rate - transformed_box_geo = self.transform_geometry(box, scale_factor_x=scale_factor_x, + transformed_box_geo = self.transform_geometry(box_obj, scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y, scale_reference=scale_reference, scale_type=scale_type, skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y, @@ -862,13 +846,13 @@ class Film(AppTool): return "Could not retrieve object: %s" % obj_name try: - box = self.app.collection.get_by_name(str(box_name)) + box_obj = self.app.collection.get_by_name(str(box_name)) except Exception: return "Could not retrieve object: %s" % box_name - if box is None: + if box_obj is None: self.app.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), obj)) - box = obj + box_obj = obj scale_factor_x = scale_factor_x scale_factor_y = scale_factor_y @@ -890,7 +874,7 @@ class Film(AppTool): scale_factor_x += dpi_rate scale_factor_y += dpi_rate - transformed_box_geo = self.transform_geometry(box, scale_factor_x=scale_factor_x, + transformed_box_geo = self.transform_geometry(box_obj, scale_factor_x=scale_factor_x, scale_factor_y=scale_factor_y, scale_reference=scale_reference, scale_type=scale_type, skew_factor_x=skew_factor_x, skew_factor_y=skew_factor_y, @@ -1113,7 +1097,7 @@ class Film(AppTool): val_x = ((xmax - xmin) + scale_factor_x) / (xmax - xmin) val_y = ((ymax - ymin) + scale_factor_y) / (ymax - ymin) - transformed_geo = affinity.scale(transformed_geo, val_x, val_y, origin=ref_scale_val) + transformed_geo = scale(transformed_geo, val_x, val_y, origin=ref_scale_val) # return transformed_geo @@ -1192,15 +1176,15 @@ class Film(AppTool): future_y = (ymax - ymin) * skew_factor_y - (ymax - ymin) skew_angle_x = math.degrees(math.atan2(future_x, ((ymax - ymin) * 0.5))) skew_angle_y = math.degrees(math.atan2(future_y, ((xmax - xmin) * 0.5))) - transformed_geo = affinity.skew(transformed_geo, skew_angle_x, skew_angle_y, origin=ref_skew_val) + transformed_geo = skew(transformed_geo, skew_angle_x, skew_angle_y, origin=ref_skew_val) if mirror: if mirror == 'x': - transformed_geo = affinity.scale(transformed_geo, 1.0, -1.0, origin='center') + transformed_geo = scale(transformed_geo, 1.0, -1.0, origin='center') if mirror == 'y': - transformed_geo = affinity.scale(transformed_geo, -1.0, 1.0, origin='center') + transformed_geo = scale(transformed_geo, -1.0, 1.0, origin='center') if mirror == 'both': - transformed_geo = affinity.scale(transformed_geo, -1.0, -1.0, origin='center') + transformed_geo = scale(transformed_geo, -1.0, -1.0, origin='center') return transformed_geo diff --git a/appPlugins/ToolFollow.py b/appPlugins/ToolFollow.py index 9919cfa5..c9594169 100644 --- a/appPlugins/ToolFollow.py +++ b/appPlugins/ToolFollow.py @@ -5,26 +5,10 @@ # License: MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCButton, FCComboBox, FCLabel, VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from appParsers.ParseGerber import Gerber -from camlib import flatten_shapely_geometry - -from copy import deepcopy - -import numpy as np - -from shapely.ops import unary_union -from shapely.geometry import Polygon - from matplotlib.backend_bases import KeyEvent as mpl_key_event - -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from camlib import flatten_shapely_geometry fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -350,7 +334,7 @@ class ToolFollow(AppTool, Gerber): def follow_init(new_obj, app_obj): new_obj.multigeo = True - + if type(app_obj.defaults["tools_mill_tooldia"]) == float: tools_list = [app_obj.defaults["tools_mill_tooldia"]] else: @@ -576,7 +560,7 @@ class ToolFollow(AppTool, Gerber): # "%.4f    " % (self.app.dx, self.app.dy)) self.app.ui.update_location_labels(self.app.dx, self.app.dy, curr_pos[0], curr_pos[1]) - units = self.app.app_units.lower() + # units = self.app.app_units.lower() # self.app.plotcanvas.text_hud.text = \ # 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( # self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) diff --git a/appPlugins/ToolImage.py b/appPlugins/ToolImage.py index f155d855..f8066b95 100644 --- a/appPlugins/ToolImage.py +++ b/appPlugins/ToolImage.py @@ -4,25 +4,11 @@ # Date: 3/10/2019 # # MIT Licence # # ########################################################## -from PyQt6 import QtGui, QtWidgets -import os - -from shapely.geometry import shape -from shapely.ops import unary_union -from shapely.affinity import scale, translate - -import numpy as np +from appTool import * from rasterio import open as rasterio_open from rasterio.features import shapes -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCComboBox, FCSpinner, FCLabel, VerticalScrollArea, FCGridLayout - -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext diff --git a/appPlugins/ToolInvertGerber.py b/appPlugins/ToolInvertGerber.py index babc15eb..e40b8e4f 100644 --- a/appPlugins/ToolInvertGerber.py +++ b/appPlugins/ToolInvertGerber.py @@ -5,22 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox, FCLabel, \ - VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from camlib import flatten_shapely_geometry -from shapely.geometry import box - -from copy import deepcopy - -import logging -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext diff --git a/appPlugins/ToolIsolation.py b/appPlugins/ToolIsolation.py index 7c5ff54a..85234389 100644 --- a/appPlugins/ToolIsolation.py +++ b/appPlugins/ToolIsolation.py @@ -5,31 +5,10 @@ # License: MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton, \ - FCComboBox, OptionalInputSection, FCSpinner, FCLabel, FCInputDialogSpinnerButton, FCComboBox2, \ - VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from appParsers.ParseGerber import Gerber -from camlib import grace - -from copy import deepcopy -import math - -import numpy as np -import simplejson as json -import sys - -from shapely.ops import unary_union, nearest_points -from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing, Point - from matplotlib.backend_bases import KeyEvent as mpl_key_event - -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from camlib import grace fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -347,6 +326,9 @@ class ToolIsolation(AppTool, Gerber): self.ui.milling_type_radio.set_value(self.app.options["tools_iso_milling_type"]) self.ui.combine_passes_cb.set_value(self.app.options["tools_iso_combine_passes"]) self.ui.valid_cb.set_value(self.app.options["tools_iso_check_valid"]) + self.ui.simplify_cb.set_value(self.app.options["tools_iso_simplification"]) + self.ui.sim_tol_entry.set_value(self.app.options["tools_iso_simplification_tol"]) + self.ui.area_shape_radio.set_value(self.app.options["tools_iso_area_shape"]) self.ui.poly_int_cb.set_value(self.app.options["tools_iso_poly_ints"]) self.ui.forced_rest_iso_cb.set_value(self.app.options["tools_iso_force"]) @@ -1756,6 +1738,11 @@ class ToolIsolation(AppTool, Gerber): selection_type = args['sel_type'] if 'sel_type' in args else self.ui.select_combo.get_value() # tool_tip_shape: ["C1", "C2", "C3", "C4", "B", "V", "L"] tool_tip_shape = args['tip_shape'] if 'tip_shape' in args else self.ui.tool_shape_combo.get_value() + # use simplification: True/False with simplificaiton_tol: Float + use_simplification = args['iso_simplification'] if 'iso_simplification' in args else \ + self.ui.simplify_cb.get_value() + simplification_tol = args['simplification_tol'] if 'simplification_tol' in args else \ + self.ui.sim_tol_entry.get_value() # update the Common Parameters values in the self.iso_tools for tool_iso in tools_storage: @@ -1763,6 +1750,8 @@ class ToolIsolation(AppTool, Gerber): if key == 'data': tools_storage[tool_iso][key]["tools_iso_rest"] = use_rest tools_storage[tool_iso][key]["tools_iso_combine_passes"] = use_combine + tools_storage[tool_iso][key]["tools_iso_simplification"] = use_simplification + tools_storage[tool_iso][key]["tools_iso_simplification_tol"] = simplification_tol tools_storage[tool_iso][key]["tools_iso_isoexcept"] = use_iso_except tools_storage[tool_iso][key]["tools_iso_selection"] = selection_type tools_storage[tool_iso][key]["tools_iso_area_shape"] = sel_area_shape @@ -1772,11 +1761,13 @@ class ToolIsolation(AppTool, Gerber): if use_combine: if use_rest: self.combined_rest(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage, - lim_area=limited_area, negative_dia=negative_dia, plot=plot) + lim_area=limited_area, negative_dia=negative_dia, + simp_en=use_simplification, simp_tol=simplification_tol, plot=plot) else: self.combined_normal(iso_obj=isolated_obj, iso2geo=geometry, tools_storage=tools_storage, sel_tools=sel_tools, iso_except=use_iso_except, lim_area=limited_area, - negative_dia=negative_dia, extra_passes=use_extra_passes, plot=plot) + negative_dia=negative_dia, extra_passes=use_extra_passes, + simp_en=use_simplification, simp_tol=simplification_tol, plot=plot) else: prog_plot = self.app.options["tools_iso_plotting"] @@ -1876,7 +1867,10 @@ class ToolIsolation(AppTool, Gerber): iso_geo = self.area_intersection(iso_geo, intersection_geo=limited_area) # make sure that no empty geometry element is in the solid_geometry - new_solid_geo = [geo for geo in iso_geo if not geo.is_empty] + new_solid_geo = self.flatten_list(iso_geo) + if use_simplification: + new_solid_geo = [ + g.simplify(tolerance=simplification_tol) for g in new_solid_geo if not g.is_empty] tool_data.update({ "name": iso_name, @@ -1940,7 +1934,8 @@ class ToolIsolation(AppTool, Gerber): # Switch notebook to Properties page # self.app.ui.notebook.setCurrentWidget(self.app.ui.properties_tab) - def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None, plot=True): + def combined_rest(self, iso_obj, iso2geo, tools_storage, lim_area, negative_dia=None, + simp_en=False, simp_tol=0.001, plot=True): """ Isolate the provided Gerber object using "rest machining" strategy @@ -1954,6 +1949,10 @@ class ToolIsolation(AppTool, Gerber): :type lim_area: Shapely Polygon or a list of them :param negative_dia: isolate the geometry with a negative value for the tool diameter :type negative_dia: bool + :param simp_en: enable simplification of geometry + :type simp_en: bool + :param simp_tol: simplification tolerance + :type simp_tol: float :param plot: if to plot the resulting geometry object :type plot: bool :return: Isolated solid geometry @@ -2071,6 +2070,12 @@ class ToolIsolation(AppTool, Gerber): if not work_geo: break + total_solid_geometry = self.flatten_list(total_solid_geometry) + total_solid_geometry = [g for g in total_solid_geometry if not g.is_empty] + if simp_en: + total_solid_geometry = [ + g.simplify(tolerance=simp_tol) for g in total_solid_geometry if not g.is_empty] + # clean the progressive plotted shapes if it was used if plot and self.app.options["tools_iso_plotting"] == 'progressive': self.temp_shapes.clear(update=True) @@ -2109,6 +2114,12 @@ class ToolIsolation(AppTool, Gerber): not isinstance(geo_obj.solid_geometry, MultiPolygon): geo_obj.solid_geometry = [geo_obj.solid_geometry] + # simplify geometry if enabled + if simp_en: + for t in geo_obj.tools: + geo_obj.tools[t]['solid_geometry'] = [ + g.simplify(tolerance=simp_tol) for g in geo_obj.tools[t]['solid_geometry']] + for g in geo_obj.solid_geometry: if g: break @@ -2138,7 +2149,7 @@ class ToolIsolation(AppTool, Gerber): self.app.inform_shell.emit(msg=msg) def combined_normal(self, iso_obj, iso2geo, tools_storage, lim_area, sel_tools, iso_except, extra_passes=None, - negative_dia=None, plot=True, prog_plot=None): + negative_dia=None, simp_en=False, simp_tol=0.001, plot=True, prog_plot=None): """ :param iso_obj: the isolated Gerber object @@ -2157,6 +2168,10 @@ class ToolIsolation(AppTool, Gerber): :type extra_passes: int :param negative_dia: isolate the geometry with a negative value for the tool diameter :type negative_dia: bool + :param simp_en: enable simplification of geometry + :type simp_en: bool + :param simp_tol: simplification tolerance + :type simp_tol: float :param plot: if to plot the resulting geometry object :type plot: bool :param prog_plot: If True a progressive plot is used @@ -2291,6 +2306,12 @@ class ToolIsolation(AppTool, Gerber): if not tool_dict['solid_geometry']: tools_storage.pop(tool, None) + total_solid_geometry = self.flatten_list(total_solid_geometry) + total_solid_geometry = [g for g in total_solid_geometry if not g.is_empty] + if simp_en: + total_solid_geometry = [ + g.simplify(tolerance=simp_tol) for g in total_solid_geometry if not g.is_empty] + def iso_init(geo_obj, app_obj): geo_obj.obj_options["tools_mill_tooldia"] = str(tool_dia) @@ -2317,6 +2338,12 @@ class ToolIsolation(AppTool, Gerber): not isinstance(geo_obj.solid_geometry, MultiPolygon): geo_obj.solid_geometry = [geo_obj.solid_geometry] + # simplify geometry if enabled + if simp_en: + for t in geo_obj.tools: + geo_obj.tools[t]['solid_geometry'] = [ + g.simplify(tolerance=simp_tol) for g in geo_obj.tools[t]['solid_geometry']] + for g in geo_obj.solid_geometry: if g: break @@ -3230,8 +3257,8 @@ class ToolIsolation(AppTool, Gerber): elif isinstance(work_geo_shp, LinearRing) and work_geo_shp is not None: work_geo_shp = [Polygon(work_geo_shp.coords[::-1])] else: - self.app.log.debug("ToolIsolation.generate_rest_geometry() Error --> Unexpected Geometry %s" % - type(work_geo)) + self.app.log.debug( + "ToolIsolation.generate_rest_geometry() Error --> Unexpected Geometry %s" % type(work_geo)) except Exception as e: self.app.log.error("ToolIsolation.generate_rest_geometry() Error --> %s" % str(e)) return 'fail', 'fail' @@ -3294,6 +3321,13 @@ class ToolIsolation(AppTool, Gerber): return None + def flatten_list(self, obj_list): + for item in obj_list: + if isinstance(item, Iterable) and not isinstance(item, (str, bytes)): + yield from self.flatten_list(item) + else: + yield item + class IsoUI: @@ -3780,13 +3814,29 @@ class IsoUI: gen_grid.addWidget(self.valid_cb, 4, 0, 1, 2) + # Simplification Tolerance + self.simplify_cb = FCCheckBox('%s' % _("Simplify")) + self.simplify_cb.setToolTip( + _("All points in the simplified object will be\n" + "within the tolerance distance of the original geometry.") + ) + self.sim_tol_entry = FCDoubleSpinner() + self.sim_tol_entry.set_precision(self.decimals) + self.sim_tol_entry.setSingleStep(10 ** -self.decimals) + self.sim_tol_entry.set_range(0.0000, 10000.0000) + + self.sois = OptionalInputSection(self.simplify_cb, [self.sim_tol_entry]) + + gen_grid.addWidget(self.simplify_cb, 6, 0) + gen_grid.addWidget(self.sim_tol_entry, 6, 1) + # Exception Areas self.except_cb = FCCheckBox(label=_('Except')) self.except_cb.setToolTip(_("When the isolation geometry is generated,\n" "by checking this, the area of the object below\n" "will be subtracted from the isolation geometry.")) self.except_cb.setObjectName("i_except") - gen_grid.addWidget(self.except_cb, 6, 0) + gen_grid.addWidget(self.except_cb, 8, 0) # Type of object to be excepted self.type_excobj_radio = RadioSet([{'label': _("Geometry"), 'value': 'geometry'}, @@ -3798,7 +3848,7 @@ class IsoUI: "of objects that will populate the 'Object' combobox.") ) - gen_grid.addWidget(self.type_excobj_radio, 6, 1) + gen_grid.addWidget(self.type_excobj_radio, 8, 1) # The object to be excepted self.exc_obj_combo = FCComboBox() @@ -3810,7 +3860,7 @@ class IsoUI: self.exc_obj_combo.is_last = True self.exc_obj_combo.obj_type = "gerber" - gen_grid.addWidget(self.exc_obj_combo, 8, 0, 1, 2) + gen_grid.addWidget(self.exc_obj_combo, 10, 0, 1, 2) self.e_ois = OptionalInputSection(self.except_cb, [ @@ -3833,8 +3883,8 @@ class IsoUI: ) self.select_combo.setObjectName("i_selection") - gen_grid.addWidget(self.select_label, 10, 0) - gen_grid.addWidget(self.select_combo, 10, 1) + gen_grid.addWidget(self.select_label, 12, 0) + gen_grid.addWidget(self.select_combo, 12, 1) # Reference Type self.reference_combo_type_label = FCLabel('%s:' % _("Type")) @@ -3842,15 +3892,15 @@ class IsoUI: self.reference_combo_type = FCComboBox2() self.reference_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")]) - gen_grid.addWidget(self.reference_combo_type_label, 12, 0) - gen_grid.addWidget(self.reference_combo_type, 12, 1) + gen_grid.addWidget(self.reference_combo_type_label, 14, 0) + gen_grid.addWidget(self.reference_combo_type, 14, 1) self.reference_combo = FCComboBox() self.reference_combo.setModel(self.app.collection) self.reference_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) self.reference_combo.is_last = True - gen_grid.addWidget(self.reference_combo, 14, 0, 1, 2) + gen_grid.addWidget(self.reference_combo, 16, 0, 1, 2) self.reference_combo.hide() self.reference_combo_type.hide() @@ -3863,7 +3913,7 @@ class IsoUI: "(holes in the polygon).") ) - gen_grid.addWidget(self.poly_int_cb, 16, 0) + gen_grid.addWidget(self.poly_int_cb, 18, 0) self.poly_int_cb.hide() @@ -3887,7 +3937,7 @@ class IsoUI: sel_hlay.addWidget(self.sel_all_btn) sel_hlay.addWidget(self.clear_all_btn) - gen_grid.addLayout(sel_hlay, 18, 0, 1, 2) + gen_grid.addLayout(sel_hlay, 20, 0, 1, 2) # Area Selection shape self.area_shape_label = FCLabel('%s:' % _("Shape")) @@ -3898,8 +3948,8 @@ class IsoUI: self.area_shape_radio = RadioSet([{'label': _("Square"), 'value': 'square'}, {'label': _("Polygon"), 'value': 'polygon'}]) - gen_grid.addWidget(self.area_shape_label, 20, 0) - gen_grid.addWidget(self.area_shape_radio, 20, 1) + gen_grid.addWidget(self.area_shape_label, 22, 0) + gen_grid.addWidget(self.area_shape_radio, 22, 1) self.area_shape_label.hide() self.area_shape_radio.hide() diff --git a/appPlugins/ToolLevelling.py b/appPlugins/ToolLevelling.py index 60994507..7cac3b9a 100644 --- a/appPlugins/ToolLevelling.py +++ b/appPlugins/ToolLevelling.py @@ -5,34 +5,19 @@ # License: MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui -from PyQt6.QtCore import Qt - +from appTool import * from appObjects.AppObjectTemplate import ObjectDeleted -from appTool import AppTool from appGUI.VisPyVisuals import * from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy -from appGUI.GUIElements import RadioSet, FCButton, FCComboBox, FCLabel, FCFileSaveDialog, FCCheckBox, FCTable, \ - FCDoubleSpinner, FCSpinner, FCDetachableTab, FCZeroAxes, FCJog, FCSliderWithDoubleSpinner, RotatedToolButton, \ - FCEntry, VerticalScrollArea, FCGridLayout, FCFrame, FCComboBox2 from appEditors.AppTextEditor import AppTextEditor from camlib import CNCjob -from copy import deepcopy import time import serial import glob import random -import sys from io import StringIO -from datetime import datetime - -import numpy as np - -from shapely.ops import unary_union -from shapely.geometry import Point, MultiPoint, box, MultiPolygon -import shapely.affinity as affinity from matplotlib.backend_bases import KeyEvent as mpl_key_event @@ -48,11 +33,6 @@ except Exception: except Exception: VORONOI_ENABLED = False -import logging -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -263,7 +243,7 @@ class ToolLevelling(AppTool, CNCjob): # Shapes container for the Voronoi cells in Autolevelling if self.app.use_3d_engine: - self.probing_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) + self.probing_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1, pool=self.app.pool) else: self.probing_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name=name + "_probing_shapes") @@ -352,7 +332,8 @@ class ToolLevelling(AppTool, CNCjob): # Shapes container for the Voronoi cells in Autolevelling if self.app.use_3d_engine: - self.probing_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) + self.probing_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1, + pool=self.app.pool) else: self.probing_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name=obj_name + "_probing_shapes") return @@ -745,7 +726,7 @@ class ToolLevelling(AppTool, CNCjob): except Exception as e: self.app.log.error("CNCJobObject.generate_voronoi_geometry() --> %s" % str(e)) for pt_index in range(len(pts)): - new_pts[pt_index] = affinity.translate( + new_pts[pt_index] = translate( new_pts[pt_index], random.random() * 1e-09, random.random() * 1e-09) pts_union = MultiPoint(new_pts) @@ -1609,7 +1590,7 @@ class ToolLevelling(AppTool, CNCjob): return except IOError: self.app.log.error("Failed to open height map file: %s" % filename) - self.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open height map file"), filename)) + self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open height map file"), filename)) return idx = 0 @@ -1729,7 +1710,7 @@ class ToolLevelling(AppTool, CNCjob): pass def reset_fields(self): - self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.ui.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) class LevelUI: @@ -1980,7 +1961,7 @@ class LevelUI: self.tools_box.addWidget(self.al_controller_label) self.c_frame = FCFrame() - self.tools_box.addWidget( self.c_frame) + self.tools_box.addWidget(self.c_frame) ctrl_grid = FCGridLayout(v_spacing=5, h_spacing=3) self.c_frame.setLayout(ctrl_grid) diff --git a/appPlugins/ToolMarkers.py b/appPlugins/ToolMarkers.py index 73114a3e..496adb6d 100644 --- a/appPlugins/ToolMarkers.py +++ b/appPlugins/ToolMarkers.py @@ -5,24 +5,10 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool +from appTool import * 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 - -from copy import deepcopy -import logging - -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -381,7 +367,8 @@ class ToolMarkers(AppTool): assert isinstance(geo_list, list), 'Geometry list should be a list but the type is: %s' % str(type(geo_list)) new_name = '%s_%s' % (str(self.grb_object.obj_options['name']), 'markers') - return_val = self.generate_gerber_obj_with_markers(new_grb_obj=g_obj, marker_geometry=geo_list, outname=new_name) + return_val = self.generate_gerber_obj_with_markers( + new_grb_obj=g_obj, marker_geometry=geo_list, outname=new_name) return return_val @@ -421,7 +408,7 @@ class ToolMarkers(AppTool): xmin, ymin, xmax, ymax = self.grb_object.bounds() center_x = (xmax - xmin) / 2 center_y = (ymax - ymin) / 2 - offset_x -= center_x + offset_x -= center_x offset_y -= center_y return offset_x, offset_y @@ -997,7 +984,7 @@ class ToolMarkers(AppTool): else: self.app.inform.emit('[success] %s' % _("Done.")) - def on_points_changed(self, *args): + def on_points_changed(self): if self.points: self.ui.insert_frame.setDisabled(False) self.ui.insert_markers_button.setDisabled(False) @@ -1126,8 +1113,8 @@ class ToolMarkers(AppTool): ]) outline = '#0000FFAF' - for shape in [line_geo_hor, line_geo_vert]: - self.temp_shapes.add(shape, color=outline, update=True, layer=0, tolerance=None) + for shp in [line_geo_hor, line_geo_vert]: + self.temp_shapes.add(shp, color=outline, update=True, layer=0, tolerance=None) if self.app.use_3d_engine: self.temp_shapes.redraw() diff --git a/appPlugins/ToolMilling.py b/appPlugins/ToolMilling.py index 89c5a878..2a3d0aa1 100644 --- a/appPlugins/ToolMilling.py +++ b/appPlugins/ToolMilling.py @@ -5,36 +5,10 @@ # License: MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton, FCComboBox2, \ - FCComboBox, OptionalInputSection, FCSpinner, NumericalEvalTupleEntry, OptionalHideInputSection, FCLabel, \ - VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from appParsers.ParseExcellon import Excellon - -from camlib import grace -import numpy as np - -from copy import deepcopy -import math -import simplejson as json -import sys -import traceback - -# from appObjects.FlatCAMObj import FlatCAMObj -# import numpy as np -# import math - -# from shapely.ops import unary_union -from shapely.geometry import Point, LineString, box - from matplotlib.backend_bases import KeyEvent as mpl_key_event - -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from camlib import grace fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -251,8 +225,8 @@ class ToolMilling(AppTool, Excellon): self.ui.level.toggled.connect(self.on_level_changed) # add Tool - self.ui.search_and_add_btn.clicked.connect(self.on_tool_add) - self.ui.deltool_btn.clicked.connect(self.on_tool_delete) + self.ui.search_and_add_btn.clicked.connect(lambda: self.on_tool_add()) + self.ui.deltool_btn.clicked.connect(lambda: self.on_tool_delete()) self.ui.addtool_from_db_btn.clicked.connect(self.on_tool_add_from_db_clicked) self.ui.target_radio.activated_custom.connect(self.on_target_changed) @@ -585,7 +559,7 @@ class ToolMilling(AppTool, Excellon): "mill_spindlespeed": "tools_mill_spindlespeed", "mill_dwell": "tools_mill_dwell", "mill_dwelltime": "tools_mill_dwelltime", - "mill_minpower":"tools_mill_min_power", + "mill_minpower": "tools_mill_min_power", # General Parameters "mill_toolchange": "tools_mill_toolchange", @@ -1154,9 +1128,8 @@ class ToolMilling(AppTool, Excellon): self.tool_row = 0 for tool_no in tools: - - drill_cnt = 0 # variable to store the nr of drills per tool - slot_cnt = 0 # variable to store the nr of slots per tool + # drill_cnt = 0 # variable to store the nr of drills per tool + # slot_cnt = 0 # variable to store the nr of slots per tool # Find no of drills for the current tool try: @@ -1946,6 +1919,7 @@ class ToolMilling(AppTool, Excellon): def on_tt_change(self): cw = self.sender() + assert isinstance(cw, FCComboBox2) tool_type = cw.currentText() self.ui_update_v_shape(tool_type) @@ -2043,14 +2017,14 @@ class ToolMilling(AppTool, Excellon): tooluid_item = int(self.ui.tools_table.item(row, 3).text()) temp_tool_data = {} - for tooluid_key, tooluid_val in self.iso_tools.items(): + for tooluid_key, tooluid_val in self.target_obj.tools.items(): if int(tooluid_key) == tooluid_item: # this will hold the 'data' key of the self.tools[tool] dictionary that corresponds to # the current row in the tool table temp_tool_data = tooluid_val['data'] break - for tooluid_key, tooluid_val in self.iso_tools.items(): + for tooluid_key, tooluid_val in self.target_obj.tools.items(): tooluid_val['data'] = deepcopy(temp_tool_data) self.app.inform.emit('[success] %s' % _("Current Tool parameters were applied to all tools.")) @@ -2060,7 +2034,7 @@ class ToolMilling(AppTool, Excellon): if order != 0: # "default" self.build_ui() - def on_tool_add(self, clicked_state, dia=None, new_geo=None): + def on_tool_add(self, dia=None, new_geo=None): self.app.log.debug("GeometryObject.on_add_tool()") if self.target_obj is None: @@ -2428,11 +2402,7 @@ class ToolMilling(AppTool, Excellon): self.builduiSig.emit() self.app.inform.emit('[success] %s' % _("Tool was copied in Tool Table.")) - def on_tool_delete(self, clicked_signal, all_tools=None): - """ - It's important to keep the not clicked_signal parameter otherwise the signal will go to the all_tools - parameter and I might get all the tool deleted - """ + def on_tool_delete(self, all_tools=None): self.ui_disconnect() if all_tools is None: @@ -2549,34 +2519,30 @@ class ToolMilling(AppTool, Excellon): outname = self.target_obj.obj_options["name"] + "_mill" if tooldia is None: - tooldia = float(self.target_obj.obj_options["tooldia"]) + tooldia = self.ui.tooldia_entry.get_value() # Sort tools by diameter. items() -> [('name', diameter), ...] - sorted_tools = sorted(list(self.tools.items()), key=lambda tl: tl[1]['tooldia']) + # sorted_tools = sorted(list(self.tools.items()), key=lambda tl: tl[1]) # no longer works in Python3 - # sort = [] - # for k, v in self.tools.items(): - # sort.append((k, v.get('tooldia'))) - # sorted_tools = sorted(sort, key=lambda t1: t1[1]) + sort = [] + for k, v in self.target_obj.tools.items(): + sort.append((k, v['tooldia'])) + sorted_tools = sorted(sort, key=lambda t1: t1[1]) if tools == "all": tools = [i[0] for i in sorted_tools] # List if ordered tool names. self.app.log.debug("Tools 'all' and sorted are: %s" % str(tools)) if len(tools) == 0: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Please select one or more tools from the list and try again.")) + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Please select one or more tools from the list and try again.")) return False, "Error: No tools." for tool in tools: - if tooldia > self.tools[tool]['data']['tools_mill_tooldia']: - self.app.inform.emit( - '[ERROR_NOTCL] %s %s: %s' % ( - _("Milling tool for DRILLS is larger than hole size. Cancelled."), - _("Tool"), - str(tool) - ) - ) + if tooldia > self.target_obj.tools[tool]["tooldia"]: + mseg = '[ERROR_NOTCL] %s %s: %s' % (_("Milling tool for DRILLS is larger than hole size. Cancelled."), + _("Tool"), + str(tool)) + self.app.inform.emit(mseg) return False, "Error: Milling tool is larger than hole." def geo_init(geo_obj, app_obj): @@ -2591,27 +2557,26 @@ class ToolMilling(AppTool, Excellon): """ assert geo_obj.kind == 'geometry', "Initializer expected a GeometryObject, got %s" % type(geo_obj) - app_obj.inform.emit(_("Generating drills milling geometry...")) - # ## Add properties to the object geo_obj.obj_options['type'] = 'Excellon Geometry' geo_obj.obj_options["tools_mill_tooldia"] = str(tooldia) - geo_obj.obj_options["tools_mill_multidepth"] = self.target_obj.obj_options["tools_mill_multidepth"] + geo_obj.obj_options["multidepth"] = app_obj.options["tools_mill_multidepth"] geo_obj.solid_geometry = [] # in case that the tool used has the same diameter with the hole, and since the maximum resolution # for FlatCAM is 6 decimals, # we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero" - for hole in self.drills: - if hole['tool'] in tools: - buffer_value = self.tools[hole['tool']]["C"] / 2 - tooldia / 2 + for etool in tools: + for drill in self.target_obj.tools[etool]['drills']: + buffer_value = self.target_obj.tools[etool]['tooldia'] / 2 - tooldia / 2 if buffer_value == 0: - geo_obj.solid_geometry.append( - Point(hole['point']).buffer(0.0000001).exterior) + geo_obj.solid_geometry.append(drill.buffer(0.0000001).exterior) else: - geo_obj.solid_geometry.append( - Point(hole['point']).buffer(buffer_value).exterior) + geo_obj.solid_geometry.append(drill.buffer(buffer_value).exterior) + + if not geo_obj.solid_geometry: + return "fail" if use_thread: def geo_thread(a_obj): @@ -2656,32 +2621,31 @@ class ToolMilling(AppTool, Excellon): tools = self.get_selected_tools_list() if outname is None: - outname = self.target_obj.obj_options["name"] + "_slots" + outname = self.target_obj.obj_options["name"] + "_mill" if tooldia is None: tooldia = float(self.target_obj.obj_options["slot_tooldia"]) # Sort tools by diameter. items() -> [('name', diameter), ...] - sorted_tools = sorted(list(self.tools.items()), key=lambda tl: tl[1]['tooldia']) - # - # sort = [] - # for k, v in self.tools.items(): - # sort.append((k, v.get('tooldia'))) - # sorted_tools = sorted(sort, key=lambda t1: t1[1]) + # sorted_tools = sorted(list(self.tools.items()), key=lambda tl: tl[1]) # no longer works in Python3 + + sort = [] + for k, v in self.target_obj.tools.items(): + sort.append((k, v['tooldia'])) + sorted_tools = sorted(sort, key=lambda t1: t1[1]) if tools == "all": tools = [i[0] for i in sorted_tools] # List if ordered tool names. self.app.log.debug("Tools 'all' and sorted are: %s" % str(tools)) if len(tools) == 0: - self.app.inform.emit('[ERROR_NOTCL] %s' % - _("Please select one or more tools from the list and try again.")) + self.app.inform.emit('[ERROR_NOTCL] %s' % _("Please select one or more tools from the list and try again.")) return False, "Error: No tools." for tool in tools: # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse adj_toolstable_tooldia = float('%.*f' % (self.decimals, float(tooldia))) - adj_file_tooldia = float('%.*f' % (self.decimals, float(self.tools[tool]['data']['tools_mill_tooldia']))) + adj_file_tooldia = float('%.*f' % (self.decimals, float(self.target_obj.tools[tool]["tooldia"]))) if adj_toolstable_tooldia > adj_file_tooldia + 0.0001: self.app.inform.emit('[ERROR_NOTCL] %s' % _("Milling tool for SLOTS is larger than hole size. Cancelled.")) @@ -2690,40 +2654,40 @@ class ToolMilling(AppTool, Excellon): def geo_init(geo_obj, app_obj): assert geo_obj.kind == 'geometry', "Initializer expected a GeometryObject, got %s" % type(geo_obj) - app_obj.inform.emit(_("Generating slot milling geometry...")) - # ## Add properties to the object geo_obj.obj_options['type'] = 'Excellon Geometry' geo_obj.obj_options["tools_mill_tooldia"] = str(tooldia) - geo_obj.obj_options["tools_mill_multidepth"] = self.target_obj.obj_options["tools_mill_multidepth"] + geo_obj.obj_options["tools_mill_multidepth"] = app_obj.options["tools_mill_multidepth"] geo_obj.solid_geometry = [] # in case that the tool used has the same diameter with the hole, and since the maximum resolution # for FlatCAM is 6 decimals, # we add a tenth of the minimum value, meaning 0.0000001, which from our point of view is "almost zero" - for slot in self.slots: - if slot['tool'] in tools: + for m_tool in tools: + for slot in self.target_obj.tools[m_tool]['slots']: toolstable_tool = float('%.*f' % (self.decimals, float(tooldia))) - file_tool = float('%.*f' % (self.decimals, float(self.tools[tool]["C"]))) + file_tool = float('%.*f' % (self.decimals, float(self.target_obj.tools[m_tool]["tooldia"]))) # I add the 0.0001 value to account for the rounding error in converting from IN to MM and reverse # for the file_tool (tooldia actually) buffer_value = float(file_tool / 2) - float(toolstable_tool / 2) + 0.0001 if buffer_value == 0: - start = slot['start'] - stop = slot['stop'] + start = slot[0] + stop = slot[1] lines_string = LineString([start, stop]) poly = lines_string.buffer(0.0000001, int(self.geo_steps_per_circle)).exterior geo_obj.solid_geometry.append(poly) else: - start = slot['start'] - stop = slot['stop'] + start = slot[0] + stop = slot[1] lines_string = LineString([start, stop]) poly = lines_string.buffer(buffer_value, int(self.geo_steps_per_circle)).exterior geo_obj.solid_geometry.append(poly) + if not geo_obj.solid_geometry: + return "fail" if use_thread: def geo_thread(a_obj): diff --git a/appPlugins/ToolMove.py b/appPlugins/ToolMove.py index 09e76616..8b9c4f47 100644 --- a/appPlugins/ToolMove.py +++ b/appPlugins/ToolMove.py @@ -5,16 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore -from appTool import AppTool +from appTool import * from appGUI.VisPyVisuals import * -from copy import copy -import logging -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -48,7 +41,7 @@ class ToolMove(AppTool): # VisPy visuals if self.app.use_3d_engine: - self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1) + self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1, pool=self.app.pool) else: from appGUI.PlotCanvasLegacy import ShapeCollectionLegacy self.sel_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name="move") @@ -336,13 +329,12 @@ class ToolMove(AppTool): self.sel_shapes.clear() self.sel_shapes.redraw() - def draw_shape(self, shape): - + def draw_shape(self, shp): if self.app.app_units.upper() == 'MM': - proc_shape = shape.buffer(-0.1) + proc_shape = shp.buffer(-0.1) proc_shape = proc_shape.buffer(0.2) else: - proc_shape = shape.buffer(-0.00393) + proc_shape = shp.buffer(-0.00393) proc_shape = proc_shape.buffer(0.00787) # face = Color('blue') diff --git a/appPlugins/ToolNCC.py b/appPlugins/ToolNCC.py index 08c3afe8..ab775161 100644 --- a/appPlugins/ToolNCC.py +++ b/appPlugins/ToolNCC.py @@ -5,33 +5,11 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCCheckBox, FCDoubleSpinner, RadioSet, FCTable, FCButton,\ - FCComboBox, OptionalInputSection, FCLabel, FCInputDialogSpinnerButton, FCComboBox2, \ - VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from appParsers.ParseGerber import Gerber - from camlib import grace, flatten_shapely_geometry - -from copy import deepcopy - -import numpy as np -from shapely.geometry import base -from shapely.ops import unary_union, nearest_points -from shapely.geometry import MultiPolygon, Polygon, MultiLineString, LineString, LinearRing - from matplotlib.backend_bases import KeyEvent as mpl_key_event -import logging -import traceback -import sys -import gettext -import simplejson as json -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -69,6 +47,8 @@ class NonCopperClear(AppTool, Gerber): # store here the default data for Geometry Data self.default_data = {} + self.grid_status_memory = None + self.obj_name = "" self.ncc_obj = None @@ -809,7 +789,8 @@ class NonCopperClear(AppTool, Gerber): # make the diameter column editable for row in range(tool_id): - flags = QtCore.Qt.ItemFlag.ItemIsEditable | QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled + flags = QtCore.Qt.ItemFlag.ItemIsEditable | QtCore.Qt.ItemFlag.ItemIsSelectable | \ + QtCore.Qt.ItemFlag.ItemIsEnabled self.ui.tools_table.item(row, 1).setFlags(flags) self.ui.tools_table.resizeColumnsToContents() @@ -1784,7 +1765,7 @@ class NonCopperClear(AppTool, Gerber): # "%.4f    " % (self.app.dx, self.app.dy)) self.app.ui.update_location_labels(self.app.dx, self.app.dy, curr_pos[0], curr_pos[1]) - units = self.app.app_units.lower() + # units = self.app.app_units.lower() # self.app.plotcanvas.text_hud.text = \ # 'Dx:\t{:<.4f} [{:s}]\nDy:\t{:<.4f} [{:s}]\n\nX: \t{:<.4f} [{:s}]\nY: \t{:<.4f} [{:s}]'.format( # self.app.dx, units, self.app.dy, units, curr_pos[0], units, curr_pos[1], units) @@ -1854,14 +1835,6 @@ class NonCopperClear(AppTool, Gerber): # restore the Grid snapping if it was active before if self.grid_status_memory is True: self.app.ui.grid_snap_btn.trigger() - - if self.app.use_3d_engine: - self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_single_poly_mouse_release) - self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press) - else: - self.app.plotcanvas.graph_event_disconnect(self.mr) - self.app.plotcanvas.graph_event_disconnect(self.kp) - self.app.tool_shapes.clear(update=True) except Exception as e: self.app.log.error("ToolNCC.on_key_press() _2 --> %s" % str(e)) @@ -1919,7 +1892,7 @@ class NonCopperClear(AppTool, Gerber): box_geo = box_obj.solid_geometry if box_kind == 'geometry': - box_geo = flatten_shapely_geometry(box_geo) + env_obj = flatten_shapely_geometry(box_geo) elif box_kind == 'gerber': box_geo = unary_union(box_obj.solid_geometry).convex_hull ncc_geo = unary_union(ncc_obj.solid_geometry).convex_hull @@ -2846,7 +2819,7 @@ class NonCopperClear(AppTool, Gerber): # Background self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]}) else: - job_thread(app_obj=self.app) + job_thread(a_obj=self.app) def clear_copper_tcl(self, ncc_obj, sel_obj=None, ncctooldia=None, isotooldia=None, margin=None, has_offset=None, offset=None, select_method=None, outname=None, overlap=None, connect=None, contour=None, @@ -3890,7 +3863,7 @@ class NonCopperClear(AppTool, Gerber): def poly2rings(poly): return [poly.exterior] + [interior for interior in poly.interiors] - def generate_envelope(self, offset, invert, envelope_iso_type=2, follow=None): + def generate_envelope(self, offset, invert, envelope_iso_type=2): # isolation_geometry produces an envelope that is going on the left of the geometry # (the copper features). To leave the least amount of burrs on the features # the tool needs to travel on the right side of the features (this is called conventional milling) @@ -3898,7 +3871,7 @@ class NonCopperClear(AppTool, Gerber): # the other passes overlap preceding ones and cut the left over copper. It is better for them # to cut on the right side of the left over copper i.e on the left side of the features. try: - geom = self.isolation_geometry(offset, iso_type=envelope_iso_type, follow=follow) + geom = self.isolation_geometry(offset, iso_type=envelope_iso_type) except Exception as e: self.app.log.error('NonCopperClear.generate_envelope() --> %s' % str(e)) return 'fail' @@ -4034,7 +4007,7 @@ class NonCopperClear(AppTool, Gerber): self.app.tools_db_tab.ui.cancel_tool_from_db.show() def reset_fields(self): - self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) + self.ui.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex())) class NccUI: diff --git a/appPlugins/ToolObjectDistance.py b/appPlugins/ToolObjectDistance.py index bce56fac..f1bddc05 100644 --- a/appPlugins/ToolObjectDistance.py +++ b/appPlugins/ToolObjectDistance.py @@ -5,20 +5,7 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui -from appTool import AppTool -from appGUI.GUIElements import FCEntry, FCLabel, FCButton, VerticalScrollArea, FCGridLayout, FCFrame, FCComboBox2 - -from shapely.ops import nearest_points -from shapely.geometry import Point, MultiPolygon -from shapely.ops import unary_union -from copy import deepcopy - -import math -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: diff --git a/appPlugins/ToolOptimal.py b/appPlugins/ToolOptimal.py index 6ecdfa8b..13b3a6f0 100644 --- a/appPlugins/ToolOptimal.py +++ b/appPlugins/ToolOptimal.py @@ -5,23 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import OptionalHideInputSection, FCTextArea, FCEntry, FCSpinner, FCCheckBox, FCComboBox, \ - FCLabel, FCButton, VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from camlib import grace -from shapely.geometry import MultiPolygon -from shapely.ops import nearest_points - -import numpy as np - -import logging -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext diff --git a/appPlugins/ToolPDF.py b/appPlugins/ToolPDF.py index d4867916..0d6ba4b5 100644 --- a/appPlugins/ToolPDF.py +++ b/appPlugins/ToolPDF.py @@ -4,29 +4,11 @@ # Date: 4/23/2019 # # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore - -from appTool import AppTool - -from appParsers.ParsePDF import PdfParser, grace -from shapely.geometry import Point, MultiPolygon -from shapely.ops import unary_union - -from copy import deepcopy -# from io import BytesIO -# -# import zlib -import re -import time -import logging -import traceback -import os +from appTool import * +from appParsers.ParsePDF import PdfParser from pikepdf import Pdf, parse_content_stream - -import gettext -import appTranslation as fcTranslate -import builtins +from camlib import grace fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -111,7 +93,7 @@ class ToolPDF(AppTool): def open_pdf(self, filename): if not os.path.exists(filename): - self.inform.emit('[ERROR_NOTCL] %s' % _("File no longer available.")) + self.app.inform.emit('[ERROR_NOTCL] %s' % _("File no longer available.")) return short_name = filename.split('/')[-1].split('\\')[-1] @@ -140,7 +122,7 @@ class ToolPDF(AppTool): for op in operands: try: line += str(op) + ' ' - except Exception as e: + except Exception: # print(str(e), operands, command) pass line += str(command) diff --git a/appPlugins/ToolPaint.py b/appPlugins/ToolPaint.py index 59ede318..3eaa8b68 100644 --- a/appPlugins/ToolPaint.py +++ b/appPlugins/ToolPaint.py @@ -5,33 +5,12 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtGui, QtCore -from PyQt6.QtCore import Qt - -from appTool import AppTool -from copy import deepcopy - +from appTool import * from appParsers.ParseGerber import Gerber from camlib import Geometry, AppRTreeStorage, grace -from appGUI.GUIElements import FCTable, FCDoubleSpinner, FCCheckBox, FCInputDoubleSpinner, RadioSet, \ - FCButton, FCComboBox, FCLabel, FCComboBox2, VerticalScrollArea, FCGridLayout, FCFrame - -from shapely.geometry import base, Polygon, MultiPolygon, LinearRing, Point, MultiLineString, LineString -from shapely.ops import unary_union, linemerge from matplotlib.backend_bases import KeyEvent as mpl_key_event -import numpy as np -from numpy import Inf -import traceback -import sys -import logging -import simplejson as json - -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -781,7 +760,8 @@ class ToolPaint(AppTool, Gerber): # make the diameter column editable for row in range(tool_id): self.ui.tools_table.item(row, 1).setFlags( - QtCore.Qt.ItemFlag.ItemIsEditable | QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) + QtCore.Qt.ItemFlag.ItemIsEditable | QtCore.Qt.ItemFlag.ItemIsSelectable | + QtCore.Qt.ItemFlag.ItemIsEnabled) # all the tools are selected by default self.ui.tools_table.selectColumn(0) @@ -1235,7 +1215,7 @@ class ToolPaint(AppTool, Gerber): self.app.ui.notebook.setDisabled(True) elif self.select_method == 3: # _("Reference Object") - self.bound_obj_name = self.reference_combo.currentText() + self.bound_obj_name = self.ui.reference_combo.currentText() # Get source object. try: self.bound_obj = self.app.collection.get_by_name(self.bound_obj_name) @@ -1703,7 +1683,8 @@ class ToolPaint(AppTool, Gerber): except grace: return "fail" except Exception as ee: - self.app.log.error("ToolPaint.paint_polygon_worker() Laser Lines -> Identify flashes/traces--> %s" % str(ee)) + self.app.log.error( + "ToolPaint.paint_polygon_worker() Laser Lines -> Identify flashes/traces--> %s" % str(ee)) cpoly = AppRTreeStorage() pads_lines_list = [] @@ -2155,15 +2136,15 @@ class ToolPaint(AppTool, Gerber): geo_elems = list(geo_res.get_objects()) # See if the polygon was completely cleared pp_cleared = unary_union(geo_elems).buffer(tool_dia / 2.0) - rest = pp.difference(pp_cleared) - if rest and not rest.is_empty: + rest_geo = pp.difference(pp_cleared) + if rest_geo and not rest_geo.is_empty: try: - for r in rest: + for r in rest_geo: if r.is_valid: rest_list.append(r) except TypeError: - if rest.is_valid: - rest_list.append(rest) + if rest_geo.is_valid: + rest_list.append(rest_geo) if geo_res: cleared_geo += geo_elems @@ -2195,15 +2176,15 @@ class ToolPaint(AppTool, Gerber): # See if the polygon was completely cleared pp_cleared = unary_union(geo_elems).buffer(tool_dia / 2.0) - rest = poly_buf.difference(pp_cleared) - if rest and not rest.is_empty: + rest_geo = poly_buf.difference(pp_cleared) + if rest_geo and not rest_geo.is_empty: try: - for r in rest: + for r in rest_geo: if r.is_valid: rest_list.append(r) except TypeError: - if rest.is_valid: - rest_list.append(rest) + if rest_geo.is_valid: + rest_list.append(rest_geo) if geo_res: cleared_geo += geo_elems @@ -2473,7 +2454,8 @@ class ToolPaint(AppTool, Gerber): if obj.kind == 'gerber': # I don't do anything here, like buffering when the Gerber is loaded without buffering????!!!! if self.app.options["gerber_buffering"] == 'no': - msg = '%s %s %s' % (_("Paint Plugin."), _("Paint all polygons task started."), _("Buffering geometry...")) + msg = '%s %s %s' % (_("Paint Plugin."), _("Paint all polygons task started."), + _("Buffering geometry...")) self.app.inform.emit(msg) else: self.app.inform.emit('%s %s' % (_("Paint Plugin."), _("Paint all polygons task started."))) @@ -2780,8 +2762,7 @@ class ToolPaint(AppTool, Gerber): for k in o: try: minx_, miny_, maxx_, maxy_ = bounds_rec(k) - except Exception as e: - # log.error("ToolPaint.bounds() --> %s" % str(e)) + except Exception: return minx = min(minx, minx_) diff --git a/appPlugins/ToolPanelize.py b/appPlugins/ToolPanelize.py index 28d7aa4b..0211d005 100644 --- a/appPlugins/ToolPanelize.py +++ b/appPlugins/ToolPanelize.py @@ -5,25 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtGui, QtCore -from appTool import AppTool - -from appGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection, FCComboBox, \ - FCButton, FCLabel, VerticalScrollArea, FCGridLayout, FCFrame +from appTool import * from camlib import grace -from copy import deepcopy -import numpy as np - -import shapely.affinity as affinity -from shapely.ops import unary_union, linemerge, snap -from shapely.geometry import LineString, MultiLineString, Polygon, MultiPolygon - -import gettext -import appTranslation as fcTranslate -import builtins -import logging - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -328,18 +312,18 @@ class Panelize(AppTool): boxname = self.ui.box_combo.currentText() try: - box = self.app.collection.get_by_name(boxname) + box_obj = self.app.collection.get_by_name(boxname) except Exception as e: self.app.log.error("Panelize.on_panelize() --> %s" % str(e)) self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), boxname)) return - if box is None: + if box_obj is None: self.app.inform.emit('[WARNING_NOTCL] %s: %s' % (_("No object Box. Using instead"), panel_source_obj)) self.ui.reference_radio.set_value('bbox') if self.ui.reference_radio.get_value() == 'bbox': - box = panel_source_obj + box_obj = panel_source_obj self.outname = name + '_panelized' @@ -365,7 +349,7 @@ class Panelize(AppTool): _("Columns or Rows are zero value. Change them to a positive integer.")) return - xmin, ymin, xmax, ymax = box.bounds() + xmin, ymin, xmax, ymax = box_obj.bounds() lenghtx = xmax - xmin + spacing_columns lenghty = ymax - ymin + spacing_rows @@ -450,7 +434,7 @@ class Panelize(AppTool): raise grace # offset / panelization - point_offseted = affinity.translate(drill, currentx, currenty) + point_offseted = translate(drill, currentx, currenty) obj_fin.tools[tool]['drills'].append(point_offseted) # update progress @@ -474,8 +458,8 @@ class Panelize(AppTool): raise grace # offset / panelization - start_offseted = affinity.translate(slot[0], currentx, currenty) - stop_offseted = affinity.translate(slot[1], currentx, currenty) + start_offseted = translate(slot[0], currentx, currenty) + stop_offseted = translate(slot[1], currentx, currenty) offseted_slot = ( start_offseted, stop_offseted @@ -522,7 +506,7 @@ class Panelize(AppTool): geoms.append(res_geo) return geoms else: - return affinity.translate(geom, xoff=currentx, yoff=currenty) + return translate(geom, xoff=currentx, yoff=currenty) new_obj.solid_geometry = [] @@ -836,7 +820,7 @@ class Panelize(AppTool): geoms.append(res_geo) return geoms else: - return affinity.translate(geom, xoff=currentx, yoff=currenty) + return translate(geom, xoff=currentx, yoff=currenty) new_obj.solid_geometry = [] diff --git a/appPlugins/ToolPcbWizard.py b/appPlugins/ToolPcbWizard.py index 27b9e4a9..0b59730c 100644 --- a/appPlugins/ToolPcbWizard.py +++ b/appPlugins/ToolPcbWizard.py @@ -5,20 +5,9 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore - -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCSpinner, FCButton, FCTable, FCLabel, VerticalScrollArea, FCGridLayout - -import re -import os -from datetime import datetime +from appTool import * from io import StringIO -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -333,7 +322,7 @@ class PcbWizard(AppTool): # Register recent file self.app.options["global_last_folder"] = os.path.split(str(filename))[0] - def on_import_excellon(self, signal=None, excellon_fileobj=None): + def on_import_excellon(self, excellon_fileobj=None): self.app.log.debug("import_2files_excellon()") # How the object should be initialized diff --git a/appPlugins/ToolPunchGerber.py b/appPlugins/ToolPunchGerber.py index 566cb488..041114c6 100644 --- a/appPlugins/ToolPunchGerber.py +++ b/appPlugins/ToolPunchGerber.py @@ -5,26 +5,12 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtCore, QtWidgets, QtGui - -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox, FCTable, FCButton, FCLabel, \ - VerticalScrollArea, FCGridLayout, FCFrame - -from copy import deepcopy -import logging -from shapely.geometry import MultiPolygon, Point -from shapely.ops import unary_union - +from appTool import * from appParsers.ParseGerber import Gerber -from camlib import Geometry, AppRTreeStorage, grace +from camlib import Geometry from matplotlib.backend_bases import KeyEvent as mpl_key_event -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -1812,26 +1798,6 @@ class ToolPunchGerber(AppTool, Gerber): key = event.key if key == QtCore.Qt.Key.Key_Escape or key == 'Escape': - if self.area_sel_disconnect_flag is True: - try: - if self.app.use_3d_engine: - self.app.plotcanvas.graph_event_disconnect('mouse_release', self.on_mouse_release) - self.app.plotcanvas.graph_event_disconnect('mouse_move', self.on_mouse_move) - self.app.plotcanvas.graph_event_disconnect('key_press', self.on_key_press) - else: - self.app.plotcanvas.graph_event_disconnect(self.mr) - self.app.plotcanvas.graph_event_disconnect(self.mm) - self.app.plotcanvas.graph_event_disconnect(self.kp) - except Exception as e: - self.app.log.error("ToolPaint.on_key_press() _1 --> %s" % str(e)) - - self.app.mp = self.app.plotcanvas.graph_event_connect('mouse_press', - self.app.on_mouse_click_over_plot) - self.app.mm = self.app.plotcanvas.graph_event_connect('mouse_move', - self.app.on_mouse_move_over_plot) - self.app.mr = self.app.plotcanvas.graph_event_connect('mouse_release', - self.app.on_mouse_click_release_over_plot) - if self.poly_sel_disconnect_flag is False: try: # restore the Grid snapping if it was active before @@ -1931,10 +1897,12 @@ class ToolPunchGerber(AppTool, Gerber): } self.manual_pads.append(deepcopy(new_elem)) + sel_color = self.app.options['global_sel_draw_color'] + 'FF' if \ + len(self.app.options['global_sel_draw_color']) == 7 else \ + self.app.options['global_sel_draw_color'] shape_id = self.app.tool_shapes.add( tolerance=self.grb_obj.drawing_tolerance, layer=0, shape=sol_geo, - color=self.app.options['global_sel_draw_color'] + 'FF', - face_color=self.app.options['global_sel_draw_color'] + 'FF', visible=True) + color=sel_color, face_color=sel_color, visible=True) self.poly_dict[shape_id] = sol_geo self.app.tool_shapes.redraw() self.app.inform.emit(_("All selectable pads are selected.")) diff --git a/appPlugins/ToolQRCode.py b/appPlugins/ToolQRCode.py index 1a396aab..e6d7cf21 100644 --- a/appPlugins/ToolQRCode.py +++ b/appPlugins/ToolQRCode.py @@ -5,33 +5,15 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui -from PyQt6.QtCore import Qt - -from appTool import AppTool -from appGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox, FCComboBox, FCFileSaveDialog, \ - VerticalScrollArea, FCGridLayout, FCLabel, FCFrame +from appTool import * from appParsers.ParseSVG import * - -from shapely.geometry.base import * -from shapely.ops import unary_union -from shapely.affinity import translate -from shapely.geometry import box - from io import StringIO, BytesIO -from collections.abc import Iterable -import logging -from copy import deepcopy import qrcode import qrcode.image.svg import qrcode.image.pil from lxml import etree as ET -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -418,21 +400,27 @@ class QRCode(AppTool): # I use the len of self.qrcode_geometry instead of the utility one because the complexity of the polygons is # better seen in this (bit what if the sel.qrcode_geometry is just one geo element? len will fail ... - if len(self.qrcode_geometry) <= self.app.options["tools_qrcode_sel_limit"]: + + qrcode_geometry_len = len(self.qrcode_geometry.geoms) if isinstance(self.qrcode_geometry, MultiPolygon) else \ + len(self.qrcode_geometry) + if qrcode_geometry_len <= self.app.options["tools_qrcode_sel_limit"]: + qrcode_geo = self.qrcode_utility_geometry.geoms if isinstance(self.qrcode_utility_geometry, MultiPolygon) \ + else self.qrcode_utility_geometry try: - for poly in self.qrcode_utility_geometry: + for poly in qrcode_geo: offset_geo.append(translate(poly.exterior, xoff=pos[0], yoff=pos[1])) for geo_int in poly.interiors: offset_geo.append(translate(geo_int, xoff=pos[0], yoff=pos[1])) except TypeError: + assert isinstance(self.qrcode_utility_geometry, Polygon) offset_geo.append(translate(self.qrcode_utility_geometry.exterior, xoff=pos[0], yoff=pos[1])) for geo_int in self.qrcode_utility_geometry.interiors: offset_geo.append(translate(geo_int, xoff=pos[0], yoff=pos[1])) else: offset_geo = [translate(self.box_poly, xoff=pos[0], yoff=pos[1])] - for shape in offset_geo: - self.shapes.add(shape, color=outline, update=True, layer=0, tolerance=None) + for shp in offset_geo: + self.shapes.add(shp, color=outline, update=True, layer=0, tolerance=None) if self.app.use_3d_engine: self.shapes.redraw() diff --git a/appPlugins/ToolReport.py b/appPlugins/ToolReport.py index 973a048b..c8193269 100644 --- a/appPlugins/ToolReport.py +++ b/appPlugins/ToolReport.py @@ -5,20 +5,7 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtGui, QtCore, QtWidgets -from appTool import AppTool -from appGUI.GUIElements import FCTree, VerticalScrollArea - -from shapely.geometry import MultiPolygon, Polygon, MultiLineString -from shapely.ops import unary_union - -from copy import deepcopy -import math - -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: diff --git a/appPlugins/ToolRulesCheck.py b/appPlugins/ToolRulesCheck.py index 872a6ac7..c986c8f7 100644 --- a/appPlugins/ToolRulesCheck.py +++ b/appPlugins/ToolRulesCheck.py @@ -5,22 +5,8 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCComboBox, FCLabel, FCButton, \ - VerticalScrollArea, FCGridLayout, FCFrame -from copy import deepcopy - +from appTool import * from appPool import * -# from os import getpid -from shapely.ops import nearest_points -from shapely.geometry import MultiPolygon, Polygon - -import logging -import gettext -import appTranslation as fcTranslate -import builtins fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -136,7 +122,6 @@ class RulesCheck(AppTool): self.ui.e1_cb.stateChanged.connect(lambda st: self.ui.e1_object.setDisabled(not st)) self.ui.e2_cb.stateChanged.connect(lambda st: self.ui.e2_object.setDisabled(not st)) - self.ui.run_button.clicked.connect(self.execute) self.ui.reset_button.clicked.connect(self.set_tool_ui) @@ -593,7 +578,7 @@ class RulesCheck(AppTool): try: # minimize the number of distances by not taking into considerations those that are too small dist = abs(geo.exterior.distance(s_geo)) - except Exception as e: + except Exception: # log.error("RulesCheck.check_gerber_annular_ring() --> %s" % str(e)) pass @@ -692,7 +677,7 @@ class RulesCheck(AppTool): _("Value is not valid."))) return - if self.copper_t_cb.get_value(): + if self.ui.copper_t_cb.get_value(): copper_t_obj = self.ui.copper_t_object.currentText() copper_t_dict = {} @@ -789,7 +774,7 @@ class RulesCheck(AppTool): _("Value is not valid."))) return - if self.ss_t_cb.get_value(): + if self.ui.ss_t_cb.get_value(): silk_obj = self.ui.ss_t_object.currentText() if silk_obj != '': silk_dict['name'] = deepcopy(silk_obj) @@ -1826,6 +1811,7 @@ class RulesUI: [self.outline_grid.itemAt(i).widget() for i in range(self.outline_grid.count()) if isinstance(self.outline_grid.itemAt(i).widget(), FCCheckBox)] for cb in cb_items: + assert isinstance(cb, FCCheckBox) if state: cb.setChecked(True) else: @@ -1835,6 +1821,7 @@ class RulesUI: cb_items = [self.exc_grid.itemAt(i).widget() for i in range(self.exc_grid.count()) if isinstance(self.exc_grid.itemAt(i).widget(), FCCheckBox)] for cb in cb_items: + assert isinstance(cb, FCCheckBox) if state: cb.setChecked(True) else: @@ -1844,6 +1831,7 @@ class RulesUI: cb_items = [self.copper_grid.itemAt(i).widget() for i in range(self.copper_grid.count()) if isinstance(self.copper_grid.itemAt(i).widget(), FCCheckBox)] for cb in cb_items: + assert isinstance(cb, FCCheckBox) if state: cb.setChecked(True) else: @@ -1853,6 +1841,7 @@ class RulesUI: cb_items = [self.silk_grid.itemAt(i).widget() for i in range(self.silk_grid.count()) if isinstance(self.silk_grid.itemAt(i).widget(), FCCheckBox)] for cb in cb_items: + assert isinstance(cb, FCCheckBox) if state: cb.setChecked(True) else: @@ -1862,6 +1851,7 @@ class RulesUI: cb_items = [self.solder_grid.itemAt(i).widget() for i in range(self.solder_grid.count()) if isinstance(self.solder_grid.itemAt(i).widget(), FCCheckBox)] for cb in cb_items: + assert isinstance(cb, FCCheckBox) if state: cb.setChecked(True) else: @@ -1871,6 +1861,7 @@ class RulesUI: cb_items = [self.holes_grid.itemAt(i).widget() for i in range(self.holes_grid.count()) if isinstance(self.holes_grid.itemAt(i).widget(), FCCheckBox)] for cb in cb_items: + assert isinstance(cb, FCCheckBox) if state: cb.setChecked(True) else: diff --git a/appPlugins/ToolShell.py b/appPlugins/ToolShell.py index 12ecea25..b472f619 100644 --- a/appPlugins/ToolShell.py +++ b/appPlugins/ToolShell.py @@ -6,7 +6,6 @@ # MIT Licence # # ########################################################## - from PyQt6 import QtCore, QtGui, QtWidgets from PyQt6.QtCore import QSettings from PyQt6.QtGui import QTextCursor, QPixmap diff --git a/appPlugins/ToolSolderPaste.py b/appPlugins/ToolSolderPaste.py index feb11482..9ec9a36c 100644 --- a/appPlugins/ToolSolderPaste.py +++ b/appPlugins/ToolSolderPaste.py @@ -5,30 +5,14 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtGui, QtCore, QtWidgets - -from appTool import AppTool +from appTool import * from appCommon.Common import LoudDict -from appGUI.GUIElements import FCComboBox, FCEntry, FCTable, FCDoubleSpinner, FCSpinner, FCFileSaveDialog, \ - FCInputSpinner, FCButton, VerticalScrollArea, FCGridLayout, FCLabel, FCFrame, FCComboBox2 from camlib import distance from appEditors.AppTextEditor import AppTextEditor -from copy import deepcopy -from datetime import datetime -import re - -from shapely.geometry import Polygon, LineString, MultiPolygon, MultiLineString, Point -from shapely.ops import unary_union - -import traceback from io import StringIO -import gettext -import appTranslation as fcTranslate -import builtins - fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: _ = gettext.gettext @@ -185,7 +169,7 @@ class SolderPaste(AppTool): for option in self.app.options: if option.find('tools_') == 0: - self.obj_options[option] = deepcopy(self.app.options[option] ) + self.obj_options[option] = deepcopy(self.app.options[option]) self.read_form_to_options() self.clear_context_menu() @@ -276,7 +260,8 @@ class SolderPaste(AppTool): # make the diameter column editable for row in range(tool_id): self.ui.tools_table.item(row, 1).setFlags( - QtCore.Qt.ItemFlag.ItemIsEditable | QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled) + QtCore.Qt.ItemFlag.ItemIsEditable | QtCore.Qt.ItemFlag.ItemIsSelectable | + QtCore.Qt.ItemFlag.ItemIsEnabled) # all the tools are selected by default self.ui.tools_table.selectColumn(0) @@ -374,6 +359,7 @@ class SolderPaste(AppTool): # on any change to the widgets that matter it will be called self.gui_form_to_storage which will save the # changes in geometry UI for grid in self.ui.tools_box.parentWidget().findChildren(FCGridLayout): + assert isinstance(grid, QtWidgets.QGridLayout) for i in range(grid.count()): wdg = grid.itemAt(i).widget() if isinstance(wdg, (FCComboBox, FCComboBox2)): @@ -389,6 +375,7 @@ class SolderPaste(AppTool): def ui_disconnect(self): # if connected, disconnect the signal from the slot on item_changed as it creates issues for grid in self.ui.tools_box.parentWidget().findChildren(FCGridLayout): + assert isinstance(grid, QtWidgets.QGridLayout) for i in range(grid.count()): wdg = grid.itemAt(i).widget() if isinstance(wdg, (FCComboBox, FCComboBox2)): @@ -820,9 +807,9 @@ class SolderPaste(AppTool): geo_obj.tools[tooluid]['solid_geometry'] = [] geo_obj.tools[tooluid]['type'] = 'SolderPaste' - geo_obj.tools[tooluid]['data']['tools_mill_offset_type']= 0 # 'Path' + geo_obj.tools[tooluid]['data']['tools_mill_offset_type'] = 0 # 'Path' geo_obj.tools[tooluid]['data']['tools_mill_offset_value'] = 0.0 - geo_obj.tools[tooluid]['data']['tools_mill_job_type'] = 'SP' #' + geo_obj.tools[tooluid]['data']['tools_mill_job_type'] = 'SP' # '' geo_obj.tools[tooluid]['data']['tools_mill_tool_shape'] = 'DN' # 'DN' # self.flat_geometry is a list of LinearRings produced by flatten() from the exteriors of the Polygons @@ -1704,7 +1691,7 @@ class SolderUI: # action to be added in the combobox context menu self.combo_context_del_action = QtGui.QAction(QtGui.QIcon(self.app.resource_location + '/trash16.png'), - _("Delete Object")) + _("Delete Object")) # #################################### FINSIHED GUI ########################### # ############################################################################# diff --git a/appPlugins/ToolSub.py b/appPlugins/ToolSub.py index cb7adcd4..50e4a360 100644 --- a/appPlugins/ToolSub.py +++ b/appPlugins/ToolSub.py @@ -5,21 +5,7 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtCore, QtGui - -from appTool import AppTool -from appGUI.GUIElements import FCCheckBox, FCButton, FCComboBox, FCLabel, VerticalScrollArea, FCGridLayout, FCFrame - -from shapely.geometry import Polygon, MultiPolygon, MultiLineString, LineString -from shapely.ops import unary_union - -import traceback -from copy import deepcopy -import time -import logging -import gettext -import appTranslation as fcTranslate -import builtins +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -283,7 +269,8 @@ class ToolSub(AppTool): self.target_grb_obj = self.app.collection.get_by_name(self.target_grb_obj_name) except Exception as e: self.app.log.error("ToolSub.on_subtract_gerber_click() --> %s" % str(e)) - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.obj_name)) + self.app.inform.emit( + '[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.target_grb_obj.obj_options['name'])) return "Could not retrieve object: %s" % self.target_grb_obj_name # -------------------------------- @@ -301,7 +288,8 @@ class ToolSub(AppTool): self.sub_grb_obj = self.app.collection.get_by_name(self.sub_grb_obj_name) except Exception as e: self.app.log.error("ToolSub.on_subtract_gerber_click() --> %s" % str(e)) - self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.obj_name)) + self.app.inform.emit( + '[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), self.target_grb_obj.obj_options['name'])) return "Could not retrieve object: %s" % self.sub_grb_obj_name if self.target_grb_obj_name == self.sub_grb_obj_name: @@ -648,6 +636,7 @@ class ToolSub(AppTool): def new_geo_object(self, outname): geo_name = outname + def obj_init(geo_obj, app_obj): # geo_obj.obj_options = self.target_options diff --git a/appPlugins/ToolTransform.py b/appPlugins/ToolTransform.py index 786334e5..bb5138bb 100644 --- a/appPlugins/ToolTransform.py +++ b/appPlugins/ToolTransform.py @@ -5,16 +5,7 @@ # MIT Licence # # ########################################################## -from PyQt6 import QtWidgets, QtGui, QtCore -from appTool import AppTool -from appGUI.GUIElements import FCDoubleSpinner, FCCheckBox, FCButton, OptionalInputSection, FCComboBox, \ - NumericalEvalTupleEntry, FCLabel, VerticalScrollArea, FCGridLayout, FCFrame - -import numpy as np - -import gettext -import appTranslation as fcTranslate -import builtins +from appTool import * fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: @@ -517,14 +508,14 @@ class ToolTransform(AppTool): self.app.inform.emit(_("CNCJob objects can't be buffered.")) elif sel_obj.kind.lower() == 'gerber': sel_obj.buffer(value, join, factor) - sel_obj.source_file = self.app.f_handlers.export_gerber(obj_name=sel_obj.obj_options['name'], - filename=None, local_use=sel_obj, - use_thread=False) + sel_obj.source_file = self.app.f_handlers.export_gerber( + obj_name=sel_obj.obj_options['name'], filename=None, local_use=sel_obj, + use_thread=False) elif sel_obj.kind.lower() == 'excellon': sel_obj.buffer(value, join, factor) - sel_obj.source_file = self.app.f_handlers.export_excellon(obj_name=sel_obj.obj_options['name'], - filename=None, local_use=sel_obj, - use_thread=False) + sel_obj.source_file = self.app.f_handlers.export_excellon( + obj_name=sel_obj.obj_options['name'], filename=None, local_use=sel_obj, + use_thread=False) elif sel_obj.kind.lower() == 'geometry': sel_obj.buffer(value, join, factor) diff --git a/appTool.py b/appTool.py index 43131dbc..3863cd83 100644 --- a/appTool.py +++ b/appTool.py @@ -6,14 +6,34 @@ # MIT Licence # # ########################################################## ## -from PyQt6 import QtCore, QtWidgets, QtGui - -from shapely.geometry import Polygon, LineString, box, MultiPolygon, MultiPoint, MultiLineString, LinearRing, Point +from shapely.geometry import Polygon, LineString, box, MultiPolygon, MultiPoint, MultiLineString, LinearRing, Point, \ + shape, base from shapely.strtree import STRtree +from shapely.ops import unary_union, nearest_points, linemerge, snap +from shapely.affinity import translate, scale, skew, rotate + +from appGUI.GUIElements import * + +from copy import copy, deepcopy +import math + +import numpy as np +from numpy import Inf + +import simplejson as json +import os +import sys +import re +import time +import platform +from collections.abc import Iterable +import traceback +from datetime import datetime import gettext import appTranslation as fcTranslate import builtins +import logging fcTranslate.apply_language('strings') if '_' not in builtins.__dict__: diff --git a/defaults.py b/defaults.py index 30842e64..d55c13c6 100644 --- a/defaults.py +++ b/defaults.py @@ -390,6 +390,8 @@ class AppDefaults: "tools_iso_poly_ints": False, "tools_iso_force": True, "tools_iso_area_shape": "square", + "tools_iso_simplification": False, + "tools_iso_simplification_tol": 0.01, "tools_iso_plotting": 'normal', # Drilling Plugin