Merged in new_ncc_tool_2 (pull request #6)

New ncc tool 2
This commit is contained in:
Marius Stanciu 2020-04-01 07:55:19 +00:00
commit d326524fe3
84 changed files with 13753 additions and 4947 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,15 @@
from PyQt5 import QtGui, QtCore, QtWidgets, QtWidgets
from PyQt5.QtCore import Qt
from shapely.geometry import Polygon
from shapely.geometry import Polygon, LineString
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
class FlatCAMTool(QtWidgets.QWidget):
@ -98,6 +106,7 @@ class FlatCAMTool(QtWidgets.QWidget):
:param old_coords: old coordinates
:param coords: new coordinates
:param kwargs:
:return:
"""
@ -135,6 +144,111 @@ class FlatCAMTool(QtWidgets.QWidget):
if self.app.is_legacy is True:
self.app.tool_shapes.redraw()
def draw_selection_shape_polygon(self, points, **kwargs):
"""
:param points: a list of points from which to create a Polygon
:param kwargs:
:return:
"""
if 'color' in kwargs:
color = kwargs['color']
else:
color = self.app.defaults['global_sel_line']
if 'face_color' in kwargs:
face_color = kwargs['face_color']
else:
face_color = self.app.defaults['global_sel_fill']
if 'face_alpha' in kwargs:
face_alpha = kwargs['face_alpha']
else:
face_alpha = 0.3
if len(points) < 3:
sel_rect = LineString(points)
else:
sel_rect = Polygon(points)
# color_t = Color(face_color)
# color_t.alpha = face_alpha
color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:]
self.app.tool_shapes.add(sel_rect, color=color, face_color=color_t, update=True,
layer=0, tolerance=None)
if self.app.is_legacy is True:
self.app.tool_shapes.redraw()
def delete_tool_selection_shape(self):
self.app.tool_shapes.clear()
self.app.tool_shapes.redraw()
def draw_moving_selection_shape_poly(self, points, data, **kwargs):
"""
:param points:
:param data:
:param kwargs:
:return:
"""
if 'color' in kwargs:
color = kwargs['color']
else:
color = self.app.defaults['global_sel_line']
if 'face_color' in kwargs:
face_color = kwargs['face_color']
else:
face_color = self.app.defaults['global_sel_fill']
if 'face_alpha' in kwargs:
face_alpha = kwargs['face_alpha']
else:
face_alpha = 0.3
temp_points = [x for x in points]
try:
if data != temp_points[-1]:
temp_points.append(data)
except IndexError:
return
l_points = len(temp_points)
if l_points == 2:
geo = LineString(temp_points)
elif l_points > 2:
geo = Polygon(temp_points)
else:
return
color_t = face_color[:-2] + str(hex(int(face_alpha * 255)))[2:]
color_t_error = "#00000000"
if geo.is_valid and not geo.is_empty:
self.app.move_tool.sel_shapes.add(geo, color=color, face_color=color_t, update=True,
layer=0, tolerance=None)
elif not geo.is_valid:
self.app.move_tool.sel_shapes.add(geo, color="red", face_color=color_t_error, update=True,
layer=0, tolerance=None)
if self.app.is_legacy is True:
self.app.move_tool.sel_shapes.redraw()
def delete_moving_selection_shape(self):
self.app.move_tool.sel_shapes.clear()
self.app.move_tool.sel_shapes.redraw()
def confirmation_message(self, accepted, minval, maxval):
if accepted is False:
self.app.inform.emit('[WARNING_NOTCL] %s: [%.*f, %.*f]' %
(_("Edited value is out of range"), self.decimals, minval, self.decimals, maxval))
else:
self.app.inform.emit('[success] %s' % _("Edited value is within limits."))
def confirmation_message_int(self, accepted, minval, maxval):
if accepted is False:
self.app.inform.emit('[WARNING_NOTCL] %s: [%d, %d]' %
(_("Edited value is out of range"), minval, maxval))
else:
self.app.inform.emit('[success] %s' % _("Edited value is within limits."))

View File

@ -338,7 +338,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
self.app.ui.menuprojectcolor.setEnabled(False)
for obj in self.get_selected():
if type(obj) == FlatCAMGerber:
if type(obj) == FlatCAMGerber or type(obj) == FlatCAMExcellon:
self.app.ui.menuprojectcolor.setEnabled(True)
if type(obj) != FlatCAMGeometry:
@ -504,7 +504,7 @@ class ObjectCollection(QtCore.QAbstractItemModel):
name += "_1"
obj.options["name"] = name
obj.set_ui(obj.ui_type(decimals=self.app.decimals))
obj.set_ui(obj.ui_type(app=self.app))
# Required before appending (Qt MVC)
group = self.group_items[obj.kind]
@ -645,6 +645,59 @@ class ObjectCollection(QtCore.QAbstractItemModel):
if not self.get_list():
self.app.ui.splitter.setSizes([0, 1])
def delete_by_name(self, name, select_project=True):
obj = self.get_by_name(name=name)
item = obj.item
group = self.group_items[obj.kind]
group_index = self.index(group.row(), 0, QtCore.QModelIndex())
item_index = self.index(item.row(), 0, group_index)
deleted = item_index.internalPointer()
group = deleted.parent_item
# some objects add a Tab on creation, close it here
for idx in range(self.app.ui.plot_tab_area.count()):
if self.app.ui.plot_tab_area.widget(idx).objectName() == deleted.obj.options['name']:
self.app.ui.plot_tab_area.removeTab(idx)
break
# update the SHELL auto-completer model data
name = deleted.obj.options['name']
try:
self.app.myKeywords.remove(name)
self.app.shell._edit.set_model_data(self.app.myKeywords)
# this is not needed any more because now the code editor is created on demand
# self.app.ui.code_editor.set_model_data(self.app.myKeywords)
except Exception as e:
log.debug(
"delete_active() --> Could not remove the old object name from auto-completer model list. %s" % str(e))
self.app.object_status_changed.emit(deleted.obj, 'delete', name)
# ############ OBJECT DELETION FROM MODEL STARTS HERE ####################
self.beginRemoveRows(self.index(group.row(), 0, QtCore.QModelIndex()), deleted.row(), deleted.row())
group.remove_child(deleted)
# after deletion of object store the current list of objects into the self.app.all_objects_list
self.app.all_objects_list = self.get_list()
self.endRemoveRows()
# ############ OBJECT DELETION FROM MODEL STOPS HERE ####################
if self.app.is_legacy is False:
self.app.plotcanvas.redraw()
if select_project:
# always go to the Project Tab after object deletion as it may be done with a shortcut key
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
self.app.should_we_save = True
# decide if to show or hide the Notebook side of the screen
if self.app.defaults["global_project_autohide"] is True:
# hide the notebook if there are no objects in the collection
if not self.get_list():
self.app.ui.splitter.setSizes([0, 1])
def delete_all(self):
FlatCAMApp.App.log.debug(str(inspect.stack()[1][3]) + "--> OC.delete_all()")

377
README.md
View File

@ -9,15 +9,262 @@ CAD program, and create G-Code for Isolation routing.
=================================================
1.04.2020
- updated the SVG parser to take into consideration the 'Close' svg element and paths that are made from a single line (we may need to switch to svgpathtools module)
30.03.2020
- working to update the Paint Tool
- fixed some issues in Paint Tool
29.03.2020
- modified the new database to accept data from NCC and Paint Tools
- fixed issues in the new database when adding the tool in a Geometry object
- fixed a bug in Geometry object that generated a change of dictionary while iterating over it
- started to add the new database links in the NCC and Paint Tools
- in the new Tools DB added ability to double click on the ID in the tree widget to execute adding a tool from DB
- working in updating NCC Tool
28.03.2020
- finished the new database based on a QTreeWidget
21.03.2020
- fixed Cutout Tool to work with negative values for Margin parameter
20.03.2020
- updated the "re-cut" feature in Geometry object; now if the re-cut parameter is non zero it will cut half of the entered distance before the isolation end and half of it after the isolation end
- added to Paint and NCC Tool a feature that allow polygon area selection when the reference is selected as Area Selection
- in Paint Tool and NCC Tool added ability to use Escape Tool to cancel Area Selection and for Paint Tool to cancel Polygon Selection
- fixed issue in "re-cut" feature when combined with multi-depth feature
- fixed bugs in cncjob TclCommand
13.03.2020
- fixed a bug in CNCJob generation out of a Excellon object; the plot failed in case some of the geometry of the CNCJob was invalid
- fixed Properties Tool due of recent changes to the FCTree widget
12.03.2020
- working on the new database
- fix a bug in the TextInputTool in FlatCAM Geometry Editor that crashed the sw when some fonts are not loaded correctly
4.03.2020
- updated all the FlatCAM Tools and the Gerber UI FCComboBoxes to update the box value with the latest object loaded in the App
- some fixes in the NCC Tool
- modified some strings
02.03.2020
- added property that allow the FCComboBox to update the view with the last item loaded; updated the app to use this property
01.03.2020
- updated the CutOut Tool such that while adding manual gaps, the cutting geometry is updated on-the-fly if the gap size or tool diameter parameters are adjusted
- updated the UI in Geometry Editor
29.02.2020
- compacted the NCC Tool UI by replacing some Radio buttons with Combo boxes due of too many elements
- fixed error in CutOut Tool when trying to create a FreeFrom Cutout out of a Gerber object with the Convex Shape checked
- working on a new type of database
28.02.2020
- some small changes in preprocessors
- solved issue #381 where there was an error when trying to generate CNCJob out of an Excellon file that have a tool with only slots and no drills
- solved some issues in the preprocessors regarding the newly introduced feature that allow control of the final move X,Y positions
25.02.2020
- fixed bug in Gerber parser: it tried to calculate a len() for a single element and not a list - a Gerber generated by Eagle exhibited this
- added a new parameter named 'End Move X,Y' for the Geometry and Excellon objects. Adding a tuple of coordinates in this field will control the X,Y position of the final move; not entering a value there will cause not to make an end move
20.02.2020
- in Paint Tool replaced the Selection radio with a combobox GUI element that is more compact
- in NCC Tool modified the UI
19.02.2020
- fixed some issues in the Geometry Editor; the jump signal disconnect was failing for repeated Editor tool operation
- fixed an issue in Gerber Editor where the multiprocessing pool was reported as closed and an ValueError exception was raised in a certain scneraio
- on Set Origin, Move to Origin and Move actions for Gerber and Excellon objects the source file will be also updated (the export functions will export an updated object)
- in FlatCAMObj.export_gerber() method took into account the possibility of polygons of type 'clear' (the ones found in the Gerber files under the LPC command)
17.02.2020
- updated the Excellon UI to hold data for each tool
- in Excellon UI removed the tools table column for Offset Z and used the UI form parameter
- updated the Excellon Editor to add for each tool a 'data' dictionary
- updated all FlatCAM tools to use the new confirmation message that show if the entered value is within range or outside
- updated all FlatCAM tools to use the new confirmation message for QSpinBoxes, too
- in Excellon UI protected the values that are common parameters from change on tool selection change
- fixed some issues related to the usage of the new confirmation message in FlatCAM Tools
- made sure that the FlatCAM Tools UI initialization is done only in set_tool_ui() method and not in the constructor
- adapted the GCode generation from Excellon to work with multiple tools data and modified the preprocessors header
- when multiple tools are selected in Excellon UI and parameters are modified it will applied to all selected
- in Excellon UI, Paint Tool and NCC Tool finished the "Apply parameters to all tools" functionality
- updated Paint Tool and NCC Tool in the UI functionality
- fixed the Offset spinbox not being controller by offset checkbox in NCC Tool
16.02.2020
- small update to NCC Tool UI
15.02.2020
- in Paint Tool added a new method of painting named Combo who will pass through all the methods until the polygon is cleared
- in Paint Tool attempting to add a new mode suitable for Laser usage
- more work in the new Laser Mode in the Paint Tool
- modified the Paint Tool UI
14.02.2020
- adjusted the UI for Excellon and Geometry objects
- added a new FlatCAM Tool: Gerber Invert Tool. It will invert the copper features in a Gerber file: where is copper there will be empty and where is empty it will be copper
- added the Preferences entries for the Gerber Invert Tool
13.02.2020
- finished Punch Gerber Tool
- minor changes in the Tool Transform and Tool Calculators UI to bring them up2date with the other tools
12.02.2020
- working on fixing a bug in FlatCAMGeometry.merge() - FIXED issue #380
- fixed bug: when deleting a FlatCAMCNCJob with annotations enabled, the annotations are not deleted from canvas; fixed issue #379
- fixed bug: creating a new project while a project is open and it contain CNCJob annotations and/or Gerber mark shapes, did not delete them from canvas
11.02.2020
- working on Tool Punch; finished the geometry update with the clear geometry for the case of Excellon method
- working on Tool Punch; finished the geometry update with the clear geometry for the case of Fixed Diameter method
10.02.2020
- optimized the Paint and NCC Tools. When the Lines type of painting/clearing is used, the lines will try to arrange themselves on the direction that the lines length clearing the polygon are bigger
- solved bug that made drilling with Marlin preprocessor very slow
- applied the fix for above bug to the TclCommand Drillcncjob too
- started a new way to clear the Gerber polygons based on the 'follow' lines
- some cleanup and bug fixes for the Paint Tool
8.02.2020
- added a new preprocessor for using laser on a Marlin 3D printer named 'Marlin_laser_use_Spindle_pin'
- modified the Geometry UI when using laser preprocessors
- added a new preprocessor file for using laser on a Marlin motion controller but with the laser connected to one of the FAN pins, named 'Marlin_laser_use_FAN_pin'
- modified the Excellon GCode generation so now it can use multi depth drilling; modified the preprocessors to show the number of passes
5.02.2020
- Modified the Distance Tool such that the Measure button can't be clicked while measuring is in progress
- optimized selection of drills in the Excellon Editor
- fixed bugs in multiple selection in Excellon Editor
- fixed selection problems in Gerber Editor
- in Distance Tool, when run in the Excellon or Gerber Editor, added a new option to snap to center of the geometry (drill for Excellon, pad for Gerber)
3.02.2020
- modified Spinbox and DoubleSpinbox Custom UI elements such that they issue a warning status message when the typed value is out of range
- fixed the preprocessors with 'laser' in the name to use the spindle direction set in the Preferences
- increased the upper limit for feedrates by an order of magnitude
2.02.2020
- fixed issue #376 where the V-Shape parameters from Gerber UI are not transferred to the resulting Geometry object if the 'combine' checkbox is not checked in the Gerber UI
- in Excellon UI, if Basic application mode is selected in Preferences, the Plot column 'P' is hidden now because some inexperienced users mistake this column checkboxes for tool selection
- fixed an error in Gerber Parser; the initial values for current_x, current_y were None but should have been 0.0
- limited the lower limit of angle of V-tip to a value of 1 because 0 makes no sense
- small changes in Gerber UI
- in Geometry Editor make sure that after an edit is finished (correctly or forced) the QTree in the Editor UI is cleared of items
31.01.2020
- added a new functionality, a variation of Set Origin named Move to Origin. It will move a selection of objects to origin such as the bottom left corner of the bounding box that fit them all is in origin.
- fixed some bugs
- fixed a division by zero error: fixed #377
30.01.2020
- remade GUI in Tool Cutout, Tool Align Objects, Tool Panelize
- some changed in the Excellon UI
- some UI changes in the common object UI
29.01.2020
- changes in how the Editor exit is handled
- small fix in some pywin32 imports
- remade the GUI + small fixes in 2Sided Tool
- updated 2Sided Tool
28.01.2020
- some changes in Excellon Editor
27.01.2020
- in Geometry Editor made sure that on final save, for MultiLineString geometry all the connected lines are merged into one LineString to minimize the number of vertical movements in GCode
- more work in Punch Gerber Tool
- the Jump To popup window will now autoselect the LineEdit therefore no more need for an extra click after launching the function
- made some structural changes in Properties Tool
- started to make some changes in Geometry Editor
- finished adding in Geometry Editor a TreeWidget with the geometry shapes found in the edited object
24.02.2020
- small changes to the Toolchange manual preprocessor
- fix for plotting Excellon objects if the color is changed and then the object is moved
- laying the GUI for a new Tool: Punch Gerber Tool which will add holes in the Gerber apertures
- fixed bugs in Minimum Distance Tool
- update in the GUI for the Punch Gerber Tool
22.01.2020
- fixed a bug in the bounding box generation
19.01.2020
- fixed some bugs that are visible in Linux regarding the ArgsThread class: on app close we need to quit the QThread running the ArgsThread class and also close the opened Socket
- make sure that the fixes above apply when rebooting app for theme change or for language change
- fixed and issue that made setting colors for the Gerber file not possible if using a translation
- made possible to set the colors for Excellon objects too
- added to the possible colors the fundamentals: black and white
- in the project context menu for setting colors added the option to set the transparency and also a default option which revert the color to the default value set in the Preferences
17.01.2020
- more changes to Excellon UI
- changes to Geometry UI
- more work in NCC Tool upgrade; each tool now work with it's own set of parameters
- some updates in NCC Tool
- optimized the object envelope generation in the redesigned NCC Tool
16.01.2020
- updated/optimized the GUI in Preferences for Paint Tool and for NCC Tool
- work in Paint Tool to bring it up to date with NCC Tool
- updated the GUI in preferences for Calculator Tool
- a small change in the Excellon UI
- updated the Excellon and Geometry UI to be similar
- put bases for future changes to Excellon Object UI such that each tool will hold it's own parameters
- in ParseExcellon.Excellon the self.tools dict has now a key 'data' which holds a dict with all the default values for Excellon and Geometry
- Excellon and Geometry objects, when started with multiple tools selected, the parameters tool name reflect this situation
- moved default_data data update from Excellon parser to the Excellon object constructor
15.01.2020
- added key shortcuts and toolbar icons for the new tools: Align Object Tool (ALT+A) and Extract Drills (ALT+I)
- added new functionality (key shortcut SHIFT+J) to locate the corners of the bounding box (and center) in a selected object
- modified the NCC Tool GUI to prepare for accepting a tool from a tool database
- started to modify the Paint Tool to be similar to NCC Tool and to accept a tool from a database
- work in Paint Tool GUI functionality
14.01.2020
@ -350,7 +597,7 @@ CAD program, and create G-Code for Isolation routing.
- in Excellon UI fixed bug that did not allow editing of the Offset Z parameter from the Tool table
- in Properties Tool added new information's for the tools in the CNCjob objects
- few bugs solved regarding the newly created empty objects
- changed everywhere the name "postprocessor" with "preprocessor"
- changed everywhere the name "preprocessor" with "preprocessor"
- updated the preprocessor files in the toolchange section in order to avoid a graphical representation of travel lines glitch
- fixed a GUI glitch in the Excellon tool table
- added units to some of the parameters in the Properties Tool
@ -489,7 +736,7 @@ CAD program, and create G-Code for Isolation routing.
16.11.2019
- fixed issue #341 that affected both postprocessors that have inlined feedrate: marlin and repetier. THe used feedrate was the Feedrate X-Y and instead had to be Feedrate Z.
- fixed issue #341 that affected both preprocessors that have inlined feedrate: marlin and repetier. The used feedrate was the Feedrate X-Y and instead had to be Feedrate Z.
15.11.2019
@ -499,7 +746,7 @@ CAD program, and create G-Code for Isolation routing.
14.11.2019
- made sure that the 'default' postprocessor file is always loaded first such that this name is always first in the GUI comboboxes
- made sure that the 'default' preprocessor file is always loaded first such that this name is always first in the GUI comboboxes
- added a class in GUIElements for a TextEdit box with line numbers and highlight
13.11.2019
@ -511,7 +758,7 @@ CAD program, and create G-Code for Isolation routing.
12.11.2019
- added two new postprocessor files for ISEL CNC and for BERTA CNC
- added two new preprocessor files for ISEL CNC and for BERTA CNC
- clicking on a FCTable GUI element empty space will also clear the focus now
11.11.2019
@ -1041,7 +1288,7 @@ CAD program, and create G-Code for Isolation routing.
- more GUI optimizations related to being part of the Advanced category or not
- added possibility to change the positive SVG exported file color in Tool Film
- fixed some issues recently introduced in the TclCommands CNCJob, DrillCNCJob and write_gcode; changed some parameters names
- fixed issue in the Laser postprocessor where the laser was turned on as soon as the GCode started creating an unwanted cut up until the job start
- fixed issue in the Laser preprocessor where the laser was turned on as soon as the GCode started creating an unwanted cut up until the job start
- added new links in Menu -> Help (Excellon, Gerber specifications and a Report Bug)
- made the splashscreen to be showed on the current monitor on systems with multiple monitors
- added a new entry in Menu -> View -> Redraw All which is doing what the name says: redraw all loaded objects
@ -1254,7 +1501,7 @@ CAD program, and create G-Code for Isolation routing.
- remade the NCC Tool in preparation for the newly added TclCommand CopperClear
- finished adding the TclCommandCopperClear that can be called with alias: 'ncc'
- added new capability in NCC Tool when the reference object is of Gerber type and fixed some newly introduced errors
- fixed issue #298. The changes in postprocessors done in Preferences dis not update the object UI layout as it was supposed to. The selection of Marlin postproc. did not unhidden the Feedrate Rapids entry.
- fixed issue #298. The changes in preprocessors done in Preferences dis not update the object UI layout as it was supposed to. The selection of Marlin postproc. did not unhidden the Feedrate Rapids entry.
- fixed minor issues
- fixed Tcl Command AddPolygon, AddPolyline
- fixed Tcl Command CncJob
@ -1780,7 +2027,7 @@ CAD program, and create G-Code for Isolation routing.
11.05.2019
- fixed issue in camlib.CNCjob.generate_from_excellon_by_tool() in the drill path optimization algorithm selection when selecting the MH algorithm. The new API's for Google OR-tools required some changes and also the time parameter can be now just an integer therefore I modified the GUI
- made the Feedrate Rapids parameter to depend on the type of postprocessor choosed. It will be showed only for a postprocessor which the name contain 'marlin' and for any postprocessor's that have 'custom' in the name
- made the Feedrate Rapids parameter to depend on the type of preprocessor choosed. It will be showed only for a preprocessor which the name contain 'marlin' and for any preprocessor's that have 'custom' in the name
- fixed the camlib.Gerber functions of mirror, scale, offset, skew and rotate to work with the new data structure for apertures geometry
- fixed Gerber Editor selection to work with the new Gerber data structure in self.apertures
- fixed Gerber Editor FCPad class to work with the new Gerber data structure in self.apertures
@ -2210,7 +2457,7 @@ CAD program, and create G-Code for Isolation routing.
- added a fix in the Gerber parser when adding the geometry in the self.apertures dict for the case that the current aperture is None (Allegro does that)
- finished support for internationalization by adding a set of .po/.mo files for the English language. Unfortunately the final action can be done only when Beta will be out of Beta (no more changes) or when I will decide to stop working on this app.
- changed the tooltip for 'feedrate_rapids' parameter to point out that this parameter is useful only for the Marlin postprocessor
- changed the tooltip for 'feedrate_rapids' parameter to point out that this parameter is useful only for the Marlin preprocessor
- fix app crash for the case that there are no translation files
- fixed some forgotten strings to be prepared for internationalization in ToolCalculators
- fixed Tools menu no longer working due of changes
@ -2249,7 +2496,7 @@ CAD program, and create G-Code for Isolation routing.
5.03.2019
- modified the grbl-laser postprocessor lift_code()
- modified the grbl-laser preprocessor lift_code()
- treated an error created by Z_Cut parameter being None
- changed the hover and selection box transparency
@ -2302,7 +2549,7 @@ CAD program, and create G-Code for Isolation routing.
- because adding shapes to the shapes collection (when doing Mark or Mark All) is time consuming I made the plot_aperture() threaded.
- made the polygon fusing in modified Gerber creation, a list comprehension in an attempt for optimization
- when right clicking the files in Project tab, the Save option for Excellon no longer export it but really save the original.
- in ToolChange Custom Code replacement, the Text Box in the CNCJob Selected tab will be active only if there is a 'toolchange_custom' in the name of the postprocessor file. This assume that it is, or was created having as template the Toolchange Custom postprocessor file.
- in ToolChange Custom Code replacement, the Text Box in the CNCJob Selected tab will be active only if there is a 'toolchange_custom' in the name of the preprocessor file. This assume that it is, or was created having as template the Toolchange Custom preprocessor file.
25.02.2019
@ -2329,7 +2576,7 @@ CAD program, and create G-Code for Isolation routing.
- added all the Tools in a new ToolBar
- fixed bug that after changing the layout all the toolbar actions are no longer working
- fixed bug in Set Origin function
- fixed a typo in Toolchange_Probe_MACH3 postprocessor
- fixed a typo in Toolchange_Probe_MACH3 preprocessor
23.02.2019
@ -2340,7 +2587,7 @@ CAD program, and create G-Code for Isolation routing.
22.02.2019
- added Repetier postprocessor file
- added Repetier preprocessor file
- removed "added ability to regenerate objects (it's actually deletion followed by recreation)" because of the way Python pass parameters to functions by reference instead of copy
- added ability to toggle globally the display of ToolTips. Edit -> Preferences -> General -> Enable ToolTips checkbox.
- added true fullscreen support (for Windows OS)
@ -2369,7 +2616,7 @@ CAD program, and create G-Code for Isolation routing.
- finished added a Tool Table for Tool SolderPaste
- working on multi tool solder paste dispensing
- finished the Edit -> Preferences defaults section
- finished the UI, created the postprocessor file template
- finished the UI, created the preprocessor file template
- finished the multi-tool solder paste dispensing: it will start using the biggest nozzle, fill the pads it can, and then go to the next smaller nozzle until there are no pads without solder.
19.02.2019
@ -2492,12 +2739,12 @@ CAD program, and create G-Code for Isolation routing.
- the SELECTED type of messages are no longer printed to shell from 2 reasons: first, too much spam and second, issue with displaying html
- on set_zero function and creation of new geometry or new excellon there is no longer a zoom fit
- repurposed shortcut key 'Delete' to delete tools in tooltable when the mouse is over the Seleted tab (with Geometry inside) or in Tools tab (when NCC Tool or Paint Tool is inside). Or in Excellon Editor when mouse is hovering the Selected tab selecting a tool, 'Delete' key will delete that tool, if on canvas 'Delete' key will delete a selected shape (drill). In rest, will delete selected objects.
- adjusted the postprocessor files so the Spindle Off command (M5) is done before the move to Toolchange Z
- adjusted the Toolchange Manual postprocessor file to have more descriptive messages on the toolchange event
- adjusted the preprocessor files so the Spindle Off command (M5) is done before the move to Toolchange Z
- adjusted the Toolchange Manual preprocessor file to have more descriptive messages on the toolchange event
- added a strong focus to the object_name entry in the Selected tab
- the keypad keyPressed are now detected correctly
- added a pause and message/warning to do a rough zero for the Z axis, in case of Toolchange_Probe_MACH3 postprocessor file
- changes in Toolchange_Probe_MACH3 postprocessor file
- added a pause and message/warning to do a rough zero for the Z axis, in case of Toolchange_Probe_MACH3 preprocessor file
- changes in Toolchange_Probe_MACH3 preprocessor file
9.02.2019
@ -2563,16 +2810,16 @@ CAD program, and create G-Code for Isolation routing.
- added a text in the Selected Tab which is showed whenever the Selected Tab is selected but without having an object selected to display it's properties
- added an initial text in the Tools tab
- added possibility to use the shortcut key for shortcut list in the Notebook tabs
- added a way to set the Probe depth if Toolchange_Probe postprocessors are selected
- finished the postprocessor file for MACH3 tool probing on toolchange event
- added a new parameter to set the feedrate of the probing in case the used postprocessor does probing (has toolchange_probe in it's name)
- fixed bug in Marlin postprocessor for the Excellon files; the header and toolchange event always used the parenthesis witch is not compatible with GCode for Marlin
- added a way to set the Probe depth if Toolchange_Probe preprocessors are selected
- finished the preprocessor file for MACH3 tool probing on toolchange event
- added a new parameter to set the feedrate of the probing in case the used preprocessor does probing (has toolchange_probe in it's name)
- fixed bug in Marlin preprocessor for the Excellon files; the header and toolchange event always used the parenthesis witch is not compatible with GCode for Marlin
- fixed a issue with a move to Z_move before any toolchange
4.02.2019
- modified the Toolchange_Probe_general postprocessor file to remove any Z moves before the actual toolchange event
- created a prototype postprocessor file for usage with tool probing in MACH3
- modified the Toolchange_Probe_general preprocessor file to remove any Z moves before the actual toolchange event
- created a prototype preprocessor file for usage with tool probing in MACH3
- added the default values for Tool Film and Tool Panelize to the Edit -> Preferences
- added a new parameter in the Tool Film which control the thickness of the stroke width in the resulting SVG. It's a scale parameter.
- whatever was the visibility of the corresponding toolbar when we enter in the Editor, it will be set after exit from the Editor (either Geometry Editor or Excellon Editor).
@ -2599,14 +2846,14 @@ CAD program, and create G-Code for Isolation routing.
- some GUI structure optimization's
- added protection against entering float numbers with comma separator instead of decimal dot separator in key points of FlatCAM (not everywhere)
- added a choice of plotting the kind of geometry for the CNC plot (all, travel and cut kind of geometries) in CNCJob Selected Tab
- added a new postprocessor file named: 'probe_from_zmove' which allow probing to be done from z_move position on toolchange event
- added a new preprocessor file named: 'probe_from_zmove' which allow probing to be done from z_move position on toolchange event
- fixed the snap magnet button in Geometry Editor, restored the checkable property to True
- some more changes in the Editors GUI in deactivate() function
- a fix for saving as empty an edited new and empty Excellon Object
1.02.2019
- fixed postprocessor files so now the bounds values are right aligned (assuming max string length of 9 chars which means 4 digits and 4 decimals)
- fixed preprocessor files so now the bounds values are right aligned (assuming max string length of 9 chars which means 4 digits and 4 decimals)
- corrected small type in list_sys Tcl command; added a protection of the Plot Area Tab after a successful edit.
- remade the way FlatCAM saves the GUI position data from a file (previously) to use PyQt QSettings
- added a 'theme' combo selection in Edit -> Preferences. Two themes are available: standard and compact.
@ -2630,7 +2877,7 @@ CAD program, and create G-Code for Isolation routing.
30.01.2019
- added a space before Y coordinate in end_code() function in some of the postprocessor files
- added a space before Y coordinate in end_code() function in some of the preprocessor files
- added in Calculators Tool an Electroplating Calculator.
- remade the App Menu for Editors: now they will be showed only when the respective Editor is active and hidden when the Editor is closed.
- added a traceback report in the TCL Shell for the errors that don't allow creation of an object; useful to trace exceptions/errors
@ -2638,9 +2885,9 @@ CAD program, and create G-Code for Isolation routing.
- fixed an issue in camlib.CNCJob where tha variable self.toolchange_xy was used for 2 different purposes which created loss of information.
- fixed unit conversion functions in case the toolchange_xy parameter is None
- more fixes in camlib.CNCJob regarding usage of toolchange (in case it is None)
- fixed postprocessor files to work with toolchange_xy parameter value = None (no values in Edit - Preferences fields)
- fixed preprocessor files to work with toolchange_xy parameter value = None (no values in Edit - Preferences fields)
- fixed Tcl commands CncJob and DrillCncJob to work with toolchange
- added to the postprocessor files the command after toolchange to go with G00 (fastest) to "Z Move" value of Z pozition.
- added to the preprocessor files the command after toolchange to go with G00 (fastest) to "Z Move" value of Z pozition.
29.01.2019
@ -2673,15 +2920,15 @@ CAD program, and create G-Code for Isolation routing.
- added options for trace segmentation that can be useful for auto-levelling (code snippet from Lei Zheng from a rejected pull request on FlatCAM https://bitbucket.org/realthunder/ )
- added shortcut key 'L' for creating 'New Excellon'
- added shortcut key combo 'SHIFT+S' for Running a Script.
- modified grbl_laser postprocessor file so it includes a Sxxxx command on the line with M03 (laser active) whenever a value is enter in the Spindlespeed entry field
- modified GRBL_laser preprocessor file so it includes a Sxxxx command on the line with M03 (laser active) whenever a value is enter in the Spindlespeed entry field
- remade the EDIT -> PREFERENCES window, the Excellon and Gerber sections. Created a new section named TOOLS
26.01.2019
- fixed grbl_11 postprocessor in linear_code() function
- fixed grbl_11 preprocessor in linear_code() function
- added icons to the Project Tab context menu
- added new entries to the Canvas context menu (Copy, Delete, Edit/Save, Move, New Excellon, New Geometry, New Project)
- fixed grbl_laser postprocessor file
- fixed GRBL_laser preprocessor file
- updated function for copy of an Excellon object for the case when the object has slots
- updated FlatCAMExcellon.merge() function to work in case some (or all) of the merged objects have slots
@ -2701,14 +2948,14 @@ CAD program, and create G-Code for Isolation routing.
- added the Copy entry to the Project context menu
- made the functions behind Disable and Enable project context menu entries, non-threaded to fix a possible issue
- added multiple object selection on Open ... and Import ... (idea and code snippet came from Travers Carter, BitBucket user https://bitbucket.org/travc/)
- fixed 'grbl_laser' postprocessor bugs (missing functions)
- fixed display geometry for 'grbl_laser' postprocessor
- fixed 'GRBL_laser' preprocessor bugs (missing functions)
- fixed display geometry for 'GRBL_laser' preprocessor
- Excellon Editor - added possibility to create an linear drill array rotated at an custom angle
- added the Edit and Properties entries to the Project context menu
23.01.2019
- added a new postprocessor file named 'line_xyz' which have x, y, z values on the same GCode line
- added a new preprocessor file named 'line_xyz' which have x, y, z values on the same GCode line
- fixed calculation of total path for Excellon Gcode file
- modified the way FlatCAM preferences are saved. Now they can be saved as new files with .FlatConfig extension by the user and shared.
- added possibility to open the folder where FlatCAM is saving the preferences files
@ -2725,19 +2972,19 @@ CAD program, and create G-Code for Isolation routing.
- fixed the HPGL code geometry rendering when travel
- fixed the message box layout when asking to save the current work
- made sure that whenever the HPGL postprocessor is selected the Toolchange is always ON and the MultiDepth is OFF
- the HPGL postprocessor entry is not allowed in Excellon Object postprocessor selection combobox as it is only applicable for Geometry
- made sure that whenever the HPGL preprocessor is selected the Toolchange is always ON and the MultiDepth is OFF
- the HPGL preprocessor entry is not allowed in Excellon Object preprocessor selection combobox as it is only applicable for Geometry
- when saving HPGL code it will be saved as a file with extension .plt
- the units mentioned in HPGL format are only METRIC therefore if FlatCAM units are in INCH they will be transform to METRIC
- the minimum unit in HPGL is 0.025mm therefore the coordinates are rounded to a multiple of 0.025mm
- removed the raise statement in do_worker_task() function as this is fatal in conjunction with PyQt5
- added a try - except clause for the situations when for a font can't be determined the family and name
- moved font parsing to the Geometry Editor: it is done everytime the Text tool is invoked
- made sure that the HPGL postprocessor is not populated in the Excellon postprocessors in Preferences as it make no sense (HPGL is useful only for Geometries)
- made sure that the HPGL preprocessor is not populated in the Excellon preprocessors in Preferences as it make no sense (HPGL is useful only for Geometries)
19.01.2019
- added initial implementation of HPGL postprocessor
- added initial implementation of HPGL preprocessor
- fixed display HPGL code geometry on canvas
11.01.2019
@ -2761,9 +3008,9 @@ CAD program, and create G-Code for Isolation routing.
6.01.2019
- fixed the Marlin postprocessor detection in GCode header
- fixed the Marlin preprocessor detection in GCode header
- the version date in GCode header is now the one set in FlatCAMApp.App.version_date
- fixed bug in postprocessor files: number of drills is now calculated only for the Excellon objects in toolchange function (only Excellon objects have drills)
- fixed bug in preprocessor files: number of drills is now calculated only for the Excellon objects in toolchange function (only Excellon objects have drills)
5.01.2019
@ -2927,8 +3174,8 @@ now there is a Tool Table in CNC Object UI and each tool GCode can be enabled or
- Geometry Tool Table: new tool added copy all the form fields (data) from the last tool
- finished work on generation of a single CNC Job file (therefore a single GCODE file) even for multiple tools in Geo Tool Table
- GCode header is added only on saving the file therefore the time generation will be reflected in the file
- modified postprocessors to accommodate the new CNC Job file with multiple tools
- modified postprocessors so the last X,Y move will be to the toolchange X,Y pos (set in Preferences)
- modified preprocessors to accommodate the new CNC Job file with multiple tools
- modified preprocessors so the last X,Y move will be to the toolchange X,Y pos (set in Preferences)
- save_project and load_project now work with the new type of multitool geometry and cncjob objects
10.12.2018
@ -2997,7 +3244,7 @@ now there is a Tool Table in CNC Object UI and each tool GCode can be enabled or
- added checks for using a Z Cut with positive value. The Z Cut parameter has to be negative so if the app will detect a positive value it will automatically convert it to negative
- started to implement rest-machining for Non Copper clearing Tool - for now the results are not great
- added Toolchange X,Y position parameters and modified the default and manual_toolchange postprocessor file to use them
- added Toolchange X,Y position parameters and modified the default and manual_toolchange preprocessor file to use them
For now they are used only for Excellon objects who do have toolchange events
- added Toolchange event selection for Geometry objects; for now it is as before, single tool on each file
- remade the GUI for objects and in Preferences to have uniformity
@ -3005,14 +3252,14 @@ For now they are used only for Excellon objects who do have toolchange events
- fixed some bugs in Tool Add feature of the new Non Copper Clear Tool
- added some messages in the Non Copper Clear Tool
- added parameters for coordinates no of decimals and for feedrate no of decimals used in the resulting GCODE. They are in EDIT -> Preferences -> CNC Job Options
- modified the postprocessors to use the "decimals" parameters
- modified the preprocessors to use the "decimals" parameters
28.11.2018
- added different methods of copper clearing (standard, seed, line_based) and "connect", "contour" options found in Paint function
- remake of the non-copper clearing tool as a separate tool
- modified the "About" menu entry to mention the main contributors to FlatCAM 3000
- modified Marlin postprocessor according to modifications made by @redbull0174 user from FlatCAM.org forum
- modified Marlin preprocessor according to modifications made by @redbull0174 user from FlatCAM.org forum
- modified Move Tool so it will detect if there is no object to move and issue a message
27.11.2018
@ -3031,7 +3278,7 @@ For now they are used only for Excellon objects who do have toolchange events
- restored the selection method in Geometry Editor to the original one found in FlatCAM 8.5
- minor changes in Clear Copper function
- minor changes in some postprocessors
- minor changes in some preprocessors
- change Join Geometry menu entry to Join Geo/Gerber
- added menu entry for Toggle Axis in Menu -> View
- added menu entry for Toggle Workspace in Menu -> View
@ -3047,7 +3294,7 @@ For now they are used only for Excellon objects who do have toolchange events
19.11.2018
- fixed issue with nested comment in postprocessors
- fixed issue with nested comment in preprocessors
- fixed issue in Paint All; reverted changes
18.11.2018
@ -3064,13 +3311,13 @@ For now they are used only for Excellon objects who do have toolchange events
12.11.2018
- fixed bug in Paint Single Polygon
- added spindle speed in laser postprocessor
- added spindle speed in laser preprocessor
- added Z start move parameter. It controls the height at which the tool travel on the fist move in the job. Leave it blank if you don't need it.
9.11.2018
- fixed a reported bug generated by a typo for feedrate_z object in camlib.py. Because of that, the project could not be saved.
- fixed a G01 usage (should be G1) in Marlin postprocessor.
- fixed a G01 usage (should be G1) in Marlin preprocessor.
- changed the position of the Tool Dia entry in the Object UI and in FlatCAMGUI
- fixed issues in the installer
@ -3289,9 +3536,9 @@ the setting in the Preferences) and drag the rectangle across the objects you wa
- work on Excellon Editor. Excellon editor working functions are: loading an Excellon object into Editor,
saving an Excellon object from editor to FlatCAM, selecting drills by left click, selection of drills by dragging rectangle, deletion of drills.
- fixed Excellon merge
- added more Gcode details (depthperpass parameter in Gcode header) in postprocessors
- deleted the Tool informations from header in postprocessors due to Mach3 not liking the lot of square brackets
- more corrections in postprocessors
- added more Gcode details (depthperpass parameter in Gcode header) in preprocessors
- deleted the Tool informations from header in preprocessors due to Mach3 not liking the lot of square brackets
- more corrections in preprocessors
28.09.2018
@ -3366,7 +3613,7 @@ an Excellon file, a G-Code file or a SVG file.
- added new information's in the object properties: all used Tool-Table items
are included in a new entry in self.options dictionary
- modified the postprocessor files so they now include information's about
- modified the preprocessor files so they now include information's about
how many drills (or slots) are for each tool. The Gcode will have this
information displayed on the message from ToolChange.
- removed some log.debug and add new log.debug especially for moments when some process is finished
@ -3404,7 +3651,7 @@ is faster
17.09.2018
- fixed Measuring Tool not working when grid is turned OFF
- fixed Roland MDX20 postprocessor
- fixed Roland MDX20 preprocessor
- added a .GBR extension in the open_gerber filter
- added ability to Scale and Offset (for all types of objects) to just
press Enter after entering a value in the Entry just like in Tool Transform
@ -3418,8 +3665,8 @@ to FlatCAM.py
15.09.2018
- removed dwell line generator and included dwell generation in the postprocessor files
- added a proposed RML1 Roland_MDX20 postprocessor file.
- removed dwell line generator and included dwell generation in the preprocessor files
- added a proposed RML1 Roland_MDX20 preprocessor file.
- added a limit of 15mm/sec (900mm/min) to the feedrate and to the feedrate_rapid. Anything faster than this
will be capped to 900mm/min regardless what is entered in the program GUI. This is because Roland MDX-20 has
a mechanical limit of the speed to 15mm/sec (900mm/min in GUI)
@ -3800,7 +4047,7 @@ total number of drills
- modified generate_milling method which had issues from the Python3 port
(it could not sort the tools due of dict to dict comparison no longer
possible).
- modified the 'default' postprocessor in order to include a space
- modified the 'default' preprocessor in order to include a space
between the value of Xcoord and the following Y
- made optional the using of threads for the milling command; by default
it is OFF (False) because in the current configuration it creates issues
@ -3820,7 +4067,7 @@ clicked and Options Combo was in Project Options
- fixed issue with Tcl Shell loosing focus after each command, therefore
needing to click in the edit line before we type a new command (borrowed
from @brainstorm
- added a header in the postprocessor files mentioning that the GCODE
- added a header in the preprocessor files mentioning that the GCODE
files were generated by FlatCAM.
- modified the number of decimals in some of the line entries to 4.
- added an alias for the millholes Tcl Command: 'mill'
@ -3923,14 +4170,14 @@ Delete: Delete Obj
22.05.2018
- Added Marlin postprocessor
- Added Marlin preprocessor
- Added a new entry into the Geometry and Excellon Object's UI:
Feedrate rapid: the purpose is to set a feedrate for the G0
command that some firmwares like Marlin don't intepret as
'move with highest speed'
- FlatCAM was not making the conversion from one type of units to
another for a lot of parameters. Corrected that.
- Modified the Marlin Postprocessor so it will generate the required
- Modified the Marlin preprocessor so it will generate the required
GCODE.
21.05.2018
@ -4010,7 +4257,7 @@ make a board cutout from a "any shape" Gerber or Geometry file
parameter value as the toolchangez parameter value and for the endz value
used a default value = 1
- added postprocessor name into the TCL command "drillcncjob" parameters
- added preprocessor name into the TCL command "drillcncjob" parameters
- when adding a new geometry the default name is now: "New_Geometry" instead
of "New Geometry". TCL commands don't handle the spaces inside the name and
@ -4097,19 +4344,19 @@ is less than 6 then the software will multiply by 10 the coordinates
- fixed bug in Geometry CNCJob generation that prevented generating
the object
- added GRBL 1.1 postprocessor and Laser postprocessor (adapted from
- added GRBL 1.1 preprocessor and Laser preprocessor (adapted from
the work of MARCO A QUEZADA)
13.05.2018
- added postprocessing in correct form
- added the possibility to select an postprocessor for Excellon Object
- added a new postprocessor, manual_toolchange.py. It allows to change
- added the possibility to select an preprocessor for Excellon Object
- added a new preprocessor, manual_toolchange.py. It allows to change
the tools and adjust the drill tip to touch the surface manually, always
in the X=0, Y=0, Z = toolchangeZ coordinates.
- fixed drillcncjob TCL command by adding toolchangeZ parameter
- fixed the posprocessor file template 'default.py' in the toolchange
- fixed the preprocessor file template 'default.py' in the toolchange
command section
- after I created a feature that the message in infobar is cleared by
moving mouse on canvas, it generated a bug in TCL shell: everytime

1008
camlib.py

File diff suppressed because it is too large Load Diff

View File

@ -124,7 +124,7 @@ class FCDrillAdd(FCShapeTool):
self.draw_app.app.jump_signal.disconnect()
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.tools_table_exc.clearSelection()
self.draw_app.plot_all()
@ -359,7 +359,7 @@ class FCDrillArray(FCShapeTool):
self.draw_app.app.jump_signal.disconnect()
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.tools_table_exc.clearSelection()
self.draw_app.plot_all()
@ -562,7 +562,7 @@ class FCSlot(FCShapeTool):
self.draw_app.app.jump_signal.disconnect()
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.tools_table_exc.clearSelection()
self.draw_app.plot_all()
@ -888,7 +888,7 @@ class FCSlotArray(FCShapeTool):
self.draw_app.app.jump_signal.disconnect()
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.tools_table_exc.clearSelection()
self.draw_app.plot_all()
@ -1128,7 +1128,7 @@ class FCDrillResize(FCShapeTool):
self.draw_app.select_tool("drill_select")
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.tools_table_exc.clearSelection()
self.draw_app.plot_all()
@ -1268,7 +1268,7 @@ class FCDrillMove(FCShapeTool):
return DrawToolUtilityShape(ss_el)
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.tools_table_exc.clearSelection()
self.draw_app.plot_all()
@ -1323,7 +1323,7 @@ class FCDrillCopy(FCDrillMove):
self.draw_app.app.jump_signal.disconnect()
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.tools_table_exc.clearSelection()
self.draw_app.plot_all()
@ -1334,8 +1334,8 @@ class FCDrillCopy(FCDrillMove):
class FCDrillSelect(DrawTool):
def __init__(self, exc_editor_app):
DrawTool.__init__(self, exc_editor_app)
def __init__(self, draw_app):
DrawTool.__init__(self, draw_app)
self.name = 'drill_select'
try:
@ -1343,7 +1343,7 @@ class FCDrillSelect(DrawTool):
except Exception as e:
pass
self.exc_editor_app = exc_editor_app
self.exc_editor_app = draw_app
self.storage = self.exc_editor_app.storage_dict
# self.selected = self.exc_editor_app.selected
@ -1368,7 +1368,7 @@ class FCDrillSelect(DrawTool):
else:
mod_key = None
if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
if mod_key == self.exc_editor_app.app.defaults["global_mselect_key"]:
pass
else:
self.exc_editor_app.selected = []
@ -1379,8 +1379,10 @@ class FCDrillSelect(DrawTool):
try:
for storage in self.exc_editor_app.storage_dict:
for sh in self.exc_editor_app.storage_dict[storage].get_objects():
self.sel_storage.insert(sh)
# for sh in self.exc_editor_app.storage_dict[storage].get_objects():
# self.sel_storage.insert(sh)
_, st_closest_shape = self.exc_editor_app.storage_dict[storage].nearest(pos)
self.sel_storage.insert(st_closest_shape)
_, closest_shape = self.sel_storage.nearest(pos)
@ -1417,37 +1419,41 @@ class FCDrillSelect(DrawTool):
else:
mod_key = None
if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
if mod_key == self.exc_editor_app.app.defaults["global_mselect_key"]:
if closest_shape in self.exc_editor_app.selected:
self.exc_editor_app.selected.remove(closest_shape)
else:
self.exc_editor_app.selected.append(closest_shape)
else:
self.draw_app.selected = []
self.draw_app.selected.append(closest_shape)
self.exc_editor_app.selected = []
self.exc_editor_app.selected.append(closest_shape)
# select the diameter of the selected shape in the tool table
try:
self.draw_app.tools_table_exc.cellPressed.disconnect()
self.exc_editor_app.tools_table_exc.cellPressed.disconnect()
except (TypeError, AttributeError):
pass
self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
# if mod_key == self.exc_editor_app.app.defaults["global_mselect_key"]:
# self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
self.sel_tools.clear()
for shape_s in self.exc_editor_app.selected:
for storage in self.exc_editor_app.storage_dict:
if shape_s in self.exc_editor_app.storage_dict[storage].get_objects():
self.sel_tools.add(storage)
self.exc_editor_app.tools_table_exc.clearSelection()
for storage in self.sel_tools:
for k, v in self.draw_app.tool2tooldia.items():
for k, v in self.exc_editor_app.tool2tooldia.items():
if v == storage:
self.exc_editor_app.tools_table_exc.selectRow(int(k) - 1)
self.draw_app.last_tool_selected = int(k)
self.exc_editor_app.last_tool_selected = int(k)
break
self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
# self.exc_editor_app.tools_table_exc.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.draw_app.tools_table_exc.cellPressed.connect(self.draw_app.on_row_selected)
self.exc_editor_app.tools_table_exc.cellPressed.connect(self.exc_editor_app.on_row_selected)
# delete whatever is in selection storage, there is no longer need for those shapes
self.sel_storage = FlatCAMExcEditor.make_storage()
@ -2068,7 +2074,6 @@ class FlatCAMExcEditor(QtCore.QObject):
self.new_drills = []
self.new_tools = {}
self.new_slots = []
self.new_tool_offset = {}
# dictionary to store the tool_row and diameters in Tool_table
# it will be updated everytime self.build_ui() is called
@ -2180,6 +2185,43 @@ class FlatCAMExcEditor(QtCore.QObject):
if option in self.app.options:
self.options[option] = self.app.options[option]
self.data_defaults = {
"plot": self.app.defaults["excellon_plot"],
"solid": self.app.defaults["excellon_solid"],
"operation": self.app.defaults["excellon_operation"],
"milling_type": self.app.defaults["excellon_milling_type"],
"milling_dia":self.app.defaults["excellon_milling_dia"],
"cutz": self.app.defaults["excellon_cutz"],
"multidepth": self.app.defaults["excellon_multidepth"],
"depthperpass": self.app.defaults["excellon_depthperpass"],
"travelz": self.app.defaults["excellon_travelz"],
"feedrate": self.app.defaults["geometry_feedrate"],
"feedrate_z": self.app.defaults["excellon_feedrate_z"],
"feedrate_rapid": self.app.defaults["excellon_feedrate_rapid"],
"tooldia": self.app.defaults["excellon_tooldia"],
"slot_tooldia": self.app.defaults["excellon_slot_tooldia"],
"toolchange": self.app.defaults["excellon_toolchange"],
"toolchangez": self.app.defaults["excellon_toolchangez"],
"toolchangexy": self.app.defaults["excellon_toolchangexy"],
"extracut": self.app.defaults["geometry_extracut"],
"extracut_length": self.app.defaults["geometry_extracut_length"],
"endz": self.app.defaults["excellon_endz"],
"endxy": self.app.defaults["excellon_endxy"],
"startz": self.app.defaults["excellon_startz"],
"offset": self.app.defaults["excellon_offset"],
"spindlespeed": self.app.defaults["excellon_spindlespeed"],
"dwell": self.app.defaults["excellon_dwell"],
"dwelltime": self.app.defaults["excellon_dwelltime"],
"ppname_e": self.app.defaults["excellon_ppname_e"],
"ppname_g": self.app.defaults["geometry_ppname_g"],
"z_pdepth": self.app.defaults["excellon_z_pdepth"],
"feedrate_probe": self.app.defaults["excellon_feedrate_probe"],
"optimization_type": self.app.defaults["excellon_optimization_type"]
}
self.rtree_exc_index = rtindex.Index()
# flag to show if the object was modified
self.is_modified = False
@ -2586,9 +2628,6 @@ class FlatCAMExcEditor(QtCore.QObject):
for deleted_tool_dia in deleted_tool_dia_list:
# delete de tool offset
self.exc_obj.tool_offset.pop(float(deleted_tool_dia), None)
# delete the storage used for that tool
storage_elem = FlatCAMGeoEditor.make_storage()
self.storage_dict[deleted_tool_dia] = storage_elem
@ -2789,7 +2828,7 @@ class FlatCAMExcEditor(QtCore.QObject):
self.new_drills = []
self.new_tools = {}
self.new_slots = []
self.new_tool_offset = {}
self.olddia_newdia = {}
self.shapes.enabled = True
@ -2974,7 +3013,7 @@ class FlatCAMExcEditor(QtCore.QObject):
except (TypeError, AttributeError):
pass
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_object)
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_command)
self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
@ -3262,7 +3301,6 @@ class FlatCAMExcEditor(QtCore.QObject):
self.edited_obj_name += "_1"
else:
self.edited_obj_name += "_edit"
self.new_tool_offset = self.exc_obj.tool_offset
self.app.worker_task.emit({'fcn': self.new_edited_excellon,
'params': [self.edited_obj_name,
@ -3270,6 +3308,8 @@ class FlatCAMExcEditor(QtCore.QObject):
self.new_slots,
self.new_tools]})
return self.edited_obj_name
def update_options(self, obj):
try:
if not obj.options:
@ -3308,9 +3348,14 @@ class FlatCAMExcEditor(QtCore.QObject):
excellon_obj.drills = deepcopy(new_drills)
excellon_obj.tools = deepcopy(new_tools)
excellon_obj.slots = deepcopy(new_slots)
excellon_obj.tool_offset = self.new_tool_offset
excellon_obj.options['name'] = outname
# add a 'data' dict for each tool with the default values
for tool in excellon_obj.tools:
excellon_obj.tools[tool]['data'] = {}
excellon_obj.tools[tool]['data'].update(deepcopy(self.data_defaults))
try:
excellon_obj.create_geometry()
except KeyError:

View File

@ -18,12 +18,12 @@ from camlib import distance, arc, three_point_circle, Geometry, FlatCAMRTreeStor
from FlatCAMTool import FlatCAMTool
from flatcamGUI.ObjectUI import RadioSet
from flatcamGUI.GUIElements import OptionalInputSection, FCCheckBox, FCEntry, FCComboBox, FCTextAreaRich, \
FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog
FCTable, FCDoubleSpinner, FCButton, EvalEntry2, FCInputDialog, FCTree
from flatcamParsers.ParseFont import *
import FlatCAMApp
from shapely.geometry import LineString, LinearRing, MultiLineString, Polygon, MultiPolygon
from shapely.ops import cascaded_union, unary_union
from shapely.ops import cascaded_union, unary_union, linemerge
import shapely.affinity as affinity
from shapely.geometry.polygon import orient
@ -202,7 +202,7 @@ class TextInputTool(FlatCAMTool):
self.text_path = []
self.decimals = self.app.decimals
self.f_parse = ParseFont(self)
self.f_parse = ParseFont(self.app)
self.f_parse.get_fonts_by_types()
# this way I can hide/show the frame
@ -364,12 +364,10 @@ class TextInputTool(FlatCAMTool):
string_to_geo = self.text_input_entry.get_value()
font_to_geo_size = self.font_size_cb.get_value()
self.text_path = self.f_parse.font_to_geometry(
char_string=string_to_geo,
font_name=self.font_name,
font_size=font_to_geo_size,
font_type=font_to_geo_type,
units=self.app.defaults['units'].upper())
self.text_path = self.f_parse.font_to_geometry(char_string=string_to_geo, font_name=self.font_name,
font_size=font_to_geo_size,
font_type=font_to_geo_type,
units=self.app.defaults['units'].upper())
def font_family(self, font):
self.text_input_entry.selectAll()
@ -403,8 +401,8 @@ class TextInputTool(FlatCAMTool):
def hide_tool(self):
self.text_tool_frame.hide()
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
self.app.ui.splitter.setSizes([0, 1])
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
# self.app.ui.splitter.setSizes([0, 1])
self.app.ui.notebook.setTabText(2, _("Tool"))
@ -451,7 +449,7 @@ class PaintOptionsTool(FlatCAMTool):
grid.addWidget(self.painttooldia_entry, 0, 1)
# Overlap
ovlabel = QtWidgets.QLabel('%s:' % _('Overlap Rate'))
ovlabel = QtWidgets.QLabel('%s:' % _('Overlap'))
ovlabel.setToolTip(
_("How much (percentage) of the tool width to overlap each tool pass.\n"
"Adjust the value starting with lower values\n"
@ -487,15 +485,20 @@ class PaintOptionsTool(FlatCAMTool):
# Method
methodlabel = QtWidgets.QLabel('%s:' % _('Method'))
methodlabel.setToolTip(
_("Algorithm to paint the polygon:<BR>"
"<B>Standard</B>: Fixed step inwards.<BR>"
"<B>Seed-based</B>: Outwards from seed.")
_("Algorithm to paint the polygons:\n"
"- Standard: Fixed step inwards.\n"
"- Seed-based: Outwards from seed.\n"
"- Line-based: Parallel lines.")
)
# self.paintmethod_combo = RadioSet([
# {"label": _("Standard"), "value": "standard"},
# {"label": _("Seed-based"), "value": "seed"},
# {"label": _("Straight lines"), "value": "lines"}
# ], orientation='vertical', stretch=False)
self.paintmethod_combo = FCComboBox()
self.paintmethod_combo.addItems(
[_("Standard"), _("Seed"), _("Lines")]
)
self.paintmethod_combo = RadioSet([
{"label": _("Standard"), "value": "standard"},
{"label": _("Seed-based"), "value": "seed"},
{"label": _("Straight lines"), "value": "lines"}
], orientation='vertical', stretch=False)
grid.addWidget(methodlabel, 3, 0)
grid.addWidget(self.paintmethod_combo, 3, 1)
@ -564,7 +567,7 @@ class PaintOptionsTool(FlatCAMTool):
if self.app.defaults["tools_paintmethod"]:
self.paintmethod_combo.set_value(self.app.defaults["tools_paintmethod"])
else:
self.paintmethod_combo.set_value("seed")
self.paintmethod_combo.set_value(_("Seed"))
if self.app.defaults["tools_pathconnect"]:
self.pathconnect_cb.set_value(self.app.defaults["tools_pathconnect"])
@ -578,8 +581,7 @@ class PaintOptionsTool(FlatCAMTool):
def on_paint(self):
if not self.fcdraw.selected:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Paint cancelled. No shape selected."))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Paint cancelled. No shape selected."))
return
tooldia = self.painttooldia_entry.get_value()
@ -1751,7 +1753,7 @@ class DrawToolShape(object):
def translate_recursion(geom):
if type(geom) == list:
geoms = list()
geoms = []
for local_geom in geom:
geoms.append(translate_recursion(local_geom))
return geoms
@ -1798,7 +1800,7 @@ class DrawToolShape(object):
def scale_recursion(geom):
if type(geom) == list:
geoms = list()
geoms = []
for local_geom in geom:
geoms.append(scale_recursion(local_geom))
return geoms
@ -1924,6 +1926,12 @@ class FCCircle(FCShapeTool):
self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"]
def click(self, point):
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
self.points.append(point)
if len(self.points) == 1:
@ -1962,7 +1970,7 @@ class FCCircle(FCShapeTool):
self.draw_app.app.inform.emit('[success] %s' % _("Done. Adding Circle completed."))
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2003,6 +2011,12 @@ class FCArc(FCShapeTool):
self.steps_per_circ = self.draw_app.app.defaults["geometry_circle_steps"]
def click(self, point):
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
self.points.append(point)
if len(self.points) == 1:
@ -2196,7 +2210,7 @@ class FCArc(FCShapeTool):
self.draw_app.app.inform.emit('[success] %s' % _("Done. Arc completed."))
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2227,6 +2241,12 @@ class FCRectangle(FCShapeTool):
self.draw_app.app.inform.emit(_("Click on 1st corner ..."))
def click(self, point):
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
self.points.append(point)
if len(self.points) == 1:
@ -2263,7 +2283,7 @@ class FCRectangle(FCShapeTool):
self.draw_app.app.inform.emit('[success] %s' % _("Done. Rectangle completed."))
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2294,6 +2314,12 @@ class FCPolygon(FCShapeTool):
self.draw_app.app.inform.emit(_("Click on 1st corner ..."))
def click(self, point):
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
self.draw_app.in_action = True
self.points.append(point)
@ -2346,7 +2372,7 @@ class FCPolygon(FCShapeTool):
return _("Backtracked one point ...")
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2411,7 +2437,7 @@ class FCPath(FCPolygon):
return _("Backtracked one point ...")
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2494,10 +2520,34 @@ class FCSelect(DrawTool):
self.draw_app.selected = []
self.draw_app.selected.append(obj_to_add)
except Exception as e:
log.error("[ERROR] Something went bad. %s" % str(e))
raise
log.error("[ERROR] FlatCAMGeoEditor.FCSelect.click_release() -> Something went bad. %s" % str(e))
# if selection is done on canvas update the Tree in Selected Tab with the selection
try:
self.draw_app.tw.itemSelectionChanged.disconnect(self.draw_app.on_tree_selection_change)
except (AttributeError, TypeError):
pass
self.draw_app.tw.selectionModel().clearSelection()
for sel_shape in self.draw_app.selected:
iterator = QtWidgets.QTreeWidgetItemIterator(self.draw_app.tw)
while iterator.value():
item = iterator.value()
try:
if int(item.text(1)) == id(sel_shape):
item.setSelected(True)
except ValueError:
pass
iterator += 1
self.draw_app.tw.itemSelectionChanged.connect(self.draw_app.on_tree_selection_change)
return ""
def clean_up(self):
pass
class FCExplode(FCShapeTool):
def __init__(self, draw_app):
@ -2521,8 +2571,8 @@ class FCExplode(FCShapeTool):
self.make()
def make(self):
to_be_deleted_list = list()
lines = list()
to_be_deleted_list = []
lines = []
for shape in self.draw_app.get_selected():
to_be_deleted_list.append(shape)
@ -2544,7 +2594,7 @@ class FCExplode(FCShapeTool):
if shape in self.draw_app.selected:
self.draw_app.selected.remove(shape)
geo_list = list()
geo_list = []
for line in lines:
geo_list.append(DrawToolShape(line))
self.geometry = geo_list
@ -2552,7 +2602,7 @@ class FCExplode(FCShapeTool):
self.draw_app.app.inform.emit('[success] %s...' % _("Done. Polygons exploded into lines."))
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2585,6 +2635,7 @@ class FCMove(FCShapeTool):
return
else:
self.draw_app.app.inform.emit(_(" MOVE: Click on reference point ..."))
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
def set_origin(self, origin):
@ -2592,6 +2643,12 @@ class FCMove(FCShapeTool):
self.origin = origin
def click(self, point):
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
if len(self.draw_app.get_selected()) == 0:
# self.complete = True
# self.draw_app.app.inform.emit(_("[WARNING_NOTCL] Move cancelled. No shape selected."))
@ -2623,7 +2680,10 @@ class FCMove(FCShapeTool):
self.draw_app.delete_selected()
self.complete = True
self.draw_app.app.inform.emit('[success] %s' % _("Done. Geometry(s) Move completed."))
self.draw_app.app.jump_signal.disconnect()
try:
self.draw_app.app.jump_signal.disconnect()
except TypeError:
pass
def selection_bbox(self):
geo_list = []
@ -2731,7 +2791,7 @@ class FCMove(FCShapeTool):
raise
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2753,10 +2813,13 @@ class FCCopy(FCMove):
for geom in self.draw_app.get_selected()]
self.complete = True
self.draw_app.app.inform.emit('[success] %s' % _("Done. Geometry(s) Copy completed."))
self.draw_app.app.jump_signal.disconnect()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2783,11 +2846,17 @@ class FCText(FCShapeTool):
self.draw_app.app.inform.emit(_("Click on 1st point ..."))
self.origin = (0, 0)
self.text_gui = TextInputTool(self.app)
self.text_gui = TextInputTool(app=self.app)
self.text_gui.run()
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
def click(self, point):
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
# Create new geometry
dx = point[0]
dy = point[1]
@ -2807,7 +2876,10 @@ class FCText(FCShapeTool):
return
else:
self.draw_app.app.inform.emit('[WARNING_NOTCL] %s' % _("No text to add."))
self.draw_app.app.jump_signal.disconnect()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
return
self.text_gui.text_path = []
@ -2832,7 +2904,7 @@ class FCText(FCShapeTool):
return
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2885,13 +2957,11 @@ class FCBuffer(FCShapeTool):
self.disactivate()
if ret_val == 'fail':
return
self.draw_app.app.inform.emit('[success] %s' %
_("Done. Buffer Tool completed."))
self.draw_app.app.inform.emit('[success] %s' % _("Done. Buffer Tool completed."))
def on_buffer_int(self):
if not self.draw_app.selected:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Buffer cancelled. No shape selected."))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Buffer cancelled. No shape selected."))
return
try:
@ -2915,13 +2985,11 @@ class FCBuffer(FCShapeTool):
self.disactivate()
if ret_val == 'fail':
return
self.draw_app.app.inform.emit('[success] %s' %
_("Done. Buffer Int Tool completed."))
self.draw_app.app.inform.emit('[success] %s' % _("Done. Buffer Int Tool completed."))
def on_buffer_ext(self):
if not self.draw_app.selected:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Buffer cancelled. No shape selected."))
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Buffer cancelled. No shape selected."))
return
try:
@ -2945,8 +3013,7 @@ class FCBuffer(FCShapeTool):
self.disactivate()
if ret_val == 'fail':
return
self.draw_app.app.inform.emit('[success] %s' %
_("Done. Buffer Ext Tool completed."))
self.draw_app.app.inform.emit('[success] %s' % _("Done. Buffer Ext Tool completed."))
def activate(self):
self.buff_tool.buffer_button.clicked.disconnect()
@ -2968,9 +3035,13 @@ class FCBuffer(FCShapeTool):
self.complete = True
self.draw_app.select_tool("select")
self.buff_tool.hide_tool()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -2979,7 +3050,6 @@ class FCBuffer(FCShapeTool):
pass
class FCEraser(FCShapeTool):
def __init__(self, draw_app):
DrawTool.__init__(self, draw_app)
@ -3007,6 +3077,12 @@ class FCEraser(FCShapeTool):
self.origin = origin
def click(self, point):
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
self.draw_app.app.jump_signal.connect(lambda x: self.draw_app.update_utility_geometry(data=x))
if len(self.draw_app.get_selected()) == 0:
for obj_shape in self.storage.get_objects():
try:
@ -3054,7 +3130,10 @@ class FCEraser(FCShapeTool):
self.draw_app.delete_utility_geometry()
self.draw_app.plot_all()
self.draw_app.app.inform.emit('[success] %s' % _("Done. Eraser tool action completed."))
self.draw_app.app.jump_signal.disconnect()
try:
self.draw_app.app.jump_signal.disconnect()
except (TypeError, AttributeError):
pass
def utility_geometry(self, data=None):
"""
@ -3084,7 +3163,7 @@ class FCEraser(FCShapeTool):
return DrawToolUtilityShape(geo_list)
def clean_up(self):
self.draw_app.selected = list()
self.draw_app.selected = []
self.draw_app.plot_all()
try:
@ -3123,6 +3202,9 @@ class FCTransform(FCShapeTool):
# ###############################################
class FlatCAMGeoEditor(QtCore.QObject):
# will emit the name of the object that was just selected
item_selected = QtCore.pyqtSignal(str)
transform_complete = QtCore.pyqtSignal()
draw_shape_idx = -1
@ -3137,6 +3219,49 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.canvas = app.plotcanvas
self.decimals = app.decimals
self.geo_edit_widget = QtWidgets.QWidget()
# ## Box for custom widgets
# This gets populated in offspring implementations.
layout = QtWidgets.QVBoxLayout()
self.geo_edit_widget.setLayout(layout)
# add a frame and inside add a vertical box layout. Inside this vbox layout I add all the Drills widgets
# this way I can hide/show the frame
self.geo_frame = QtWidgets.QFrame()
self.geo_frame.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.geo_frame)
self.tools_box = QtWidgets.QVBoxLayout()
self.tools_box.setContentsMargins(0, 0, 0, 0)
self.geo_frame.setLayout(self.tools_box)
# ## Page Title box (spacing between children)
self.title_box = QtWidgets.QHBoxLayout()
self.tools_box.addLayout(self.title_box)
# ## Page Title icon
pixmap = QtGui.QPixmap(self.app.resource_location + '/flatcam_icon32.png')
self.icon = QtWidgets.QLabel()
self.icon.setPixmap(pixmap)
self.title_box.addWidget(self.icon, stretch=0)
# ## Title label
self.title_label = QtWidgets.QLabel("<font size=5><b>%s</b></font>" % _('Geometry Editor'))
self.title_label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.title_box.addWidget(self.title_label, stretch=1)
self.title_box.addWidget(QtWidgets.QLabel(''))
self.tw = FCTree(columns=3, header_hidden=False, protected_column=[0, 1], extended_sel=True)
self.tw.setHeaderLabels(["ID", _("Type"), _("Name")])
self.tw.setIndentation(0)
self.tw.header().setStretchLastSection(True)
self.tw.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
self.tools_box.addWidget(self.tw)
self.geo_font = QtGui.QFont()
self.geo_font.setBold(True)
self.geo_parent = self.tw.invisibleRootItem()
# ## Toolbar events and properties
self.tools = {
"select": {"button": self.app.ui.geo_select_btn,
@ -3338,10 +3463,84 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.delete_selected()
self.replot()
def set_ui(self):
# updated units
self.units = self.app.defaults['units'].upper()
self.decimals = self.app.decimals
# Remove anything else in the GUI Selected Tab
self.app.ui.selected_scroll_area.takeWidget()
# Put ourselves in the GUI Selected Tab
self.app.ui.selected_scroll_area.setWidget(self.geo_edit_widget)
# Switch notebook to Selected page
self.app.ui.notebook.setCurrentWidget(self.app.ui.selected_tab)
def build_ui(self, first_run=None):
# try:
# # if connected, disconnect the signal from the slot on item_changed as it creates issues
# self.apertures_table.itemChanged.disconnect()
# except (TypeError, AttributeError):
# pass
iterator = QtWidgets.QTreeWidgetItemIterator(self.geo_parent)
to_delete = []
while iterator.value():
item = iterator.value()
to_delete.append(item)
iterator += 1
for it in to_delete:
self.geo_parent.removeChild(it)
for elem in self.storage.get_objects():
geo_type = type(elem.geo)
el_type = None
if geo_type is LinearRing:
el_type = _('Ring')
elif geo_type is LineString:
el_type = _('Line')
elif geo_type is Polygon:
el_type = _('Polygon')
elif geo_type is MultiLineString:
el_type = _('Multi-Line')
elif geo_type is MultiPolygon:
el_type = _('Multi-Polygon')
self.tw.addParentEditable(
self.geo_parent,
[
str(id(elem)),
'%s' % el_type,
_("Geo Elem")
],
font=self.geo_font,
font_items=2,
# color=QtGui.QColor("#FF0000"),
editable=True
)
self.tw.resize_sig.emit()
def on_geo_elem_selected(self):
pass
def on_tree_selection_change(self):
self.selected = []
selected_tree_items = self.tw.selectedItems()
for sel in selected_tree_items:
for obj_shape in self.storage.get_objects():
try:
if id(obj_shape) == int(sel.text(0)):
self.selected.append(obj_shape)
except ValueError:
pass
self.replot()
def activate(self):
# adjust the status of the menu entries related to the editor
self.app.ui.menueditedit.setDisabled(True)
self.app.ui.menueditok.setDisabled(False)
# adjust the visibility of some of the canvas context menu
self.app.ui.popmenu_edit.setVisible(False)
self.app.ui.popmenu_save.setVisible(True)
@ -3379,23 +3578,34 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.ui.g_editor_cmenu.menuAction().setVisible(True)
# prevent the user to change anything in the Selected Tab while the Geo Editor is active
sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget)
for w in sel_tab_widget_list:
w.setEnabled(False)
# sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget)
# for w in sel_tab_widget_list:
# w.setEnabled(False)
# Tell the App that the editor is active
self.editor_active = True
self.item_selected.connect(self.on_geo_elem_selected)
# ## GUI Events
self.tw.itemSelectionChanged.connect(self.on_tree_selection_change)
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
self.geo_frame.show()
log.debug("Finished activating the Geometry Editor...")
def deactivate(self):
try:
QtGui.QGuiApplication.restoreOverrideCursor()
except Exception as e:
except Exception:
pass
# adjust the status of the menu entries related to the editor
self.app.ui.menueditedit.setDisabled(False)
self.app.ui.menueditok.setDisabled(True)
# adjust the visibility of some of the canvas context menu
self.app.ui.popmenu_edit.setVisible(True)
self.app.ui.popmenu_save.setVisible(False)
@ -3454,16 +3664,37 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.ui.g_editor_cmenu.menuAction().setVisible(False)
try:
# re-enable all the widgets in the Selected Tab that were disabled after entering in Edit Geometry Mode
sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget)
for w in sel_tab_widget_list:
w.setEnabled(True)
except Exception as e:
log.debug("FlatCAMGeoEditor.deactivate() --> %s" % str(e))
self.item_selected.disconnect()
except (AttributeError, TypeError):
pass
try:
# ## GUI Events
self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change)
# self.tw.keyPressed.connect(self.app.ui.keyPressEvent)
# self.tw.customContextMenuRequested.connect(self.on_menu_request)
except (AttributeError, TypeError):
pass
# try:
# # re-enable all the widgets in the Selected Tab that were disabled after entering in Edit Geometry Mode
# sel_tab_widget_list = self.app.ui.selected_tab.findChildren(QtWidgets.QWidget)
# for w in sel_tab_widget_list:
# w.setEnabled(True)
# except Exception as e:
# log.debug("FlatCAMGeoEditor.deactivate() --> %s" % str(e))
# Show original geometry
if self.fcgeometry:
self.fcgeometry.visible = True
# clear the Tree
self.tw.clear()
self.geo_parent = self.tw.invisibleRootItem()
# hide the UI
self.geo_frame.hide()
log.debug("Finished deactivating the Geometry Editor...")
def connect_canvas_event_handlers(self):
@ -3553,7 +3784,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
except (TypeError, AttributeError):
pass
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_object)
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_command)
self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
@ -3665,6 +3896,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.utility.append(shape)
else:
self.storage.insert(shape) # TODO: Check performance
self.build_ui()
def delete_utility_geometry(self):
# for_deletion = [shape for shape in self.shape_buffer if shape.utility]
@ -3705,6 +3937,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.deactivate()
self.activate()
self.set_ui()
# Hide original geometry
self.fcgeometry = fcgeometry
fcgeometry.visible = False
@ -3739,8 +3973,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
)
)
else:
geo_to_edit = self.flatten(geometry=fcgeometry.solid_geometry,
orient_val=milling_type)
geo_to_edit = self.flatten(geometry=fcgeometry.solid_geometry, orient_val=milling_type)
for shape in geo_to_edit:
if shape is not None: # TODO: Make flatten never create a None
@ -3827,7 +4060,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.pos = self.canvas.translate_coords(event_pos)
if self.app.grid_status() == True:
if self.app.grid_status():
self.pos = self.app.geo_editor.snap(self.pos[0], self.pos[1])
else:
self.pos = (self.pos[0], self.pos[1])
@ -3840,7 +4073,9 @@ class FlatCAMGeoEditor(QtCore.QObject):
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
if modifiers == QtCore.Qt.ShiftModifier:
self.app.clipboard.setText(
self.app.defaults["global_point_clipboard_format"] % (self.pos[0], self.pos[1]))
self.app.defaults["global_point_clipboard_format"] %
(self.decimals, self.pos[0], self.decimals, self.pos[1])
)
return
# Selection with left mouse button
@ -3907,7 +4142,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
return
# ### Snap coordinates ###
if self.app.grid_status() == True:
if self.app.grid_status():
x, y = self.snap(x, y)
# Update cursor
@ -3921,7 +4156,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# update the position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.position_label.setText("&nbsp;&nbsp;&nbsp;&nbsp;<b>X</b>: %.4f&nbsp;&nbsp; "
"<b>Y</b>: %.4f" % (x, y))
"<b>Y</b>: %.4f" % (x, y))
if self.pos is None:
self.pos = (0, 0)
@ -3930,7 +4165,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# update the reference position label in the infobar since the APP mouse event handlers are disconnected
self.app.ui.rel_position_label.setText("<b>Dx</b>: %.4f&nbsp;&nbsp; <b>Dy</b>: "
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
"%.4f&nbsp;&nbsp;&nbsp;&nbsp;" % (dx, dy))
if event.button == 1 and event_is_dragging and isinstance(self.active_tool, FCEraser):
pass
@ -3943,8 +4178,8 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.app.delete_selection_shape()
if dx < 0:
self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y),
color=self.app.defaults["global_alt_sel_line"],
face_color=self.app.defaults['global_alt_sel_fill'])
color=self.app.defaults["global_alt_sel_line"],
face_color=self.app.defaults['global_alt_sel_fill'])
self.app.selection_type = False
else:
self.app.draw_moving_selection_shape((self.pos[0], self.pos[1]), (x, y))
@ -3993,19 +4228,18 @@ class FlatCAMGeoEditor(QtCore.QObject):
# self.app.inform.emit(msg)
self.replot()
elif event.button == right_button: # right click
if self.app.ui.popMenu.mouse_is_panning == False:
if self.app.ui.popMenu.mouse_is_panning is False:
if self.in_action is False:
try:
QtGui.QGuiApplication.restoreOverrideCursor()
except Exception as e:
except Exception:
pass
if self.active_tool.complete is False and not isinstance(self.active_tool, FCSelect):
self.active_tool.complete = True
self.in_action = False
self.delete_utility_geometry()
self.app.inform.emit('[success] %s' %
_("Done."))
self.app.inform.emit('[success] %s' % _("Done."))
self.select_tool('select')
else:
self.app.cursor = QtGui.QCursor()
@ -4019,8 +4253,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.active_tool.make()
if self.active_tool.complete:
self.on_shape_complete()
self.app.inform.emit('[success] %s' %
_("Done."))
self.app.inform.emit('[success] %s' % _("Done."))
self.select_tool(self.active_tool.name)
except Exception as e:
log.warning("FLatCAMGeoEditor.on_geo_click_release() --> Error: %s" % str(e))
@ -4064,6 +4297,27 @@ class FlatCAMGeoEditor(QtCore.QObject):
self.selected = []
self.selected = sel_objects_list
# if selection is done on canvas update the Tree in Selected Tab with the selection
try:
self.tw.itemSelectionChanged.disconnect(self.on_tree_selection_change)
except (AttributeError, TypeError):
pass
self.tw.selectionModel().clearSelection()
for sel_shape in self.selected:
iterator = QtWidgets.QTreeWidgetItemIterator(self.tw)
while iterator.value():
item = iterator.value()
try:
if int(item.text(1)) == id(sel_shape):
item.setSelected(True)
except ValueError:
pass
iterator += 1
self.tw.itemSelectionChanged.connect(self.on_tree_selection_change)
self.replot()
def draw_utility_geometry(self, geo):
@ -4113,6 +4367,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
for shape in tempref:
self.delete_shape(shape)
self.selected = []
self.build_ui()
def delete_shape(self, shape):
@ -4275,7 +4530,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
log.debug("FlatCAMGeoEditor.on_shape_complete() Error --> %s" % str(e))
return 'fail'
shape_list = list()
shape_list = []
try:
for geo in geom:
shape_list.append(DrawToolShape(geo))
@ -4396,13 +4651,24 @@ class FlatCAMGeoEditor(QtCore.QObject):
fcgeometry.tools[self.multigeo_tool]['solid_geometry'] = []
# for shape in self.shape_buffer:
for shape in self.storage.get_objects():
fcgeometry.tools[self.multigeo_tool]['solid_geometry'].append(shape.geo)
new_geo = shape.geo
# simplify the MultiLineString
if isinstance(new_geo, MultiLineString):
new_geo = linemerge(new_geo)
fcgeometry.tools[self.multigeo_tool]['solid_geometry'].append(new_geo)
self.multigeo_tool = None
fcgeometry.solid_geometry = []
# for shape in self.shape_buffer:
for shape in self.storage.get_objects():
fcgeometry.solid_geometry.append(shape.geo)
new_geo = shape.geo
# simplify the MultiLineString
if isinstance(new_geo, MultiLineString):
new_geo = linemerge(new_geo)
fcgeometry.solid_geometry.append(new_geo)
def update_options(self, obj):
if self.paint_tooldia:
@ -4777,11 +5043,11 @@ class FlatCAMGeoEditor(QtCore.QObject):
else:
poly_buf = Polygon(geo_obj).buffer(-margin)
if method == "seed":
if method == _("Seed"):
cp = Geometry.clear_polygon2(self, polygon_to_clear=poly_buf, tooldia=tooldia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=overlap, contour=contour, connect=connect)
elif method == "lines":
elif method == _("Lines"):
cp = Geometry.clear_polygon3(self, polygon=poly_buf, tooldia=tooldia,
steps_per_circle=self.app.defaults["geometry_circle_steps"],
overlap=overlap, contour=contour, connect=connect)
@ -4794,12 +5060,10 @@ class FlatCAMGeoEditor(QtCore.QObject):
local_results += list(cp.get_objects())
except Exception as e:
log.debug("Could not Paint the polygons. %s" % str(e))
self.app.inform.emit('[ERROR] %s\n%s' %
(_("Could not do Paint. Try a different combination of"
" parameters. Or a different method of Paint"),
str(e)
)
)
self.app.inform.emit(
'[ERROR] %s\n%s' % (_("Could not do Paint. Try a different combination of parameters. "
"Or a different method of Paint"), str(e))
)
return
# add the result to the results list
@ -4808,8 +5072,7 @@ class FlatCAMGeoEditor(QtCore.QObject):
# This is a dirty patch:
for r in results:
self.add_shape(DrawToolShape(r))
self.app.inform.emit(
'[success] %s' % _("Paint done."))
self.app.inform.emit('[success] %s' % _("Paint done."))
self.replot()
def flatten(self, geometry, orient_val=1, reset=True, pathonly=False):

View File

@ -288,14 +288,14 @@ class FCPad(FCShapeTool):
ap_type = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['type']
if ap_type == 'C':
new_geo_el = dict()
new_geo_el = {}
center = Point([point_x, point_y])
new_geo_el['solid'] = center.buffer(self.radius)
new_geo_el['follow'] = center
return new_geo_el
elif ap_type == 'R':
new_geo_el = dict()
new_geo_el = {}
p1 = (point_x - self.half_width, point_y - self.half_height)
p2 = (point_x + self.half_width, point_y - self.half_height)
@ -307,7 +307,7 @@ class FCPad(FCShapeTool):
return new_geo_el
elif ap_type == 'O':
geo = []
new_geo_el = dict()
new_geo_el = {}
if self.half_height > self.half_width:
p1 = (point_x - self.half_width, point_y - self.half_height + self.half_width)
@ -545,7 +545,7 @@ class FCPadArray(FCShapeTool):
)
if static is None or static is False:
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geo_el:
new_geo_el['solid'] = affinity.translate(
@ -602,14 +602,14 @@ class FCPadArray(FCShapeTool):
ap_type = self.draw_app.storage_dict[self.draw_app.last_aperture_selected]['type']
if ap_type == 'C':
new_geo_el = dict()
new_geo_el = {}
center = Point([point_x, point_y])
new_geo_el['solid'] = center.buffer(self.radius)
new_geo_el['follow'] = center
return new_geo_el
elif ap_type == 'R':
new_geo_el = dict()
new_geo_el = {}
p1 = (point_x - self.half_width, point_y - self.half_height)
p2 = (point_x + self.half_width, point_y - self.half_height)
@ -620,7 +620,7 @@ class FCPadArray(FCShapeTool):
return new_geo_el
elif ap_type == 'O':
geo = []
new_geo_el = dict()
new_geo_el = {}
if self.half_height > self.half_width:
p1 = (point_x - self.half_width, point_y - self.half_height + self.half_width)
@ -812,7 +812,7 @@ class FCPoligonize(FCShapeTool):
except KeyError:
self.draw_app.on_aperture_add(apid='0')
current_storage = self.draw_app.storage_dict['0']['geometry']
new_el = dict()
new_el = {}
new_el['solid'] = geo
new_el['follow'] = geo.exterior
self.draw_app.on_grb_shape_complete(current_storage, specific_shape=DrawToolShape(deepcopy(new_el)))
@ -827,7 +827,7 @@ class FCPoligonize(FCShapeTool):
self.draw_app.on_aperture_add(apid='0')
current_storage = self.draw_app.storage_dict['0']['geometry']
new_el = dict()
new_el = {}
new_el['solid'] = fused_geo
new_el['follow'] = fused_geo.exterior
self.draw_app.on_grb_shape_complete(current_storage, specific_shape=DrawToolShape(deepcopy(new_el)))
@ -915,7 +915,7 @@ class FCRegion(FCShapeTool):
self.gridy_size = float(self.draw_app.app.ui.grid_gap_y_entry.get_value())
def utility_geometry(self, data=None):
new_geo_el = dict()
new_geo_el = {}
x = data[0]
y = data[1]
@ -983,7 +983,7 @@ class FCRegion(FCShapeTool):
self.inter_point = data
self.temp_points.append(data)
new_geo_el = dict()
new_geo_el = {}
if len(self.temp_points) > 1:
try:
@ -1049,7 +1049,7 @@ class FCRegion(FCShapeTool):
self.temp_points.append(self.inter_point)
self.temp_points.append(data)
new_geo_el = dict()
new_geo_el = {}
new_geo_el['solid'] = LinearRing(self.temp_points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4),
@ -1070,7 +1070,7 @@ class FCRegion(FCShapeTool):
else:
self.draw_app.last_aperture_selected = '0'
new_geo_el = dict()
new_geo_el = {}
new_geo_el['solid'] = Polygon(self.points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4),
@ -1183,7 +1183,7 @@ class FCTrack(FCRegion):
self.draw_app.app.inform.emit(_('Track Mode 1: 45 degrees ...'))
def make(self):
new_geo_el = dict()
new_geo_el = {}
if len(self.temp_points) == 1:
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
resolution=int(self.steps_per_circle / 4))
@ -1219,7 +1219,7 @@ class FCTrack(FCRegion):
except IndexError:
self.points.append(point)
new_geo_el = dict()
new_geo_el = {}
if len(self.temp_points) == 1:
new_geo_el['solid'] = Point(self.temp_points).buffer(self.buf_val,
@ -1242,7 +1242,7 @@ class FCTrack(FCRegion):
def utility_geometry(self, data=None):
self.update_grid_info()
new_geo_el = dict()
new_geo_el = {}
if len(self.points) == 0:
new_geo_el['solid'] = Point(data).buffer(self.buf_val,
@ -1427,10 +1427,10 @@ class FCDisc(FCShapeTool):
if '0' in self.draw_app.storage_dict:
self.storage_obj = self.draw_app.storage_dict['0']['geometry']
else:
self.draw_app.storage_dict['0'] = dict()
self.draw_app.storage_dict['0'] = {}
self.draw_app.storage_dict['0']['type'] = 'C'
self.draw_app.storage_dict['0']['size'] = 0.0
self.draw_app.storage_dict['0']['geometry'] = list()
self.draw_app.storage_dict['0']['geometry'] = []
self.storage_obj = self.draw_app.storage_dict['0']['geometry']
self.draw_app.app.inform.emit(_("Click on Center point ..."))
@ -1453,7 +1453,7 @@ class FCDisc(FCShapeTool):
return ""
def utility_geometry(self, data=None):
new_geo_el = dict()
new_geo_el = {}
if len(self.points) == 1:
p1 = self.points[0]
p2 = data
@ -1464,7 +1464,7 @@ class FCDisc(FCShapeTool):
return None
def make(self):
new_geo_el = dict()
new_geo_el = {}
try:
QtGui.QGuiApplication.restoreOverrideCursor()
@ -1530,10 +1530,10 @@ class FCSemiDisc(FCShapeTool):
if '0' in self.draw_app.storage_dict:
self.storage_obj = self.draw_app.storage_dict['0']['geometry']
else:
self.draw_app.storage_dict['0'] = dict()
self.draw_app.storage_dict['0'] = {}
self.draw_app.storage_dict['0']['type'] = 'C'
self.draw_app.storage_dict['0']['size'] = 0.0
self.draw_app.storage_dict['0']['geometry'] = list()
self.draw_app.storage_dict['0']['geometry'] = []
self.storage_obj = self.draw_app.storage_dict['0']['geometry']
self.steps_per_circ = self.draw_app.app.defaults["gerber_circle_steps"]
@ -1592,10 +1592,10 @@ class FCSemiDisc(FCShapeTool):
return _('Mode: Center -> Start -> Stop. Click on Center point ...')
def utility_geometry(self, data=None):
new_geo_el = dict()
new_geo_el_pt1 = dict()
new_geo_el_pt2 = dict()
new_geo_el_pt3 = dict()
new_geo_el = {}
new_geo_el_pt1 = {}
new_geo_el_pt2 = {}
new_geo_el_pt3 = {}
if len(self.points) == 1: # Show the radius
center = self.points[0]
@ -1681,7 +1681,7 @@ class FCSemiDisc(FCShapeTool):
def make(self):
self.draw_app.current_storage = self.storage_obj
new_geo_el = dict()
new_geo_el = {}
if self.mode == 'c12':
center = self.points[0]
@ -2031,7 +2031,7 @@ class FCApertureMove(FCShapeTool):
for select_shape in self.draw_app.get_selected():
if select_shape in self.current_storage:
geometric_data = select_shape.geo
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geometric_data:
new_geo_el['solid'] = affinity.translate(geometric_data['solid'], xoff=dx, yoff=dy)
if 'follow' in geometric_data:
@ -2084,7 +2084,7 @@ class FCApertureMove(FCShapeTool):
if len(self.draw_app.get_selected()) <= self.sel_limit:
for geom in self.draw_app.get_selected():
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geom.geo:
new_geo_el['solid'] = affinity.translate(geom.geo['solid'], xoff=dx, yoff=dy)
if 'follow' in geom.geo:
@ -2094,7 +2094,7 @@ class FCApertureMove(FCShapeTool):
geo_list.append(deepcopy(new_geo_el))
return DrawToolUtilityShape(geo_list)
else:
ss_el = dict()
ss_el = {}
ss_el['solid'] = affinity.translate(self.selection_shape, xoff=dx, yoff=dy)
return DrawToolUtilityShape(ss_el)
@ -2115,7 +2115,7 @@ class FCApertureCopy(FCApertureMove):
for select_shape in self.draw_app.get_selected():
if select_shape in self.current_storage:
geometric_data = select_shape.geo
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geometric_data:
new_geo_el['solid'] = affinity.translate(geometric_data['solid'], xoff=dx, yoff=dy)
if 'follow' in geometric_data:
@ -2274,7 +2274,7 @@ class FCEraser(FCShapeTool):
dy = data[1] - self.origin[1]
for geom in self.draw_app.get_selected():
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geom.geo:
new_geo_el['solid'] = affinity.translate(geom.geo['solid'], xoff=dx, yoff=dy)
if 'follow' in geom.geo:
@ -2300,10 +2300,10 @@ class FCApertureSelect(DrawTool):
# since FCApertureSelect tool is activated whenever a tool is exited I place here the reinitialization of the
# bending modes using in FCRegion and FCTrack
self.draw_app.bend_mode = 1
self.grb_editor_app.bend_mode = 1
# here store the selected apertures
self.sel_aperture = set()
self.sel_aperture = []
try:
self.grb_editor_app.apertures_table.clearSelection()
@ -2332,7 +2332,7 @@ class FCApertureSelect(DrawTool):
else:
mod_key = None
if mod_key == self.draw_app.app.defaults["global_mselect_key"]:
if mod_key == self.grb_editor_app.app.defaults["global_mselect_key"]:
pass
else:
self.grb_editor_app.selected = []
@ -2348,46 +2348,53 @@ class FCApertureSelect(DrawTool):
else:
mod_key = None
if mod_key != self.grb_editor_app.app.defaults["global_mselect_key"]:
self.grb_editor_app.selected.clear()
self.sel_aperture.clear()
for storage in self.grb_editor_app.storage_dict:
try:
for geo_el in self.grb_editor_app.storage_dict[storage]['geometry']:
if 'solid' in geo_el.geo:
geometric_data = geo_el.geo['solid']
for shape_stored in self.grb_editor_app.storage_dict[storage]['geometry']:
if 'solid' in shape_stored.geo:
geometric_data = shape_stored.geo['solid']
if Point(point).within(geometric_data):
if mod_key == self.grb_editor_app.app.defaults["global_mselect_key"]:
if geo_el in self.draw_app.selected:
self.draw_app.selected.remove(geo_el)
self.sel_aperture.remove(storage)
else:
# add the object to the selected shapes
self.draw_app.selected.append(geo_el)
self.sel_aperture.add(storage)
if shape_stored in self.grb_editor_app.selected:
self.grb_editor_app.selected.remove(shape_stored)
else:
self.draw_app.selected.append(geo_el)
self.sel_aperture.add(storage)
# add the object to the selected shapes
self.grb_editor_app.selected.append(shape_stored)
except KeyError:
pass
# select the aperture in the Apertures Table that is associated with the selected shape
self.sel_aperture.clear()
self.grb_editor_app.apertures_table.clearSelection()
try:
self.draw_app.apertures_table.cellPressed.disconnect()
self.grb_editor_app.apertures_table.cellPressed.disconnect()
except Exception as e:
log.debug("FlatCAMGrbEditor.FCApertureSelect.click_release() --> %s" % str(e))
self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
for shape_s in self.grb_editor_app.selected:
for storage in self.grb_editor_app.storage_dict:
if shape_s in self.grb_editor_app.storage_dict[storage]['geometry']:
self.sel_aperture.append(storage)
# self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
for aper in self.sel_aperture:
for row in range(self.grb_editor_app.apertures_table.rowCount()):
if str(aper) == self.grb_editor_app.apertures_table.item(row, 1).text():
self.grb_editor_app.apertures_table.selectRow(row)
self.draw_app.last_aperture_selected = aper
self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
if not self.grb_editor_app.apertures_table.item(row, 0).isSelected():
self.grb_editor_app.apertures_table.selectRow(row)
self.grb_editor_app.last_aperture_selected = aper
# self.grb_editor_app.apertures_table.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.draw_app.apertures_table.cellPressed.connect(self.draw_app.on_row_selected)
self.grb_editor_app.apertures_table.cellPressed.connect(self.grb_editor_app.on_row_selected)
return ""
def clean_up(self):
self.draw_app.plot_all()
self.grb_editor_app.plot_all()
class FCTransform(FCShapeTool):
@ -2915,30 +2922,30 @@ class FlatCAMGrbEditor(QtCore.QObject):
# # ## Data
self.active_tool = None
self.storage_dict = dict()
self.current_storage = list()
self.storage_dict = {}
self.current_storage = []
self.sorted_apid = list()
self.sorted_apid = []
self.new_apertures = dict()
self.new_aperture_macros = dict()
self.new_apertures = {}
self.new_aperture_macros = {}
# store here the plot promises, if empty the delayed plot will be activated
self.grb_plot_promises = list()
self.grb_plot_promises = []
# dictionary to store the tool_row and aperture codes in Tool_table
# it will be updated everytime self.build_ui() is called
self.olddia_newdia = dict()
self.olddia_newdia = {}
self.tool2tooldia = dict()
self.tool2tooldia = {}
# this will store the value for the last selected tool, for use after clicking on canvas when the selection
# is cleared but as a side effect also the selected tool is cleared
self.last_aperture_selected = None
self.utility = list()
self.utility = []
# this will store the polygons marked by mark are to be perhaps deleted
self.geo_to_delete = list()
self.geo_to_delete = []
# this will flag if the Editor "tools" are launched from key shortcuts (True) or from menu toolbar (False)
self.launched_from_shortcuts = False
@ -2950,7 +2957,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.apdim_lbl.hide()
self.apdim_entry.hide()
self.gerber_obj = None
self.gerber_obj_options = dict()
self.gerber_obj_options = {}
# VisPy Visuals
if self.app.is_legacy is False:
@ -3033,11 +3040,14 @@ class FlatCAMGrbEditor(QtCore.QObject):
self.pool = self.app.pool
# Multiprocessing results
self.results = list()
self.results = []
# A QTimer
self.plot_thread = None
# a QThread for the edit process
self.thread = QtCore.QThread()
# store the status of the editor so the Delete at object level will not work until the edit is finished
self.editor_active = False
@ -3099,6 +3109,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
def pool_recreated(self, pool):
self.shapes.pool = pool
self.tool_shape.pool = pool
self.pool = pool
def set_ui(self):
# updated units
@ -3423,7 +3434,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
# I've added this flag_del variable because dictionary don't like
# having keys deleted while iterating through them
flag_del = list()
flag_del = []
for deleted_tool in self.tool2tooldia:
if self.tool2tooldia[deleted_tool] == deleted_aperture:
flag_del.append(deleted_tool)
@ -3493,7 +3504,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
geometry = []
for geo_el in self.storage_dict[dia_changed]:
geometric_data = geo_el.geo
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geometric_data:
new_geo_el['solid'] = deepcopy(affinity.scale(geometric_data['solid'],
xfact=factor, yfact=factor))
@ -3742,7 +3753,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
except (TypeError, AttributeError):
pass
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_object)
self.app.ui.popmenu_copy.triggered.connect(self.app.on_copy_command)
self.app.ui.popmenu_delete.triggered.connect(self.app.on_delete)
self.app.ui.popmenu_move.triggered.connect(self.app.obj_move)
@ -3920,101 +3931,130 @@ class FlatCAMGrbEditor(QtCore.QObject):
# # and add the first aperture to have something to play with
# self.on_aperture_add('10')
def worker_job(app_obj):
with app_obj.app.proc_container.new('%s ...' % _("Loading Gerber into Editor")):
# ############################################################# ##
# APPLY CLEAR_GEOMETRY on the SOLID_GEOMETRY
# ############################################################# ##
# self.app.worker_task.emit({'fcn': worker_job, 'params': [self]})
# list of clear geos that are to be applied to the entire file
global_clear_geo = []
class Execute_Edit(QtCore.QObject):
# create one big geometry made out of all 'negative' (clear) polygons
for apid in app_obj.gerber_obj.apertures:
# first check if we have any clear_geometry (LPC) and if yes added it to the global_clear_geo
if 'geometry' in app_obj.gerber_obj.apertures[apid]:
for elem in app_obj.gerber_obj.apertures[apid]['geometry']:
if 'clear' in elem:
global_clear_geo.append(elem['clear'])
log.warning("Found %d clear polygons." % len(global_clear_geo))
start = QtCore.pyqtSignal(str)
global_clear_geo = MultiPolygon(global_clear_geo)
if isinstance(global_clear_geo, Polygon):
global_clear_geo = list(global_clear_geo)
def __init__(self, app):
super(Execute_Edit, self).__init__()
self.app = app
self.start.connect(self.run)
# we subtract the big "negative" (clear) geometry from each solid polygon but only the part of
# clear geometry that fits inside the solid. otherwise we may loose the solid
for apid in app_obj.gerber_obj.apertures:
temp_solid_geometry = []
if 'geometry' in app_obj.gerber_obj.apertures[apid]:
# for elem in self.gerber_obj.apertures[apid]['geometry']:
# if 'solid' in elem:
# solid_geo = elem['solid']
# for clear_geo in global_clear_geo:
# # Make sure that the clear_geo is within the solid_geo otherwise we loose
# # the solid_geometry. We want for clear_geometry just to cut into solid_geometry not to
# # delete it
# if clear_geo.within(solid_geo):
# solid_geo = solid_geo.difference(clear_geo)
# try:
# for poly in solid_geo:
# new_elem = dict()
#
# new_elem['solid'] = poly
# if 'clear' in elem:
# new_elem['clear'] = poly
# if 'follow' in elem:
# new_elem['follow'] = poly
# temp_elem.append(deepcopy(new_elem))
# except TypeError:
# new_elem = dict()
# new_elem['solid'] = solid_geo
# if 'clear' in elem:
# new_elem['clear'] = solid_geo
# if 'follow' in elem:
# new_elem['follow'] = solid_geo
# temp_elem.append(deepcopy(new_elem))
for elem in app_obj.gerber_obj.apertures[apid]['geometry']:
new_elem = dict()
if 'solid' in elem:
solid_geo = elem['solid']
@staticmethod
def worker_job(app_obj):
with app_obj.app.proc_container.new('%s ...' % _("Loading Gerber into Editor")):
# ############################################################# ##
# APPLY CLEAR_GEOMETRY on the SOLID_GEOMETRY
# ############################################################# ##
for clear_geo in global_clear_geo:
# Make sure that the clear_geo is within the solid_geo otherwise we loose
# the solid_geometry. We want for clear_geometry just to cut into solid_geometry
# not to delete it
if clear_geo.within(solid_geo):
solid_geo = solid_geo.difference(clear_geo)
# list of clear geos that are to be applied to the entire file
global_clear_geo = []
new_elem['solid'] = solid_geo
if 'clear' in elem:
new_elem['clear'] = elem['clear']
if 'follow' in elem:
new_elem['follow'] = elem['follow']
temp_solid_geometry.append(deepcopy(new_elem))
# create one big geometry made out of all 'negative' (clear) polygons
for apid in app_obj.gerber_obj.apertures:
# first check if we have any clear_geometry (LPC) and if yes added it to the global_clear_geo
if 'geometry' in app_obj.gerber_obj.apertures[apid]:
for elem in app_obj.gerber_obj.apertures[apid]['geometry']:
if 'clear' in elem:
global_clear_geo.append(elem['clear'])
log.warning("Found %d clear polygons." % len(global_clear_geo))
app_obj.gerber_obj.apertures[apid]['geometry'] = deepcopy(temp_solid_geometry)
log.warning("Polygon difference done for %d apertures." % len(app_obj.gerber_obj.apertures))
if global_clear_geo:
global_clear_geo = MultiPolygon(global_clear_geo)
if isinstance(global_clear_geo, Polygon):
global_clear_geo = list(global_clear_geo)
# Loading the Geometry into Editor Storage
for ap_id, ap_dict in app_obj.gerber_obj.apertures.items():
app_obj.results.append(app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_id, ap_dict)))
# we subtract the big "negative" (clear) geometry from each solid polygon but only the part of
# clear geometry that fits inside the solid. otherwise we may loose the solid
for apid in app_obj.gerber_obj.apertures:
temp_solid_geometry = []
if 'geometry' in app_obj.gerber_obj.apertures[apid]:
# for elem in self.gerber_obj.apertures[apid]['geometry']:
# if 'solid' in elem:
# solid_geo = elem['solid']
# for clear_geo in global_clear_geo:
# # Make sure that the clear_geo is within the solid_geo otherwise we loose
# # the solid_geometry. We want for clear_geometry just to cut into solid_geometry not to
# # delete it
# if clear_geo.within(solid_geo):
# solid_geo = solid_geo.difference(clear_geo)
# try:
# for poly in solid_geo:
# new_elem = {}
#
# new_elem['solid'] = poly
# if 'clear' in elem:
# new_elem['clear'] = poly
# if 'follow' in elem:
# new_elem['follow'] = poly
# temp_elem.append(deepcopy(new_elem))
# except TypeError:
# new_elem = {}
# new_elem['solid'] = solid_geo
# if 'clear' in elem:
# new_elem['clear'] = solid_geo
# if 'follow' in elem:
# new_elem['follow'] = solid_geo
# temp_elem.append(deepcopy(new_elem))
for elem in app_obj.gerber_obj.apertures[apid]['geometry']:
new_elem = {}
if 'solid' in elem:
solid_geo = elem['solid']
if not global_clear_geo or global_clear_geo.is_empty:
pass
else:
for clear_geo in global_clear_geo:
# Make sure that the clear_geo is within the solid_geo otherwise we loose
# the solid_geometry. We want for clear_geometry just to cut into
# solid_geometry not to delete it
if clear_geo.within(solid_geo):
solid_geo = solid_geo.difference(clear_geo)
output = list()
for p in app_obj.results:
output.append(p.get())
new_elem['solid'] = solid_geo
if 'clear' in elem:
new_elem['clear'] = elem['clear']
if 'follow' in elem:
new_elem['follow'] = elem['follow']
temp_solid_geometry.append(deepcopy(new_elem))
for elem in output:
app_obj.storage_dict[elem[0]] = deepcopy(elem[1])
app_obj.gerber_obj.apertures[apid]['geometry'] = deepcopy(temp_solid_geometry)
log.warning("Polygon difference done for %d apertures." % len(app_obj.gerber_obj.apertures))
app_obj.mp_finished.emit(output)
try:
# Loading the Geometry into Editor Storage
for ap_id, ap_dict in app_obj.gerber_obj.apertures.items():
app_obj.results.append(
app_obj.pool.apply_async(app_obj.add_apertures, args=(ap_id, ap_dict))
)
except Exception as e:
log.debug(
"FlatCAMGrbEditor.edit_fcgerber.worker_job() Adding processes to pool --> %s" % str(e))
traceback.print_exc()
self.app.worker_task.emit({'fcn': worker_job, 'params': [self]})
output = []
for p in app_obj.results:
output.append(p.get())
for elem in output:
app_obj.storage_dict[elem[0]] = deepcopy(elem[1])
app_obj.mp_finished.emit(output)
def run(self):
self.worker_job(self.app)
self.thread.start(QtCore.QThread.NormalPriority)
executable_edit = Execute_Edit(app=self)
executable_edit.moveToThread(self.thread)
executable_edit.start.emit("Started")
@staticmethod
def add_apertures(aperture_id, aperture_dict):
storage_elem = list()
storage_dict = dict()
storage_elem = []
storage_dict = {}
for k, v in list(aperture_dict.items()):
try:
@ -4073,7 +4113,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
def update_options(obj):
try:
if not obj.options:
obj.options = dict()
obj.options = {}
obj.options['xmin'] = 0
obj.options['ymin'] = 0
obj.options['xmax'] = 0
@ -4082,7 +4122,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
else:
return False
except AttributeError:
obj.options = dict()
obj.options = {}
return True
def new_edited_gerber(self, outname, aperture_storage):
@ -4101,7 +4141,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
out_name = outname
storage_dict = aperture_storage
local_storage_dict = dict()
local_storage_dict = {}
for aperture in storage_dict:
if 'geometry' in storage_dict[aperture]:
# add aperture only if it has geometry
@ -4122,7 +4162,7 @@ class FlatCAMGrbEditor(QtCore.QObject):
grb_obj.apertures[storage_apid][k] = []
for geo_el in val:
geometric_data = geo_el.geo
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geometric_data:
new_geo_el['solid'] = geometric_data['solid']
poly_buffer.append(deepcopy(new_geo_el['solid']))
@ -4197,12 +4237,12 @@ class FlatCAMGrbEditor(QtCore.QObject):
except Exception as e:
log.error("Error on Edited object creation: %s" % str(e))
# make sure to clean the previous results
self.results = list()
self.results = []
return
self.app.inform.emit('[success] %s' % _("Done. Gerber editing finished."))
# make sure to clean the previous results
self.results = list()
self.results = []
def on_tool_select(self, tool):
"""
@ -4371,7 +4411,8 @@ class FlatCAMGrbEditor(QtCore.QObject):
# If the SHIFT key is pressed when LMB is clicked then the coordinates are copied to clipboard
if modifiers == QtCore.Qt.ShiftModifier:
self.app.clipboard.setText(
self.app.defaults["global_point_clipboard_format"] % (self.pos[0], self.pos[1])
self.app.defaults["global_point_clipboard_format"] %
(self.decimals, self.pos[0], self.decimals, self.pos[1])
)
self.app.inform.emit('[success] %s' %
_("Coordinates copied to clipboard."))
@ -4911,14 +4952,14 @@ class FlatCAMGrbEditor(QtCore.QObject):
def buffer_recursion(geom_el, selection):
if type(geom_el) == list:
geoms = list()
geoms = []
for local_geom in geom_el:
geoms.append(buffer_recursion(local_geom, selection=selection))
return geoms
else:
if geom_el in selection:
geometric_data = geom_el.geo
buffered_geom_el = dict()
buffered_geom_el = {}
if 'solid' in geometric_data:
buffered_geom_el['solid'] = geometric_data['solid'].buffer(buff_value, join_style=join_style)
if 'follow' in geometric_data:
@ -4967,14 +5008,14 @@ class FlatCAMGrbEditor(QtCore.QObject):
def scale_recursion(geom_el, selection):
if type(geom_el) == list:
geoms = list()
geoms = []
for local_geom in geom_el:
geoms.append(scale_recursion(local_geom, selection=selection))
return geoms
else:
if geom_el in selection:
geometric_data = geom_el.geo
scaled_geom_el = dict()
scaled_geom_el = {}
if 'solid' in geometric_data:
scaled_geom_el['solid'] = affinity.scale(
geometric_data['solid'], scale_factor, scale_factor, origin='center'

View File

@ -29,6 +29,8 @@ class TextEditor(QtWidgets.QWidget):
super().__init__()
self.app = app
self.plain_text = plain_text
self.setSizePolicy(
QtWidgets.QSizePolicy.MinimumExpanding,
QtWidgets.QSizePolicy.MinimumExpanding
@ -45,7 +47,7 @@ class TextEditor(QtWidgets.QWidget):
self.work_editor_layout.setContentsMargins(2, 2, 2, 2)
self.t_frame.setLayout(self.work_editor_layout)
if plain_text:
if self.plain_text:
self.editor_class = FCTextAreaLineNumber()
self.code_editor = self.editor_class.edit
@ -313,7 +315,7 @@ class TextEditor(QtWidgets.QWidget):
if qc.hasSelection():
qc.insertText(new)
else:
self.ui.code_editor.moveCursor(QtGui.QTextCursor.Start)
self.code_editor.moveCursor(QtGui.QTextCursor.Start)
break
# Mark end of undo block
cursor.endEditBlock()

View File

@ -371,6 +371,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.menuedit.addSeparator()
self.menueditorigin = self.menuedit.addAction(
QtGui.QIcon(self.app.resource_location + '/origin16.png'), _('Se&t Origin\tO'))
self.menuedit_move2origin = self.menuedit.addAction(
QtGui.QIcon(self.app.resource_location + '/origin2_16.png'), _('Move to Origin\tSHIFT+O'))
self.menueditjump = self.menuedit.addAction(
QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location\tJ'))
self.menueditlocate = self.menuedit.addAction(
@ -686,9 +689,25 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.menuproject_brown = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/brown32.png'), _('Brown'))
self.menuproject_brown = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/white32.png'), _('White'))
self.menuproject_brown = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/black32.png'), _('Black'))
self.menuprojectcolor.addSeparator()
self.menuproject_custom = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/set_color32.png'), _('Custom'))
self.menuprojectcolor.addSeparator()
self.menuproject_custom = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/set_color32.png'), _('Opacity'))
self.menuproject_custom = self.menuprojectcolor.addAction(
QtGui.QIcon(self.app.resource_location + '/set_color32.png'), _('Default'))
self.menuproject.addSeparator()
self.menuprojectgeneratecnc = self.menuproject.addAction(
@ -825,6 +844,9 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
QtGui.QIcon(self.app.resource_location + '/distance_min32.png'), _("Distance Min Tool"))
self.origin_btn = self.toolbargeo.addAction(
QtGui.QIcon(self.app.resource_location + '/origin32.png'), _('Set Origin'))
self.move2origin_btn = self.toolbargeo.addAction(
QtGui.QIcon(self.app.resource_location + '/origin2_32.png'), _('Move to Origin'))
self.jmp_btn = self.toolbargeo.addAction(
QtGui.QIcon(self.app.resource_location + '/jump_to16.png'), _('Jump to Location'))
self.locate_btn = self.toolbargeo.addAction(
@ -904,6 +926,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
QtGui.QIcon(self.app.resource_location + '/fiducials_32.png'), _("Fiducials Tool"))
self.cal_btn = self.toolbartools.addAction(
QtGui.QIcon(self.app.resource_location + '/calibrate_32.png'), _("Calibration Tool"))
self.punch_btn = self.toolbartools.addAction(
QtGui.QIcon(self.app.resource_location + '/punch32.png'), _("Punch Gerber Tool"))
self.invert_btn = self.toolbartools.addAction(
QtGui.QIcon(self.app.resource_location + '/invert32.png'), _("Invert Gerber Tool"))
# ########################################################################
# ########################## Excellon Editor Toolbar# ####################
@ -1535,6 +1561,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
<td height="20"><strong>ALT+E</strong></td>
<td>&nbsp;%s</td>
</tr>
<tr height="20">
<td height="20"><strong>ALT+H</strong></td>
<td>&nbsp;%s</td>
</tr>
<tr height="20">
<td height="20"><strong>ALT+I</strong></td>
<td>&nbsp;%s</td>
@ -1664,7 +1694,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
_("Skew on Y axis"),
# ALT section
_("Align Objects Tool"), _("Calculators Tool"), _("2-Sided PCB Tool"), _("Transformations Tool"),
_("Extract Drills Tool"), _("Fiducials Tool"),
_("Punch Gerber Tool"), _("Extract Drills Tool"), _("Fiducials Tool"),
_("Solder Paste Dispensing Tool"),
_("Film PCB Tool"), _("Non-Copper Clearing Tool"), _("Optimal Tool"),
_("Paint Area Tool"), _("QRCode Tool"), _("Rules Check Tool"),
@ -2797,7 +2827,7 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.app.tools_db_tab.on_tool_copy()
return
self.app.on_copy_object()
self.app.on_copy_command()
# Copy an FlatCAM object
if key == QtCore.Qt.Key_D:
@ -2950,6 +2980,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
return
# Align in Object Tool
if key == QtCore.Qt.Key_H:
self.app.punch_tool.run(toggle=True)
# Extract Drills Tool
if key == QtCore.Qt.Key_I:
self.app.edrills_tool.run(toggle=True)

View File

@ -152,6 +152,124 @@ class RadioSet(QtWidgets.QWidget):
# wgt.show()
class FCTree(QtWidgets.QTreeWidget):
resize_sig = QtCore.pyqtSignal()
def __init__(self, parent=None, columns=2, header_hidden=True, extended_sel=False, protected_column=None):
super(FCTree, self).__init__(parent)
self.setColumnCount(columns)
self.setHeaderHidden(header_hidden)
self.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
self.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Expanding)
palette = QtGui.QPalette()
palette.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.Highlight,
palette.color(QtGui.QPalette.Active, QtGui.QPalette.Highlight))
# make inactive rows text some color as active; may be useful in the future
# palette.setColor(QtGui.QPalette.Inactive, QtGui.QPalette.HighlightedText,
# palette.color(QtGui.QPalette.Active, QtGui.QPalette.HighlightedText))
self.setPalette(palette)
if extended_sel:
self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.protected_column = protected_column
self.itemDoubleClicked.connect(self.on_double_click)
self.header().sectionDoubleClicked.connect(self.on_header_double_click)
self.resize_sig.connect(self.on_resize)
def on_double_click(self, item, column):
# from here: https://stackoverflow.com/questions/2801959/making-only-one-column-of-a-qtreewidgetitem-editable
tmp_flags = item.flags()
if self.is_editable(column):
item.setFlags(tmp_flags | QtCore.Qt.ItemIsEditable)
elif tmp_flags & QtCore.Qt.ItemIsEditable:
item.setFlags(tmp_flags ^ QtCore.Qt.ItemIsEditable)
def on_header_double_click(self, column):
header = self.header()
header.setSectionResizeMode(column, QtWidgets.QHeaderView.ResizeToContents)
width = header.sectionSize(column)
header.setSectionResizeMode(column, QtWidgets.QHeaderView.Interactive)
header.resizeSection(column, width)
def is_editable(self, tested_col):
try:
ret_val = False if tested_col in self.protected_column else True
except TypeError:
ret_val = False
return ret_val
def addParent(self, parent, title, expanded=False, color=None, font=None):
item = QtWidgets.QTreeWidgetItem(parent, [title])
item.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.ShowIndicator)
item.setExpanded(expanded)
if color is not None:
# item.setTextColor(0, color) # PyQt4
item.setForeground(0, QtGui.QBrush(color))
if font is not None:
item.setFont(0, font)
return item
def addParentEditable(self, parent, title, color=None, font=None, font_items=None, editable=False):
item = QtWidgets.QTreeWidgetItem(parent)
item.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.DontShowIndicator)
if editable:
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
item.setFlags(item.flags() | QtCore.Qt.ItemIsSelectable)
for t in range(len(title)):
item.setText(t, title[t])
if color is not None:
# item.setTextColor(0, color) # PyQt4
item.setForeground(0, QtGui.QBrush(color))
if font and font_items:
try:
for fi in font_items:
item.setFont(fi, font)
except TypeError:
item.setFont(font_items, font)
elif font:
item.setFont(0, font)
return item
def addChild(self, parent, title, column1=None, font=None, font_items=None, editable=False):
item = QtWidgets.QTreeWidgetItem(parent)
if editable:
item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
item.setText(0, str(title[0]))
if column1 is not None:
item.setText(1, str(title[1]))
if font and font_items:
try:
for fi in font_items:
item.setFont(fi, font)
except TypeError:
item.setFont(font_items, font)
def resizeEvent(self, event):
""" Resize all sections to content and user interactive """
super(FCTree, self).resizeEvent(event)
self.on_resize()
def on_resize(self):
header = self.header()
for column in range(header.count()):
header.setSectionResizeMode(column, QtWidgets.QHeaderView.ResizeToContents)
width = header.sectionSize(column)
header.setSectionResizeMode(column, QtWidgets.QHeaderView.Interactive)
header.resizeSection(column, width)
class LengthEntry(QtWidgets.QLineEdit):
def __init__(self, output_units='IN', decimals=None, parent=None):
super(LengthEntry, self).__init__(parent)
@ -201,7 +319,7 @@ class LengthEntry(QtWidgets.QLineEdit):
units = raw[-2:]
units = self.scales[self.output_units][units.upper()]
value = raw[:-2]
return float(eval(value))* units
return float(eval(value)) * units
except IndexError:
value = raw
return float(eval(value))
@ -233,7 +351,7 @@ class FloatEntry(QtWidgets.QLineEdit):
def mousePressEvent(self, e, Parent=None):
super(FloatEntry, self).mousePressEvent(e) # required to deselect on 2e click
if self.readyToEdit == True:
if self.readyToEdit is True:
self.selectAll()
self.readyToEdit = False
@ -407,6 +525,8 @@ class FCEntry(QtWidgets.QLineEdit):
decimal_digits = decimals if decimals is not None else self.decimals
if type(val) is float:
self.setText('%.*f' % (decimal_digits, val))
elif val is None:
self.setText('')
else:
self.setText(str(val))
@ -538,12 +658,16 @@ class EvalEntry2(QtWidgets.QLineEdit):
class FCSpinner(QtWidgets.QSpinBox):
returnPressed = QtCore.pyqtSignal()
confirmation_signal = QtCore.pyqtSignal(bool, float, float)
def __init__(self, suffix=None, alignment=None, parent=None):
def __init__(self, suffix=None, alignment=None, parent=None, callback=None):
super(FCSpinner, self).__init__(parent)
self.readyToEdit = True
self.editingFinished.connect(self.on_edit_finished)
if callback:
self.confirmation_signal.connect(callback)
self.lineEdit().installEventFilter(self)
if suffix:
@ -613,8 +737,30 @@ class FCSpinner(QtWidgets.QSpinBox):
return
self.setValue(k)
def validate(self, p_str, p_int):
text = p_str
min_val = self.minimum()
max_val = self.maximum()
try:
if int(text) < min_val or int(text) > max_val:
self.confirmation_signal.emit(False, min_val, max_val)
return QtGui.QValidator.Intermediate, text, p_int
except ValueError:
pass
self.confirmation_signal.emit(True, min_val, max_val)
return QtGui.QValidator.Acceptable, p_str, p_int
def set_range(self, min_val, max_val):
self.blockSignals(True)
self.setRange(min_val, max_val)
self.blockSignals(False)
def set_step(self, p_int):
self.blockSignals(True)
self.setSingleStep(p_int)
self.blockSignals(False)
# def sizeHint(self):
# default_hint_size = super(FCSpinner, self).sizeHint()
@ -624,12 +770,23 @@ class FCSpinner(QtWidgets.QSpinBox):
class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
returnPressed = QtCore.pyqtSignal()
confirmation_signal = QtCore.pyqtSignal(bool, float, float)
def __init__(self, suffix=None, alignment=None, parent=None):
def __init__(self, suffix=None, alignment=None, parent=None, callback=None):
"""
:param suffix: a char added to the end of the value in the LineEdit; like a '%' or '$' etc
:param alignment: the value is aligned to left or right
:param parent:
:param callback: called when the entered value is outside limits; the min and max value will be passed to it
"""
super(FCDoubleSpinner, self).__init__(parent)
self.readyToEdit = True
self.editingFinished.connect(self.on_edit_finished)
if callback:
self.confirmation_signal.connect(callback)
self.lineEdit().installEventFilter(self)
# by default don't allow the minus sign to be entered as the default for QDoubleSpinBox is the positive range
@ -699,11 +856,17 @@ class FCDoubleSpinner(QtWidgets.QDoubleSpinBox):
def validate(self, p_str, p_int):
text = p_str.replace(',', '.')
min_val = self.minimum()
max_val = self.maximum()
try:
if float(text) < self.minimum() or float(text) > self.maximum():
if float(text) < min_val or float(text) > max_val:
self.confirmation_signal.emit(False, min_val, max_val)
return QtGui.QValidator.Intermediate, text, p_int
except ValueError:
pass
self.confirmation_signal.emit(True, min_val, max_val)
return QtGui.QValidator.Acceptable, p_str, p_int
def get_value(self):
@ -1139,6 +1302,9 @@ class FCComboBox(QtWidgets.QComboBox):
self.view.viewport().installEventFilter(self)
self.view.setContextMenuPolicy(Qt.CustomContextMenu)
self._set_last = False
self._obj_type = None
# the callback() will be called on customcontextmenu event and will be be passed 2 parameters:
# pos = mouse right click click position
# self = is the combobox object itself
@ -1160,6 +1326,29 @@ class FCComboBox(QtWidgets.QComboBox):
def set_value(self, val):
self.setCurrentIndex(self.findText(str(val)))
@property
def is_last(self):
return self._set_last
@is_last.setter
def is_last(self, val):
self._set_last = val
if self._set_last is True:
self.model().rowsInserted.connect(self.on_model_changed)
self.setCurrentIndex(1)
@property
def obj_type(self):
return self._obj_type
@obj_type.setter
def obj_type(self, val):
self._obj_type = val
def on_model_changed(self, parent, first, last):
if self.model().data(parent, QtCore.Qt.DisplayRole) == self.obj_type:
self.setCurrentIndex(first)
class FCInputDialog(QtWidgets.QInputDialog):
def __init__(self, parent=None, ok=False, val=None, title=None, text=None, min=None, max=None, decimals=None,
@ -1290,7 +1479,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
self.protect_by_name = protect_by_name if isinstance(protect_by_name, list) else None
# Close all detached tabs if the application is closed explicitly
QtWidgets.qApp.aboutToQuit.connect(self.closeDetachedTabs) # @UndefinedVariable
QtWidgets.qApp.aboutToQuit.connect(self.closeDetachedTabs) # @UndefinedVariable
# used by the property self.useOldIndex(param)
self.use_old_index = None
@ -1770,7 +1959,7 @@ class FCDetachableTab(QtWidgets.QTabWidget):
self.dragInitiated = True
# If the current movement is a drag initiated by the left button
if ((event.buttons() & QtCore.Qt.LeftButton)) and self.dragInitiated and self.can_be_dragged:
if (event.buttons() & QtCore.Qt.LeftButton) and self.dragInitiated and self.can_be_dragged:
# Stop the move event
finishMoveEvent = QtGui.QMouseEvent(
@ -2026,7 +2215,7 @@ class FCTable(QtWidgets.QTableWidget):
self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
self.rows_not_for_drag_and_drop = list()
self.rows_not_for_drag_and_drop = []
if protected_rows:
try:
for r in protected_rows:
@ -2034,7 +2223,7 @@ class FCTable(QtWidgets.QTableWidget):
except TypeError:
self.rows_not_for_drag_and_drop = [protected_rows]
self.rows_to_move = list()
self.rows_to_move = []
def sizeHint(self):
default_hint_size = super(FCTable, self).sizeHint()
@ -2090,7 +2279,7 @@ class FCTable(QtWidgets.QTableWidget):
# # ]
# self.rows_to_move[:] = []
# for row_index in rows:
# row_items = list()
# row_items = []
# for column_index in range(self.columnCount()):
# r_item = self.item(row_index, column_index)
# w_item = self.cellWidget(row_index, column_index)
@ -2171,7 +2360,7 @@ class FCTable(QtWidgets.QTableWidget):
for _ in range(len(rows)):
self.insertRow(targetRow)
rowMapping = dict() # Src row to target row.
rowMapping = {} # Src row to target row.
for idx, row in enumerate(rows):
if row < targetRow:
rowMapping[row] = targetRow + idx
@ -2278,7 +2467,7 @@ class Dialog_box(QtWidgets.QWidget):
class DialogBoxRadio(QtWidgets.QDialog):
def __init__(self, title=None, label=None, icon=None, initial_text=None, reference='abs'):
def __init__(self, title=None, label=None, icon=None, initial_text=None, reference='abs', parent=None):
"""
:param title: string with the window title
@ -2322,8 +2511,10 @@ class DialogBoxRadio(QtWidgets.QDialog):
"If the reference is Relative then the Jump will be at the (x,y) distance\n"
"from the current mouse location point.")
)
self.lineEdit = EvalEntry()
self.lineEdit = EvalEntry(self)
self.lineEdit.setText(str(self.location).replace('(', '').replace(')', ''))
self.lineEdit.selectAll()
self.lineEdit.setFocus()
self.form.addRow(self.loc_label, self.lineEdit)
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel,

File diff suppressed because it is too large Load Diff

View File

@ -62,7 +62,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
# self.b_line, self.r_line, self.t_line, self.l_line = None, None, None, None
self.workspace_line = None
self.pagesize_dict = dict()
self.pagesize_dict = {}
self.pagesize_dict.update(
{
'A0': (841, 1189),

View File

@ -158,7 +158,7 @@ class PlotCanvasLegacy(QtCore.QObject):
# self.b_line, self.r_line, self.t_line, self.l_line = None, None, None, None
self.workspace_line = None
self.pagesize_dict = dict()
self.pagesize_dict = {}
self.pagesize_dict.update(
{
'A0': (841, 1189),
@ -959,8 +959,8 @@ class ShapeCollectionLegacy:
self.app = app
self.annotation_job = annotation_job
self._shapes = dict()
self.shape_dict = dict()
self._shapes = {}
self.shape_dict = {}
self.shape_id = 0
self._color = None

File diff suppressed because it is too large Load Diff

View File

@ -43,6 +43,7 @@ class Excellon(Geometry):
================ ====================================
C Diameter of the tool
solid_geometry Geometry list for each tool
data dictionary which holds the options for each tool
Others Not supported (Ignored).
================ ====================================
@ -94,11 +95,11 @@ class Excellon(Geometry):
Geometry.__init__(self, geo_steps_per_circle=int(geo_steps_per_circle))
# dictionary to store tools, see above for description
self.tools = dict()
self.tools = {}
# list to store the drills, see above for description
self.drills = list()
self.drills = []
# self.slots (list) to store the slots; each is a dictionary
self.slots = list()
self.slots = []
self.source_file = ''
@ -109,8 +110,8 @@ class Excellon(Geometry):
self.match_routing_start = None
self.match_routing_stop = None
self.num_tools = list() # List for keeping the tools sorted
self.index_per_tool = dict() # Dictionary to store the indexed points for each tool
self.num_tools = [] # List for keeping the tools sorted
self.index_per_tool = {} # Dictionary to store the indexed points for each tool
# ## IN|MM -> Units are inherited from Geometry
self.units = self.app.defaults['units']
@ -961,10 +962,8 @@ class Excellon(Geometry):
try:
# clear the solid_geometry in self.tools
for tool in self.tools:
try:
self.tools[tool]['solid_geometry'][:] = []
except KeyError:
self.tools[tool]['solid_geometry'] = []
self.tools[tool]['solid_geometry'] = []
self.tools[tool]['data'] = {}
for drill in self.drills:
# poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0)
@ -979,7 +978,10 @@ class Excellon(Geometry):
tooldia = self.tools[drill['tool']]['C']
poly = drill['point'].buffer(tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
self.solid_geometry.append(poly)
self.tools[drill['tool']]['solid_geometry'].append(poly)
tool_in_drills = drill['tool']
self.tools[tool_in_drills]['solid_geometry'].append(poly)
self.tools[tool_in_drills]['data'] = deepcopy(self.default_data)
for slot in self.slots:
slot_tooldia = self.tools[slot['tool']]['C']
@ -989,8 +991,10 @@ class Excellon(Geometry):
lines_string = LineString([start, stop])
poly = lines_string.buffer(slot_tooldia / 2.0, int(int(self.geo_steps_per_circle) / 4))
self.solid_geometry.append(poly)
self.tools[slot['tool']]['solid_geometry'].append(poly)
tool_in_slots = slot['tool']
self.tools[tool_in_slots]['solid_geometry'].append(poly)
self.tools[tool_in_slots]['data'] = deepcopy(self.default_data)
except Exception as e:
log.debug("flatcamParsers.ParseExcellon.Excellon.create_geometry() -> "
"Excellon geometry creation failed due of ERROR: %s" % str(e))

View File

@ -394,10 +394,10 @@ class Gerber(Geometry):
current_operation_code = None
# Current coordinates
current_x = None
current_y = None
previous_x = None
previous_y = None
current_x = 0
current_y = 0
previous_x = 0
previous_y = 0
current_d = None
@ -456,13 +456,18 @@ class Gerber(Geometry):
new_polarity = match.group(1)
# log.info("Polarity CHANGE, LPC = %s, poly_buff = %s" % (self.is_lpc, poly_buffer))
self.is_lpc = True if new_polarity == 'C' else False
if len(path) > 1 and current_polarity != new_polarity:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length > 1 and current_polarity != new_polarity:
# finish the current path and add it to the storage
# --- Buffered ----
width = self.apertures[last_path_aperture]["size"]
geo_dict = dict()
geo_dict = {}
geo_f = LineString(path)
if not geo_f.is_empty:
follow_buffer.append(geo_f)
@ -481,7 +486,7 @@ class Gerber(Geometry):
geo_dict['solid'] = geo_s
if last_path_aperture not in self.apertures:
self.apertures[last_path_aperture] = dict()
self.apertures[last_path_aperture] = {}
if 'geometry' not in self.apertures[last_path_aperture]:
self.apertures[last_path_aperture]['geometry'] = []
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
@ -491,7 +496,12 @@ class Gerber(Geometry):
# --- Apply buffer ---
# If added for testing of bug #83
# TODO: Remove when bug fixed
if len(poly_buffer) > 0:
try:
buff_length = len(poly_buffer)
except TypeError:
buff_length = 1
if buff_length > 0:
if current_polarity == 'D':
self.solid_geometry = self.solid_geometry.union(cascaded_union(poly_buffer))
@ -667,7 +677,7 @@ class Gerber(Geometry):
# --- Buffered ---
try:
# log.debug("Bare op-code %d." % current_operation_code)
geo_dict = dict()
geo_dict = {}
flash = self.create_flash_geometry(
Point(current_x, current_y), self.apertures[current_aperture],
self.steps_per_circle)
@ -685,7 +695,7 @@ class Gerber(Geometry):
geo_dict['solid'] = flash
if current_aperture not in self.apertures:
self.apertures[current_aperture] = dict()
self.apertures[current_aperture] = {}
if 'geometry' not in self.apertures[current_aperture]:
self.apertures[current_aperture]['geometry'] = []
self.apertures[current_aperture]['geometry'].append(deepcopy(geo_dict))
@ -714,12 +724,17 @@ class Gerber(Geometry):
# log.debug(self.apertures[current_aperture])
# Take care of the current path with the previous tool
if len(path) > 1:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length > 1:
if self.apertures[last_path_aperture]["type"] == 'R':
# do nothing because 'R' type moving aperture is none at once
pass
else:
geo_dict = dict()
geo_dict = {}
geo_f = LineString(path)
if not geo_f.is_empty:
follow_buffer.append(geo_f)
@ -739,7 +754,7 @@ class Gerber(Geometry):
geo_dict['solid'] = geo_s
if last_path_aperture not in self.apertures:
self.apertures[last_path_aperture] = dict()
self.apertures[last_path_aperture] = {}
if 'geometry' not in self.apertures[last_path_aperture]:
self.apertures[last_path_aperture]['geometry'] = []
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
@ -751,10 +766,15 @@ class Gerber(Geometry):
# ################ G36* - Begin region ########################
# ################################################################
if self.regionon_re.search(gline):
if len(path) > 1:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length > 1:
# Take care of what is left in the path
geo_dict = dict()
geo_dict = {}
geo_f = LineString(path)
if not geo_f.is_empty:
follow_buffer.append(geo_f)
@ -774,7 +794,7 @@ class Gerber(Geometry):
geo_dict['solid'] = geo_s
if last_path_aperture not in self.apertures:
self.apertures[last_path_aperture] = dict()
self.apertures[last_path_aperture] = {}
if 'geometry' not in self.apertures[last_path_aperture]:
self.apertures[last_path_aperture]['geometry'] = []
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
@ -794,14 +814,19 @@ class Gerber(Geometry):
self.apertures['0'] = {}
self.apertures['0']['type'] = 'REG'
self.apertures['0']['size'] = 0.0
self.apertures['0']['geometry'] = list()
self.apertures['0']['geometry'] = []
# if D02 happened before G37 we now have a path with 1 element only; we have to add the current
# geo to the poly_buffer otherwise we loose it
if current_operation_code == 2:
if len(path) == 1:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length == 1:
# this means that the geometry was prepared previously and we just need to add it
geo_dict = dict()
geo_dict = {}
if geo_f:
if not geo_f.is_empty:
follow_buffer.append(geo_f)
@ -825,7 +850,12 @@ class Gerber(Geometry):
# Only one path defines region?
# This can happen if D02 happened before G37 and
# is not and error.
if len(path) < 3:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length < 3:
# print "ERROR: Path contains less than 3 points:"
# path = [[current_x, current_y]]
continue
@ -833,7 +863,7 @@ class Gerber(Geometry):
# For regions we may ignore an aperture that is None
# --- Buffered ---
geo_dict = dict()
geo_dict = {}
if current_aperture in self.apertures:
# the following line breaks loading of Circuit Studio Gerber files
# buff_value = float(self.apertures[current_aperture]['size']) / 2.0
@ -935,7 +965,7 @@ class Gerber(Geometry):
maxy = max(path[0][1], path[1][1]) + height / 2
log.debug("Coords: %s - %s - %s - %s" % (minx, miny, maxx, maxy))
geo_dict = dict()
geo_dict = {}
geo_f = Point([current_x, current_y])
follow_buffer.append(geo_f)
geo_dict['follow'] = geo_f
@ -952,7 +982,7 @@ class Gerber(Geometry):
geo_dict['solid'] = geo_s
if current_aperture not in self.apertures:
self.apertures[current_aperture] = dict()
self.apertures[current_aperture] = {}
if 'geometry' not in self.apertures[current_aperture]:
self.apertures[current_aperture]['geometry'] = []
self.apertures[current_aperture]['geometry'].append(deepcopy(geo_dict))
@ -974,10 +1004,15 @@ class Gerber(Geometry):
_("GERBER file might be CORRUPT. Check the file !!!"))
elif current_operation_code == 2:
if len(path) > 1:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length > 1:
geo_s = None
geo_dict = dict()
geo_dict = {}
# --- BUFFERED ---
# this treats the case when we are storing geometry as paths only
if making_region:
@ -1054,7 +1089,7 @@ class Gerber(Geometry):
geo_dict['solid'] = geo_s
if last_path_aperture not in self.apertures:
self.apertures[last_path_aperture] = dict()
self.apertures[last_path_aperture] = {}
if 'geometry' not in self.apertures[last_path_aperture]:
self.apertures[last_path_aperture]['geometry'] = []
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
@ -1073,9 +1108,14 @@ class Gerber(Geometry):
elif current_operation_code == 3:
# Create path draw so far.
if len(path) > 1:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length > 1:
# --- Buffered ----
geo_dict = dict()
geo_dict = {}
# this treats the case when we are storing geometry as paths
geo_f = LineString(path)
@ -1116,7 +1156,7 @@ class Gerber(Geometry):
geo_dict['solid'] = geo_s
if last_path_aperture not in self.apertures:
self.apertures[last_path_aperture] = dict()
self.apertures[last_path_aperture] = {}
if 'geometry' not in self.apertures[last_path_aperture]:
self.apertures[last_path_aperture]['geometry'] = []
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
@ -1127,7 +1167,7 @@ class Gerber(Geometry):
# --- BUFFERED ---
# Draw the flash
# this treats the case when we are storing geometry as paths
geo_dict = dict()
geo_dict = {}
geo_flash = Point([linear_x, linear_y])
follow_buffer.append(geo_flash)
geo_dict['follow'] = geo_flash
@ -1150,7 +1190,7 @@ class Gerber(Geometry):
geo_dict['solid'] = flash
if current_aperture not in self.apertures:
self.apertures[current_aperture] = dict()
self.apertures[current_aperture] = {}
if 'geometry' not in self.apertures[current_aperture]:
self.apertures[current_aperture]['geometry'] = []
self.apertures[current_aperture]['geometry'].append(deepcopy(geo_dict))
@ -1229,8 +1269,13 @@ class Gerber(Geometry):
# Nothing created! Pen Up.
if current_operation_code == 2:
log.warning("Arc with D2. (%d)" % line_num)
if len(path) > 1:
geo_dict = dict()
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length > 1:
geo_dict = {}
if last_path_aperture is None:
log.warning("No aperture defined for curent path. (%d)" % line_num)
@ -1258,7 +1303,7 @@ class Gerber(Geometry):
geo_dict['solid'] = buffered
if last_path_aperture not in self.apertures:
self.apertures[last_path_aperture] = dict()
self.apertures[last_path_aperture] = {}
if 'geometry' not in self.apertures[last_path_aperture]:
self.apertures[last_path_aperture]['geometry'] = []
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
@ -1372,7 +1417,12 @@ class Gerber(Geometry):
# ################################################################
log.warning("Line ignored (%d): %s" % (line_num, gline))
if len(path) > 1:
try:
path_length = len(path)
except TypeError:
path_length = 1
if path_length > 1:
# In case that G01 (moving) aperture is rectangular, there is no need to still create
# another geo since we already created a shapely box using the start and end coordinates found in
# path variable. We do it only for other apertures than 'R' type
@ -1382,7 +1432,7 @@ class Gerber(Geometry):
# EOF, create shapely LineString if something still in path
# ## --- Buffered ---
geo_dict = dict()
geo_dict = {}
# this treats the case when we are storing geometry as paths
geo_f = LineString(path)
if not geo_f.is_empty:
@ -1404,7 +1454,7 @@ class Gerber(Geometry):
geo_dict['solid'] = geo_s
if last_path_aperture not in self.apertures:
self.apertures[last_path_aperture] = dict()
self.apertures[last_path_aperture] = {}
if 'geometry' not in self.apertures[last_path_aperture]:
self.apertures[last_path_aperture]['geometry'] = []
self.apertures[last_path_aperture]['geometry'].append(deepcopy(geo_dict))
@ -1414,13 +1464,26 @@ class Gerber(Geometry):
self.follow_geometry = follow_buffer
# this treats the case when we are storing geometry as solids
try:
buff_length = len(poly_buffer)
except TypeError:
buff_length = 1
if len(poly_buffer) == 0 and len(self.solid_geometry) == 0:
log.error("Object is not Gerber file or empty. Aborting Object creation.")
try:
sol_geo_length = len(self.solid_geometry)
except TypeError:
sol_geo_length = 1
try:
if buff_length == 0 and sol_geo_length == 0:
log.error("Object is not Gerber file or empty. Aborting Object creation.")
return 'fail'
except TypeError as e:
log.error("Object is not Gerber file or empty. Aborting Object creation. %s" % str(e))
return 'fail'
log.warning("Joining %d polygons." % len(poly_buffer))
self.app.inform.emit('%s: %d.' % (_("Gerber processing. Joining polygons"), len(poly_buffer)))
log.warning("Joining %d polygons." % buff_length)
self.app.inform.emit('%s: %d.' % (_("Gerber processing. Joining polygons"), buff_length))
if self.use_buffer_for_union:
log.debug("Union by buffer...")
@ -1462,7 +1525,7 @@ class Gerber(Geometry):
# it use a filled bounding box polygon to which add clear polygons (negative) to isolate the copper
# features
if self.app.defaults['gerber_extra_buffering']:
candidate_geo = list()
candidate_geo = []
try:
for p in self.solid_geometry:
candidate_geo.append(p.buffer(-0.0000001))
@ -1714,7 +1777,7 @@ class Gerber(Geometry):
# Add to object
if self.solid_geometry is None:
self.solid_geometry = list()
self.solid_geometry = []
# if type(self.solid_geometry) == list:
# if type(geos) == list:
@ -1726,8 +1789,13 @@ class Gerber(Geometry):
if type(geos) == list:
# HACK for importing QRCODE exported by FlatCAM
if len(geos) == 1:
geo_qrcode = list()
try:
geos_length = len(geos)
except TypeError:
geos_length = 1
if geos_length == 1:
geo_qrcode = []
geo_qrcode.append(Polygon(geos[0].exterior))
for i_el in geos[0].interiors:
geo_qrcode.append(Polygon(i_el).buffer(0))
@ -1754,13 +1822,13 @@ class Gerber(Geometry):
self.solid_geometry = [self.solid_geometry]
if '0' not in self.apertures:
self.apertures['0'] = dict()
self.apertures['0'] = {}
self.apertures['0']['type'] = 'REG'
self.apertures['0']['size'] = 0.0
self.apertures['0']['geometry'] = list()
self.apertures['0']['geometry'] = []
for pol in self.solid_geometry:
new_el = dict()
new_el = {}
new_el['solid'] = pol
new_el['follow'] = pol.exterior
self.apertures['0']['geometry'].append(deepcopy(new_el))
@ -1849,10 +1917,10 @@ class Gerber(Geometry):
# we need to scale the geometry stored in the Gerber apertures, too
try:
for apid in self.apertures:
new_geometry = list()
new_geometry = []
if 'geometry' in self.apertures[apid]:
for geo_el in self.apertures[apid]['geometry']:
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geo_el:
new_geo_el['solid'] = scale_geom(geo_el['solid'])
if 'follow' in geo_el:
@ -2245,10 +2313,10 @@ class Gerber(Geometry):
# we need to buffer the geometry stored in the Gerber apertures, too
try:
for apid in self.apertures:
new_geometry = list()
new_geometry = []
if 'geometry' in self.apertures[apid]:
for geo_el in self.apertures[apid]['geometry']:
new_geo_el = dict()
new_geo_el = {}
if 'solid' in geo_el:
new_geo_el['solid'] = buffer_geom(geo_el['solid'])
if 'follow' in geo_el:
@ -2296,10 +2364,10 @@ class Gerber(Geometry):
except KeyError:
pass
new_geometry = list()
new_geometry = []
if 'geometry' in self.apertures[apid]:
for geo_el in self.apertures[apid]['geometry']:
new_geo_el = dict()
new_geo_el = {}
if 'follow' in geo_el:
new_geo_el['follow'] = geo_el['follow']
size = float(self.apertures[apid]['size'])
@ -2337,7 +2405,7 @@ class Gerber(Geometry):
return 'fail'
# make the new solid_geometry
new_solid_geo = list()
new_solid_geo = []
for apid in self.apertures:
if 'geometry' in self.apertures[apid]:
new_solid_geo += [geo_el['solid'] for geo_el in self.apertures[apid]['geometry']]

View File

@ -49,9 +49,9 @@ class HPGL2:
self.units = 'MM'
# storage for the tools
self.tools = dict()
self.tools = {}
self.default_data = dict()
self.default_data = {}
self.default_data.update({
"name": '_ncc',
"plot": self.app.defaults["geometry_plot"],
@ -72,6 +72,8 @@ class HPGL2:
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangez": self.app.defaults["geometry_toolchangez"],
"endz": self.app.defaults["geometry_endz"],
"endxy": self.app.defaults["geometry_endxy"],
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"startz": self.app.defaults["geometry_startz"],
@ -151,7 +153,7 @@ class HPGL2:
"""
# Coordinates of the current path, each is [x, y]
path = list()
path = []
geo_buffer = []
@ -207,7 +209,7 @@ class HPGL2:
match = self.sp_re.search(gline)
if match:
tool = match.group(1)
# self.tools[tool] = dict()
# self.tools[tool] = {}
self.tools.update({
tool: {
'tooldia': float('%.*f' %

View File

@ -21,7 +21,7 @@
# import xml.etree.ElementTree as ET
from svg.path import Line, Arc, CubicBezier, QuadraticBezier, parse_path
from svg.path.path import Move
from svg.path.path import Move, Close
from shapely.geometry import LineString, LinearRing, MultiLineString
from shapely.affinity import skew, affine_transform, rotate
import numpy as np
@ -69,6 +69,7 @@ def path2shapely(path, object_type, res=1.0):
geometry = []
geo_element = None
rings = []
closed = False
for component in path:
# Line
@ -88,7 +89,8 @@ def path2shapely(path, object_type, res=1.0):
# How many points to use in the discrete representation.
length = component.length(res / 10.0)
steps = int(length / res + 0.5)
# steps = int(length / res + 0.5)
steps = int(length) * 2
# solve error when step is below 1,
# it may cause other problems, but LineString needs at least two points
@ -109,11 +111,29 @@ def path2shapely(path, object_type, res=1.0):
# Move
if isinstance(component, Move):
if not points:
continue
else:
rings.append(points)
if closed is False:
points = []
else:
closed = False
start = component.start
x, y = start.real, start.imag
points = [(x, y)]
continue
closed = False
# Close
if isinstance(component, Close):
if not points:
continue
else:
rings.append(points)
points = []
closed = True
continue
log.warning("I don't know what this is: %s" % str(component))
continue
@ -122,8 +142,12 @@ def path2shapely(path, object_type, res=1.0):
if points:
rings.append(points)
try:
rings = MultiLineString(rings)
except Exception as e:
log.debug("ParseSVG.path2shapely() MString --> %s" % str(e))
return None
rings = MultiLineString(rings)
if len(rings) > 0:
if len(rings) == 1 and not isinstance(rings, MultiLineString):
# Polygons are closed and require more than 2 points
@ -135,11 +159,14 @@ def path2shapely(path, object_type, res=1.0):
try:
geo_element = Polygon(rings[0], rings[1:])
except Exception:
coords = list()
coords = []
for line in rings:
coords.append(line.coords[0])
coords.append(line.coords[1])
geo_element = Polygon(coords)
try:
geo_element = Polygon(coords)
except Exception:
geo_element = LineString(coords)
geometry.append(geo_element)
return geometry
@ -305,7 +332,7 @@ def getsvggeo(node, object_type, root=None):
root = node
kind = re.search('(?:\{.*\})?(.*)$', node.tag).group(1)
geo = list()
geo = []
# Recurse
if len(node) > 0:

View File

@ -50,94 +50,86 @@ class AlignObjects(FlatCAMTool):
""")
self.layout.addWidget(title_label)
self.layout.addWidget(QtWidgets.QLabel(''))
# Form Layout
grid0 = QtWidgets.QGridLayout()
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
self.layout.addLayout(grid0)
self.aligned_label = QtWidgets.QLabel('<b>%s</b>' % _("Selection of the WORKING object"))
self.aligned_label = QtWidgets.QLabel('<b>%s:</b>' % _("MOVING object"))
grid0.addWidget(self.aligned_label, 0, 0, 1, 2)
# Type of object to be aligned
self.type_obj_combo = FCComboBox()
self.type_obj_combo.addItem("Gerber")
self.type_obj_combo.addItem("Excellon")
self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.type_obj_combo_label.setToolTip(
self.aligned_label.setToolTip(
_("Specify the type of object to be aligned.\n"
"It can be of type: Gerber or Excellon.\n"
"The selection here decide the type of objects that will be\n"
"in the Object combobox.")
)
grid0.addWidget(self.type_obj_combo_label, 2, 0)
grid0.addWidget(self.type_obj_combo, 2, 1)
# Type of object to be aligned
self.type_obj_radio = RadioSet([
{"label": _("Gerber"), "value": "grb"},
{"label": _("Excellon"), "value": "exc"},
], orientation='vertical', stretch=False)
grid0.addWidget(self.type_obj_radio, 3, 0, 1, 2)
# Object to be aligned
self.object_combo = FCComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
self.object_combo.is_last = True
self.object_label = QtWidgets.QLabel('%s:' % _("Object"))
self.object_label.setToolTip(
self.object_combo.setToolTip(
_("Object to be aligned.")
)
grid0.addWidget(self.object_label, 3, 0)
grid0.addWidget(self.object_combo, 3, 1)
grid0.addWidget(self.object_combo, 4, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 4, 0, 1, 2)
grid0.addWidget(separator_line, 5, 0, 1, 2)
self.aligned_label = QtWidgets.QLabel('<b>%s</b>' % _("Selection of the TARGET object"))
grid0.addWidget(QtWidgets.QLabel(''), 6, 0, 1, 2)
self.aligned_label = QtWidgets.QLabel('<b>%s:</b>' % _("TARGET object"))
self.aligned_label.setToolTip(
_("Object to which the other objects will be aligned to (moved to).")
)
grid0.addWidget(self.aligned_label, 6, 0, 1, 2)
# Type of object to be aligned to = aligner
self.type_aligner_obj_combo = FCComboBox()
self.type_aligner_obj_combo.addItem("Gerber")
self.type_aligner_obj_combo.addItem("Excellon")
self.type_aligner_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.type_aligner_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
self.type_aligner_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.type_aligner_obj_combo_label.setToolTip(
_("Specify the type of object to be aligned to.\n"
"It can be of type: Gerber or Excellon.\n"
"The selection here decide the type of objects that will be\n"
"in the Object combobox.")
)
grid0.addWidget(self.type_aligner_obj_combo_label, 7, 0)
grid0.addWidget(self.type_aligner_obj_combo, 7, 1)
grid0.addWidget(self.aligned_label, 7, 0, 1, 2)
# Type of object to be aligned to = aligner
self.type_aligner_obj_radio = RadioSet([
{"label": _("Gerber"), "value": "grb"},
{"label": _("Excellon"), "value": "exc"},
], orientation='vertical', stretch=False)
grid0.addWidget(self.type_aligner_obj_radio, 8, 0, 1, 2)
# Object to be aligned to = aligner
self.aligner_object_combo = FCComboBox()
self.aligner_object_combo.setModel(self.app.collection)
self.aligner_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.aligner_object_combo.setCurrentIndex(1)
self.aligner_object_combo.is_last = True
self.aligner_object_label = QtWidgets.QLabel('%s:' % _("Object"))
self.aligner_object_label.setToolTip(
self.aligner_object_combo.setToolTip(
_("Object to be aligned to. Aligner.")
)
grid0.addWidget(self.aligner_object_label, 8, 0)
grid0.addWidget(self.aligner_object_combo, 8, 1)
grid0.addWidget(self.aligner_object_combo, 9, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 9, 0, 1, 2)
grid0.addWidget(separator_line, 10, 0, 1, 2)
grid0.addWidget(QtWidgets.QLabel(''), 11, 0, 1, 2)
# Alignment Type
self.a_type_lbl = QtWidgets.QLabel('<b>%s:</b>' % _("Alignment Type"))
@ -151,17 +143,17 @@ class AlignObjects(FlatCAMTool):
{'label': _('Single Point'), 'value': 'sp'},
{'label': _('Dual Point'), 'value': 'dp'}
],
orientation='horizontal',
orientation='vertical',
stretch=False
)
grid0.addWidget(self.a_type_lbl, 10, 0, 1, 2)
grid0.addWidget(self.a_type_radio, 11, 0, 1, 2)
grid0.addWidget(self.a_type_lbl, 12, 0, 1, 2)
grid0.addWidget(self.a_type_radio, 13, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 12, 0, 1, 2)
grid0.addWidget(separator_line, 14, 0, 1, 2)
# Buttons
self.align_object_button = QtWidgets.QPushButton(_("Align Object"))
@ -195,8 +187,8 @@ class AlignObjects(FlatCAMTool):
# Signals
self.align_object_button.clicked.connect(self.on_align)
self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
self.type_aligner_obj_combo.currentIndexChanged.connect(self.on_type_aligner_index_changed)
self.type_obj_radio.activated_custom.connect(self.on_type_obj_changed)
self.type_aligner_obj_radio.activated_custom.connect(self.on_type_aligner_changed)
self.reset_button.clicked.connect(self.set_tool_ui)
self.mr = None
@ -214,7 +206,7 @@ class AlignObjects(FlatCAMTool):
self.target_obj = None
# here store the alignment points
self.clicked_points = list()
self.clicked_points = []
self.align_type = None
@ -257,7 +249,7 @@ class AlignObjects(FlatCAMTool):
def set_tool_ui(self):
self.reset_fields()
self.clicked_points = list()
self.clicked_points = []
self.target_obj = None
self.aligned_obj = None
self.aligner_obj = None
@ -268,19 +260,23 @@ class AlignObjects(FlatCAMTool):
self.aligned_old_line_color = None
self.a_type_radio.set_value(self.app.defaults["tools_align_objects_align_type"])
self.type_obj_radio.set_value('grb')
self.type_aligner_obj_radio.set_value('grb')
if self.local_connected is True:
self.disconnect_cal_events()
def on_type_obj_index_changed(self):
obj_type = self.type_obj_combo.currentIndex()
def on_type_obj_changed(self, val):
obj_type = {'grb': 0, 'exc': 1}[val]
self.object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(0)
self.object_combo.obj_type = {'grb': "Gerber", 'exc': "Excellon"}[val]
def on_type_aligner_index_changed(self):
obj_type = self.type_aligner_obj_combo.currentIndex()
def on_type_aligner_changed(self, val):
obj_type = {'grb': 0, 'exc': 1}[val]
self.aligner_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.aligner_object_combo.setCurrentIndex(0)
self.aligner_object_combo.obj_type = {'grb': "Gerber", 'exc': "Excellon"}[val]
def on_align(self):
self.app.delete_selection_shape()
@ -379,7 +375,7 @@ class AlignObjects(FlatCAMTool):
elif event.button == right_button and self.app.event_is_dragging is False:
self.reset_color()
self.clicked_points = list()
self.clicked_points = []
self.disconnect_cal_events()
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Cancelled by user request."))

View File

@ -92,7 +92,7 @@ class ToolCalculator(FlatCAMTool):
self.layout.addLayout(form_layout)
self.tipDia_label = QtWidgets.QLabel('%s:' % _("Tip Diameter"))
self.tipDia_entry = FCDoubleSpinner()
self.tipDia_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.tipDia_entry.set_precision(self.decimals)
self.tipDia_entry.set_range(0.0, 9999.9999)
self.tipDia_entry.setSingleStep(0.1)
@ -103,16 +103,16 @@ class ToolCalculator(FlatCAMTool):
"It is specified by manufacturer.")
)
self.tipAngle_label = QtWidgets.QLabel('%s:' % _("Tip Angle"))
self.tipAngle_entry = FCSpinner()
self.tipAngle_entry = FCSpinner(callback=self.confirmation_message_int)
self.tipAngle_entry.set_range(0,180)
self.tipAngle_entry.setSingleStep(5)
self.tipAngle_entry.set_step(5)
# self.tipAngle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.tipAngle_label.setToolTip(_("This is the angle of the tip of the tool.\n"
"It is specified by manufacturer."))
self.cutDepth_label = QtWidgets.QLabel('%s:' % _("Cut Z"))
self.cutDepth_entry = FCDoubleSpinner()
self.cutDepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.cutDepth_entry.set_range(-9999.9999, 9999.9999)
self.cutDepth_entry.set_precision(self.decimals)
@ -121,7 +121,7 @@ class ToolCalculator(FlatCAMTool):
"In the CNCJob is the CutZ parameter."))
self.effectiveToolDia_label = QtWidgets.QLabel('%s:' % _("Tool Diameter"))
self.effectiveToolDia_entry = FCDoubleSpinner()
self.effectiveToolDia_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.effectiveToolDia_entry.set_precision(self.decimals)
# self.effectiveToolDia_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
@ -165,7 +165,7 @@ class ToolCalculator(FlatCAMTool):
self.layout.addLayout(plate_form_layout)
self.pcblengthlabel = QtWidgets.QLabel('%s:' % _("Board Length"))
self.pcblength_entry = FCDoubleSpinner()
self.pcblength_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.pcblength_entry.set_precision(self.decimals)
self.pcblength_entry.set_range(0.0, 9999.9999)
@ -173,7 +173,7 @@ class ToolCalculator(FlatCAMTool):
self.pcblengthlabel.setToolTip(_('This is the board length. In centimeters.'))
self.pcbwidthlabel = QtWidgets.QLabel('%s:' % _("Board Width"))
self.pcbwidth_entry = FCDoubleSpinner()
self.pcbwidth_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.pcbwidth_entry.set_precision(self.decimals)
self.pcbwidth_entry.set_range(0.0, 9999.9999)
@ -181,7 +181,7 @@ class ToolCalculator(FlatCAMTool):
self.pcbwidthlabel.setToolTip(_('This is the board width.In centimeters.'))
self.cdensity_label = QtWidgets.QLabel('%s:' % _("Current Density"))
self.cdensity_entry = FCDoubleSpinner()
self.cdensity_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.cdensity_entry.set_precision(self.decimals)
self.cdensity_entry.set_range(0.0, 9999.9999)
self.cdensity_entry.setSingleStep(0.1)
@ -191,7 +191,7 @@ class ToolCalculator(FlatCAMTool):
"In Amps per Square Feet ASF."))
self.growth_label = QtWidgets.QLabel('%s:' % _("Copper Growth"))
self.growth_entry = FCDoubleSpinner()
self.growth_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.growth_entry.set_precision(self.decimals)
self.growth_entry.set_range(0.0, 9999.9999)
self.growth_entry.setSingleStep(0.01)
@ -203,7 +203,7 @@ class ToolCalculator(FlatCAMTool):
# self.growth_entry.setEnabled(False)
self.cvaluelabel = QtWidgets.QLabel('%s:' % _("Current Value"))
self.cvalue_entry = FCDoubleSpinner()
self.cvalue_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.cvalue_entry.set_precision(self.decimals)
self.cvalue_entry.set_range(0.0, 9999.9999)
self.cvalue_entry.setSingleStep(0.1)
@ -214,7 +214,7 @@ class ToolCalculator(FlatCAMTool):
self.cvalue_entry.setReadOnly(True)
self.timelabel = QtWidgets.QLabel('%s:' % _("Time"))
self.time_entry = FCDoubleSpinner()
self.time_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.time_entry.set_precision(self.decimals)
self.time_entry.set_range(0.0, 9999.9999)
self.time_entry.setSingleStep(0.1)
@ -242,6 +242,19 @@ class ToolCalculator(FlatCAMTool):
self.layout.addStretch()
# ## Reset Tool
self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.layout.addWidget(self.reset_button)
self.units = ''
# ## Signals
@ -255,6 +268,7 @@ class ToolCalculator(FlatCAMTool):
self.inch_entry.editingFinished.connect(self.on_calculate_mm_units)
self.calculate_plate_button.clicked.connect(self.on_calculate_eplate)
self.reset_button.clicked.connect(self.set_tool_ui)
def run(self, toggle=True):
self.app.report_usage("ToolCalculators()")

View File

@ -76,7 +76,7 @@ class ToolCalibration(FlatCAMTool):
_("Height (Z) for travelling between the points.")
)
self.travelz_entry = FCDoubleSpinner()
self.travelz_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.travelz_entry.set_range(-9999.9999, 9999.9999)
self.travelz_entry.set_precision(self.decimals)
self.travelz_entry.setSingleStep(0.1)
@ -90,7 +90,7 @@ class ToolCalibration(FlatCAMTool):
_("Height (Z) for checking the point.")
)
self.verz_entry = FCDoubleSpinner()
self.verz_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.verz_entry.set_range(-9999.9999, 9999.9999)
self.verz_entry.set_precision(self.decimals)
self.verz_entry.setSingleStep(0.1)
@ -113,7 +113,7 @@ class ToolCalibration(FlatCAMTool):
_("Height (Z) for mounting the verification probe.")
)
self.toolchangez_entry = FCDoubleSpinner()
self.toolchangez_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.toolchangez_entry.set_range(0.0000, 9999.9999)
self.toolchangez_entry.set_precision(self.decimals)
self.toolchangez_entry.setSingleStep(0.1)
@ -195,7 +195,6 @@ class ToolCalibration(FlatCAMTool):
self.obj_type_combo = FCComboBox()
self.obj_type_combo.addItem(_("Gerber"))
self.obj_type_combo.addItem(_("Excellon"))
self.obj_type_combo.setCurrentIndex(1)
self.obj_type_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.obj_type_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
@ -206,7 +205,7 @@ class ToolCalibration(FlatCAMTool):
self.object_combo = FCComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
self.object_combo.is_last = True
self.object_label = QtWidgets.QLabel("%s:" % _("Source object selection"))
self.object_label.setToolTip(
@ -263,8 +262,6 @@ class ToolCalibration(FlatCAMTool):
self.bottom_left_coordy_found = EvalEntry()
self.points_table.setCellWidget(row, 3, self.bottom_left_coordy_found)
self.bottom_left_coordx_found.set_value(_("Origin"))
self.bottom_left_coordy_found.set_value(_("Origin"))
self.bottom_left_coordx_found.setDisabled(True)
self.bottom_left_coordy_found.setDisabled(True)
row += 1
@ -471,7 +468,7 @@ class ToolCalibration(FlatCAMTool):
self.scalex_label.setToolTip(
_("Factor for Scale action over X axis.")
)
self.scalex_entry = FCDoubleSpinner()
self.scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.scalex_entry.set_range(0, 9999.9999)
self.scalex_entry.set_precision(self.decimals)
self.scalex_entry.setSingleStep(0.1)
@ -483,7 +480,7 @@ class ToolCalibration(FlatCAMTool):
self.scaley_label.setToolTip(
_("Factor for Scale action over Y axis.")
)
self.scaley_entry = FCDoubleSpinner()
self.scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.scaley_entry.set_range(0, 9999.9999)
self.scaley_entry.set_precision(self.decimals)
self.scaley_entry.setSingleStep(0.1)
@ -508,7 +505,7 @@ class ToolCalibration(FlatCAMTool):
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
)
self.skewx_entry = FCDoubleSpinner()
self.skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.skewx_entry.set_range(-360, 360)
self.skewx_entry.set_precision(self.decimals)
self.skewx_entry.setSingleStep(0.1)
@ -521,7 +518,7 @@ class ToolCalibration(FlatCAMTool):
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 359.")
)
self.skewy_entry = FCDoubleSpinner()
self.skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.skewy_entry.set_range(-360, 360)
self.skewy_entry.set_precision(self.decimals)
self.skewy_entry.setSingleStep(0.1)
@ -552,7 +549,7 @@ class ToolCalibration(FlatCAMTool):
# self.fin_scalex_label.setToolTip(
# _("Final factor for Scale action over X axis.")
# )
# self.fin_scalex_entry = FCDoubleSpinner()
# self.fin_scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.fin_scalex_entry.set_range(0, 9999.9999)
# self.fin_scalex_entry.set_precision(self.decimals)
# self.fin_scalex_entry.setSingleStep(0.1)
@ -564,7 +561,7 @@ class ToolCalibration(FlatCAMTool):
# self.fin_scaley_label.setToolTip(
# _("Final factor for Scale action over Y axis.")
# )
# self.fin_scaley_entry = FCDoubleSpinner()
# self.fin_scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.fin_scaley_entry.set_range(0, 9999.9999)
# self.fin_scaley_entry.set_precision(self.decimals)
# self.fin_scaley_entry.setSingleStep(0.1)
@ -577,7 +574,7 @@ class ToolCalibration(FlatCAMTool):
# _("Final value for angle for Skew action, in degrees.\n"
# "Float number between -360 and 359.")
# )
# self.fin_skewx_entry = FCDoubleSpinner()
# self.fin_skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.fin_skewx_entry.set_range(-360, 360)
# self.fin_skewx_entry.set_precision(self.decimals)
# self.fin_skewx_entry.setSingleStep(0.1)
@ -590,7 +587,7 @@ class ToolCalibration(FlatCAMTool):
# _("Final value for angle for Skew action, in degrees.\n"
# "Float number between -360 and 359.")
# )
# self.fin_skewy_entry = FCDoubleSpinner()
# self.fin_skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.fin_skewy_entry.set_range(-360, 360)
# self.fin_skewy_entry.set_precision(self.decimals)
# self.fin_skewy_entry.setSingleStep(0.1)
@ -630,18 +627,15 @@ class ToolCalibration(FlatCAMTool):
)
grid_lay.addWidget(step_5, 45, 0, 1, 3)
self.adj_object_type_combo = QtWidgets.QComboBox()
self.adj_object_type_combo = FCComboBox()
self.adj_object_type_combo.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
self.adj_object_type_combo.setCurrentIndex(0)
self.adj_object_type_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.adj_object_type_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
self.adj_object_type_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.adj_object_type_label = QtWidgets.QLabel("%s:" % _("Adjusted object type"))
self.adj_object_type_label.setToolTip(
_("Type of the FlatCAM Object to be adjusted.")
)
self.adj_object_type_label.setToolTip(_("Type of the FlatCAM Object to be adjusted."))
grid_lay.addWidget(self.adj_object_type_label, 46, 0, 1, 3)
grid_lay.addWidget(self.adj_object_type_combo, 47, 0, 1, 3)
@ -649,7 +643,10 @@ class ToolCalibration(FlatCAMTool):
self.adj_object_combo = FCComboBox()
self.adj_object_combo.setModel(self.app.collection)
self.adj_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.adj_object_combo.setCurrentIndex(0)
self.adj_object_combo.is_last = True
self.adj_object_combo.obj_type = {
_("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
}[self.adj_object_type_combo.get_value()]
self.adj_object_label = QtWidgets.QLabel("%s:" % _("Adjusted object selection"))
self.adj_object_label.setToolTip(
@ -770,6 +767,9 @@ class ToolCalibration(FlatCAMTool):
if self.local_connected is True:
self.disconnect_cal_events()
self.bottom_left_coordx_found.set_value(_("Origin"))
self.bottom_left_coordy_found.set_value(_("Origin"))
self.reset_calibration_points()
self.cal_source_radio.set_value(self.app.defaults['tools_cal_calsource'])
@ -786,6 +786,14 @@ class ToolCalibration(FlatCAMTool):
self.skewx_entry.set_value(0.0)
self.skewy_entry.set_value(0.0)
# default object selection is Excellon = index_1
self.obj_type_combo.setCurrentIndex(1)
self.on_obj_type_combo()
self.adj_object_type_combo.setCurrentIndex(0)
self.on_adj_obj_type_combo()
# self.adj_object_combo.setCurrentIndex(0)
# calibrated object
self.cal_object = None
@ -794,12 +802,18 @@ class ToolCalibration(FlatCAMTool):
def on_obj_type_combo(self):
obj_type = self.obj_type_combo.currentIndex()
self.object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(0)
# self.object_combo.setCurrentIndex(0)
self.object_combo.obj_type = {
_("Gerber"): "Gerber", _("Excellon"): "Excellon"
}[self.obj_type_combo.get_value()]
def on_adj_obj_type_combo(self):
obj_type = self.adj_object_type_combo.currentIndex()
self.adj_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.adj_object_combo.setCurrentIndex(0)
# self.adj_object_combo.setCurrentIndex(0)
self.adj_object_combo.obj_type = {
_("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
}[self.adj_object_type_combo.get_value()]
def on_cal_source_radio(self, val):
if val == 'object':
@ -925,7 +939,7 @@ class ToolCalibration(FlatCAMTool):
self.disconnect_cal_events()
def reset_calibration_points(self):
self.click_points = list()
self.click_points = []
self.bottom_left_coordx_tgt.set_value('')
self.bottom_left_coordy_tgt.set_value('')
@ -1275,7 +1289,7 @@ class ToolCalibration(FlatCAMTool):
if obj.tools:
obj_init.tools = deepcopy(obj.tools)
except Exception as ee:
log.debug("App.on_copy_object() --> %s" % str(ee))
log.debug("ToolCalibration.new_calibrated_object.initialize_geometry() --> %s" % str(ee))
obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))
@ -1301,7 +1315,7 @@ class ToolCalibration(FlatCAMTool):
if obj.tools:
obj_init.tools = deepcopy(obj.tools)
except Exception as err:
log.debug("App.on_copy_object() --> %s" % str(err))
log.debug("ToolCalibration.new_calibrated_object.initialize_gerber() --> %s" % str(err))
obj_init.scale(xfactor=scalex, yfactor=scaley, point=(origin_x, origin_y))
obj_init.skew(angle_x=skewx, angle_y=skewy, point=(origin_x, origin_y))

View File

@ -9,7 +9,7 @@ from PyQt5 import QtWidgets, QtCore
import FlatCAMApp
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, FCEntry, FCComboBox
from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMExcellon
import shapely.geometry.base as base
@ -66,10 +66,11 @@ class ToolCopperThieving(FlatCAMTool):
i_grid_lay.setColumnStretch(0, 0)
i_grid_lay.setColumnStretch(1, 1)
self.grb_object_combo = QtWidgets.QComboBox()
self.grb_object_combo = FCComboBox()
self.grb_object_combo.setModel(self.app.collection)
self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.grb_object_combo.setCurrentIndex(1)
self.grb_object_combo.is_last = True
self.grb_object_combo.obj_type = 'Gerber'
self.grbobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.grbobj_label.setToolTip(
@ -99,7 +100,7 @@ class ToolCopperThieving(FlatCAMTool):
"(the polygon fill may be split in multiple polygons)\n"
"and the copper traces in the Gerber file.")
)
self.clearance_entry = FCDoubleSpinner()
self.clearance_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_entry.set_range(0.00001, 9999.9999)
self.clearance_entry.set_precision(self.decimals)
self.clearance_entry.setSingleStep(0.1)
@ -112,7 +113,7 @@ class ToolCopperThieving(FlatCAMTool):
self.margin_label.setToolTip(
_("Bounding box margin.")
)
self.margin_entry = FCDoubleSpinner()
self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.margin_entry.set_range(0.0, 9999.9999)
self.margin_entry.set_precision(self.decimals)
self.margin_entry.setSingleStep(0.1)
@ -135,35 +136,36 @@ class ToolCopperThieving(FlatCAMTool):
grid_lay.addWidget(self.reference_label, 3, 0)
grid_lay.addWidget(self.reference_radio, 3, 1)
self.box_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
self.box_combo_type_label.setToolTip(
self.ref_combo_type_label = QtWidgets.QLabel('%s:' % _("Ref. Type"))
self.ref_combo_type_label.setToolTip(
_("The type of FlatCAM object to be used as copper thieving reference.\n"
"It can be Gerber, Excellon or Geometry.")
)
self.box_combo_type = QtWidgets.QComboBox()
self.box_combo_type.addItem(_("Reference Gerber"))
self.box_combo_type.addItem(_("Reference Excellon"))
self.box_combo_type.addItem(_("Reference Geometry"))
self.ref_combo_type = FCComboBox()
self.ref_combo_type.addItems([_("Gerber"), _("Excellon"), _("Geometry")])
grid_lay.addWidget(self.box_combo_type_label, 4, 0)
grid_lay.addWidget(self.box_combo_type, 4, 1)
grid_lay.addWidget(self.ref_combo_type_label, 4, 0)
grid_lay.addWidget(self.ref_combo_type, 4, 1)
self.box_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))
self.box_combo_label.setToolTip(
self.ref_combo_label = QtWidgets.QLabel('%s:' % _("Ref. Object"))
self.ref_combo_label.setToolTip(
_("The FlatCAM object to be used as non copper clearing reference.")
)
self.box_combo = QtWidgets.QComboBox()
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
self.ref_combo = FCComboBox()
self.ref_combo.setModel(self.app.collection)
self.ref_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.ref_combo.is_last = True
self.ref_combo.obj_type = {
_("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
}[self.ref_combo_type.get_value()]
grid_lay.addWidget(self.box_combo_label, 5, 0)
grid_lay.addWidget(self.box_combo, 5, 1)
grid_lay.addWidget(self.ref_combo_label, 5, 0)
grid_lay.addWidget(self.ref_combo, 5, 1)
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
self.ref_combo.hide()
self.ref_combo_label.hide()
self.ref_combo_type.hide()
self.ref_combo_type_label.hide()
# Bounding Box Type #
self.bbox_type_radio = RadioSet([
@ -221,7 +223,7 @@ class ToolCopperThieving(FlatCAMTool):
self.dotdia_label.setToolTip(
_("Dot diameter in Dots Grid.")
)
self.dot_dia_entry = FCDoubleSpinner()
self.dot_dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.dot_dia_entry.set_range(0.0, 9999.9999)
self.dot_dia_entry.set_precision(self.decimals)
self.dot_dia_entry.setSingleStep(0.1)
@ -234,7 +236,7 @@ class ToolCopperThieving(FlatCAMTool):
self.dotspacing_label.setToolTip(
_("Distance between each two dots in Dots Grid.")
)
self.dot_spacing_entry = FCDoubleSpinner()
self.dot_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.dot_spacing_entry.set_range(0.0, 9999.9999)
self.dot_spacing_entry.set_precision(self.decimals)
self.dot_spacing_entry.setSingleStep(0.1)
@ -261,7 +263,7 @@ class ToolCopperThieving(FlatCAMTool):
self.square_size_label.setToolTip(
_("Square side size in Squares Grid.")
)
self.square_size_entry = FCDoubleSpinner()
self.square_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.square_size_entry.set_range(0.0, 9999.9999)
self.square_size_entry.set_precision(self.decimals)
self.square_size_entry.setSingleStep(0.1)
@ -274,7 +276,7 @@ class ToolCopperThieving(FlatCAMTool):
self.squares_spacing_label.setToolTip(
_("Distance between each two squares in Squares Grid.")
)
self.squares_spacing_entry = FCDoubleSpinner()
self.squares_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.squares_spacing_entry.set_range(0.0, 9999.9999)
self.squares_spacing_entry.set_precision(self.decimals)
self.squares_spacing_entry.setSingleStep(0.1)
@ -301,7 +303,7 @@ class ToolCopperThieving(FlatCAMTool):
self.line_size_label.setToolTip(
_("Line thickness size in Lines Grid.")
)
self.line_size_entry = FCDoubleSpinner()
self.line_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.line_size_entry.set_range(0.0, 9999.9999)
self.line_size_entry.set_precision(self.decimals)
self.line_size_entry.setSingleStep(0.1)
@ -314,7 +316,7 @@ class ToolCopperThieving(FlatCAMTool):
self.lines_spacing_label.setToolTip(
_("Distance between each two lines in Lines Grid.")
)
self.lines_spacing_entry = FCDoubleSpinner()
self.lines_spacing_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.lines_spacing_entry.set_range(0.0, 9999.9999)
self.lines_spacing_entry.set_precision(self.decimals)
self.lines_spacing_entry.setSingleStep(0.1)
@ -362,7 +364,7 @@ class ToolCopperThieving(FlatCAMTool):
self.rb_margin_label.setToolTip(
_("Bounding box margin for robber bar.")
)
self.rb_margin_entry = FCDoubleSpinner()
self.rb_margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.rb_margin_entry.set_range(-9999.9999, 9999.9999)
self.rb_margin_entry.set_precision(self.decimals)
self.rb_margin_entry.setSingleStep(0.1)
@ -375,7 +377,7 @@ class ToolCopperThieving(FlatCAMTool):
self.rb_thickness_label.setToolTip(
_("The robber bar thickness.")
)
self.rb_thickness_entry = FCDoubleSpinner()
self.rb_thickness_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.rb_thickness_entry.set_range(0.0000, 9999.9999)
self.rb_thickness_entry.set_precision(self.decimals)
self.rb_thickness_entry.setSingleStep(0.1)
@ -417,10 +419,11 @@ class ToolCopperThieving(FlatCAMTool):
"the pattern plating mask.")
)
self.sm_object_combo = QtWidgets.QComboBox()
self.sm_object_combo = FCComboBox()
self.sm_object_combo.setModel(self.app.collection)
self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sm_object_combo.setCurrentIndex(1)
self.sm_object_combo.is_last = True
self.sm_object_combo.obj_type = 'Gerber'
grid_lay_1.addWidget(self.sm_obj_label, 7, 0, 1, 3)
grid_lay_1.addWidget(self.sm_object_combo, 8, 0, 1, 3)
@ -431,7 +434,7 @@ class ToolCopperThieving(FlatCAMTool):
_("The distance between the possible copper thieving elements\n"
"and/or robber bar and the actual openings in the mask.")
)
self.clearance_ppm_entry = FCDoubleSpinner()
self.clearance_ppm_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_ppm_entry.set_range(-9999.9999, 9999.9999)
self.clearance_ppm_entry.set_precision(self.decimals)
self.clearance_ppm_entry.setSingleStep(0.1)
@ -494,11 +497,11 @@ class ToolCopperThieving(FlatCAMTool):
# Objects involved in Copper thieving
self.grb_object = None
self.ref_obj = None
self.sel_rect = list()
self.sel_rect = []
self.sm_object = None
# store the flattened geometry here:
self.flat_geometry = list()
self.flat_geometry = []
# Events ID
self.mr = None
@ -517,7 +520,7 @@ class ToolCopperThieving(FlatCAMTool):
self.geo_steps_per_circle = 128
# Thieving geometry storage
self.new_solid_geometry = list()
self.new_solid_geometry = []
# Robber bar geometry storage
self.robber_geo = None
@ -526,7 +529,7 @@ class ToolCopperThieving(FlatCAMTool):
self.rb_thickness = None
# SIGNALS
self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type)
self.ref_combo_type.currentIndexChanged.connect(self.on_ref_combo_type_change)
self.reference_radio.group_toggle_fn = self.on_toggle_reference
self.fill_type_radio.activated_custom.connect(self.on_thieving_type)
@ -594,22 +597,25 @@ class ToolCopperThieving(FlatCAMTool):
self.robber_line = None
self.new_solid_geometry = None
def on_combo_box_type(self):
obj_type = self.box_combo_type.currentIndex()
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(0)
def on_ref_combo_type_change(self):
obj_type = self.ref_combo_type.currentIndex()
self.ref_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.ref_combo.setCurrentIndex(0)
self.ref_combo.obj_type = {
_("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
}[self.ref_combo_type.get_value()]
def on_toggle_reference(self):
if self.reference_radio.get_value() == "itself" or self.reference_radio.get_value() == "area":
self.box_combo.hide()
self.box_combo_label.hide()
self.box_combo_type.hide()
self.box_combo_type_label.hide()
self.ref_combo.hide()
self.ref_combo_label.hide()
self.ref_combo_type.hide()
self.ref_combo_type_label.hide()
else:
self.box_combo.show()
self.box_combo_label.show()
self.box_combo_type.show()
self.box_combo_type_label.show()
self.ref_combo.show()
self.ref_combo_label.show()
self.ref_combo_type.show()
self.ref_combo_type_label.show()
if self.reference_radio.get_value() == "itself":
self.bbox_type_label.show()
@ -681,7 +687,7 @@ class ToolCopperThieving(FlatCAMTool):
break
if aperture_found:
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = self.robber_geo
geo_elem['follow'] = self.robber_line
self.grb_object.apertures[aperture_found]['geometry'].append(deepcopy(geo_elem))
@ -692,19 +698,19 @@ class ToolCopperThieving(FlatCAMTool):
else:
new_apid = '10'
self.grb_object.apertures[new_apid] = dict()
self.grb_object.apertures[new_apid] = {}
self.grb_object.apertures[new_apid]['type'] = 'C'
self.grb_object.apertures[new_apid]['size'] = self.rb_thickness
self.grb_object.apertures[new_apid]['geometry'] = list()
self.grb_object.apertures[new_apid]['geometry'] = []
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = self.robber_geo
geo_elem['follow'] = self.robber_line
self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
geo_obj = self.grb_object.solid_geometry
if isinstance(geo_obj, MultiPolygon):
s_list = list()
s_list = []
for pol in geo_obj.geoms:
s_list.append(pol)
s_list.append(self.robber_geo)
@ -778,7 +784,7 @@ class ToolCopperThieving(FlatCAMTool):
self.mm = self.app.plotcanvas.graph_event_connect('mouse_move', self.on_mouse_move)
elif reference_method == 'box':
bound_obj_name = self.box_combo.currentText()
bound_obj_name = self.ref_combo.currentText()
# Get reference object.
try:
@ -1127,7 +1133,7 @@ class ToolCopperThieving(FlatCAMTool):
if fill_type == 'dot' or fill_type == 'square':
# build the MultiPolygon of dots/squares that will fill the entire bounding box
thieving_list = list()
thieving_list = []
if fill_type == 'dot':
radius = dot_dia / 2.0
@ -1169,7 +1175,7 @@ class ToolCopperThieving(FlatCAMTool):
except TypeError:
thieving_box_geo = [thieving_box_geo]
thieving_geo = list()
thieving_geo = []
for dot_geo in thieving_box_geo:
for geo_t in app_obj.new_solid_geometry:
if dot_geo.within(geo_t):
@ -1212,7 +1218,7 @@ class ToolCopperThieving(FlatCAMTool):
app_obj.app.proc_container.update_view_text(' %s' % _("Buffering"))
outline_geometry = unary_union(outline_geometry)
outline_line = list()
outline_line = []
try:
for geo_o in outline_geometry:
outline_line.append(
@ -1238,7 +1244,7 @@ class ToolCopperThieving(FlatCAMTool):
)
bx0, by0, bx1, by1 = box_outline_geo.bounds
thieving_lines_geo = list()
thieving_lines_geo = []
new_x = bx0
new_y = by0
while new_x <= x1 - half_thick_line:
@ -1258,7 +1264,7 @@ class ToolCopperThieving(FlatCAMTool):
new_y += line_size + line_spacing
# merge everything together
diff_lines_geo = list()
diff_lines_geo = []
for line_poly in thieving_lines_geo:
rest_line = line_poly.difference(clearance_geometry)
diff_lines_geo.append(rest_line)
@ -1271,8 +1277,8 @@ class ToolCopperThieving(FlatCAMTool):
geo_list = list(app_obj.grb_object.solid_geometry.geoms)
if '0' not in app_obj.grb_object.apertures:
app_obj.grb_object.apertures['0'] = dict()
app_obj.grb_object.apertures['0']['geometry'] = list()
app_obj.grb_object.apertures['0'] = {}
app_obj.grb_object.apertures['0']['geometry'] = []
app_obj.grb_object.apertures['0']['type'] = 'REG'
app_obj.grb_object.apertures['0']['size'] = 0.0
@ -1282,7 +1288,7 @@ class ToolCopperThieving(FlatCAMTool):
geo_list.append(poly)
# append into the '0' aperture
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = poly
geo_elem['follow'] = poly.exterior
app_obj.grb_object.apertures['0']['geometry'].append(deepcopy(geo_elem))
@ -1291,7 +1297,7 @@ class ToolCopperThieving(FlatCAMTool):
geo_list.append(app_obj.new_solid_geometry)
# append into the '0' aperture
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = app_obj.new_solid_geometry
geo_elem['follow'] = app_obj.new_solid_geometry.exterior
app_obj.grb_object.apertures['0']['geometry'].append(deepcopy(geo_elem))
@ -1350,7 +1356,7 @@ class ToolCopperThieving(FlatCAMTool):
# if the clearance is negative apply it to the original soldermask too
if ppm_clearance < 0:
temp_geo_list = list()
temp_geo_list = []
for geo in geo_list:
temp_geo_list.append(geo.buffer(ppm_clearance))
geo_list = temp_geo_list
@ -1372,11 +1378,11 @@ class ToolCopperThieving(FlatCAMTool):
def obj_init(grb_obj, app_obj):
grb_obj.multitool = False
grb_obj.source_file = list()
grb_obj.source_file = []
grb_obj.multigeo = False
grb_obj.follow = False
grb_obj.apertures = dict()
grb_obj.solid_geometry = list()
grb_obj.apertures = {}
grb_obj.solid_geometry = []
# try:
# grb_obj.options['xmin'] = 0
@ -1389,8 +1395,8 @@ class ToolCopperThieving(FlatCAMTool):
# if we have copper thieving geometry, add it
if thieving_solid_geo:
if '0' not in grb_obj.apertures:
grb_obj.apertures['0'] = dict()
grb_obj.apertures['0']['geometry'] = list()
grb_obj.apertures['0'] = {}
grb_obj.apertures['0']['geometry'] = []
grb_obj.apertures['0']['type'] = 'REG'
grb_obj.apertures['0']['size'] = 0.0
@ -1402,7 +1408,7 @@ class ToolCopperThieving(FlatCAMTool):
geo_list.append(poly_b)
# append into the '0' aperture
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = poly_b
geo_elem['follow'] = poly_b.exterior
grb_obj.apertures['0']['geometry'].append(deepcopy(geo_elem))
@ -1411,7 +1417,7 @@ class ToolCopperThieving(FlatCAMTool):
geo_list.append(thieving_solid_geo.buffer(ppm_clearance))
# append into the '0' aperture
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = thieving_solid_geo.buffer(ppm_clearance)
geo_elem['follow'] = thieving_solid_geo.buffer(ppm_clearance).exterior
grb_obj.apertures['0']['geometry'].append(deepcopy(geo_elem))
@ -1425,7 +1431,7 @@ class ToolCopperThieving(FlatCAMTool):
break
if aperture_found:
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = robber_solid_geo
geo_elem['follow'] = robber_line
grb_obj.apertures[aperture_found]['geometry'].append(deepcopy(geo_elem))
@ -1437,12 +1443,12 @@ class ToolCopperThieving(FlatCAMTool):
else:
new_apid = '10'
grb_obj.apertures[new_apid] = dict()
grb_obj.apertures[new_apid] = {}
grb_obj.apertures[new_apid]['type'] = 'C'
grb_obj.apertures[new_apid]['size'] = rb_thickness + ppm_clearance
grb_obj.apertures[new_apid]['geometry'] = list()
grb_obj.apertures[new_apid]['geometry'] = []
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = robber_solid_geo.buffer(ppm_clearance)
geo_elem['follow'] = Polygon(robber_line).buffer(ppm_clearance / 2.0).exterior
grb_obj.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
@ -1510,7 +1516,7 @@ class ToolCopperThieving(FlatCAMTool):
self.grb_object = None
self.sm_object = None
self.ref_obj = None
self.sel_rect = list()
self.sel_rect = []
# Events ID
self.mr = None

View File

@ -7,7 +7,7 @@
from PyQt5 import QtWidgets, QtGui, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, OptionalInputSection
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, RadioSet, FCComboBox, OptionalInputSection, FCButton
from FlatCAMObj import FlatCAMGerber
from shapely.geometry import box, MultiPolygon, Polygon, LineString, LinearRing
@ -59,49 +59,21 @@ class CutOut(FlatCAMTool):
""")
self.layout.addWidget(title_label)
self.layout.addWidget(QtWidgets.QLabel(''))
# Form Layout
grid0 = QtWidgets.QGridLayout()
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
self.layout.addLayout(grid0)
# Type of object to be cutout
self.type_obj_combo = QtWidgets.QComboBox()
self.type_obj_combo.addItem("Gerber")
self.type_obj_combo.addItem("Excellon")
self.type_obj_combo.addItem("Geometry")
# we get rid of item1 ("Excellon") as it is not suitable for creating film
self.type_obj_combo.view().setRowHidden(1, True)
self.type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
# self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be cutout.\n"
"It can be of type: Gerber or Geometry.\n"
"What is selected here will dictate the kind\n"
"of objects that will populate the 'Object' combobox.")
)
self.type_obj_combo_label.setMinimumWidth(60)
grid0.addWidget(self.type_obj_combo_label, 0, 0)
grid0.addWidget(self.type_obj_combo, 0, 1)
self.object_label = QtWidgets.QLabel('<b>%s:</b>' % _("Object to be cutout"))
self.object_label = QtWidgets.QLabel('<b>%s:</b>' % _("Source Object"))
self.object_label.setToolTip('%s.' % _("Object to be cutout"))
# Object to be cutout
self.obj_combo = QtWidgets.QComboBox()
self.obj_combo.setModel(self.app.collection)
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(1)
grid0.addWidget(self.object_label, 1, 0, 1, 2)
grid0.addWidget(self.obj_combo, 2, 0, 1, 2)
grid0.addWidget(self.object_label, 0, 0, 1, 2)
# Object kind
self.kindlabel = QtWidgets.QLabel('%s:' % _('Object kind'))
self.kindlabel = QtWidgets.QLabel('%s:' % _('Kind'))
self.kindlabel.setToolTip(
_("Choice of what kind the object we want to cutout is.<BR>"
"- <B>Single</B>: contain a single PCB Gerber outline object.<BR>"
@ -112,11 +84,46 @@ class CutOut(FlatCAMTool):
{"label": _("Single"), "value": "single"},
{"label": _("Panel"), "value": "panel"},
])
grid0.addWidget(self.kindlabel, 3, 0)
grid0.addWidget(self.obj_kind_combo, 3, 1)
grid0.addWidget(self.kindlabel, 1, 0)
grid0.addWidget(self.obj_kind_combo, 1, 1)
# Type of object to be cutout
self.type_obj_radio = RadioSet([
{"label": _("Gerber"), "value": "grb"},
{"label": _("Geometry"), "value": "geo"},
])
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be cutout.\n"
"It can be of type: Gerber or Geometry.\n"
"What is selected here will dictate the kind\n"
"of objects that will populate the 'Object' combobox.")
)
grid0.addWidget(self.type_obj_combo_label, 2, 0)
grid0.addWidget(self.type_obj_radio, 2, 1)
# Object to be cutout
self.obj_combo = FCComboBox()
self.obj_combo.setModel(self.app.collection)
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_combo.is_last = True
grid0.addWidget(self.obj_combo, 3, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 4, 0, 1, 2)
grid0.addWidget(QtWidgets.QLabel(''), 5, 0, 1, 2)
self.param_label = QtWidgets.QLabel('<b>%s:</b>' % _("Tool Parameters"))
grid0.addWidget(self.param_label, 6, 0, 1, 2)
# Tool Diameter
self.dia = FCDoubleSpinner()
self.dia = FCDoubleSpinner(callback=self.confirmation_message)
self.dia.set_precision(self.decimals)
self.dia.set_range(0.0000, 9999.9999)
@ -125,8 +132,8 @@ class CutOut(FlatCAMTool):
_("Diameter of the tool used to cutout\n"
"the PCB shape out of the surrounding material.")
)
grid0.addWidget(self.dia_label, 4, 0)
grid0.addWidget(self.dia, 4, 1)
grid0.addWidget(self.dia_label, 8, 0)
grid0.addWidget(self.dia, 8, 1)
# Cut Z
cutzlabel = QtWidgets.QLabel('%s:' % _('Cut Z'))
@ -136,7 +143,7 @@ class CutOut(FlatCAMTool):
"below the copper surface."
)
)
self.cutz_entry = FCDoubleSpinner()
self.cutz_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.cutz_entry.set_precision(self.decimals)
if machinist_setting == 0:
@ -146,8 +153,8 @@ class CutOut(FlatCAMTool):
self.cutz_entry.setSingleStep(0.1)
grid0.addWidget(cutzlabel, 5, 0)
grid0.addWidget(self.cutz_entry, 5, 1)
grid0.addWidget(cutzlabel, 9, 0)
grid0.addWidget(self.cutz_entry, 9, 1)
# Multi-pass
self.mpass_cb = FCCheckBox('%s:' % _("Multi-Depth"))
@ -160,7 +167,7 @@ class CutOut(FlatCAMTool):
)
)
self.maxdepth_entry = FCDoubleSpinner()
self.maxdepth_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.maxdepth_entry.set_precision(self.decimals)
self.maxdepth_entry.setRange(0, 9999.9999)
self.maxdepth_entry.setSingleStep(0.1)
@ -172,11 +179,11 @@ class CutOut(FlatCAMTool):
)
self.ois_mpass_geo = OptionalInputSection(self.mpass_cb, [self.maxdepth_entry])
grid0.addWidget(self.mpass_cb, 6, 0)
grid0.addWidget(self.maxdepth_entry, 6, 1)
grid0.addWidget(self.mpass_cb, 10, 0)
grid0.addWidget(self.maxdepth_entry, 10, 1)
# Margin
self.margin = FCDoubleSpinner()
self.margin = FCDoubleSpinner(callback=self.confirmation_message)
self.margin.set_range(-9999.9999, 9999.9999)
self.margin.setSingleStep(0.1)
self.margin.set_precision(self.decimals)
@ -187,11 +194,11 @@ class CutOut(FlatCAMTool):
"will make the cutout of the PCB further from\n"
"the actual PCB border")
)
grid0.addWidget(self.margin_label, 7, 0)
grid0.addWidget(self.margin, 7, 1)
grid0.addWidget(self.margin_label, 11, 0)
grid0.addWidget(self.margin, 11, 1)
# Gapsize
self.gapsize = FCDoubleSpinner()
self.gapsize = FCDoubleSpinner(callback=self.confirmation_message)
self.gapsize.set_precision(self.decimals)
self.gapsize_label = QtWidgets.QLabel('%s:' % _("Gap size"))
@ -201,8 +208,8 @@ class CutOut(FlatCAMTool):
"the surrounding material (the one \n"
"from which the PCB is cutout).")
)
grid0.addWidget(self.gapsize_label, 8, 0)
grid0.addWidget(self.gapsize, 8, 1)
grid0.addWidget(self.gapsize_label, 13, 0)
grid0.addWidget(self.gapsize, 13, 1)
# How gaps wil be rendered:
# lr - left + right
@ -219,12 +226,14 @@ class CutOut(FlatCAMTool):
_("Create a convex shape surrounding the entire PCB.\n"
"Used only if the source object type is Gerber.")
)
grid0.addWidget(self.convex_box, 9, 0, 1, 2)
grid0.addWidget(self.convex_box, 15, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 10, 0, 1, 2)
grid0.addWidget(separator_line, 16, 0, 1, 2)
grid0.addWidget(QtWidgets.QLabel(''), 17, 0, 1, 2)
# Title2
title_param_label = QtWidgets.QLabel("<font size=4><b>%s</b></font>" % _('A. Automatic Bridge Gaps'))
@ -309,10 +318,11 @@ class CutOut(FlatCAMTool):
self.layout.addLayout(form_layout_3)
# Manual Geo Object
self.man_object_combo = QtWidgets.QComboBox()
self.man_object_combo = FCComboBox()
self.man_object_combo.setModel(self.app.collection)
self.man_object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.man_object_combo.setCurrentIndex(1)
self.man_object_combo.is_last = True
self.man_object_combo.obj_type = "Geometry"
self.man_object_label = QtWidgets.QLabel('%s:' % _("Geometry Object"))
self.man_object_label.setToolTip(
@ -398,15 +408,16 @@ class CutOut(FlatCAMTool):
self.ff_cutout_object_btn.clicked.connect(self.on_freeform_cutout)
self.rect_cutout_object_btn.clicked.connect(self.on_rectangular_cutout)
self.type_obj_combo.currentIndexChanged.connect(self.on_type_obj_index_changed)
self.type_obj_radio.activated_custom.connect(self.on_type_obj_changed)
self.man_geo_creation_btn.clicked.connect(self.on_manual_geo)
self.man_gaps_creation_btn.clicked.connect(self.on_manual_gap_click)
self.reset_button.clicked.connect(self.set_tool_ui)
def on_type_obj_index_changed(self, index):
obj_type = self.type_obj_combo.currentIndex()
def on_type_obj_changed(self, val):
obj_type = {'grb': 0, 'geo': 2}[val]
self.obj_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(0)
self.obj_combo.obj_type = {"grb": "Gerber", "geo": "Geometry"}[val]
def run(self, toggle=True):
self.app.report_usage("ToolCutOut()")
@ -451,6 +462,7 @@ class CutOut(FlatCAMTool):
self.gapsize.set_value(float(self.app.defaults["tools_cutoutgapsize"]))
self.gaps.set_value(self.app.defaults["tools_gaps_ff"])
self.convex_box.set_value(self.app.defaults['tools_cutout_convexshape'])
self.type_obj_radio.set_value('grb')
def on_freeform_cutout(self):
@ -514,9 +526,16 @@ class CutOut(FlatCAMTool):
solid_geo = []
if isinstance(cutout_obj, FlatCAMGerber):
if convex_box:
object_geo = cutout_obj.solid_geometry.convex_hull
else:
if isinstance(cutout_obj.solid_geometry, list):
cutout_obj.solid_geometry = MultiPolygon(cutout_obj.solid_geometry)
try:
if convex_box:
object_geo = cutout_obj.solid_geometry.convex_hull
else:
object_geo = cutout_obj.solid_geometry
except Exception as err:
log.debug("CutOut.on_freeform_cutout().geo_init() --> %s" % str(err))
object_geo = cutout_obj.solid_geometry
else:
object_geo = cutout_obj.solid_geometry
@ -588,12 +607,14 @@ class CutOut(FlatCAMTool):
if isinstance(object_geo, MultiPolygon):
x0, y0, x1, y1 = object_geo.bounds
object_geo = box(x0, y0, x1, y1)
if margin >= 0:
geo_buf = object_geo.buffer(margin + abs(dia / 2))
else:
geo_buf = object_geo.buffer(margin - abs(dia / 2))
geo_buf = object_geo.buffer(margin + abs(dia / 2))
geo = geo_buf.exterior
else:
geo = object_geo
solid_geo = cutout_handler(geom=geo)
else:
try:
@ -603,7 +624,11 @@ class CutOut(FlatCAMTool):
for geom_struct in object_geo:
if isinstance(cutout_obj, FlatCAMGerber):
geom_struct = (geom_struct.buffer(margin + abs(dia / 2))).exterior
if margin >= 0:
geom_struct = (geom_struct.buffer(margin + abs(dia / 2))).exterior
else:
geom_struct_buff = geom_struct.buffer(-margin + abs(dia / 2))
geom_struct = geom_struct_buff.interiors
solid_geo += cutout_handler(geom=geom_struct)
@ -623,7 +648,7 @@ class CutOut(FlatCAMTool):
cutout_obj.plot()
self.app.inform.emit('[success] %s' % _("Any form CutOut operation finished."))
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
# self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
self.app.should_we_save = True
def on_rectangular_cutout(self):
@ -751,24 +776,43 @@ class CutOut(FlatCAMTool):
# if Gerber create a buffer at a distance
# if Geometry then cut through the geometry
if isinstance(cutout_obj, FlatCAMGerber):
geo = geo.buffer(margin + abs(dia / 2))
if margin >= 0:
geo = geo.buffer(margin + abs(dia / 2))
else:
geo = geo.buffer(margin - abs(dia / 2))
solid_geo = cutout_rect_handler(geom=geo)
else:
try:
__ = iter(object_geo)
except TypeError:
object_geo = [object_geo]
if cutout_obj.kind == 'geometry':
try:
__ = iter(object_geo)
except TypeError:
object_geo = [object_geo]
for geom_struct in object_geo:
geom_struct = unary_union(geom_struct)
xmin, ymin, xmax, ymax = geom_struct.bounds
geom_struct = box(xmin, ymin, xmax, ymax)
for geom_struct in object_geo:
geom_struct = unary_union(geom_struct)
xmin, ymin, xmax, ymax = geom_struct.bounds
geom_struct = box(xmin, ymin, xmax, ymax)
solid_geo += cutout_rect_handler(geom=geom_struct)
elif cutout_obj.kind == 'gerber' and margin >= 0:
try:
__ = iter(object_geo)
except TypeError:
object_geo = [object_geo]
for geom_struct in object_geo:
geom_struct = unary_union(geom_struct)
xmin, ymin, xmax, ymax = geom_struct.bounds
geom_struct = box(xmin, ymin, xmax, ymax)
if isinstance(cutout_obj, FlatCAMGerber):
geom_struct = geom_struct.buffer(margin + abs(dia / 2))
solid_geo += cutout_rect_handler(geom=geom_struct)
solid_geo += cutout_rect_handler(geom=geom_struct)
elif cutout_obj.kind == 'gerber' and margin < 0:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Rectangular cutout with negative margin is not possible."))
return "fail"
geo_obj.solid_geometry = deepcopy(solid_geo)
geo_obj.options['cnctooldia'] = str(dia)
@ -777,12 +821,12 @@ class CutOut(FlatCAMTool):
geo_obj.options['depthperpass'] = self.maxdepth_entry.get_value()
outname = cutout_obj.options["name"] + "_cutout"
self.app.new_object('geometry', outname, geo_init)
ret = self.app.new_object('geometry', outname, geo_init)
# cutout_obj.plot()
self.app.inform.emit('[success] %s' %
_("Any form CutOut operation finished."))
self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
if ret != 'fail':
# cutout_obj.plot()
self.app.inform.emit('[success] %s' % _("Any form CutOut operation finished."))
# self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
self.app.should_we_save = True
def on_manual_gap_click(self):
@ -923,6 +967,9 @@ class CutOut(FlatCAMTool):
self.app.new_object('geometry', outname, geo_init)
def cutting_geo(self, pos):
self.cutting_dia = float(self.dia.get_value())
self.cutting_gapsize = float(self.gapsize.get_value())
offset = self.cutting_dia / 2 + self.cutting_gapsize / 2
# cutting area definition
@ -1018,7 +1065,7 @@ class CutOut(FlatCAMTool):
except TypeError:
return
if self.app.grid_status() == True:
if self.app.grid_status():
snap_x, snap_y = self.app.geo_editor.snap(x, y)
else:
snap_x, snap_y = x, y
@ -1048,7 +1095,7 @@ class CutOut(FlatCAMTool):
else:
radian = math.atan(dx / dy)
angle = radian * 180 / math.pi
except Exception as e:
except Exception:
angle = 0
return angle

View File

@ -2,7 +2,7 @@
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry, FCEntry
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, EvalEntry, FCEntry, FCButton, FCComboBox
from FlatCAMObj import FlatCAMGerber, FlatCAMExcellon, FlatCAMGeometry
from numpy import Inf
@ -41,22 +41,28 @@ class DblSidedTool(FlatCAMTool):
""")
self.layout.addWidget(title_label)
self.empty_lb = QtWidgets.QLabel("")
self.layout.addWidget(self.empty_lb)
self.layout.addWidget(QtWidgets.QLabel(""))
# ## Grid Layout
grid_lay = QtWidgets.QGridLayout()
self.layout.addLayout(grid_lay)
grid_lay.setColumnStretch(0, 1)
grid_lay.setColumnStretch(1, 0)
self.layout.addLayout(grid_lay)
# Objects to be mirrored
self.m_objects_label = QtWidgets.QLabel("<b>%s:</b>" % _("Mirror Operation"))
self.m_objects_label.setToolTip('%s.' % _("Objects to be mirrored"))
grid_lay.addWidget(self.m_objects_label, 0, 0, 1, 2)
# ## Gerber Object to mirror
self.gerber_object_combo = QtWidgets.QComboBox()
self.gerber_object_combo = FCComboBox()
self.gerber_object_combo.setModel(self.app.collection)
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_object_combo.setCurrentIndex(1)
self.gerber_object_combo.is_last = True
self.gerber_object_combo.obj_type = "Gerber"
self.botlay_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.botlay_label = QtWidgets.QLabel("%s:" % _("GERBER"))
self.botlay_label.setToolTip('%s.' % _("Gerber to be mirrored"))
self.mirror_gerber_button = QtWidgets.QPushButton(_("Mirror"))
@ -73,18 +79,18 @@ class DblSidedTool(FlatCAMTool):
""")
self.mirror_gerber_button.setMinimumWidth(60)
# grid_lay.addRow("Bottom Layer:", self.object_combo)
grid_lay.addWidget(self.botlay_label, 0, 0)
grid_lay.addWidget(self.gerber_object_combo, 1, 0)
grid_lay.addWidget(self.mirror_gerber_button, 1, 1)
grid_lay.addWidget(self.botlay_label, 1, 0)
grid_lay.addWidget(self.gerber_object_combo, 2, 0)
grid_lay.addWidget(self.mirror_gerber_button, 2, 1)
# ## Excellon Object to mirror
self.exc_object_combo = QtWidgets.QComboBox()
self.exc_object_combo = FCComboBox()
self.exc_object_combo.setModel(self.app.collection)
self.exc_object_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.exc_object_combo.setCurrentIndex(1)
self.exc_object_combo.is_last = True
self.exc_object_combo.obj_type = "Excellon"
self.excobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("EXCELLON"))
self.excobj_label = QtWidgets.QLabel("%s:" % _("EXCELLON"))
self.excobj_label.setToolTip(_("Excellon Object to be mirrored."))
self.mirror_exc_button = QtWidgets.QPushButton(_("Mirror"))
@ -101,18 +107,18 @@ class DblSidedTool(FlatCAMTool):
""")
self.mirror_exc_button.setMinimumWidth(60)
# grid_lay.addRow("Bottom Layer:", self.object_combo)
grid_lay.addWidget(self.excobj_label, 2, 0)
grid_lay.addWidget(self.exc_object_combo, 3, 0)
grid_lay.addWidget(self.mirror_exc_button, 3, 1)
grid_lay.addWidget(self.excobj_label, 3, 0)
grid_lay.addWidget(self.exc_object_combo, 4, 0)
grid_lay.addWidget(self.mirror_exc_button, 4, 1)
# ## Geometry Object to mirror
self.geo_object_combo = QtWidgets.QComboBox()
self.geo_object_combo = FCComboBox()
self.geo_object_combo.setModel(self.app.collection)
self.geo_object_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.geo_object_combo.setCurrentIndex(1)
self.geo_object_combo.is_last = True
self.geo_object_combo.obj_type = "Geometry"
self.geoobj_label = QtWidgets.QLabel("<b>%s</b>:" % _("GEOMETRY"))
self.geoobj_label = QtWidgets.QLabel("%s:" % _("GEOMETRY"))
self.geoobj_label.setToolTip(
_("Geometry Obj to be mirrored.")
)
@ -132,161 +138,315 @@ class DblSidedTool(FlatCAMTool):
self.mirror_geo_button.setMinimumWidth(60)
# grid_lay.addRow("Bottom Layer:", self.object_combo)
grid_lay.addWidget(self.geoobj_label, 4, 0)
grid_lay.addWidget(self.geo_object_combo, 5, 0)
grid_lay.addWidget(self.mirror_geo_button, 5, 1)
grid_lay.addWidget(self.geoobj_label, 5, 0)
grid_lay.addWidget(self.geo_object_combo, 6, 0)
grid_lay.addWidget(self.mirror_geo_button, 6, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay.addWidget(separator_line, 7, 0, 1, 2)
self.layout.addWidget(QtWidgets.QLabel(""))
# ## Grid Layout
grid_lay1 = QtWidgets.QGridLayout()
grid_lay1.setColumnStretch(0, 0)
grid_lay1.setColumnStretch(1, 1)
self.layout.addLayout(grid_lay1)
# Objects to be mirrored
self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Mirror Parameters"))
self.param_label.setToolTip('%s.' % _("Parameters for the mirror operation"))
grid_lay1.addWidget(self.param_label, 0, 0, 1, 3)
# ## Axis
self.mirax_label = QtWidgets.QLabel('%s:' % _("Mirror Axis"))
self.mirax_label.setToolTip(_("Mirror vertically (X) or horizontally (Y)."))
self.mirror_axis = RadioSet([{'label': 'X', 'value': 'X'},
{'label': 'Y', 'value': 'Y'}])
self.mirax_label = QtWidgets.QLabel(_("Mirror Axis:"))
self.mirax_label.setToolTip(_("Mirror vertically (X) or horizontally (Y)."))
# grid_lay.addRow("Mirror Axis:", self.mirror_axis)
self.empty_lb1 = QtWidgets.QLabel("")
grid_lay1.addWidget(self.empty_lb1, 6, 0)
grid_lay1.addWidget(self.mirax_label, 7, 0)
grid_lay1.addWidget(self.mirror_axis, 7, 1)
grid_lay1.addWidget(self.mirax_label, 2, 0)
grid_lay1.addWidget(self.mirror_axis, 2, 1, 1, 2)
# ## Axis Location
self.axloc_label = QtWidgets.QLabel('%s:' % _("Reference"))
self.axloc_label.setToolTip(
_("The coordinates used as reference for the mirror operation.\n"
"Can be:\n"
"- Point -> a set of coordinates (x,y) around which the object is mirrored\n"
"- Box -> a set of coordinates (x, y) obtained from the center of the\n"
"bounding box of another object selected below")
)
self.axis_location = RadioSet([{'label': _('Point'), 'value': 'point'},
{'label': _('Box'), 'value': 'box'}])
self.axloc_label = QtWidgets.QLabel('%s:' % _("Axis Ref"))
self.axloc_label.setToolTip(
_("The axis should pass through a <b>point</b> or cut\n "
"a specified <b>box</b> (in a FlatCAM object) through \n"
"the center.")
)
# grid_lay.addRow("Axis Location:", self.axis_location)
grid_lay1.addWidget(self.axloc_label, 8, 0)
grid_lay1.addWidget(self.axis_location, 8, 1)
self.empty_lb2 = QtWidgets.QLabel("")
grid_lay1.addWidget(self.empty_lb2, 9, 0)
# ## Grid Layout
grid_lay2 = QtWidgets.QGridLayout()
self.layout.addLayout(grid_lay2)
grid_lay2.setColumnStretch(0, 1)
grid_lay2.setColumnStretch(1, 0)
grid_lay1.addWidget(self.axloc_label, 4, 0)
grid_lay1.addWidget(self.axis_location, 4, 1, 1, 2)
# ## Point/Box
self.point_box_container = QtWidgets.QVBoxLayout()
self.pb_label = QtWidgets.QLabel("<b>%s:</b>" % _('Point/Box Reference'))
self.pb_label.setToolTip(
_("If 'Point' is selected above it store the coordinates (x, y) through which\n"
"the mirroring axis passes.\n"
"If 'Box' is selected above, select here a FlatCAM object (Gerber, Exc or Geo).\n"
"Through the center of this object pass the mirroring axis selected above.")
)
self.point_entry = EvalEntry()
# Add a reference
self.add_point_button = QtWidgets.QPushButton(_("Add"))
self.add_point_button.setToolTip(
_("Add the coordinates in format <b>(x, y)</b> through which the mirroring axis \n "
"selected in 'MIRROR AXIS' pass.\n"
"The (x, y) coordinates are captured by pressing SHIFT key\n"
"and left mouse button click on canvas or you can enter the coords manually.")
"and left mouse button click on canvas or you can enter the coordinates manually.")
)
self.add_point_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
QPushButton
{
font-weight: bold;
}
""")
self.add_point_button.setMinimumWidth(60)
grid_lay2.addWidget(self.pb_label, 10, 0)
grid_lay2.addLayout(self.point_box_container, 11, 0)
grid_lay2.addWidget(self.add_point_button, 11, 1)
grid_lay1.addWidget(self.point_entry, 7, 0, 1, 2)
grid_lay1.addWidget(self.add_point_button, 7, 2)
self.point_entry = EvalEntry()
self.point_box_container.addWidget(self.point_entry)
# ## Grid Layout
grid_lay2 = QtWidgets.QGridLayout()
grid_lay2.setColumnStretch(0, 0)
grid_lay2.setColumnStretch(1, 1)
self.layout.addLayout(grid_lay2)
self.box_combo = QtWidgets.QComboBox()
self.box_type_label = QtWidgets.QLabel('%s:' % _("Reference Object"))
self.box_type_label.setToolTip(
_("It can be of type: Gerber or Excellon or Geometry.\n"
"The coordinates of the center of the bounding box are used\n"
"as reference for mirror operation.")
)
# Type of object used as BOX reference
self.box_type_radio = RadioSet([{'label': _('Gerber'), 'value': 'grb'},
{'label': _('Excellon'), 'value': 'exc'},
{'label': _('Geometry'), 'value': 'geo'}])
self.box_type_label.hide()
self.box_type_radio.hide()
grid_lay2.addWidget(self.box_type_label, 0, 0, 1, 2)
grid_lay2.addWidget(self.box_type_radio, 1, 0, 1, 2)
# Object used as BOX reference
self.box_combo = FCComboBox()
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
self.box_combo.is_last = True
self.box_combo_type = QtWidgets.QComboBox()
self.box_combo_type.addItem(_("Reference Gerber"))
self.box_combo_type.addItem(_("Reference Excellon"))
self.box_combo_type.addItem(_("Reference Geometry"))
self.point_box_container.addWidget(self.box_combo_type)
self.point_box_container.addWidget(self.box_combo)
self.box_combo.hide()
self.box_combo_type.hide()
grid_lay2.addWidget(self.box_combo, 3, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay2.addWidget(separator_line, 12, 0, 1, 2)
grid_lay2.addWidget(separator_line, 4, 0, 1, 2)
grid_lay2.addWidget(QtWidgets.QLabel(""), 5, 0, 1, 2)
# ## Title Bounds Values
self.bv_label = QtWidgets.QLabel("<b>%s:</b>" % _('Bounds Values'))
self.bv_label.setToolTip(
_("Select on canvas the object(s)\n"
"for which to calculate bounds values.")
)
grid_lay2.addWidget(self.bv_label, 6, 0, 1, 2)
# Xmin value
self.xmin_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.xmin_entry.set_precision(self.decimals)
self.xmin_entry.set_range(-9999.9999, 9999.9999)
self.xmin_btn = FCButton('%s:' % _("X min"))
self.xmin_btn.setToolTip(
_("Minimum location.")
)
self.xmin_entry.setReadOnly(True)
grid_lay2.addWidget(self.xmin_btn, 7, 0)
grid_lay2.addWidget(self.xmin_entry, 7, 1)
# Ymin value
self.ymin_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.ymin_entry.set_precision(self.decimals)
self.ymin_entry.set_range(-9999.9999, 9999.9999)
self.ymin_btn = FCButton('%s:' % _("Y min"))
self.ymin_btn.setToolTip(
_("Minimum location.")
)
self.ymin_entry.setReadOnly(True)
grid_lay2.addWidget(self.ymin_btn, 8, 0)
grid_lay2.addWidget(self.ymin_entry, 8, 1)
# Xmax value
self.xmax_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.xmax_entry.set_precision(self.decimals)
self.xmax_entry.set_range(-9999.9999, 9999.9999)
self.xmax_btn = FCButton('%s:' % _("X max"))
self.xmax_btn.setToolTip(
_("Maximum location.")
)
self.xmax_entry.setReadOnly(True)
grid_lay2.addWidget(self.xmax_btn, 9, 0)
grid_lay2.addWidget(self.xmax_entry, 9, 1)
# Ymax value
self.ymax_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.ymax_entry.set_precision(self.decimals)
self.ymax_entry.set_range(-9999.9999, 9999.9999)
self.ymax_btn = FCButton('%s:' % _("Y max"))
self.ymax_btn.setToolTip(
_("Maximum location.")
)
self.ymax_entry.setReadOnly(True)
grid_lay2.addWidget(self.ymax_btn, 10, 0)
grid_lay2.addWidget(self.ymax_entry, 10, 1)
# Center point value
self.center_entry = FCEntry()
self.center_btn = FCButton('%s:' % _("Centroid"))
self.center_btn.setToolTip(
_("The center point location for the rectangular\n"
"bounding shape. Centroid. Format is (x, y).")
)
self.center_entry.setReadOnly(True)
grid_lay2.addWidget(self.center_btn, 12, 0)
grid_lay2.addWidget(self.center_entry, 12, 1)
# Calculate Bounding box
self.calculate_bb_button = QtWidgets.QPushButton(_("Calculate Bounds Values"))
self.calculate_bb_button.setToolTip(
_("Calculate the enveloping rectangular shape coordinates,\n"
"for the selection of objects.\n"
"The envelope shape is parallel with the X, Y axis.")
)
self.calculate_bb_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
grid_lay2.addWidget(self.calculate_bb_button, 13, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay2.addWidget(separator_line, 14, 0, 1, 2)
grid_lay2.addWidget(QtWidgets.QLabel(""), 15, 0, 1, 2)
# ## Alignment holes
self.ah_label = QtWidgets.QLabel("<b>%s:</b>" % _('Alignment Drill Coordinates'))
self.alignment_label = QtWidgets.QLabel("<b>%s:</b>" % _('PCB Alignment'))
self.alignment_label.setToolTip(
_("Creates an Excellon Object containing the\n"
"specified alignment holes and their mirror\n"
"images.")
)
grid_lay2.addWidget(self.alignment_label, 25, 0, 1, 2)
# ## Drill diameter for alignment holes
self.dt_label = QtWidgets.QLabel("%s:" % _('Drill Diameter'))
self.dt_label.setToolTip(
_("Diameter of the drill for the alignment holes.")
)
self.drill_dia = FCDoubleSpinner(callback=self.confirmation_message)
self.drill_dia.setToolTip(
_("Diameter of the drill for the alignment holes.")
)
self.drill_dia.set_precision(self.decimals)
self.drill_dia.set_range(0.0000, 9999.9999)
grid_lay2.addWidget(self.dt_label, 26, 0)
grid_lay2.addWidget(self.drill_dia, 26, 1)
# ## Alignment Axis
self.align_ax_label = QtWidgets.QLabel('%s:' % _("Align Axis"))
self.align_ax_label.setToolTip(
_("Mirror vertically (X) or horizontally (Y).")
)
self.align_axis_radio = RadioSet([{'label': 'X', 'value': 'X'},
{'label': 'Y', 'value': 'Y'}])
grid_lay2.addWidget(self.align_ax_label, 27, 0)
grid_lay2.addWidget(self.align_axis_radio, 27, 1)
# ## Alignment Reference Point
self.align_ref_label = QtWidgets.QLabel('%s:' % _("Reference"))
self.align_ref_label.setToolTip(
_("The reference point used to create the second alignment drill\n"
"from the first alignment drill, by doing mirror.\n"
"It can be modified in the Mirror Parameters -> Reference section")
)
self.align_ref_label_val = EvalEntry()
self.align_ref_label_val.setToolTip(
_("The reference point used to create the second alignment drill\n"
"from the first alignment drill, by doing mirror.\n"
"It can be modified in the Mirror Parameters -> Reference section")
)
self.align_ref_label_val.setDisabled(True)
grid_lay2.addWidget(self.align_ref_label, 28, 0)
grid_lay2.addWidget(self.align_ref_label_val, 28, 1)
grid_lay4 = QtWidgets.QGridLayout()
self.layout.addLayout(grid_lay4)
# ## Alignment holes
self.ah_label = QtWidgets.QLabel("%s:" % _('Alignment Drill Coordinates'))
self.ah_label.setToolTip(
_("Alignment holes (x1, y1), (x2, y2), ... "
"on one side of the mirror axis. For each set of (x, y) coordinates\n"
"entered here, a pair of drills will be created:\n\n"
"- one drill at the coordinates from the field\n"
"- one drill in mirror position over the axis selected above in the 'Mirror Axis'.")
"- one drill in mirror position over the axis selected above in the 'Align Axis'.")
)
self.layout.addWidget(self.ah_label)
grid_lay3 = QtWidgets.QGridLayout()
self.layout.addLayout(grid_lay3)
self.alignment_holes = EvalEntry()
self.add_drill_point_button = QtWidgets.QPushButton(_("Add"))
grid_lay4.addWidget(self.ah_label, 0, 0, 1, 2)
grid_lay4.addWidget(self.alignment_holes, 1, 0, 1, 2)
self.add_drill_point_button = FCButton(_("Add"))
self.add_drill_point_button.setToolTip(
_("Add alignment drill holes coords in the format: (x1, y1), (x2, y2), ... \n"
"on one side of the mirror axis.\n\n"
_("Add alignment drill holes coordinates in the format: (x1, y1), (x2, y2), ... \n"
"on one side of the alignment axis.\n\n"
"The coordinates set can be obtained:\n"
"- press SHIFT key and left mouse clicking on canvas. Then click Add.\n"
"- press SHIFT key and left mouse clicking on canvas. Then CTRL+V in the field.\n"
"- press SHIFT key and left mouse clicking on canvas. Then RMB click in the field and click Paste.\n"
"- by entering the coords manually in the format: (x1, y1), (x2, y2), ...")
)
self.add_drill_point_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.add_drill_point_button.setMinimumWidth(60)
# self.add_drill_point_button.setStyleSheet("""
# QPushButton
# {
# font-weight: bold;
# }
# """)
grid_lay3.addWidget(self.alignment_holes, 0, 0)
grid_lay3.addWidget(self.add_drill_point_button, 0, 1)
grid0 = QtWidgets.QGridLayout()
self.layout.addLayout(grid0)
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
# ## Drill diameter for alignment holes
self.dt_label = QtWidgets.QLabel("<b>%s:</b>" % _('Alignment Drill Diameter'))
self.dt_label.setToolTip(
_("Diameter of the drill for the "
"alignment holes.")
self.delete_drill_point_button = FCButton(_("Delete Last"))
self.delete_drill_point_button.setToolTip(
_("Delete the last coordinates tuple in the list.")
)
grid0.addWidget(self.dt_label, 0, 0, 1, 2)
drill_hlay = QtWidgets.QHBoxLayout()
# Drill diameter value
self.drill_dia = FCDoubleSpinner()
self.drill_dia.set_precision(self.decimals)
self.drill_dia.set_range(0.0000, 9999.9999)
drill_hlay.addWidget(self.add_drill_point_button)
drill_hlay.addWidget(self.delete_drill_point_button)
self.drill_dia.setToolTip(
_("Diameter of the drill for the "
"alignment holes.")
)
grid0.addWidget(self.drill_dia, 1, 0, 1, 2)
grid_lay4.addLayout(drill_hlay, 2, 0, 1, 2)
# ## Buttons
self.create_alignment_hole_button = QtWidgets.QPushButton(_("Create Excellon Object"))
@ -303,102 +463,6 @@ class DblSidedTool(FlatCAMTool):
""")
self.layout.addWidget(self.create_alignment_hole_button)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
self.layout.addWidget(separator_line)
self.layout.addWidget(QtWidgets.QLabel(''))
grid1 = QtWidgets.QGridLayout()
self.layout.addLayout(grid1)
grid1.setColumnStretch(0, 0)
grid1.setColumnStretch(1, 1)
# Xmin value
self.xmin_entry = FCDoubleSpinner()
self.xmin_entry.set_precision(self.decimals)
self.xmin_entry.set_range(-9999.9999, 9999.9999)
self.xmin_label = QtWidgets.QLabel('%s:' % _("X min"))
self.xmin_label.setToolTip(
_("Minimum location.")
)
self.xmin_entry.setReadOnly(True)
grid1.addWidget(self.xmin_label, 1, 0)
grid1.addWidget(self.xmin_entry, 1, 1)
# Ymin value
self.ymin_entry = FCDoubleSpinner()
self.ymin_entry.set_precision(self.decimals)
self.ymin_entry.set_range(-9999.9999, 9999.9999)
self.ymin_label = QtWidgets.QLabel('%s:' % _("Y min"))
self.ymin_label.setToolTip(
_("Minimum location.")
)
self.ymin_entry.setReadOnly(True)
grid1.addWidget(self.ymin_label, 2, 0)
grid1.addWidget(self.ymin_entry, 2, 1)
# Xmax value
self.xmax_entry = FCDoubleSpinner()
self.xmax_entry.set_precision(self.decimals)
self.xmax_entry.set_range(-9999.9999, 9999.9999)
self.xmax_label = QtWidgets.QLabel('%s:' % _("X max"))
self.xmax_label.setToolTip(
_("Maximum location.")
)
self.xmax_entry.setReadOnly(True)
grid1.addWidget(self.xmax_label, 3, 0)
grid1.addWidget(self.xmax_entry, 3, 1)
# Ymax value
self.ymax_entry = FCDoubleSpinner()
self.ymax_entry.set_precision(self.decimals)
self.ymax_entry.set_range(-9999.9999, 9999.9999)
self.ymax_label = QtWidgets.QLabel('%s:' % _("Y max"))
self.ymax_label.setToolTip(
_("Maximum location.")
)
self.ymax_entry.setReadOnly(True)
grid1.addWidget(self.ymax_label, 4, 0)
grid1.addWidget(self.ymax_entry, 4, 1)
# Center point value
self.center_entry = FCEntry()
self.center_label = QtWidgets.QLabel('%s:' % _("Centroid"))
self.center_label.setToolTip(
_("The center point location for the rectangular\n"
"bounding shape. Centroid. Format is (x, y).")
)
self.center_entry.setReadOnly(True)
grid1.addWidget(self.center_label, 5, 0)
grid1.addWidget(self.center_entry, 5, 1)
# Calculate Bounding box
self.calculate_bb_button = QtWidgets.QPushButton(_("Calculate Bounds Values"))
self.calculate_bb_button.setToolTip(
_("Calculate the enveloping rectangular shape coordinates,\n"
"for the selection of objects.\n"
"The envelope shape is parallel with the X, Y axis.")
)
self.calculate_bb_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.layout.addWidget(self.calculate_bb_button)
self.layout.addStretch()
# ## Reset Tool
@ -418,12 +482,26 @@ class DblSidedTool(FlatCAMTool):
self.mirror_gerber_button.clicked.connect(self.on_mirror_gerber)
self.mirror_exc_button.clicked.connect(self.on_mirror_exc)
self.mirror_geo_button.clicked.connect(self.on_mirror_geo)
self.add_point_button.clicked.connect(self.on_point_add)
self.add_drill_point_button.clicked.connect(self.on_drill_add)
self.box_combo_type.currentIndexChanged.connect(self.on_combo_box_type)
self.delete_drill_point_button.clicked.connect(self.on_drill_delete_last)
self.box_type_radio.activated_custom.connect(self.on_combo_box_type)
self.axis_location.group_toggle_fn = self.on_toggle_pointbox
self.point_entry.textChanged.connect(lambda val: self.align_ref_label_val.set_value(val))
self.xmin_btn.clicked.connect(self.on_xmin_clicked)
self.ymin_btn.clicked.connect(self.on_ymin_clicked)
self.xmax_btn.clicked.connect(self.on_xmax_clicked)
self.ymax_btn.clicked.connect(self.on_ymax_clicked)
self.center_btn.clicked.connect(
lambda: self.point_entry.set_value(self.center_entry.get_value())
)
self.create_alignment_hole_button.clicked.connect(self.on_create_alignment_holes)
self.calculate_bb_button.clicked.connect(self.on_bbox_coordinates)
@ -470,6 +548,7 @@ class DblSidedTool(FlatCAMTool):
self.mirror_axis.set_value(self.app.defaults["tools_2sided_mirror_axis"])
self.axis_location.set_value(self.app.defaults["tools_2sided_axis_loc"])
self.drill_dia.set_value(self.app.defaults["tools_2sided_drilldia"])
self.align_axis_radio.set_value(self.app.defaults["tools_2sided_allign_axis"])
self.xmin_entry.set_value(0.0)
self.ymin_entry.set_value(0.0)
@ -477,13 +556,21 @@ class DblSidedTool(FlatCAMTool):
self.ymax_entry.set_value(0.0)
self.center_entry.set_value('')
def on_combo_box_type(self):
obj_type = self.box_combo_type.currentIndex()
self.align_ref_label_val.set_value('%.*f' % (self.decimals, 0.0))
# run once to make sure that the obj_type attribute is updated in the FCComboBox
self.box_type_radio.set_value('grb')
self.on_combo_box_type('grb')
def on_combo_box_type(self, val):
obj_type = {'grb': 0, 'exc': 1, 'geo': 2}[val]
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(0)
self.box_combo.obj_type = {
"grb": "Gerber", "exc": "Excellon", "geo": "Geometry"}[val]
def on_create_alignment_holes(self):
axis = self.mirror_axis.get_value()
axis = self.align_axis_radio.get_value()
mode = self.axis_location.get_value()
if mode == "point":
@ -524,7 +611,10 @@ class DblSidedTool(FlatCAMTool):
_("No value or wrong format in Drill Dia entry. Add it and retry."))
return
tools = {"1": {"C": dia}}
tools = {}
tools["1"] = {}
tools["1"]["C"] = dia
tools["1"]['solid_geometry'] = []
# holes = self.alignment_holes.get_value()
holes = eval('[{}]'.format(self.alignment_holes.text()))
@ -533,23 +623,24 @@ class DblSidedTool(FlatCAMTool):
"Add them and retry."))
return
drills = list()
drills = []
for hole in holes:
point = Point(hole)
point_mirror = affinity.scale(point, xscale, yscale, origin=(px, py))
drills.append({"point": point, "tool": "1"})
drills.append({"point": point_mirror, "tool": "1"})
if 'solid_geometry' not in tools["1"]:
tools["1"]['solid_geometry'] = list()
else:
tools["1"]['solid_geometry'].append(point)
tools["1"]['solid_geometry'].append(point_mirror)
tools["1"]['solid_geometry'].append(point)
tools["1"]['solid_geometry'].append(point_mirror)
def obj_init(obj_inst, app_inst):
obj_inst.tools = tools
obj_inst.drills = drills
obj_inst.create_geometry()
obj_inst.source_file = app_inst.export_excellon(obj_name=obj_inst.options['name'], local_use=obj_inst,
filename=None, use_thread=False)
self.app.new_object("excellon", "Alignment Drills", obj_init)
self.drill_values = ''
@ -561,7 +652,7 @@ class DblSidedTool(FlatCAMTool):
model_index = self.app.collection.index(selection_index, 0, self.gerber_object_combo.rootModelIndex())
try:
fcobj = model_index.internalPointer().obj
except Exception as e:
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
return
@ -585,7 +676,7 @@ class DblSidedTool(FlatCAMTool):
model_index_box = self.app.collection.index(selection_index_box, 0, self.box_combo.rootModelIndex())
try:
bb_obj = model_index_box.internalPointer().obj
except Exception as e:
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Box object loaded ..."))
return
@ -604,7 +695,7 @@ class DblSidedTool(FlatCAMTool):
model_index = self.app.collection.index(selection_index, 0, self.exc_object_combo.rootModelIndex())
try:
fcobj = model_index.internalPointer().obj
except Exception as e:
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Excellon object loaded ..."))
return
@ -648,7 +739,7 @@ class DblSidedTool(FlatCAMTool):
model_index = self.app.collection.index(selection_index, 0, self.geo_object_combo.rootModelIndex())
try:
fcobj = model_index.internalPointer().obj
except Exception as e:
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Geometry object loaded ..."))
return
@ -666,7 +757,7 @@ class DblSidedTool(FlatCAMTool):
model_index_box = self.app.collection.index(selection_index_box, 0, self.box_combo.rootModelIndex())
try:
bb_obj = model_index_box.internalPointer().obj
except Exception as e:
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Box object loaded ..."))
return
@ -679,27 +770,39 @@ class DblSidedTool(FlatCAMTool):
fcobj.plot()
self.app.inform.emit('[success] Geometry %s %s...' % (str(fcobj.options['name']), _("was mirrored")))
def on_point_add(self):
val = self.app.defaults["global_point_clipboard_format"] % (self.app.pos[0], self.app.pos[1])
val = self.app.defaults["global_point_clipboard_format"] % \
(self.decimals, self.app.pos[0], self.decimals, self.app.pos[1])
self.point_entry.set_value(val)
def on_drill_add(self):
self.drill_values += (self.app.defaults["global_point_clipboard_format"] %
(self.app.pos[0], self.app.pos[1])) + ','
(self.decimals, self.app.pos[0], self.decimals, self.app.pos[1])) + ','
self.alignment_holes.set_value(self.drill_values)
def on_drill_delete_last(self):
drill_values_without_last_tupple = self.drill_values.rpartition('(')[0]
self.drill_values = drill_values_without_last_tupple
self.alignment_holes.set_value(self.drill_values)
def on_toggle_pointbox(self):
if self.axis_location.get_value() == "point":
self.point_entry.show()
self.add_point_button.show()
self.box_type_label.hide()
self.box_type_radio.hide()
self.box_combo.hide()
self.box_combo_type.hide()
self.add_point_button.setDisabled(False)
self.align_ref_label_val.set_value(self.point_entry.get_value())
else:
self.point_entry.hide()
self.add_point_button.hide()
self.box_type_label.show()
self.box_type_radio.show()
self.box_combo.show()
self.box_combo_type.show()
self.add_point_button.setDisabled(True)
self.align_ref_label_val.set_value("Box centroid")
def on_bbox_coordinates(self):
@ -735,6 +838,51 @@ class DblSidedTool(FlatCAMTool):
self.center_entry.set_value(val_txt)
self.axis_location.set_value('point')
self.point_entry.set_value(val_txt)
self.app.delete_selection_shape()
def on_xmin_clicked(self):
xmin = self.xmin_entry.get_value()
self.axis_location.set_value('point')
try:
px, py = self.point_entry.get_value()
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, xmin, self.decimals, py)
except TypeError:
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, xmin, self.decimals, 0.0)
self.point_entry.set_value(val)
def on_ymin_clicked(self):
ymin = self.ymin_entry.get_value()
self.axis_location.set_value('point')
try:
px, py = self.point_entry.get_value()
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, px, self.decimals, ymin)
except TypeError:
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, 0.0, self.decimals, ymin)
self.point_entry.set_value(val)
def on_xmax_clicked(self):
xmax = self.xmax_entry.get_value()
self.axis_location.set_value('point')
try:
px, py = self.point_entry.get_value()
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, xmax, self.decimals, py)
except TypeError:
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, xmax, self.decimals, 0.0)
self.point_entry.set_value(val)
def on_ymax_clicked(self):
ymax = self.ymax_entry.get_value()
self.axis_location.set_value('point')
try:
px, py = self.point_entry.get_value()
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, px, self.decimals, ymax)
except TypeError:
val = self.app.defaults["global_point_clipboard_format"] % (self.decimals, 0.0, self.decimals, ymax)
self.point_entry.set_value(val)
def reset_fields(self):
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@ -746,6 +894,7 @@ class DblSidedTool(FlatCAMTool):
self.exc_object_combo.setCurrentIndex(0)
self.geo_object_combo.setCurrentIndex(0)
self.box_combo.setCurrentIndex(0)
self.box_combo_type.setCurrentIndex(0)
self.box_type_radio.set_value('grb')
self.drill_values = ""
self.align_ref_label_val.set_value('')

View File

@ -9,13 +9,18 @@ from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.VisPyVisuals import *
from flatcamGUI.GUIElements import FCEntry
from flatcamGUI.GUIElements import FCEntry, FCButton, FCCheckBox
from shapely.geometry import Point, MultiLineString, Polygon
import FlatCAMTranslation as fcTranslate
from camlib import FlatCAMRTreeStorage
from flatcamEditors.FlatCAMGeoEditor import DrawToolShape
from copy import copy
import math
import logging
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
@ -43,82 +48,101 @@ class Distance(FlatCAMTool):
self.layout.addWidget(title_label)
# ## Form Layout
form_layout = QtWidgets.QFormLayout()
self.layout.addLayout(form_layout)
grid0 = QtWidgets.QGridLayout()
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
self.layout.addLayout(grid0)
self.units_label = QtWidgets.QLabel('%s:' % _("Units"))
self.units_label.setToolTip(_("Those are the units in which the distance is measured."))
self.units_value = QtWidgets.QLabel("%s" % str({'mm': _("METRIC (mm)"), 'in': _("INCH (in)")}[self.units]))
self.units_value.setDisabled(True)
grid0.addWidget(self.units_label, 0, 0)
grid0.addWidget(self.units_value, 0, 1)
self.snap_center_cb = FCCheckBox(_("Snap to center"))
self.snap_center_cb.setToolTip(
_("Mouse cursor will snap to the center of the pad/drill\n"
"when it is hovering over the geometry of the pad/drill.")
)
grid0.addWidget(self.snap_center_cb, 1, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 2, 0, 1, 2)
self.start_label = QtWidgets.QLabel("%s:" % _('Start Coords'))
self.start_label.setToolTip(_("This is measuring Start point coordinates."))
self.stop_label = QtWidgets.QLabel("%s:" % _('Stop Coords'))
self.stop_label.setToolTip(_("This is the measuring Stop point coordinates."))
self.distance_x_label = QtWidgets.QLabel('%s:' % _("Dx"))
self.distance_x_label.setToolTip(_("This is the distance measured over the X axis."))
self.distance_y_label = QtWidgets.QLabel('%s:' % _("Dy"))
self.distance_y_label.setToolTip(_("This is the distance measured over the Y axis."))
self.angle_label = QtWidgets.QLabel('%s:' % _("Angle"))
self.angle_label.setToolTip(_("This is orientation angle of the measuring line."))
self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))
self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance."))
self.start_entry = FCEntry()
self.start_entry.setReadOnly(True)
self.start_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.start_entry.setToolTip(_("This is measuring Start point coordinates."))
grid0.addWidget(self.start_label, 3, 0)
grid0.addWidget(self.start_entry, 3, 1)
self.stop_label = QtWidgets.QLabel("%s:" % _('Stop Coords'))
self.stop_label.setToolTip(_("This is the measuring Stop point coordinates."))
self.stop_entry = FCEntry()
self.stop_entry.setReadOnly(True)
self.stop_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.stop_entry.setToolTip(_("This is the measuring Stop point coordinates."))
grid0.addWidget(self.stop_label, 4, 0)
grid0.addWidget(self.stop_entry, 4, 1)
self.distance_x_label = QtWidgets.QLabel('%s:' % _("Dx"))
self.distance_x_label.setToolTip(_("This is the distance measured over the X axis."))
self.distance_x_entry = FCEntry()
self.distance_x_entry.setReadOnly(True)
self.distance_x_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.distance_x_entry.setToolTip(_("This is the distance measured over the X axis."))
grid0.addWidget(self.distance_x_label, 5, 0)
grid0.addWidget(self.distance_x_entry, 5, 1)
self.distance_y_label = QtWidgets.QLabel('%s:' % _("Dy"))
self.distance_y_label.setToolTip(_("This is the distance measured over the Y axis."))
self.distance_y_entry = FCEntry()
self.distance_y_entry.setReadOnly(True)
self.distance_y_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.distance_y_entry.setToolTip(_("This is the distance measured over the Y axis."))
grid0.addWidget(self.distance_y_label, 6, 0)
grid0.addWidget(self.distance_y_entry, 6, 1)
self.angle_label = QtWidgets.QLabel('%s:' % _("Angle"))
self.angle_label.setToolTip(_("This is orientation angle of the measuring line."))
self.angle_entry = FCEntry()
self.angle_entry.setReadOnly(True)
self.angle_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.angle_entry.setToolTip(_("This is orientation angle of the measuring line."))
grid0.addWidget(self.angle_label, 7, 0)
grid0.addWidget(self.angle_entry, 7, 1)
self.total_distance_label = QtWidgets.QLabel("<b>%s:</b>" % _('DISTANCE'))
self.total_distance_label.setToolTip(_("This is the point to point Euclidian distance."))
self.total_distance_entry = FCEntry()
self.total_distance_entry.setReadOnly(True)
self.total_distance_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.total_distance_entry.setToolTip(_("This is the point to point Euclidian distance."))
self.measure_btn = QtWidgets.QPushButton(_("Measure"))
grid0.addWidget(self.total_distance_label, 8, 0)
grid0.addWidget(self.total_distance_entry, 8, 1)
self.measure_btn = FCButton(_("Measure"))
# self.measure_btn.setFixedWidth(70)
self.layout.addWidget(self.measure_btn)
form_layout.addRow(self.units_label, self.units_value)
form_layout.addRow(self.start_label, self.start_entry)
form_layout.addRow(self.stop_label, self.stop_entry)
form_layout.addRow(self.distance_x_label, self.distance_x_entry)
form_layout.addRow(self.distance_y_label, self.distance_y_entry)
form_layout.addRow(self.angle_label, self.angle_entry)
form_layout.addRow(self.total_distance_label, self.total_distance_entry)
# initial view of the layout
self.start_entry.set_value('(0, 0)')
self.stop_entry.set_value('(0, 0)')
self.distance_x_entry.set_value('0.0')
self.distance_y_entry.set_value('0.0')
self.angle_entry.set_value('0.0')
self.total_distance_entry.set_value('0.0')
self.layout.addStretch()
# store here the first click and second click of the measurement process
@ -137,6 +161,15 @@ class Distance(FlatCAMTool):
self.mm = None
self.mr = None
# monitor if the tool was used
self.tool_done = False
# store the grid status here
self.grid_status_memory = False
# store here if the snap button was clicked
self.snap_toggled = None
# VisPy visuals
if self.app.is_legacy is False:
self.sel_shapes = ShapeCollection(parent=self.app.plotcanvas.view.scene, layers=1)
@ -145,6 +178,7 @@ class Distance(FlatCAMTool):
self.sel_shapes = ShapeCollectionLegacy(obj=self, app=self.app, name='measurement')
self.measure_btn.clicked.connect(self.activate_measure_tool)
self.snap_center_cb.toggled.connect(self.on_snap_toggled)
def run(self, toggle=False):
self.app.report_usage("ToolDistance()")
@ -154,6 +188,8 @@ class Distance(FlatCAMTool):
self.rel_point1 = None
self.rel_point2 = None
self.tool_done = False
if self.app.tool_tab_locked is True:
return
@ -177,7 +213,7 @@ class Distance(FlatCAMTool):
# Remove anything else in the GUI
self.app.ui.tool_scroll_area.takeWidget()
# Put ourself in the GUI
# Put ourselves in the GUI
self.app.ui.tool_scroll_area.setWidget(self)
# Switch notebook to tool page
@ -195,20 +231,45 @@ class Distance(FlatCAMTool):
self.angle_entry.set_value('0.0')
self.total_distance_entry.set_value('0.0')
self.snap_center_cb.set_value(self.app.defaults['tools_dist_snap_center'])
# snap center works only for Gerber and Execellon Editor's
if self.original_call_source == 'exc_editor' or self.original_call_source == 'grb_editor':
self.snap_center_cb.show()
else:
self.snap_center_cb.hide()
# this is a hack; seems that triggering the grid will make the visuals better
# trigger it twice to return to the original state
self.app.ui.grid_snap_btn.trigger()
self.app.ui.grid_snap_btn.trigger()
if self.app.ui.grid_snap_btn.isChecked():
self.grid_status_memory = True
log.debug("Distance Tool --> tool initialized")
def on_snap_toggled(self, state):
self.app.defaults['tools_dist_snap_center'] = state
if state:
# disengage the grid snapping since it will be hard to find the drills or pads on grid
if self.app.ui.grid_snap_btn.isChecked():
self.app.ui.grid_snap_btn.trigger()
def activate_measure_tool(self):
# ENABLE the Measuring TOOL
self.active = True
# disable the measuring button
self.measure_btn.setDisabled(True)
self.measure_btn.setText('%s...' % _("Working"))
self.clicked_meas = 0
self.original_call_source = copy(self.app.call_source)
snap_center = self.app.defaults['tools_dist_snap_center']
self.on_snap_toggled(snap_center)
self.app.inform.emit(_("MEASURING: Click on the Start point ..."))
self.units = self.app.defaults['units'].lower()
@ -267,6 +328,10 @@ class Distance(FlatCAMTool):
self.active = False
self.points = []
# disable the measuring button
self.measure_btn.setDisabled(False)
self.measure_btn.setText(_("Measure"))
self.app.call_source = copy(self.original_call_source)
if self.original_call_source == 'app':
self.app.mm = self.canvas.graph_event_connect('mouse_move', self.app.on_mouse_move_over_plot)
@ -307,8 +372,16 @@ class Distance(FlatCAMTool):
# delete the measuring line
self.delete_shape()
# restore the grid status
if (self.app.ui.grid_snap_btn.isChecked() and self.grid_status_memory is False) or \
(not self.app.ui.grid_snap_btn.isChecked() and self.grid_status_memory is True):
self.app.ui.grid_snap_btn.trigger()
log.debug("Distance Tool --> exit tool")
if self.tool_done is False:
self.app.inform.emit('%s' % _("Distance Tool finished."))
def on_mouse_click_release(self, event):
# mouse click releases will be accepted only if the left button is clicked
# this is necessary because right mouse click or middle mouse click
@ -323,11 +396,71 @@ class Distance(FlatCAMTool):
pos_canvas = self.canvas.translate_coords(event_pos)
# if GRID is active we need to get the snapped positions
if self.app.grid_status() == True:
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
if self.snap_center_cb.get_value() is False:
# if GRID is active we need to get the snapped positions
if self.app.grid_status():
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
else:
pos = pos_canvas[0], pos_canvas[1]
else:
pos = pos_canvas[0], pos_canvas[1]
pos = (pos_canvas[0], pos_canvas[1])
current_pt = Point(pos)
shapes_storage = self.make_storage()
if self.original_call_source == 'exc_editor':
for storage in self.app.exc_editor.storage_dict:
__, st_closest_shape = self.app.exc_editor.storage_dict[storage].nearest(pos)
shapes_storage.insert(st_closest_shape)
__, closest_shape = shapes_storage.nearest(pos)
# if it's a drill
if isinstance(closest_shape.geo, MultiLineString):
radius = closest_shape.geo[0].length / 2.0
center_pt = closest_shape.geo.centroid
geo_buffered = center_pt.buffer(radius)
if current_pt.within(geo_buffered):
pos = (center_pt.x, center_pt.y)
# if it's a slot
elif isinstance(closest_shape.geo, Polygon):
geo_buffered = closest_shape.geo.buffer(0)
center_pt = geo_buffered.centroid
if current_pt.within(geo_buffered):
pos = (center_pt.x, center_pt.y)
elif self.original_call_source == 'grb_editor':
clicked_pads = []
for storage in self.app.grb_editor.storage_dict:
try:
for shape_stored in self.app.grb_editor.storage_dict[storage]['geometry']:
if 'solid' in shape_stored.geo:
geometric_data = shape_stored.geo['solid']
if Point(current_pt).within(geometric_data):
if isinstance(shape_stored.geo['follow'], Point):
clicked_pads.append(shape_stored.geo['follow'])
except KeyError:
pass
if len(clicked_pads) > 1:
self.tool_done = True
self.deactivate_measure_tool()
self.app.inform.emit('[WARNING_NOTCL] %s' % _("Pads overlapped. Aborting."))
return
pos = (clicked_pads[0].x, clicked_pads[0].y)
self.app.on_jump_to(custom_location=pos, fit_center=False)
# Update cursor
self.app.app_cursor.enabled = True
self.app.app_cursor.set_data(np.asarray([(pos[0], pos[1])]),
symbol='++', edge_color='#000000',
edge_width=self.app.defaults["global_cursor_width"],
size=self.app.defaults["global_cursor_size"])
self.points.append(pos)
# Reset here the relative coordinates so there is a new reference on the click position
@ -340,41 +473,46 @@ class Distance(FlatCAMTool):
self.rel_point2 = copy(self.rel_point1)
self.rel_point1 = pos
if len(self.points) == 1:
self.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
self.app.inform.emit(_("MEASURING: Click on the Destination point ..."))
elif len(self.points) == 2:
dx = self.points[1][0] - self.points[0][0]
dy = self.points[1][1] - self.points[0][1]
d = math.sqrt(dx ** 2 + dy ** 2)
self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
self.calculate_distance(pos=pos)
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | {tx3} = {d_z}".format(
tx1=_("MEASURING"),
tx2=_("Result"),
tx3=_("Distance"),
d_x='%*f' % (self.decimals, abs(dx)),
d_y='%*f' % (self.decimals, abs(dy)),
d_z='%*f' % (self.decimals, abs(d)))
def calculate_distance(self, pos):
if len(self.points) == 1:
self.start_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
self.app.inform.emit(_("MEASURING: Click on the Destination point ..."))
elif len(self.points) == 2:
self.app.app_cursor.enabled = False
dx = self.points[1][0] - self.points[0][0]
dy = self.points[1][1] - self.points[0][1]
d = math.sqrt(dx ** 2 + dy ** 2)
self.stop_entry.set_value("(%.*f, %.*f)" % (self.decimals, pos[0], self.decimals, pos[1]))
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | {tx3} = {d_z}".format(
tx1=_("MEASURING"),
tx2=_("Result"),
tx3=_("Distance"),
d_x='%*f' % (self.decimals, abs(dx)),
d_y='%*f' % (self.decimals, abs(dy)),
d_z='%*f' % (self.decimals, abs(d)))
)
self.distance_x_entry.set_value('%.*f' % (self.decimals, abs(dx)))
self.distance_y_entry.set_value('%.*f' % (self.decimals, abs(dy)))
if dx != 0.0:
try:
angle = math.degrees(math.atan(dy / dx))
self.angle_entry.set_value('%.*f' % (self.decimals, angle))
except Exception:
pass
self.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d)))
self.app.ui.rel_position_label.setText(
"<b>Dx</b>: {}&nbsp;&nbsp; <b>Dy</b>: {}&nbsp;&nbsp;&nbsp;&nbsp;".format(
'%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1])
)
self.distance_x_entry.set_value('%.*f' % (self.decimals, abs(dx)))
self.distance_y_entry.set_value('%.*f' % (self.decimals, abs(dy)))
if dx != 0.0:
try:
angle = math.degrees(math.atan(dy / dx))
self.angle_entry.set_value('%.*f' % (self.decimals, angle))
except Exception as e:
pass
self.total_distance_entry.set_value('%.*f' % (self.decimals, abs(d)))
self.app.ui.rel_position_label.setText(
"<b>Dx</b>: {}&nbsp;&nbsp; <b>Dy</b>: {}&nbsp;&nbsp;&nbsp;&nbsp;".format(
'%.*f' % (self.decimals, pos[0]), '%.*f' % (self.decimals, pos[1])
)
)
self.deactivate_measure_tool()
)
self.tool_done = True
self.deactivate_measure_tool()
def on_mouse_move_meas(self, event):
try: # May fail in case mouse not within axes
@ -391,7 +529,7 @@ class Distance(FlatCAMTool):
pos_canvas = self.app.plotcanvas.translate_coords((x, y))
if self.app.grid_status() == True:
if self.app.grid_status():
pos = self.app.geo_editor.snap(pos_canvas[0], pos_canvas[1])
# Update cursor
@ -465,7 +603,15 @@ class Distance(FlatCAMTool):
self.sel_shapes.clear()
self.sel_shapes.redraw()
def set_meas_units(self, units):
self.meas.units_label.setText("[" + self.app.options["units"].lower() + "]")
@staticmethod
def make_storage():
# ## Shape storage.
storage = FlatCAMRTreeStorage()
storage.get_points = DrawToolShape.get_pts
return storage
# def set_meas_units(self, units):
# self.meas.units_label.setText("[" + self.app.options["units"].lower() + "]")
# end of file

View File

@ -11,7 +11,8 @@ from flatcamGUI.VisPyVisuals import *
from flatcamGUI.GUIElements import FCEntry
from shapely.ops import nearest_points
from shapely.geometry import Point
from shapely.geometry import Point, MultiPolygon
from shapely.ops import cascaded_union
import math
import logging
@ -127,15 +128,6 @@ class DistanceMin(FlatCAMTool):
form_layout.addRow(self.total_distance_label, self.total_distance_entry)
form_layout.addRow(self.half_point_label, self.half_point_entry)
# initial view of the layout
self.start_entry.set_value('(0, 0)')
self.stop_entry.set_value('(0, 0)')
self.distance_x_entry.set_value('0.0')
self.distance_y_entry.set_value('0.0')
self.angle_entry.set_value('0.0')
self.total_distance_entry.set_value('0.0')
self.half_point_entry.set_value('(0, 0)')
self.layout.addStretch()
self.h_point = (0, 0)
@ -205,6 +197,17 @@ class DistanceMin(FlatCAMTool):
str(len(selected_objs))))
return
else:
if isinstance(selected_objs[0].solid_geometry, list):
try:
selected_objs[0].solid_geometry = MultiPolygon(selected_objs[0].solid_geometry)
except Exception:
selected_objs[0].solid_geometry = cascaded_union(selected_objs[0].solid_geometry)
try:
selected_objs[1].solid_geometry = MultiPolygon(selected_objs[1].solid_geometry)
except Exception:
selected_objs[1].solid_geometry = cascaded_union(selected_objs[1].solid_geometry)
first_pos, last_pos = nearest_points(selected_objs[0].solid_geometry, selected_objs[1].solid_geometry)
elif self.app.call_source == 'geo_editor':
@ -278,7 +281,7 @@ class DistanceMin(FlatCAMTool):
)
if d != 0:
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | (tx3} = {d_z}".format(
self.app.inform.emit("{tx1}: {tx2} D(x) = {d_x} | D(y) = {d_y} | {tx3} = {d_z}".format(
tx1=_("MEASURING"),
tx2=_("Result"),
tx3=_("Distance"),

View File

@ -8,7 +8,7 @@
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox
from shapely.geometry import Point
@ -43,8 +43,7 @@ class ToolExtractDrills(FlatCAMTool):
""")
self.layout.addWidget(title_label)
self.empty_lb = QtWidgets.QLabel("")
self.layout.addWidget(self.empty_lb)
self.layout.addWidget(QtWidgets.QLabel(""))
# ## Grid Layout
grid_lay = QtWidgets.QGridLayout()
@ -53,10 +52,11 @@ class ToolExtractDrills(FlatCAMTool):
grid_lay.setColumnStretch(1, 0)
# ## Gerber Object
self.gerber_object_combo = QtWidgets.QComboBox()
self.gerber_object_combo = FCComboBox()
self.gerber_object_combo.setModel(self.app.collection)
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_object_combo.setCurrentIndex(1)
self.gerber_object_combo.is_last = True
self.gerber_object_combo.obj_type = "Gerber"
self.grb_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.grb_label.setToolTip('%s.' % _("Gerber from which to extract drill holes"))
@ -77,7 +77,7 @@ class ToolExtractDrills(FlatCAMTool):
# Circular Aperture Selection
self.circular_cb = FCCheckBox('%s' % _("Circular"))
self.circular_cb.setToolTip(
_("Create drills from circular pads.")
_("Process Circular Pads.")
)
grid_lay.addWidget(self.circular_cb, 3, 0, 1, 2)
@ -85,7 +85,7 @@ class ToolExtractDrills(FlatCAMTool):
# Oblong Aperture Selection
self.oblong_cb = FCCheckBox('%s' % _("Oblong"))
self.oblong_cb.setToolTip(
_("Create drills from oblong pads.")
_("Process Oblong Pads.")
)
grid_lay.addWidget(self.oblong_cb, 4, 0, 1, 2)
@ -93,7 +93,7 @@ class ToolExtractDrills(FlatCAMTool):
# Square Aperture Selection
self.square_cb = FCCheckBox('%s' % _("Square"))
self.square_cb.setToolTip(
_("Create drills from square pads.")
_("Process Square Pads.")
)
grid_lay.addWidget(self.square_cb, 5, 0, 1, 2)
@ -101,7 +101,7 @@ class ToolExtractDrills(FlatCAMTool):
# Rectangular Aperture Selection
self.rectangular_cb = FCCheckBox('%s' % _("Rectangular"))
self.rectangular_cb.setToolTip(
_("Create drills from rectangular pads.")
_("Process Rectangular Pads.")
)
grid_lay.addWidget(self.rectangular_cb, 6, 0, 1, 2)
@ -109,7 +109,7 @@ class ToolExtractDrills(FlatCAMTool):
# Others type of Apertures Selection
self.other_cb = FCCheckBox('%s' % _("Others"))
self.other_cb.setToolTip(
_("Create drills from other types of pad shape.")
_("Process pads not in the categories above.")
)
grid_lay.addWidget(self.other_cb, 7, 0, 1, 2)
@ -126,9 +126,14 @@ class ToolExtractDrills(FlatCAMTool):
grid1.setColumnStretch(1, 1)
self.method_label = QtWidgets.QLabel('<b>%s</b>' % _("Method"))
self.method_label.setToolTip(
_("The method for processing pads. Can be:\n"
"- Fixed Diameter -> all holes will have a set size\n"
"- Fixed Annular Ring -> all holes will have a set annular ring\n"
"- Proportional -> each hole size will be a fraction of the pad size"))
grid1.addWidget(self.method_label, 2, 0, 1, 2)
# ## Axis
# ## Holes Size
self.hole_size_radio = RadioSet(
[
{'label': _("Fixed Diameter"), 'value': 'fixed'},
@ -138,15 +143,7 @@ class ToolExtractDrills(FlatCAMTool):
orientation='vertical',
stretch=False)
self.hole_size_label = QtWidgets.QLabel('%s:' % _("Hole Size"))
self.hole_size_label.setToolTip(
_("The selected method of extracting the drills. Can be:\n"
"- Fixed Diameter -> all holes will have a set size\n"
"- Fixed Annular Ring -> all holes will have a set annular ring\n"
"- Proportional -> each hole size will be a fraction of the pad size"))
grid1.addWidget(self.hole_size_label, 3, 0)
grid1.addWidget(self.hole_size_radio, 3, 1)
grid1.addWidget(self.hole_size_radio, 3, 0, 1, 2)
# grid_lay1.addWidget(QtWidgets.QLabel(''))
@ -160,7 +157,7 @@ class ToolExtractDrills(FlatCAMTool):
grid1.addWidget(self.fixed_label, 6, 0, 1, 2)
# Diameter value
self.dia_entry = FCDoubleSpinner()
self.dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.dia_entry.set_precision(self.decimals)
self.dia_entry.set_range(0.0000, 9999.9999)
@ -195,7 +192,7 @@ class ToolExtractDrills(FlatCAMTool):
self.ring_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Annular Ring"))
self.ring_label.setToolTip(
_("The size of annular ring.\n"
"The copper sliver between the drill hole exterior\n"
"The copper sliver between the hole exterior\n"
"and the margin of the copper pad.")
)
grid2.addWidget(self.ring_label, 0, 0, 1, 2)
@ -206,7 +203,7 @@ class ToolExtractDrills(FlatCAMTool):
_("The size of annular ring for circular pads.")
)
self.circular_ring_entry = FCDoubleSpinner()
self.circular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.circular_ring_entry.set_precision(self.decimals)
self.circular_ring_entry.set_range(0.0000, 9999.9999)
@ -219,7 +216,7 @@ class ToolExtractDrills(FlatCAMTool):
_("The size of annular ring for oblong pads.")
)
self.oblong_ring_entry = FCDoubleSpinner()
self.oblong_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.oblong_ring_entry.set_precision(self.decimals)
self.oblong_ring_entry.set_range(0.0000, 9999.9999)
@ -232,7 +229,7 @@ class ToolExtractDrills(FlatCAMTool):
_("The size of annular ring for square pads.")
)
self.square_ring_entry = FCDoubleSpinner()
self.square_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.square_ring_entry.set_precision(self.decimals)
self.square_ring_entry.set_range(0.0000, 9999.9999)
@ -245,7 +242,7 @@ class ToolExtractDrills(FlatCAMTool):
_("The size of annular ring for rectangular pads.")
)
self.rectangular_ring_entry = FCDoubleSpinner()
self.rectangular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.rectangular_ring_entry.set_precision(self.decimals)
self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
@ -258,7 +255,7 @@ class ToolExtractDrills(FlatCAMTool):
_("The size of annular ring for other pads.")
)
self.other_ring_entry = FCDoubleSpinner()
self.other_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.other_ring_entry.set_precision(self.decimals)
self.other_ring_entry.set_range(0.0000, 9999.9999)
@ -280,7 +277,7 @@ class ToolExtractDrills(FlatCAMTool):
grid3.addWidget(self.prop_label, 2, 0, 1, 2)
# Diameter value
self.factor_entry = FCDoubleSpinner(suffix='%')
self.factor_entry = FCDoubleSpinner(callback=self.confirmation_message, suffix='%')
self.factor_entry.set_precision(self.decimals)
self.factor_entry.set_range(0.0000, 100.0000)
self.factor_entry.setSingleStep(0.1)
@ -288,7 +285,7 @@ class ToolExtractDrills(FlatCAMTool):
self.factor_label = QtWidgets.QLabel('%s:' % _("Value"))
self.factor_label.setToolTip(
_("Proportional Diameter.\n"
"The drill diameter will be a fraction of the pad size.")
"The hole diameter will be a fraction of the pad size.")
)
grid3.addWidget(self.factor_label, 3, 0)
@ -427,8 +424,8 @@ class ToolExtractDrills(FlatCAMTool):
prop_factor = self.factor_entry.get_value() / 100.0
drills = list()
tools = dict()
drills = []
tools = {}
selection_index = self.gerber_object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.gerber_object_combo.rootModelIndex())
@ -473,7 +470,7 @@ class ToolExtractDrills(FlatCAMTool):
if 'follow' in geo_el and isinstance(geo_el['follow'], Point):
drills.append({"point": geo_el['follow'], "tool": "1"})
if 'solid_geometry' not in tools["1"]:
tools["1"]['solid_geometry'] = list()
tools["1"]['solid_geometry'] = []
else:
tools["1"]['solid_geometry'].append(geo_el['follow'])
@ -552,7 +549,7 @@ class ToolExtractDrills(FlatCAMTool):
drills.append({"point": geo_el['follow'], "tool": tool_in_drills})
if 'solid_geometry' not in tools[tool_in_drills]:
tools[tool_in_drills]['solid_geometry'] = list()
tools[tool_in_drills]['solid_geometry'] = []
else:
tools[tool_in_drills]['solid_geometry'].append(geo_el['follow'])
@ -637,7 +634,7 @@ class ToolExtractDrills(FlatCAMTool):
drills.append({"point": geo_el['follow'], "tool": tool_in_drills})
if 'solid_geometry' not in tools[tool_in_drills]:
tools[tool_in_drills]['solid_geometry'] = list()
tools[tool_in_drills]['solid_geometry'] = []
else:
tools[tool_in_drills]['solid_geometry'].append(geo_el['follow'])

View File

@ -8,7 +8,7 @@
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable
from flatcamGUI.GUIElements import FCDoubleSpinner, RadioSet, EvalEntry, FCTable, FCComboBox
from shapely.geometry import Point, Polygon, MultiPolygon, LineString
from shapely.geometry import box as box
@ -159,7 +159,7 @@ class ToolFiducials(FlatCAMTool):
"otherwise is the size of the fiducial.\n"
"The soldermask opening is double than that.")
)
self.fid_size_entry = FCDoubleSpinner()
self.fid_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.fid_size_entry.set_range(1.0000, 3.0000)
self.fid_size_entry.set_precision(self.decimals)
self.fid_size_entry.setWrapping(True)
@ -173,7 +173,7 @@ class ToolFiducials(FlatCAMTool):
self.margin_label.setToolTip(
_("Bounding box margin.")
)
self.margin_entry = FCDoubleSpinner()
self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.margin_entry.set_range(-9999.9999, 9999.9999)
self.margin_entry.set_precision(self.decimals)
self.margin_entry.setSingleStep(0.1)
@ -236,7 +236,7 @@ class ToolFiducials(FlatCAMTool):
self.line_thickness_label.setToolTip(
_("Bounding box margin.")
)
self.line_thickness_entry = FCDoubleSpinner()
self.line_thickness_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.line_thickness_entry.set_range(0.00001, 9999.9999)
self.line_thickness_entry.set_precision(self.decimals)
self.line_thickness_entry.setSingleStep(0.1)
@ -250,10 +250,11 @@ class ToolFiducials(FlatCAMTool):
grid_lay.addWidget(separator_line_1, 8, 0, 1, 2)
# Copper Gerber object
self.grb_object_combo = QtWidgets.QComboBox()
self.grb_object_combo = FCComboBox()
self.grb_object_combo.setModel(self.app.collection)
self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.grb_object_combo.setCurrentIndex(1)
self.grb_object_combo.is_last = True
self.grb_object_combo.obj_type = "Gerber"
self.grbobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("Copper Gerber"))
self.grbobj_label.setToolTip(
@ -286,10 +287,11 @@ class ToolFiducials(FlatCAMTool):
self.sm_object_label.setToolTip(
_("The Soldermask Gerber object.")
)
self.sm_object_combo = QtWidgets.QComboBox()
self.sm_object_combo = FCComboBox()
self.sm_object_combo.setModel(self.app.collection)
self.sm_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sm_object_combo.setCurrentIndex(1)
self.sm_object_combo.is_last = True
self.sm_object_combo.obj_type = "Gerber"
grid_lay.addWidget(self.sm_object_label, 13, 0, 1, 2)
grid_lay.addWidget(self.sm_object_combo, 14, 0, 1, 2)
@ -333,7 +335,7 @@ class ToolFiducials(FlatCAMTool):
self.sm_obj_set = set()
# store the flattened geometry here:
self.flat_geometry = list()
self.flat_geometry = []
# Events ID
self.mr = None
@ -353,7 +355,7 @@ class ToolFiducials(FlatCAMTool):
self.sec_position = None
self.geo_steps_per_circle = 128
self.click_points = list()
self.click_points = []
# SIGNALS
self.add_cfid_button.clicked.connect(self.add_fiducials)
@ -404,7 +406,7 @@ class ToolFiducials(FlatCAMTool):
self.fid_type_radio.set_value(self.app.defaults["tools_fiducials_type"])
self.line_thickness_entry.set_value(float(self.app.defaults["tools_fiducials_line_thickness"]))
self.click_points = list()
self.click_points = []
self.bottom_left_coords_entry.set_value('')
self.top_right_coords_entry.set_value('')
self.sec_points_coords_entry.set_value('')
@ -429,7 +431,7 @@ class ToolFiducials(FlatCAMTool):
:return: None
"""
if val == 'auto':
self.click_points = list()
self.click_points = []
try:
self.disconnect_event_handlers()
@ -451,7 +453,7 @@ class ToolFiducials(FlatCAMTool):
self.sec_position = self.pos_radio.get_value()
fid_type = self.fid_type_radio.get_value()
self.click_points = list()
self.click_points = []
# get the Gerber object on which the Fiducial will be inserted
selection_index = self.grb_object_combo.currentIndex()
@ -547,7 +549,7 @@ class ToolFiducials(FlatCAMTool):
if aperture_found:
for geo in geo_list:
dict_el = dict()
dict_el = {}
dict_el['follow'] = geo.centroid
dict_el['solid'] = geo
g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
@ -558,18 +560,18 @@ class ToolFiducials(FlatCAMTool):
else:
new_apid = '10'
g_obj.apertures[new_apid] = dict()
g_obj.apertures[new_apid] = {}
g_obj.apertures[new_apid]['type'] = 'C'
g_obj.apertures[new_apid]['size'] = fid_size
g_obj.apertures[new_apid]['geometry'] = list()
g_obj.apertures[new_apid]['geometry'] = []
for geo in geo_list:
dict_el = dict()
dict_el = {}
dict_el['follow'] = geo.centroid
dict_el['solid'] = geo
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
s_list = list()
s_list = []
if g_obj.solid_geometry:
try:
for poly in g_obj.solid_geometry:
@ -580,7 +582,7 @@ class ToolFiducials(FlatCAMTool):
s_list += geo_list
g_obj.solid_geometry = MultiPolygon(s_list)
elif fid_type == 'cross':
geo_list = list()
geo_list = []
for pt in points_list:
x = pt[0]
@ -599,7 +601,7 @@ class ToolFiducials(FlatCAMTool):
aperture_found = ap_id
break
geo_buff_list = list()
geo_buff_list = []
if aperture_found:
for geo in geo_list:
geo_buff_h = geo[0].buffer(line_thickness / 2.0)
@ -607,7 +609,7 @@ class ToolFiducials(FlatCAMTool):
geo_buff_list.append(geo_buff_h)
geo_buff_list.append(geo_buff_v)
dict_el = dict()
dict_el = {}
dict_el['follow'] = geo_buff_h.centroid
dict_el['solid'] = geo_buff_h
g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
@ -621,10 +623,10 @@ class ToolFiducials(FlatCAMTool):
else:
new_apid = '10'
g_obj.apertures[new_apid] = dict()
g_obj.apertures[new_apid] = {}
g_obj.apertures[new_apid]['type'] = 'C'
g_obj.apertures[new_apid]['size'] = line_thickness
g_obj.apertures[new_apid]['geometry'] = list()
g_obj.apertures[new_apid]['geometry'] = []
for geo in geo_list:
geo_buff_h = geo[0].buffer(line_thickness / 2.0)
@ -632,7 +634,7 @@ class ToolFiducials(FlatCAMTool):
geo_buff_list.append(geo_buff_h)
geo_buff_list.append(geo_buff_v)
dict_el = dict()
dict_el = {}
dict_el['follow'] = geo_buff_h.centroid
dict_el['solid'] = geo_buff_h
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
@ -640,7 +642,7 @@ class ToolFiducials(FlatCAMTool):
dict_el['solid'] = geo_buff_v
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
s_list = list()
s_list = []
if g_obj.solid_geometry:
try:
for poly in g_obj.solid_geometry:
@ -655,7 +657,7 @@ class ToolFiducials(FlatCAMTool):
g_obj.solid_geometry = MultiPolygon(s_list)
else:
# chess pattern fiducial type
geo_list = list()
geo_list = []
def make_square_poly(center_pt, side_size):
half_s = side_size / 2
@ -684,12 +686,12 @@ class ToolFiducials(FlatCAMTool):
aperture_found = ap_id
break
geo_buff_list = list()
geo_buff_list = []
if aperture_found:
for geo in geo_list:
geo_buff_list.append(geo)
dict_el = dict()
dict_el = {}
dict_el['follow'] = geo.centroid
dict_el['solid'] = geo
g_obj.apertures[aperture_found]['geometry'].append(deepcopy(dict_el))
@ -700,22 +702,22 @@ class ToolFiducials(FlatCAMTool):
else:
new_apid = '10'
g_obj.apertures[new_apid] = dict()
g_obj.apertures[new_apid] = {}
g_obj.apertures[new_apid]['type'] = 'R'
g_obj.apertures[new_apid]['size'] = new_ap_size
g_obj.apertures[new_apid]['width'] = fid_size
g_obj.apertures[new_apid]['height'] = fid_size
g_obj.apertures[new_apid]['geometry'] = list()
g_obj.apertures[new_apid]['geometry'] = []
for geo in geo_list:
geo_buff_list.append(geo)
dict_el = dict()
dict_el = {}
dict_el['follow'] = geo.centroid
dict_el['solid'] = geo
g_obj.apertures[new_apid]['geometry'].append(deepcopy(dict_el))
s_list = list()
s_list = []
if g_obj.solid_geometry:
try:
for poly in g_obj.solid_geometry:

View File

@ -65,15 +65,13 @@ class Film(FlatCAMTool):
grid0.setColumnStretch(1, 1)
# Type of object for which to create the film
self.tf_type_obj_combo = QtWidgets.QComboBox()
self.tf_type_obj_combo.addItem("Gerber")
self.tf_type_obj_combo.addItem("Excellon")
self.tf_type_obj_combo.addItem("Geometry")
self.tf_type_obj_combo = FCComboBox()
self.tf_type_obj_combo.addItems([_("Gerber"), _("Geometry")])
# we get rid of item1 ("Excellon") as it is not suitable for creating film
self.tf_type_obj_combo.view().setRowHidden(1, True)
# self.tf_type_obj_combo.view().setRowHidden(1, True)
self.tf_type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.tf_type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.tf_type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.tf_type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.tf_type_obj_combo_label.setToolTip(
@ -86,10 +84,10 @@ class Film(FlatCAMTool):
grid0.addWidget(self.tf_type_obj_combo, 0, 1)
# List of objects for which we can create the film
self.tf_object_combo = QtWidgets.QComboBox()
self.tf_object_combo = FCComboBox()
self.tf_object_combo.setModel(self.app.collection)
self.tf_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.tf_object_combo.setCurrentIndex(1)
self.tf_object_combo.is_last = True
self.tf_object_label = QtWidgets.QLabel('%s:' % _("Film Object"))
self.tf_object_label.setToolTip(
@ -100,15 +98,17 @@ class Film(FlatCAMTool):
# Type of Box Object to be used as an envelope for film creation
# Within this we can create negative
self.tf_type_box_combo = QtWidgets.QComboBox()
self.tf_type_box_combo.addItem("Gerber")
self.tf_type_box_combo.addItem("Excellon")
self.tf_type_box_combo.addItem("Geometry")
self.tf_type_box_combo = FCComboBox()
self.tf_type_box_combo.addItems([_("Gerber"), _("Geometry")])
# self.tf_type_box_combo.addItem("Gerber")
# self.tf_type_box_combo.addItem("Excellon")
# self.tf_type_box_combo.addItem("Geometry")
# we get rid of item1 ("Excellon") as it is not suitable for box when creating film
self.tf_type_box_combo.view().setRowHidden(1, True)
# self.tf_type_box_combo.view().setRowHidden(1, True)
self.tf_type_box_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.tf_type_box_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.tf_type_box_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.tf_type_box_combo_label = QtWidgets.QLabel(_("Box Type:"))
self.tf_type_box_combo_label.setToolTip(
@ -121,10 +121,10 @@ class Film(FlatCAMTool):
grid0.addWidget(self.tf_type_box_combo, 2, 1)
# Box
self.tf_box_combo = QtWidgets.QComboBox()
self.tf_box_combo = FCComboBox()
self.tf_box_combo.setModel(self.app.collection)
self.tf_box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.tf_box_combo.setCurrentIndex(1)
self.tf_box_combo.is_last = True
self.tf_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
self.tf_box_combo_label.setToolTip(
@ -160,7 +160,7 @@ class Film(FlatCAMTool):
grid0.addWidget(self.film_scale_cb, 6, 0, 1, 2)
self.film_scalex_label = QtWidgets.QLabel('%s:' % _("X factor"))
self.film_scalex_entry = FCDoubleSpinner()
self.film_scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.film_scalex_entry.set_range(-999.9999, 999.9999)
self.film_scalex_entry.set_precision(self.decimals)
self.film_scalex_entry.setSingleStep(0.01)
@ -169,7 +169,7 @@ class Film(FlatCAMTool):
grid0.addWidget(self.film_scalex_entry, 7, 1)
self.film_scaley_label = QtWidgets.QLabel('%s:' % _("Y factor"))
self.film_scaley_entry = FCDoubleSpinner()
self.film_scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.film_scaley_entry.set_range(-999.9999, 999.9999)
self.film_scaley_entry.set_precision(self.decimals)
self.film_scaley_entry.setSingleStep(0.01)
@ -199,7 +199,7 @@ class Film(FlatCAMTool):
grid0.addWidget(self.film_skew_cb, 10, 0, 1, 2)
self.film_skewx_label = QtWidgets.QLabel('%s:' % _("X angle"))
self.film_skewx_entry = FCDoubleSpinner()
self.film_skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.film_skewx_entry.set_range(-999.9999, 999.9999)
self.film_skewx_entry.set_precision(self.decimals)
self.film_skewx_entry.setSingleStep(0.01)
@ -208,7 +208,7 @@ class Film(FlatCAMTool):
grid0.addWidget(self.film_skewx_entry, 11, 1)
self.film_skewy_label = QtWidgets.QLabel('%s:' % _("Y angle"))
self.film_skewy_entry = FCDoubleSpinner()
self.film_skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.film_skewy_entry.set_range(-999.9999, 999.9999)
self.film_skewy_entry.set_precision(self.decimals)
self.film_skewy_entry.setSingleStep(0.01)
@ -275,7 +275,7 @@ class Film(FlatCAMTool):
grid0.addWidget(self.film_param_label, 18, 0, 1, 2)
# Scale Stroke size
self.film_scale_stroke_entry = FCDoubleSpinner()
self.film_scale_stroke_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.film_scale_stroke_entry.set_range(-999.9999, 999.9999)
self.film_scale_stroke_entry.setSingleStep(0.01)
self.film_scale_stroke_entry.set_precision(self.decimals)
@ -308,7 +308,7 @@ class Film(FlatCAMTool):
grid0.addWidget(self.film_type, 21, 1)
# Boundary for negative film generation
self.boundary_entry = FCDoubleSpinner()
self.boundary_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.boundary_entry.set_range(-999.9999, 999.9999)
self.boundary_entry.setSingleStep(0.01)
self.boundary_entry.set_precision(self.decimals)
@ -366,10 +366,12 @@ class Film(FlatCAMTool):
self.exc_label.setToolTip(
_("Remove the geometry of Excellon from the Film to create the holes in pads.")
)
self.exc_combo = QtWidgets.QComboBox()
self.exc_combo = FCComboBox()
self.exc_combo.setModel(self.app.collection)
self.exc_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.exc_combo.setCurrentIndex(1)
self.exc_combo.is_last = True
self.exc_combo.obj_type = "Excellon"
punch_grid.addWidget(self.exc_label, 1, 0)
punch_grid.addWidget(self.exc_combo, 1, 1)
@ -378,7 +380,7 @@ class Film(FlatCAMTool):
self.punch_size_label = QtWidgets.QLabel('%s:' % _("Punch Size"))
self.punch_size_label.setToolTip(_("The value here will control how big is the punch hole in the pads."))
self.punch_size_spinner = FCDoubleSpinner()
self.punch_size_spinner = FCDoubleSpinner(callback=self.confirmation_message)
self.punch_size_spinner.set_range(0, 999.9999)
self.punch_size_spinner.setSingleStep(0.1)
self.punch_size_spinner.set_precision(self.decimals)
@ -434,7 +436,7 @@ class Film(FlatCAMTool):
self.pagesize_combo = FCComboBox()
self.pagesize = dict()
self.pagesize = {}
self.pagesize.update(
{
'Bounds': None,
@ -539,15 +541,21 @@ class Film(FlatCAMTool):
self.file_type_radio.activated_custom.connect(self.on_file_type)
self.reset_button.clicked.connect(self.set_tool_ui)
def on_type_obj_index_changed(self, index):
def on_type_obj_index_changed(self):
obj_type = self.tf_type_obj_combo.currentIndex()
self.tf_object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.tf_object_combo.setCurrentIndex(0)
self.tf_object_combo.obj_type = {
_("Gerber"): "Gerber", _("Geometry"): "Geometry"
}[self.tf_type_obj_combo.get_value()]
def on_type_box_index_changed(self, index):
def on_type_box_index_changed(self):
obj_type = self.tf_type_box_combo.currentIndex()
self.tf_box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.tf_box_combo.setCurrentIndex(0)
self.tf_box_combo.obj_type = {
_("Gerber"): "Gerber", _("Geometry"): "Geometry"
}[self.tf_type_obj_combo.get_value()]
def run(self, toggle=True):
self.app.report_usage("ToolFilm()")
@ -610,6 +618,10 @@ class Film(FlatCAMTool):
self.orientation_radio.set_value(self.app.defaults["tools_film_orientation"])
self.pagesize_combo.set_value(self.app.defaults["tools_film_pagesize"])
# run once to update the obj_type attribute in the FCCombobox so the last object is showed in cb
self.on_type_obj_index_changed()
self.on_type_box_index_changed()
def on_film_type(self, val):
type_of_film = val
@ -786,7 +798,7 @@ class Film(FlatCAMTool):
punch_size = float(self.punch_size_spinner.get_value())
punching_geo = list()
punching_geo = []
for apid in film_obj.apertures:
if film_obj.apertures[apid]['type'] == 'C':
if punch_size >= float(film_obj.apertures[apid]['size']):

View File

@ -46,8 +46,7 @@ class ToolImage(FlatCAMTool):
# Type of object to create for the image
self.tf_type_obj_combo = FCComboBox()
self.tf_type_obj_combo.addItem("Gerber")
self.tf_type_obj_combo.addItem("Geometry")
self.tf_type_obj_combo.addItems([_("Gerber"), _("Geometry")])
self.tf_type_obj_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.tf_type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
@ -61,7 +60,7 @@ class ToolImage(FlatCAMTool):
ti_form_layout.addRow(self.tf_type_obj_combo_label, self.tf_type_obj_combo)
# DPI value of the imported image
self.dpi_entry = FCSpinner()
self.dpi_entry = FCSpinner(callback=self.confirmation_message_int)
self.dpi_entry.set_range(0, 99999)
self.dpi_label = QtWidgets.QLabel('%s:' % _("DPI value"))
self.dpi_label.setToolTip(_("Specify a DPI value for the image.") )
@ -87,7 +86,7 @@ class ToolImage(FlatCAMTool):
ti2_form_layout.addRow(self.image_type_label, self.image_type)
# Mask value of the imported image when image monochrome
self.mask_bw_entry = FCSpinner()
self.mask_bw_entry = FCSpinner(callback=self.confirmation_message_int)
self.mask_bw_entry.set_range(0, 255)
self.mask_bw_label = QtWidgets.QLabel("%s <b>B/W</b>:" % _('Mask value'))
@ -102,7 +101,7 @@ class ToolImage(FlatCAMTool):
ti2_form_layout.addRow(self.mask_bw_label, self.mask_bw_entry)
# Mask value of the imported image for RED color when image color
self.mask_r_entry = FCSpinner()
self.mask_r_entry = FCSpinner(callback=self.confirmation_message_int)
self.mask_r_entry.set_range(0, 255)
self.mask_r_label = QtWidgets.QLabel("%s <b>R:</b>" % _('Mask value'))
@ -115,7 +114,7 @@ class ToolImage(FlatCAMTool):
ti2_form_layout.addRow(self.mask_r_label, self.mask_r_entry)
# Mask value of the imported image for GREEN color when image color
self.mask_g_entry = FCSpinner()
self.mask_g_entry = FCSpinner(callback=self.confirmation_message_int)
self.mask_g_entry.set_range(0, 255)
self.mask_g_label = QtWidgets.QLabel("%s <b>G:</b>" % _('Mask value'))
@ -128,7 +127,7 @@ class ToolImage(FlatCAMTool):
ti2_form_layout.addRow(self.mask_g_label, self.mask_g_entry)
# Mask value of the imported image for BLUE color when image color
self.mask_b_entry = FCSpinner()
self.mask_b_entry = FCSpinner(callback=self.confirmation_message_int)
self.mask_b_entry.set_range(0, 255)
self.mask_b_label = QtWidgets.QLabel("%s <b>B:</b>" % _('Mask value'))
@ -223,7 +222,7 @@ class ToolImage(FlatCAMTool):
:type type_of_obj: str
:return: None
"""
mask = list()
mask = []
self.app.log.debug("on_file_importimage()")
_filter = "Image Files(*.BMP *.PNG *.JPG *.JPEG);;" \
@ -238,7 +237,7 @@ class ToolImage(FlatCAMTool):
filename, _f = QtWidgets.QFileDialog.getOpenFileName(caption=_("Import IMAGE"), filter=filter)
filename = str(filename)
type_obj = self.tf_type_obj_combo.get_value().lower()
type_obj = self.tf_type_obj_combo.get_value()
dpi = self.dpi_entry.get_value()
mode = self.image_type.get_value()
mask = [self.mask_bw_entry.get_value(), self.mask_r_entry.get_value(), self.mask_g_entry.get_value(),
@ -250,7 +249,7 @@ class ToolImage(FlatCAMTool):
self.app.worker_task.emit({'fcn': self.import_image,
'params': [filename, type_obj, dpi, mode, mask]})
def import_image(self, filename, o_type='gerber', dpi=96, mode='black', mask=None, outname=None):
def import_image(self, filename, o_type=_("Gerber"), dpi=96, mode='black', mask=None, outname=None):
"""
Adds a new Geometry Object to the projects and populates
it with shapes extracted from the SVG file.
@ -269,10 +268,10 @@ class ToolImage(FlatCAMTool):
if mask is None:
mask = [250, 250, 250, 250]
if o_type is None or o_type == "geometry":
if o_type is None or o_type == _("Geometry"):
obj_type = "geometry"
elif o_type == "gerber":
obj_type = o_type
elif o_type == _("Gerber"):
obj_type = "gerber"
else:
self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Not supported type is picked as parameter. "

View File

@ -0,0 +1,304 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 2/14/2020 #
# MIT Licence #
# ##########################################################
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCButton, FCDoubleSpinner, RadioSet, FCComboBox
from shapely.geometry import box
from copy import deepcopy
import logging
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolInvertGerber(FlatCAMTool):
toolName = _("Invert Gerber Tool")
def __init__(self, app):
self.app = app
self.decimals = self.app.decimals
FlatCAMTool.__init__(self, app)
self.tools_frame = QtWidgets.QFrame()
self.tools_frame.setContentsMargins(0, 0, 0, 0)
self.layout.addWidget(self.tools_frame)
self.tools_box = QtWidgets.QVBoxLayout()
self.tools_box.setContentsMargins(0, 0, 0, 0)
self.tools_frame.setLayout(self.tools_box)
# Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
self.tools_box.addWidget(title_label)
# Grid Layout
grid0 = QtWidgets.QGridLayout()
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
self.tools_box.addLayout(grid0)
grid0.addWidget(QtWidgets.QLabel(''), 0, 0, 1, 2)
# Target Gerber Object
self.gerber_combo = FCComboBox()
self.gerber_combo.setModel(self.app.collection)
self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_combo.is_last = True
self.gerber_combo.obj_type = "Gerber"
self.gerber_label = QtWidgets.QLabel('<b>%s:</b>' % _("GERBER"))
self.gerber_label.setToolTip(
_("Gerber object that will be inverted.")
)
grid0.addWidget(self.gerber_label, 1, 0, 1, 2)
grid0.addWidget(self.gerber_combo, 2, 0, 1, 2)
grid0.addWidget(QtWidgets.QLabel(""), 3, 0, 1, 2)
self.param_label = QtWidgets.QLabel("<b>%s:</b>" % _("Parameters"))
self.param_label.setToolTip('%s.' % _("Parameters for this tool"))
grid0.addWidget(self.param_label, 4, 0, 1, 2)
# Margin
self.margin_label = QtWidgets.QLabel('%s:' % _('Margin'))
self.margin_label.setToolTip(
_("Distance by which to avoid\n"
"the edges of the Gerber object.")
)
self.margin_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.margin_entry.set_precision(self.decimals)
self.margin_entry.set_range(0.0000, 9999.9999)
self.margin_entry.setObjectName(_("Margin"))
grid0.addWidget(self.margin_label, 5, 0, 1, 2)
grid0.addWidget(self.margin_entry, 6, 0, 1, 2)
self.join_label = QtWidgets.QLabel('%s:' % _("Lines Join Style"))
self.join_label.setToolTip(
_("The way that the lines in the object outline will be joined.\n"
"Can be:\n"
"- rounded -> an arc is added between two joining lines\n"
"- square -> the lines meet in 90 degrees angle\n"
"- bevel -> the lines are joined by a third line")
)
self.join_radio = RadioSet([
{'label': 'Rounded', 'value': 'r'},
{'label': 'Square', 'value': 's'},
{'label': 'Bevel', 'value': 'b'}
], orientation='vertical', stretch=False)
grid0.addWidget(self.join_label, 7, 0, 1, 2)
grid0.addWidget(self.join_radio, 8, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 9, 0, 1, 2)
self.invert_btn = FCButton(_('Invert Gerber'))
self.invert_btn.setToolTip(
_("Will invert the Gerber object: areas that have copper\n"
"will be empty of copper and previous empty area will be\n"
"filled with copper.")
)
self.invert_btn.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
grid0.addWidget(self.invert_btn, 10, 0, 1, 2)
self.tools_box.addStretch()
# ## Reset Tool
self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.tools_box.addWidget(self.reset_button)
self.invert_btn.clicked.connect(self.on_grb_invert)
self.reset_button.clicked.connect(self.set_tool_ui)
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='', **kwargs)
def run(self, toggle=True):
self.app.report_usage("ToolInvertGerber()")
log.debug("ToolInvertGerber() is running ...")
if toggle:
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Invert Tool"))
def set_tool_ui(self):
self.margin_entry.set_value(float(self.app.defaults["tools_invert_margin"]))
self.join_radio.set_value(self.app.defaults["tools_invert_join_style"])
def on_grb_invert(self):
margin = self.margin_entry.get_value()
if round(margin, self.decimals) == 0.0:
margin = 1E-10
join_style = {'r': 1, 'b': 3, 's': 2}[self.join_radio.get_value()]
if join_style is None:
join_style = 'r'
grb_circle_steps = int(self.app.defaults["gerber_circle_steps"])
obj_name = self.gerber_combo.currentText()
outname = obj_name + "_inverted"
# Get source object.
try:
grb_obj = self.app.collection.get_by_name(obj_name)
except Exception as e:
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Could not retrieve object"), str(obj_name)))
return "Could not retrieve object: %s with error: %s" % (obj_name, str(e))
if grb_obj is None:
if obj_name == '':
obj_name = 'None'
self.app.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Object not found"), str(obj_name)))
return
xmin, ymin, xmax, ymax = grb_obj.bounds()
grb_box = box(xmin, ymin, xmax, ymax).buffer(margin, resolution=grb_circle_steps, join_style=join_style)
try:
__ = iter(grb_obj.solid_geometry)
except TypeError:
grb_obj.solid_geometry = list(grb_obj.solid_geometry)
new_solid_geometry = deepcopy(grb_box)
for poly in grb_obj.solid_geometry:
new_solid_geometry = new_solid_geometry.difference(poly)
new_options = {}
for opt in grb_obj.options:
new_options[opt] = deepcopy(grb_obj.options[opt])
new_apertures = {}
# for apid, val in grb_obj.apertures.items():
# new_apertures[apid] = {}
# for key in val:
# if key == 'geometry':
# new_apertures[apid]['geometry'] = []
# for elem in val['geometry']:
# geo_elem = {}
# if 'follow' in elem:
# try:
# geo_elem['clear'] = elem['follow'].buffer(val['size'] / 2.0).exterior
# except AttributeError:
# # TODO should test if width or height is bigger
# geo_elem['clear'] = elem['follow'].buffer(val['width'] / 2.0).exterior
# if 'clear' in elem:
# if isinstance(elem['clear'], Polygon):
# try:
# geo_elem['solid'] = elem['clear'].buffer(val['size'] / 2.0, grb_circle_steps)
# except AttributeError:
# # TODO should test if width or height is bigger
# geo_elem['solid'] = elem['clear'].buffer(val['width'] / 2.0, grb_circle_steps)
# else:
# geo_elem['follow'] = elem['clear']
# new_apertures[apid]['geometry'].append(deepcopy(geo_elem))
# else:
# new_apertures[apid][key] = deepcopy(val[key])
if '0' not in new_apertures:
new_apertures['0'] = {}
new_apertures['0']['type'] = 'C'
new_apertures['0']['size'] = 0.0
new_apertures['0']['geometry'] = []
try:
for poly in new_solid_geometry:
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
new_apertures['0']['geometry'].append(new_el)
except TypeError:
new_el = {}
new_el['solid'] = new_solid_geometry
new_el['follow'] = new_solid_geometry.exterior
new_apertures['0']['geometry'].append(new_el)
for td in new_apertures:
print(td, new_apertures[td])
def init_func(new_obj, app_obj):
new_obj.options.update(new_options)
new_obj.options['name'] = outname
new_obj.fill_color = deepcopy(grb_obj.fill_color)
new_obj.outline_color = deepcopy(grb_obj.outline_color)
new_obj.apertures = deepcopy(new_apertures)
new_obj.solid_geometry = deepcopy(new_solid_geometry)
new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None,
local_use=new_obj, use_thread=False)
self.app.new_object('gerber', outname, init_func)
def reset_fields(self):
self.gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
@staticmethod
def poly2rings(poly):
return [poly.exterior] + [interior for interior in poly.interiors]
# end of file

View File

@ -188,6 +188,16 @@ class ToolMove(FlatCAMTool):
sel_obj.options['ymin'] = b
sel_obj.options['xmax'] = c
sel_obj.options['ymax'] = d
# update the source_file with the new positions
for sel_obj in obj_list:
out_name = sel_obj.options["name"]
if sel_obj.kind == 'gerber':
sel_obj.source_file = self.app.export_gerber(
obj_name=out_name, filename=None, local_use=sel_obj, use_thread=False)
elif sel_obj.kind == 'excellon':
sel_obj.source_file = self.app.export_excellon(
obj_name=out_name, filename=None, local_use=sel_obj, use_thread=False)
except Exception as e:
log.debug('[ERROR_NOTCL] %s --> %s' % ('ToolMove.on_left_click()', str(e)))
return "fail"

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
from PyQt5 import QtWidgets, QtCore, QtGui
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import OptionalHideInputSection, FCTextArea, FCEntry, FCSpinner, FCCheckBox
from flatcamGUI.GUIElements import OptionalHideInputSection, FCTextArea, FCEntry, FCSpinner, FCCheckBox, FCComboBox
from FlatCAMObj import FlatCAMGerber
import FlatCAMApp
@ -63,10 +63,11 @@ class ToolOptimal(FlatCAMTool):
form_lay.addRow(QtWidgets.QLabel(""))
# ## Gerber Object to mirror
self.gerber_object_combo = QtWidgets.QComboBox()
self.gerber_object_combo = FCComboBox()
self.gerber_object_combo.setModel(self.app.collection)
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_object_combo.setCurrentIndex(1)
self.gerber_object_combo.is_last = True
self.gerber_object_combo.obj_type = "Gerber"
self.gerber_object_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.gerber_object_label.setToolTip(
@ -78,7 +79,7 @@ class ToolOptimal(FlatCAMTool):
self.precision_label = QtWidgets.QLabel('%s:' % _("Precision"))
self.precision_label.setToolTip(_("Number of decimals kept for found distances."))
self.precision_spinner = FCSpinner()
self.precision_spinner = FCSpinner(callback=self.confirmation_message_int)
self.precision_spinner.set_range(2, 10)
self.precision_spinner.setWrapping(True)
form_lay.addRow(self.precision_label, self.precision_spinner)
@ -259,7 +260,7 @@ class ToolOptimal(FlatCAMTool):
# dict to hold the distances between every two elements in Gerber as keys and the actual locations where that
# distances happen as values
self.min_dict = dict()
self.min_dict = {}
# ############################################################################
# ############################ Signals #######################################
@ -282,9 +283,6 @@ class ToolOptimal(FlatCAMTool):
def run(self, toggle=True):
self.app.report_usage("ToolOptimal()")
self.result_entry.set_value(0.0)
self.freq_entry.set_value('0')
if toggle:
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
if self.app.ui.splitter.sizes()[0] == 0:
@ -310,6 +308,9 @@ class ToolOptimal(FlatCAMTool):
self.app.ui.notebook.setTabText(2, _("Optimal Tool"))
def set_tool_ui(self):
self.result_entry.set_value(0.0)
self.freq_entry.set_value('0')
self.precision_spinner.set_value(int(self.app.defaults["tools_opt_precision"]))
self.locations_textb.clear()
# new cursor - select all document
@ -354,7 +355,7 @@ class ToolOptimal(FlatCAMTool):
old_disp_number = 0
pol_nr = 0
app_obj.proc_container.update_view_text(' %d%%' % 0)
total_geo = list()
total_geo = []
for ap in list(fcobj.apertures.keys()):
if 'geometry' in fcobj.apertures[ap]:
@ -388,7 +389,7 @@ class ToolOptimal(FlatCAMTool):
'%s: %s' % (_("Optimal Tool. Finding the distances between each two elements. Iterations"),
str(geo_len)))
self.min_dict = dict()
self.min_dict = {}
idx = 1
for geo in total_geo:
for s_geo in total_geo[idx:]:

View File

@ -105,7 +105,7 @@ class ToolPDF(FlatCAMTool):
self.restore_gs_re = re.compile(r'^.*Q.*$')
# graphic stack where we save parameters like transformation, line_width
self.gs = dict()
self.gs = {}
# each element is a list composed of sublist elements
# (each sublist has 2 lists each having 2 elements: first is offset like:
# offset_geo = [off_x, off_y], second element is scale list with 2 elements, like: scale_geo = [sc_x, sc_yy])
@ -434,12 +434,12 @@ class ToolPDF(FlatCAMTool):
traceback.print_exc()
def parse_pdf(self, pdf_content):
path = dict()
path = {}
path['lines'] = [] # it's a list of lines subpaths
path['bezier'] = [] # it's a list of bezier arcs subpaths
path['rectangle'] = [] # it's a list of rectangle subpaths
subpath = dict()
subpath = {}
subpath['lines'] = [] # it's a list of points
subpath['bezier'] = [] # it's a list of sublists each like this [start, c1, c2, stop]
subpath['rectangle'] = [] # it's a list of sublists of points
@ -473,9 +473,9 @@ class ToolPDF(FlatCAMTool):
# store the apertures with clear geometry here
# we are interested only in the circular geometry (drill holes) therefore we target only Bezier subpaths
clear_apertures_dict = dict()
clear_apertures_dict = {}
# everything will be stored in the '0' aperture since we are dealing with clear polygons not strokes
clear_apertures_dict['0'] = dict()
clear_apertures_dict['0'] = {}
clear_apertures_dict['0']['size'] = 0.0
clear_apertures_dict['0']['type'] = 'C'
clear_apertures_dict['0']['geometry'] = []
@ -515,7 +515,7 @@ class ToolPDF(FlatCAMTool):
apertures_dict.clear()
layer_nr += 1
object_dict[layer_nr] = dict()
object_dict[layer_nr] = {}
old_color = copy(color)
# we make sure that the following geometry is added to the right storage
flag_clear_geo = False
@ -778,7 +778,7 @@ class ToolPDF(FlatCAMTool):
if match:
# scale the size here; some PDF printers apply transformation after the size is declared
applied_size = size * scale_geo[0] * self.point_to_unit_factor
path_geo = list()
path_geo = []
if current_subpath == 'lines':
if path['lines']:
for subp in path['lines']:
@ -859,12 +859,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
@ -879,12 +879,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
@ -896,12 +896,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
@ -913,7 +913,7 @@ class ToolPDF(FlatCAMTool):
if match:
# scale the size here; some PDF printers apply transformation after the size is declared
applied_size = size * scale_geo[0] * self.point_to_unit_factor
path_geo = list()
path_geo = []
if current_subpath == 'lines':
if path['lines']:
@ -1007,11 +1007,11 @@ class ToolPDF(FlatCAMTool):
if path_geo:
try:
for g in path_geo:
new_el = dict()
new_el = {}
new_el['clear'] = g
clear_apertures_dict['0']['geometry'].append(new_el)
except TypeError:
new_el = dict()
new_el = {}
new_el['clear'] = path_geo
clear_apertures_dict['0']['geometry'].append(new_el)
@ -1022,11 +1022,11 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['clear'] = poly
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['clear'] = pdf_geo
apertures_dict['0']['geometry'].append(deepcopy(new_el))
except KeyError:
@ -1038,11 +1038,11 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['clear'] = poly
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['clear'] = pdf_geo
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
@ -1051,12 +1051,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))
@ -1069,12 +1069,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))
@ -1085,8 +1085,8 @@ class ToolPDF(FlatCAMTool):
if match:
# scale the size here; some PDF printers apply transformation after the size is declared
applied_size = size * scale_geo[0] * self.point_to_unit_factor
path_geo = list()
fill_geo = list()
path_geo = []
fill_geo = []
if current_subpath == 'lines':
if path['lines']:
@ -1222,12 +1222,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict[copy(found_aperture)]['geometry'].append(deepcopy(new_el))
@ -1242,12 +1242,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
@ -1259,12 +1259,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict[str(aperture)]['geometry'].append(deepcopy(new_el))
@ -1279,11 +1279,11 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in fill_geo:
new_el = dict()
new_el = {}
new_el['clear'] = poly
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['clear'] = pdf_geo
apertures_dict['0']['geometry'].append(deepcopy(new_el))
except KeyError:
@ -1295,11 +1295,11 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in fill_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['clear'] = poly
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['clear'] = pdf_geo
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
@ -1307,12 +1307,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in path_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in fill_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))
@ -1325,12 +1325,12 @@ class ToolPDF(FlatCAMTool):
for pdf_geo in fill_geo:
if isinstance(pdf_geo, MultiPolygon):
for poly in pdf_geo:
new_el = dict()
new_el = {}
new_el['solid'] = poly
new_el['follow'] = poly.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))
else:
new_el = dict()
new_el = {}
new_el['solid'] = pdf_geo
new_el['follow'] = pdf_geo.exterior
apertures_dict['0']['geometry'].append(deepcopy(new_el))

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@
from PyQt5 import QtWidgets, QtGui, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection
from flatcamGUI.GUIElements import FCSpinner, FCDoubleSpinner, RadioSet, FCCheckBox, OptionalInputSection, FCComboBox
from FlatCAMObj import FlatCAMGeometry, FlatCAMGerber, FlatCAMExcellon
import FlatCAMApp
from copy import deepcopy
@ -49,12 +49,24 @@ class Panelize(FlatCAMTool):
""")
self.layout.addWidget(title_label)
self.layout.addWidget(QtWidgets.QLabel(''))
self.object_label = QtWidgets.QLabel('<b>%s:</b>' % _("Source Object"))
self.object_label.setToolTip(
_("Specify the type of object to be panelized\n"
"It can be of type: Gerber, Excellon or Geometry.\n"
"The selection here decide the type of objects that will be\n"
"in the Object combobox.")
)
self.layout.addWidget(self.object_label)
# Form Layout
form_layout_0 = QtWidgets.QFormLayout()
self.layout.addLayout(form_layout_0)
# Type of object to be panelized
self.type_obj_combo = QtWidgets.QComboBox()
self.type_obj_combo = FCComboBox()
self.type_obj_combo.addItem("Gerber")
self.type_obj_combo.addItem("Excellon")
self.type_obj_combo.addItem("Geometry")
@ -63,27 +75,21 @@ class Panelize(FlatCAMTool):
self.type_obj_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/drill16.png"))
self.type_obj_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.type_obj_combo_label = QtWidgets.QLabel('%s:' % _("Object Type"))
self.type_obj_combo_label.setToolTip(
_("Specify the type of object to be panelized\n"
"It can be of type: Gerber, Excellon or Geometry.\n"
"The selection here decide the type of objects that will be\n"
"in the Object combobox.")
)
form_layout_0.addRow(self.type_obj_combo_label, self.type_obj_combo)
self.type_object_label = QtWidgets.QLabel('%s:' % _("Object Type"))
form_layout_0.addRow(self.type_object_label, self.type_obj_combo)
# Object to be panelized
self.object_combo = QtWidgets.QComboBox()
self.object_combo = FCComboBox()
self.object_combo.setModel(self.app.collection)
self.object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(1)
self.object_combo.is_last = True
self.object_label = QtWidgets.QLabel('%s:' % _("Object"))
self.object_label.setToolTip(
self.object_combo.setToolTip(
_("Object to be panelized. This means that it will\n"
"be duplicated in an array of rows and columns.")
)
form_layout_0.addRow(self.object_label, self.object_combo)
form_layout_0.addRow(self.object_combo)
form_layout_0.addRow(QtWidgets.QLabel(""))
# Form Layout
@ -108,15 +114,13 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.reference_radio)
# Type of Box Object to be used as an envelope for panelization
self.type_box_combo = QtWidgets.QComboBox()
self.type_box_combo.addItem("Gerber")
self.type_box_combo.addItem("Excellon")
self.type_box_combo.addItem("Geometry")
self.type_box_combo = FCComboBox()
self.type_box_combo.addItems([_("Gerber"), _("Geometry")])
# we get rid of item1 ("Excellon") as it is not suitable for use as a "box" for panelizing
self.type_box_combo.view().setRowHidden(1, True)
# self.type_box_combo.view().setRowHidden(1, True)
self.type_box_combo.setItemIcon(0, QtGui.QIcon(self.app.resource_location + "/flatcam_icon16.png"))
self.type_box_combo.setItemIcon(2, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.type_box_combo.setItemIcon(1, QtGui.QIcon(self.app.resource_location + "/geometry16.png"))
self.type_box_combo_label = QtWidgets.QLabel('%s:' % _("Box Type"))
self.type_box_combo_label.setToolTip(
@ -128,17 +132,16 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.type_box_combo_label, self.type_box_combo)
# Box
self.box_combo = QtWidgets.QComboBox()
self.box_combo = FCComboBox()
self.box_combo.setModel(self.app.collection)
self.box_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(1)
self.box_combo.is_last = True
self.box_combo_label = QtWidgets.QLabel('%s:' % _("Box Object"))
self.box_combo_label.setToolTip(
self.box_combo.setToolTip(
_("The actual object that is used a container for the\n "
"selected object that is to be panelized.")
)
form_layout.addRow(self.box_combo_label, self.box_combo)
form_layout.addRow(self.box_combo)
form_layout.addRow(QtWidgets.QLabel(""))
panel_data_label = QtWidgets.QLabel("<b>%s:</b>" % _("Panel Data"))
@ -153,7 +156,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(panel_data_label)
# Spacing Columns
self.spacing_columns = FCDoubleSpinner()
self.spacing_columns = FCDoubleSpinner(callback=self.confirmation_message)
self.spacing_columns.set_range(0, 9999)
self.spacing_columns.set_precision(4)
@ -165,7 +168,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.spacing_columns_label, self.spacing_columns)
# Spacing Rows
self.spacing_rows = FCDoubleSpinner()
self.spacing_rows = FCDoubleSpinner(callback=self.confirmation_message)
self.spacing_rows.set_range(0, 9999)
self.spacing_rows.set_precision(4)
@ -177,7 +180,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.spacing_rows_label, self.spacing_rows)
# Columns
self.columns = FCSpinner()
self.columns = FCSpinner(callback=self.confirmation_message_int)
self.columns.set_range(0, 9999)
self.columns_label = QtWidgets.QLabel('%s:' % _("Columns"))
@ -187,7 +190,7 @@ class Panelize(FlatCAMTool):
form_layout.addRow(self.columns_label, self.columns)
# Rows
self.rows = FCSpinner()
self.rows = FCSpinner(callback=self.confirmation_message_int)
self.rows.set_range(0, 9999)
self.rows_label = QtWidgets.QLabel('%s:' % _("Rows"))
@ -220,7 +223,7 @@ class Panelize(FlatCAMTool):
)
form_layout.addRow(self.constrain_cb)
self.x_width_entry = FCDoubleSpinner()
self.x_width_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.x_width_entry.set_precision(4)
self.x_width_entry.set_range(0, 9999)
@ -231,7 +234,7 @@ class Panelize(FlatCAMTool):
)
form_layout.addRow(self.x_width_lbl, self.x_width_entry)
self.y_height_entry = FCDoubleSpinner()
self.y_height_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.y_height_entry.set_range(0, 9999)
self.y_height_entry.set_precision(4)
@ -358,10 +361,18 @@ class Panelize(FlatCAMTool):
self.app.defaults["tools_panelize_panel_type"] else 'gerber'
self.panel_type_radio.set_value(panel_type)
# run once the following so the obj_type attribute is updated in the FCComboBoxes
# such that the last loaded object is populated in the combo boxes
self.on_type_obj_index_changed()
self.on_type_box_index_changed()
def on_type_obj_index_changed(self):
obj_type = self.type_obj_combo.currentIndex()
self.object_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.object_combo.setCurrentIndex(0)
self.object_combo.obj_type = {
_("Gerber"): "Gerber", _("Excellon"): "Excellon", _("Geometry"): "Geometry"
}[self.type_obj_combo.get_value()]
# hide the panel type for Excellons, the panel can be only of type Geometry
if self.type_obj_combo.currentText() != 'Excellon':
@ -376,18 +387,19 @@ class Panelize(FlatCAMTool):
obj_type = self.type_box_combo.currentIndex()
self.box_combo.setRootModelIndex(self.app.collection.index(obj_type, 0, QtCore.QModelIndex()))
self.box_combo.setCurrentIndex(0)
self.box_combo.obj_type = {
_("Gerber"): "Gerber", _("Geometry"): "Geometry"
}[self.type_box_combo.get_value()]
def on_reference_radio_changed(self, current_val):
if current_val == 'object':
self.type_box_combo.setDisabled(False)
self.type_box_combo_label.setDisabled(False)
self.box_combo.setDisabled(False)
self.box_combo_label.setDisabled(False)
else:
self.type_box_combo.setDisabled(True)
self.type_box_combo_label.setDisabled(True)
self.box_combo.setDisabled(True)
self.box_combo_label.setDisabled(True)
def on_panelize(self):
name = self.object_combo.currentText()
@ -470,13 +482,13 @@ class Panelize(FlatCAMTool):
if isinstance(panel_obj, FlatCAMExcellon) or isinstance(panel_obj, FlatCAMGeometry):
# make a copy of the panelized Excellon or Geometry tools
copied_tools = dict()
copied_tools = {}
for tt, tt_val in list(panel_obj.tools.items()):
copied_tools[tt] = deepcopy(tt_val)
if isinstance(panel_obj, FlatCAMGerber):
# make a copy of the panelized Gerber apertures
copied_apertures = dict()
copied_apertures = {}
for tt, tt_val in list(panel_obj.apertures.items()):
copied_apertures[tt] = deepcopy(tt_val)
@ -574,7 +586,7 @@ class Panelize(FlatCAMTool):
def translate_recursion(geom):
if type(geom) == list:
geoms = list()
geoms = []
for local_geom in geom:
res_geo = translate_recursion(local_geom)
try:
@ -597,7 +609,7 @@ class Panelize(FlatCAMTool):
elif isinstance(panel_obj, FlatCAMGerber):
obj_fin.apertures = copied_apertures
for ap in obj_fin.apertures:
obj_fin.apertures[ap]['geometry'] = list()
obj_fin.apertures[ap]['geometry'] = []
# find the number of polygons in the source solid_geometry
geo_len = 0
@ -733,7 +745,7 @@ class Panelize(FlatCAMTool):
# graceful abort requested by the user
raise FlatCAMApp.GracefulException
new_el = dict()
new_el = {}
if 'solid' in el:
geo_aper = translate_recursion(el['solid'])
new_el['solid'] = geo_aper

View File

@ -90,7 +90,7 @@ class PcbWizard(FlatCAMTool):
self.layout.addLayout(form_layout1)
# Integral part of the coordinates
self.int_entry = FCSpinner()
self.int_entry = FCSpinner(callback=self.confirmation_message_int)
self.int_entry.set_range(1, 10)
self.int_label = QtWidgets.QLabel('%s:' % _("Int. digits"))
self.int_label.setToolTip(
@ -99,7 +99,7 @@ class PcbWizard(FlatCAMTool):
form_layout1.addRow(self.int_label, self.int_entry)
# Fractional part of the coordinates
self.frac_entry = FCSpinner()
self.frac_entry = FCSpinner(callback=self.confirmation_message_int)
self.frac_entry.set_range(1, 10)
self.frac_label = QtWidgets.QLabel('%s:' % _("Frac. digits"))
self.frac_label.setToolTip(

View File

@ -7,6 +7,7 @@
from PyQt5 import QtGui, QtCore, QtWidgets
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCTree
from shapely.geometry import MultiPolygon, Polygon
from shapely.ops import cascaded_union
@ -64,11 +65,7 @@ class Properties(FlatCAMTool):
self.properties_box.addLayout(self.vlay)
self.treeWidget = QtWidgets.QTreeWidget()
self.treeWidget.setColumnCount(2)
self.treeWidget.setHeaderHidden(True)
self.treeWidget.header().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
self.treeWidget.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Expanding)
self.treeWidget = FCTree(columns=2)
self.vlay.addWidget(self.treeWidget)
self.vlay.setStretch(0, 0)
@ -132,6 +129,10 @@ class Properties(FlatCAMTool):
for obj in obj_list:
self.addItems(obj)
self.app.inform.emit('[success] %s' % _("Object Properties are displayed."))
# make sure that the FCTree widget columns are resized to content
self.treeWidget.resize_sig.emit()
self.app.ui.notebook.setTabText(2, _("Properties Tool"))
def addItems(self, obj):
@ -146,36 +147,50 @@ class Properties(FlatCAMTool):
font.setBold(True)
# main Items categories
obj_type = self.addParent(parent, _('TYPE'), expanded=True, color=QtGui.QColor("#000000"), font=font)
obj_name = self.addParent(parent, _('NAME'), expanded=True, color=QtGui.QColor("#000000"), font=font)
dims = self.addParent(parent, _('Dimensions'), expanded=True, color=QtGui.QColor("#000000"), font=font)
units = self.addParent(parent, _('Units'), expanded=True, color=QtGui.QColor("#000000"), font=font)
options = self.addParent(parent, _('Options'), color=QtGui.QColor("#000000"), font=font)
obj_type = self.treeWidget.addParent(parent, _('TYPE'), expanded=True, color=QtGui.QColor("#000000"), font=font)
obj_name = self.treeWidget.addParent(parent, _('NAME'), expanded=True, color=QtGui.QColor("#000000"), font=font)
dims = self.treeWidget.addParent(
parent, _('Dimensions'), expanded=True, color=QtGui.QColor("#000000"), font=font)
units = self.treeWidget.addParent(parent, _('Units'), expanded=True, color=QtGui.QColor("#000000"), font=font)
options = self.treeWidget.addParent(parent, _('Options'), color=QtGui.QColor("#000000"), font=font)
if obj.kind.lower() == 'gerber':
apertures = self.addParent(parent, _('Apertures'), expanded=True, color=QtGui.QColor("#000000"), font=font)
apertures = self.treeWidget.addParent(
parent, _('Apertures'), expanded=True, color=QtGui.QColor("#000000"), font=font)
else:
tools = self.addParent(parent, _('Tools'), expanded=True, color=QtGui.QColor("#000000"), font=font)
tools = self.treeWidget.addParent(
parent, _('Tools'), expanded=True, color=QtGui.QColor("#000000"), font=font)
if obj.kind.lower() == 'excellon':
drills = self.addParent(parent, _('Drills'), expanded=True, color=QtGui.QColor("#000000"), font=font)
slots = self.addParent(parent, _('Slots'), expanded=True, color=QtGui.QColor("#000000"), font=font)
drills = self.treeWidget.addParent(
parent, _('Drills'), expanded=True, color=QtGui.QColor("#000000"), font=font)
slots = self.treeWidget.addParent(
parent, _('Slots'), expanded=True, color=QtGui.QColor("#000000"), font=font)
if obj.kind.lower() == 'cncjob':
others = self.addParent(parent, _('Others'), expanded=True, color=QtGui.QColor("#000000"), font=font)
others = self.treeWidget.addParent(
parent, _('Others'), expanded=True, color=QtGui.QColor("#000000"), font=font)
separator = self.addParent(parent, '')
separator = self.treeWidget.addParent(parent, '')
self.addChild(obj_type, ['%s:' % _('Object Type'), ('%s' % (obj.kind.upper()))], True, font=font, font_items=1)
self.treeWidget.addChild(
obj_type, ['%s:' % _('Object Type'), ('%s' % (obj.kind.upper()))], True, font=font, font_items=1)
try:
self.addChild(obj_type,
['%s:' % _('Geo Type'),
('%s' % ({False: _("Single-Geo"), True: _("Multi-Geo")}[obj.multigeo]))],
True)
self.treeWidget.addChild(obj_type,
[
'%s:' % _('Geo Type'),
('%s' % (
{
False: _("Single-Geo"),
True: _("Multi-Geo")
}[obj.multigeo])
)
],
True)
except Exception as e:
log.debug("Properties.addItems() --> %s" % str(e))
self.addChild(obj_name, [obj.options['name']])
self.treeWidget.addChild(obj_name, [obj.options['name']])
def job_thread(obj_prop):
proc = self.app.proc_container.new(_("Calculating dimensions ... Please wait."))
@ -193,8 +208,8 @@ class Properties(FlatCAMTool):
length = abs(xmax - xmin)
width = abs(ymax - ymin)
except Exception as e:
log.debug("PropertiesTool.addItems() -> calculate dimensions --> %s" % str(e))
except Exception as ee:
log.debug("PropertiesTool.addItems() -> calculate dimensions --> %s" % str(ee))
# calculate box area
if self.app.defaults['units'].lower() == 'mm':
@ -266,7 +281,7 @@ class Properties(FlatCAMTool):
# calculate copper area
# create a complete solid_geometry from the tools
geo_tools = list()
geo_tools = []
for tool_k in obj_prop.tools:
if 'solid_geometry' in obj_prop.tools[tool_k]:
for geo_el in obj_prop.tools[tool_k]['solid_geometry']:
@ -278,24 +293,27 @@ class Properties(FlatCAMTool):
except TypeError:
copper_area += geo_tools.area
copper_area /= 100
except Exception as e:
log.debug("Properties.addItems() --> %s" % str(e))
except Exception as err:
log.debug("Properties.addItems() --> %s" % str(err))
area_chull = 0.0
if obj_prop.kind.lower() != 'cncjob':
# calculate and add convex hull area
if geo:
if isinstance(geo, MultiPolygon):
env_obj = geo.convex_hull
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
env_obj = cascaded_union(obj_prop.solid_geometry)
env_obj = env_obj.convex_hull
else:
env_obj = cascaded_union(obj_prop.solid_geometry)
env_obj = env_obj.convex_hull
if isinstance(geo, list) and geo[0] is not None:
if isinstance(geo, MultiPolygon):
env_obj = geo.convex_hull
elif (isinstance(geo, MultiPolygon) and len(geo) == 1) or \
(isinstance(geo, list) and len(geo) == 1) and isinstance(geo[0], Polygon):
env_obj = cascaded_union(geo)
env_obj = env_obj.convex_hull
else:
env_obj = cascaded_union(geo)
env_obj = env_obj.convex_hull
area_chull = env_obj.area
area_chull = env_obj.area
else:
area_chull = 0
else:
try:
area_chull = []
@ -303,9 +321,9 @@ class Properties(FlatCAMTool):
area_el = cascaded_union(obj_prop.tools[tool_k]['solid_geometry']).convex_hull
area_chull.append(area_el.area)
area_chull = max(area_chull)
except Exception as e:
except Exception as er:
area_chull = None
log.debug("Properties.addItems() --> %s" % str(e))
log.debug("Properties.addItems() --> %s" % str(er))
if self.app.defaults['units'].lower() == 'mm' and area_chull:
area_chull = area_chull / 100
@ -319,7 +337,7 @@ class Properties(FlatCAMTool):
# Units items
f_unit = {'in': _('Inch'), 'mm': _('Metric')}[str(self.app.defaults['units'].lower())]
self.addChild(units, ['FlatCAM units:', f_unit], True)
self.treeWidget.addChild(units, ['FlatCAM units:', f_unit], True)
o_unit = {
'in': _('Inch'),
@ -327,17 +345,17 @@ class Properties(FlatCAMTool):
'inch': _('Inch'),
'metric': _('Metric')
}[str(obj.units_found.lower())]
self.addChild(units, ['Object units:', o_unit], True)
self.treeWidget.addChild(units, ['Object units:', o_unit], True)
# Options items
for option in obj.options:
if option is 'name':
continue
self.addChild(options, [str(option), str(obj.options[option])], True)
self.treeWidget.addChild(options, [str(option), str(obj.options[option])], True)
# Items that depend on the object type
if obj.kind.lower() == 'gerber':
temp_ap = dict()
temp_ap = {}
for ap in obj.apertures:
temp_ap.clear()
temp_ap = deepcopy(obj.apertures[ap])
@ -363,15 +381,17 @@ class Properties(FlatCAMTool):
temp_ap['Follow_Geo'] = '%s LineStrings' % str(follow_nr)
temp_ap['Clear_Geo'] = '%s Polygons' % str(clear_nr)
apid = self.addParent(apertures, str(ap), expanded=False, color=QtGui.QColor("#000000"), font=font)
apid = self.treeWidget.addParent(
apertures, str(ap), expanded=False, color=QtGui.QColor("#000000"), font=font)
for key in temp_ap:
self.addChild(apid, [str(key), str(temp_ap[key])], True)
self.treeWidget.addChild(apid, [str(key), str(temp_ap[key])], True)
elif obj.kind.lower() == 'excellon':
tot_drill_cnt = 0
tot_slot_cnt = 0
for tool, value in obj.tools.items():
toolid = self.addParent(tools, str(tool), expanded=False, color=QtGui.QColor("#000000"), font=font)
toolid = self.treeWidget.addParent(
tools, str(tool), expanded=False, color=QtGui.QColor("#000000"), font=font)
drill_cnt = 0 # variable to store the nr of drills per tool
slot_cnt = 0 # variable to store the nr of slots per tool
@ -390,7 +410,7 @@ class Properties(FlatCAMTool):
tot_slot_cnt += slot_cnt
self.addChild(
self.treeWidget.addChild(
toolid,
[
_('Diameter'),
@ -398,52 +418,59 @@ class Properties(FlatCAMTool):
],
True
)
self.addChild(toolid, [_('Drills number'), str(drill_cnt)], True)
self.addChild(toolid, [_('Slots number'), str(slot_cnt)], True)
self.treeWidget.addChild(toolid, [_('Drills number'), str(drill_cnt)], True)
self.treeWidget.addChild(toolid, [_('Slots number'), str(slot_cnt)], True)
self.addChild(drills, [_('Drills total number:'), str(tot_drill_cnt)], True)
self.addChild(slots, [_('Slots total number:'), str(tot_slot_cnt)], True)
self.treeWidget.addChild(drills, [_('Drills total number:'), str(tot_drill_cnt)], True)
self.treeWidget.addChild(slots, [_('Slots total number:'), str(tot_slot_cnt)], True)
elif obj.kind.lower() == 'geometry':
for tool, value in obj.tools.items():
geo_tool = self.addParent(tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font)
geo_tool = self.treeWidget.addParent(
tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font)
for k, v in value.items():
if k == 'solid_geometry':
printed_value = _('Present') if v else _('None')
self.addChild(geo_tool, [str(k), printed_value], True)
# printed_value = _('Present') if v else _('None')
try:
printed_value = str(len(v))
except (TypeError, AttributeError):
printed_value = '1'
self.treeWidget.addChild(geo_tool, [str(k), printed_value], True)
elif k == 'data':
tool_data = self.addParent(geo_tool, str(k).capitalize(),
color=QtGui.QColor("#000000"), font=font)
tool_data = self.treeWidget.addParent(
geo_tool, str(k).capitalize(), color=QtGui.QColor("#000000"), font=font)
for data_k, data_v in v.items():
self.addChild(tool_data, [str(data_k), str(data_v)], True)
self.treeWidget.addChild(tool_data, [str(data_k), str(data_v)], True)
else:
self.addChild(geo_tool, [str(k), str(v)], True)
self.treeWidget.addChild(geo_tool, [str(k), str(v)], True)
elif obj.kind.lower() == 'cncjob':
# for cncjob objects made from gerber or geometry
for tool, value in obj.cnc_tools.items():
geo_tool = self.addParent(tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font)
geo_tool = self.treeWidget.addParent(
tools, str(tool), expanded=True, color=QtGui.QColor("#000000"), font=font)
for k, v in value.items():
if k == 'solid_geometry':
printed_value = _('Present') if v else _('None')
self.addChild(geo_tool, [_("Solid Geometry"), printed_value], True)
self.treeWidget.addChild(geo_tool, [_("Solid Geometry"), printed_value], True)
elif k == 'gcode':
printed_value = _('Present') if v != '' else _('None')
self.addChild(geo_tool, [_("GCode Text"), printed_value], True)
self.treeWidget.addChild(geo_tool, [_("GCode Text"), printed_value], True)
elif k == 'gcode_parsed':
printed_value = _('Present') if v else _('None')
self.addChild(geo_tool, [_("GCode Geometry"), printed_value], True)
self.treeWidget.addChild(geo_tool, [_("GCode Geometry"), printed_value], True)
elif k == 'data':
tool_data = self.addParent(geo_tool, _("Data"), color=QtGui.QColor("#000000"), font=font)
tool_data = self.treeWidget.addParent(
geo_tool, _("Data"), color=QtGui.QColor("#000000"), font=font)
for data_k, data_v in v.items():
self.addChild(tool_data, [str(data_k).capitalize(), str(data_v)], True)
self.treeWidget.addChild(tool_data, [str(data_k).capitalize(), str(data_v)], True)
else:
self.addChild(geo_tool, [str(k), str(v)], True)
self.treeWidget.addChild(geo_tool, [str(k), str(v)], True)
# for cncjob objects made from excellon
for tool_dia, value in obj.exc_cnc_tools.items():
exc_tool = self.addParent(
exc_tool = self.treeWidget.addParent(
tools, str(value['tool']), expanded=False, color=QtGui.QColor("#000000"), font=font
)
self.addChild(
self.treeWidget.addChild(
exc_tool,
[
_('Diameter'),
@ -454,15 +481,15 @@ class Properties(FlatCAMTool):
for k, v in value.items():
if k == 'solid_geometry':
printed_value = _('Present') if v else _('None')
self.addChild(exc_tool, [_("Solid Geometry"), printed_value], True)
self.treeWidget.addChild(exc_tool, [_("Solid Geometry"), printed_value], True)
elif k == 'nr_drills':
self.addChild(exc_tool, [_("Drills number"), str(v)], True)
self.treeWidget.addChild(exc_tool, [_("Drills number"), str(v)], True)
elif k == 'nr_slots':
self.addChild(exc_tool, [_("Slots number"), str(v)], True)
self.treeWidget.addChild(exc_tool, [_("Slots number"), str(v)], True)
else:
pass
self.addChild(
self.treeWidget.addChild(
exc_tool,
[
_("Depth of Cut"),
@ -474,7 +501,7 @@ class Properties(FlatCAMTool):
],
True
)
self.addChild(
self.treeWidget.addChild(
exc_tool,
[
_("Clearance Height"),
@ -486,7 +513,7 @@ class Properties(FlatCAMTool):
],
True
)
self.addChild(
self.treeWidget.addChild(
exc_tool,
[
_("Feedrate"),
@ -506,14 +533,14 @@ class Properties(FlatCAMTool):
r_time *= 60
units_lbl = 'sec'
r_time = math.ceil(float(r_time))
self.addChild(
self.treeWidget.addChild(
others,
[
'%s:' % _('Routing time'),
'%.*f %s' % (self.decimals, r_time, units_lbl)],
True
)
self.addChild(
self.treeWidget.addChild(
others,
[
'%s:' % _('Travelled distance'),
@ -522,40 +549,17 @@ class Properties(FlatCAMTool):
True
)
self.addChild(separator, [''])
def addParent(self, parent, title, expanded=False, color=None, font=None):
item = QtWidgets.QTreeWidgetItem(parent, [title])
item.setChildIndicatorPolicy(QtWidgets.QTreeWidgetItem.ShowIndicator)
item.setExpanded(expanded)
if color is not None:
# item.setTextColor(0, color) # PyQt4
item.setForeground(0, QtGui.QBrush(color))
if font is not None:
item.setFont(0, font)
return item
def addChild(self, parent, title, column1=None, font=None, font_items=None):
item = QtWidgets.QTreeWidgetItem(parent)
item.setText(0, str(title[0]))
if column1 is not None:
item.setText(1, str(title[1]))
if font and font_items:
try:
for fi in font_items:
item.setFont(fi, font)
except TypeError:
item.setFont(font_items, font)
self.treeWidget.addChild(separator, [''])
def show_area_chull(self, area, length, width, chull_area, copper_area, location):
# add dimensions
self.addChild(
self.treeWidget.addChild(
location,
['%s:' % _('Length'), '%.*f %s' % (self.decimals, length, self.app.defaults['units'].lower())],
True
)
self.addChild(
self.treeWidget.addChild(
location,
['%s:' % _('Width'), '%.*f %s' % (self.decimals, width, self.app.defaults['units'].lower())],
True
@ -563,16 +567,16 @@ class Properties(FlatCAMTool):
# add box area
if self.app.defaults['units'].lower() == 'mm':
self.addChild(location, ['%s:' % _('Box Area'), '%.*f %s' % (self.decimals, area, 'cm2')], True)
self.addChild(
self.treeWidget.addChild(location, ['%s:' % _('Box Area'), '%.*f %s' % (self.decimals, area, 'cm2')], True)
self.treeWidget.addChild(
location,
['%s:' % _('Convex_Hull Area'), '%.*f %s' % (self.decimals, chull_area, 'cm2')],
True
)
else:
self.addChild(location, ['%s:' % _('Box Area'), '%.*f %s' % (self.decimals, area, 'in2')], True)
self.addChild(
self.treeWidget.addChild(location, ['%s:' % _('Box Area'), '%.*f %s' % (self.decimals, area, 'in2')], True)
self.treeWidget.addChild(
location,
['%s:' % _('Convex_Hull Area'), '%.*f %s' % (self.decimals, chull_area, 'in2')],
True
@ -580,8 +584,10 @@ class Properties(FlatCAMTool):
# add copper area
if self.app.defaults['units'].lower() == 'mm':
self.addChild(location, ['%s:' % _('Copper Area'), '%.*f %s' % (self.decimals, copper_area, 'cm2')], True)
self.treeWidget.addChild(
location, ['%s:' % _('Copper Area'), '%.*f %s' % (self.decimals, copper_area, 'cm2')], True)
else:
self.addChild(location, ['%s:' % _('Copper Area'), '%.*f %s' % (self.decimals, copper_area, 'in2')], True)
self.treeWidget.addChild(
location, ['%s:' % _('Copper Area'), '%.*f %s' % (self.decimals, copper_area, 'in2')], True)
# end of file

View File

@ -0,0 +1,994 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# File Author: Marius Adrian Stanciu (c) #
# Date: 1/24/2020 #
# MIT Licence #
# ##########################################################
from PyQt5 import QtCore, QtWidgets
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, FCComboBox
from copy import deepcopy
import logging
from shapely.geometry import MultiPolygon, Point
import gettext
import FlatCAMTranslation as fcTranslate
import builtins
fcTranslate.apply_language('strings')
if '_' not in builtins.__dict__:
_ = gettext.gettext
log = logging.getLogger('base')
class ToolPunchGerber(FlatCAMTool):
toolName = _("Punch Gerber")
def __init__(self, app):
FlatCAMTool.__init__(self, app)
self.decimals = self.app.decimals
# Title
title_label = QtWidgets.QLabel("%s" % self.toolName)
title_label.setStyleSheet("""
QLabel
{
font-size: 16px;
font-weight: bold;
}
""")
self.layout.addWidget(title_label)
# Punch Drill holes
self.layout.addWidget(QtWidgets.QLabel(""))
# ## Grid Layout
grid_lay = QtWidgets.QGridLayout()
self.layout.addLayout(grid_lay)
grid_lay.setColumnStretch(0, 1)
grid_lay.setColumnStretch(1, 0)
# ## Gerber Object
self.gerber_object_combo = FCComboBox()
self.gerber_object_combo.setModel(self.app.collection)
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.gerber_object_combo.is_last = True
self.gerber_object_combo.obj_type = "Gerber"
self.grb_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.grb_label.setToolTip('%s.' % _("Gerber into which to punch holes"))
grid_lay.addWidget(self.grb_label, 0, 0, 1, 2)
grid_lay.addWidget(self.gerber_object_combo, 1, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay.addWidget(separator_line, 2, 0, 1, 2)
self.padt_label = QtWidgets.QLabel("<b>%s</b>" % _("Processed Pads Type"))
self.padt_label.setToolTip(
_("The type of pads shape to be processed.\n"
"If the PCB has many SMD pads with rectangular pads,\n"
"disable the Rectangular aperture.")
)
grid_lay.addWidget(self.padt_label, 3, 0, 1, 2)
# Select all
self.select_all_cb = FCCheckBox('%s' % _("ALL"))
grid_lay.addWidget(self.select_all_cb)
# Circular Aperture Selection
self.circular_cb = FCCheckBox('%s' % _("Circular"))
self.circular_cb.setToolTip(
_("Process Circular Pads.")
)
grid_lay.addWidget(self.circular_cb, 5, 0, 1, 2)
# Oblong Aperture Selection
self.oblong_cb = FCCheckBox('%s' % _("Oblong"))
self.oblong_cb.setToolTip(
_("Process Oblong Pads.")
)
grid_lay.addWidget(self.oblong_cb, 6, 0, 1, 2)
# Square Aperture Selection
self.square_cb = FCCheckBox('%s' % _("Square"))
self.square_cb.setToolTip(
_("Process Square Pads.")
)
grid_lay.addWidget(self.square_cb, 7, 0, 1, 2)
# Rectangular Aperture Selection
self.rectangular_cb = FCCheckBox('%s' % _("Rectangular"))
self.rectangular_cb.setToolTip(
_("Process Rectangular Pads.")
)
grid_lay.addWidget(self.rectangular_cb, 8, 0, 1, 2)
# Others type of Apertures Selection
self.other_cb = FCCheckBox('%s' % _("Others"))
self.other_cb.setToolTip(
_("Process pads not in the categories above.")
)
grid_lay.addWidget(self.other_cb, 9, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid_lay.addWidget(separator_line, 10, 0, 1, 2)
# Grid Layout
grid0 = QtWidgets.QGridLayout()
self.layout.addLayout(grid0)
grid0.setColumnStretch(0, 0)
grid0.setColumnStretch(1, 1)
self.method_label = QtWidgets.QLabel('<b>%s:</b>' % _("Method"))
self.method_label.setToolTip(
_("The punch hole source can be:\n"
"- Excellon Object-> the Excellon object drills center will serve as reference.\n"
"- Fixed Diameter -> will try to use the pads center as reference adding fixed diameter holes.\n"
"- Fixed Annular Ring -> will try to keep a set annular ring.\n"
"- Proportional -> will make a Gerber punch hole having the diameter a percentage of the pad diameter.\n")
)
self.method_punch = RadioSet(
[
{'label': _('Excellon'), 'value': 'exc'},
{'label': _("Fixed Diameter"), 'value': 'fixed'},
{'label': _("Fixed Annular Ring"), 'value': 'ring'},
{'label': _("Proportional"), 'value': 'prop'}
],
orientation='vertical',
stretch=False)
grid0.addWidget(self.method_label, 0, 0, 1, 2)
grid0.addWidget(self.method_punch, 1, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 2, 0, 1, 2)
self.exc_label = QtWidgets.QLabel('<b>%s</b>' % _("Excellon"))
self.exc_label.setToolTip(
_("Remove the geometry of Excellon from the Gerber to create the holes in pads.")
)
self.exc_combo = FCComboBox()
self.exc_combo.setModel(self.app.collection)
self.exc_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.exc_combo.is_last = True
self.exc_combo.obj_type = "Excellon"
grid0.addWidget(self.exc_label, 3, 0, 1, 2)
grid0.addWidget(self.exc_combo, 4, 0, 1, 2)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 5, 0, 1, 2)
# Fixed Dia
self.fixed_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Diameter"))
grid0.addWidget(self.fixed_label, 6, 0, 1, 2)
# Diameter value
self.dia_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.dia_entry.set_precision(self.decimals)
self.dia_entry.set_range(0.0000, 9999.9999)
self.dia_label = QtWidgets.QLabel('%s:' % _("Value"))
self.dia_label.setToolTip(
_("Fixed hole diameter.")
)
grid0.addWidget(self.dia_label, 8, 0)
grid0.addWidget(self.dia_entry, 8, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 9, 0, 1, 2)
self.ring_frame = QtWidgets.QFrame()
self.ring_frame.setContentsMargins(0, 0, 0, 0)
grid0.addWidget(self.ring_frame, 10, 0, 1, 2)
self.ring_box = QtWidgets.QVBoxLayout()
self.ring_box.setContentsMargins(0, 0, 0, 0)
self.ring_frame.setLayout(self.ring_box)
# Annular Ring value
self.ring_label = QtWidgets.QLabel('<b>%s</b>' % _("Fixed Annular Ring"))
self.ring_label.setToolTip(
_("The size of annular ring.\n"
"The copper sliver between the hole exterior\n"
"and the margin of the copper pad.")
)
self.ring_box.addWidget(self.ring_label)
# ## Grid Layout
self.grid1 = QtWidgets.QGridLayout()
self.grid1.setColumnStretch(0, 0)
self.grid1.setColumnStretch(1, 1)
self.ring_box.addLayout(self.grid1)
# Circular Annular Ring Value
self.circular_ring_label = QtWidgets.QLabel('%s:' % _("Circular"))
self.circular_ring_label.setToolTip(
_("The size of annular ring for circular pads.")
)
self.circular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.circular_ring_entry.set_precision(self.decimals)
self.circular_ring_entry.set_range(0.0000, 9999.9999)
self.grid1.addWidget(self.circular_ring_label, 3, 0)
self.grid1.addWidget(self.circular_ring_entry, 3, 1)
# Oblong Annular Ring Value
self.oblong_ring_label = QtWidgets.QLabel('%s:' % _("Oblong"))
self.oblong_ring_label.setToolTip(
_("The size of annular ring for oblong pads.")
)
self.oblong_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.oblong_ring_entry.set_precision(self.decimals)
self.oblong_ring_entry.set_range(0.0000, 9999.9999)
self.grid1.addWidget(self.oblong_ring_label, 4, 0)
self.grid1.addWidget(self.oblong_ring_entry, 4, 1)
# Square Annular Ring Value
self.square_ring_label = QtWidgets.QLabel('%s:' % _("Square"))
self.square_ring_label.setToolTip(
_("The size of annular ring for square pads.")
)
self.square_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.square_ring_entry.set_precision(self.decimals)
self.square_ring_entry.set_range(0.0000, 9999.9999)
self.grid1.addWidget(self.square_ring_label, 5, 0)
self.grid1.addWidget(self.square_ring_entry, 5, 1)
# Rectangular Annular Ring Value
self.rectangular_ring_label = QtWidgets.QLabel('%s:' % _("Rectangular"))
self.rectangular_ring_label.setToolTip(
_("The size of annular ring for rectangular pads.")
)
self.rectangular_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.rectangular_ring_entry.set_precision(self.decimals)
self.rectangular_ring_entry.set_range(0.0000, 9999.9999)
self.grid1.addWidget(self.rectangular_ring_label, 6, 0)
self.grid1.addWidget(self.rectangular_ring_entry, 6, 1)
# Others Annular Ring Value
self.other_ring_label = QtWidgets.QLabel('%s:' % _("Others"))
self.other_ring_label.setToolTip(
_("The size of annular ring for other pads.")
)
self.other_ring_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.other_ring_entry.set_precision(self.decimals)
self.other_ring_entry.set_range(0.0000, 9999.9999)
self.grid1.addWidget(self.other_ring_label, 7, 0)
self.grid1.addWidget(self.other_ring_entry, 7, 1)
separator_line = QtWidgets.QFrame()
separator_line.setFrameShape(QtWidgets.QFrame.HLine)
separator_line.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line, 11, 0, 1, 2)
# Proportional value
self.prop_label = QtWidgets.QLabel('<b>%s</b>' % _("Proportional Diameter"))
grid0.addWidget(self.prop_label, 12, 0, 1, 2)
# Diameter value
self.factor_entry = FCDoubleSpinner(callback=self.confirmation_message, suffix='%')
self.factor_entry.set_precision(self.decimals)
self.factor_entry.set_range(0.0000, 100.0000)
self.factor_entry.setSingleStep(0.1)
self.factor_label = QtWidgets.QLabel('%s:' % _("Value"))
self.factor_label.setToolTip(
_("Proportional Diameter.\n"
"The hole diameter will be a fraction of the pad size.")
)
grid0.addWidget(self.factor_label, 13, 0)
grid0.addWidget(self.factor_entry, 13, 1)
separator_line3 = QtWidgets.QFrame()
separator_line3.setFrameShape(QtWidgets.QFrame.HLine)
separator_line3.setFrameShadow(QtWidgets.QFrame.Sunken)
grid0.addWidget(separator_line3, 14, 0, 1, 2)
# Buttons
self.punch_object_button = QtWidgets.QPushButton(_("Punch Gerber"))
self.punch_object_button.setToolTip(
_("Create a Gerber object from the selected object, within\n"
"the specified box.")
)
self.punch_object_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.layout.addWidget(self.punch_object_button)
self.layout.addStretch()
# ## Reset Tool
self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.layout.addWidget(self.reset_button)
self.units = self.app.defaults['units']
# self.cb_items = [
# self.grid1.itemAt(w).widget() for w in range(self.grid1.count())
# if isinstance(self.grid1.itemAt(w).widget(), FCCheckBox)
# ]
self.circular_ring_entry.setEnabled(False)
self.oblong_ring_entry.setEnabled(False)
self.square_ring_entry.setEnabled(False)
self.rectangular_ring_entry.setEnabled(False)
self.other_ring_entry.setEnabled(False)
self.dia_entry.setDisabled(True)
self.dia_label.setDisabled(True)
self.factor_label.setDisabled(True)
self.factor_entry.setDisabled(True)
# ## Signals
self.method_punch.activated_custom.connect(self.on_method)
self.reset_button.clicked.connect(self.set_tool_ui)
self.punch_object_button.clicked.connect(self.on_generate_object)
self.circular_cb.stateChanged.connect(
lambda state:
self.circular_ring_entry.setDisabled(False) if state else self.circular_ring_entry.setDisabled(True)
)
self.oblong_cb.stateChanged.connect(
lambda state:
self.oblong_ring_entry.setDisabled(False) if state else self.oblong_ring_entry.setDisabled(True)
)
self.square_cb.stateChanged.connect(
lambda state:
self.square_ring_entry.setDisabled(False) if state else self.square_ring_entry.setDisabled(True)
)
self.rectangular_cb.stateChanged.connect(
lambda state:
self.rectangular_ring_entry.setDisabled(False) if state else self.rectangular_ring_entry.setDisabled(True)
)
self.other_cb.stateChanged.connect(
lambda state:
self.other_ring_entry.setDisabled(False) if state else self.other_ring_entry.setDisabled(True)
)
def run(self, toggle=True):
self.app.report_usage("ToolPunchGerber()")
if toggle:
# if the splitter is hidden, display it, else hide it but only if the current widget is the same
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
else:
try:
if self.app.ui.tool_scroll_area.widget().objectName() == self.toolName:
# if tab is populated with the tool but it does not have the focus, focus on it
if not self.app.ui.notebook.currentWidget() is self.app.ui.tool_tab:
# focus on Tool Tab
self.app.ui.notebook.setCurrentWidget(self.app.ui.tool_tab)
else:
self.app.ui.splitter.setSizes([0, 1])
except AttributeError:
pass
else:
if self.app.ui.splitter.sizes()[0] == 0:
self.app.ui.splitter.setSizes([1, 1])
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Punch Tool"))
def install(self, icon=None, separator=None, **kwargs):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+H', **kwargs)
def set_tool_ui(self):
self.reset_fields()
self.ui_connect()
self.method_punch.set_value(self.app.defaults["tools_punch_hole_type"])
self.select_all_cb.set_value(False)
self.dia_entry.set_value(float(self.app.defaults["tools_punch_hole_fixed_dia"]))
self.circular_ring_entry.set_value(float(self.app.defaults["tools_punch_circular_ring"]))
self.oblong_ring_entry.set_value(float(self.app.defaults["tools_punch_oblong_ring"]))
self.square_ring_entry.set_value(float(self.app.defaults["tools_punch_square_ring"]))
self.rectangular_ring_entry.set_value(float(self.app.defaults["tools_punch_rectangular_ring"]))
self.other_ring_entry.set_value(float(self.app.defaults["tools_punch_others_ring"]))
self.circular_cb.set_value(self.app.defaults["tools_punch_circular"])
self.oblong_cb.set_value(self.app.defaults["tools_punch_oblong"])
self.square_cb.set_value(self.app.defaults["tools_punch_square"])
self.rectangular_cb.set_value(self.app.defaults["tools_punch_rectangular"])
self.other_cb.set_value(self.app.defaults["tools_punch_others"])
self.factor_entry.set_value(float(self.app.defaults["tools_punch_hole_prop_factor"]))
def on_select_all(self, state):
self.ui_disconnect()
if state:
self.circular_cb.setChecked(True)
self.oblong_cb.setChecked(True)
self.square_cb.setChecked(True)
self.rectangular_cb.setChecked(True)
self.other_cb.setChecked(True)
else:
self.circular_cb.setChecked(False)
self.oblong_cb.setChecked(False)
self.square_cb.setChecked(False)
self.rectangular_cb.setChecked(False)
self.other_cb.setChecked(False)
self.ui_connect()
def on_method(self, val):
self.exc_label.setEnabled(False)
self.exc_combo.setEnabled(False)
self.fixed_label.setEnabled(False)
self.dia_label.setEnabled(False)
self.dia_entry.setEnabled(False)
self.ring_frame.setEnabled(False)
self.prop_label.setEnabled(False)
self.factor_label.setEnabled(False)
self.factor_entry.setEnabled(False)
if val == 'exc':
self.exc_label.setEnabled(True)
self.exc_combo.setEnabled(True)
elif val == 'fixed':
self.fixed_label.setEnabled(True)
self.dia_label.setEnabled(True)
self.dia_entry.setEnabled(True)
elif val == 'ring':
self.ring_frame.setEnabled(True)
elif val == 'prop':
self.prop_label.setEnabled(True)
self.factor_label.setEnabled(True)
self.factor_entry.setEnabled(True)
def ui_connect(self):
self.select_all_cb.stateChanged.connect(self.on_select_all)
def ui_disconnect(self):
try:
self.select_all_cb.stateChanged.disconnect()
except (AttributeError, TypeError):
pass
def on_generate_object(self):
# get the Gerber file who is the source of the punched Gerber
selection_index = self.gerber_object_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.gerber_object_combo.rootModelIndex())
try:
grb_obj = model_index.internalPointer().obj
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Gerber object loaded ..."))
return
name = grb_obj.options['name'].rpartition('.')[0]
outname = name + "_punched"
punch_method = self.method_punch.get_value()
new_options = {}
for opt in grb_obj.options:
new_options[opt] = deepcopy(grb_obj.options[opt])
if punch_method == 'exc':
# get the Excellon file whose geometry will create the punch holes
selection_index = self.exc_combo.currentIndex()
model_index = self.app.collection.index(selection_index, 0, self.exc_combo.rootModelIndex())
try:
exc_obj = model_index.internalPointer().obj
except Exception:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("There is no Excellon object loaded ..."))
return
# this is the punching geometry
exc_solid_geometry = MultiPolygon(exc_obj.solid_geometry)
if isinstance(grb_obj.solid_geometry, list):
grb_solid_geometry = MultiPolygon(grb_obj.solid_geometry)
else:
grb_solid_geometry = grb_obj.solid_geometry
# create the punched Gerber solid_geometry
punched_solid_geometry = grb_solid_geometry.difference(exc_solid_geometry)
# update the gerber apertures to include the clear geometry so it can be exported successfully
new_apertures = deepcopy(grb_obj.apertures)
new_apertures_items = new_apertures.items()
# find maximum aperture id
new_apid = max([int(x) for x, __ in new_apertures_items])
# store here the clear geometry, the key is the drill size
holes_apertures = {}
for apid, val in new_apertures_items:
for elem in val['geometry']:
# make it work only for Gerber Flashes who are Points in 'follow'
if 'solid' in elem and isinstance(elem['follow'], Point):
for drill in exc_obj.drills:
clear_apid_size = exc_obj.tools[drill['tool']]['C']
# since there may be drills that do not drill into a pad we test only for drills in a pad
if drill['point'].within(elem['solid']):
geo_elem = {}
geo_elem['clear'] = drill['point']
if clear_apid_size not in holes_apertures:
holes_apertures[clear_apid_size] = {}
holes_apertures[clear_apid_size]['type'] = 'C'
holes_apertures[clear_apid_size]['size'] = clear_apid_size
holes_apertures[clear_apid_size]['geometry'] = []
holes_apertures[clear_apid_size]['geometry'].append(deepcopy(geo_elem))
# add the clear geometry to new apertures; it's easier than to test if there are apertures with the same
# size and add there the clear geometry
for hole_size, ap_val in holes_apertures.items():
new_apid += 1
new_apertures[str(new_apid)] = deepcopy(ap_val)
def init_func(new_obj, app_obj):
new_obj.options.update(new_options)
new_obj.options['name'] = outname
new_obj.fill_color = deepcopy(grb_obj.fill_color)
new_obj.outline_color = deepcopy(grb_obj.outline_color)
new_obj.apertures = deepcopy(new_apertures)
new_obj.solid_geometry = deepcopy(punched_solid_geometry)
new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None,
local_use=new_obj, use_thread=False)
self.app.new_object('gerber', outname, init_func)
elif punch_method == 'fixed':
punch_size = float(self.dia_entry.get_value())
if punch_size == 0.0:
self.app.inform.emit('[WARNING_NOTCL] %s' % _("The value of the fixed diameter is 0.0. Aborting."))
return 'fail'
punching_geo = []
for apid in grb_obj.apertures:
if grb_obj.apertures[apid]['type'] == 'C' and self.circular_cb.get_value():
if punch_size >= float(grb_obj.apertures[apid]['size']):
self.app.inform.emit('[ERROR_NOTCL] %s' %
_(" Could not generate punched hole Gerber because the punch hole size"
"is bigger than some of the apertures in the Gerber object."))
return 'fail'
else:
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(punch_size / 2))
elif grb_obj.apertures[apid]['type'] == 'R':
if punch_size >= float(grb_obj.apertures[apid]['width']) or \
punch_size >= float(grb_obj.apertures[apid]['height']):
self.app.inform.emit('[ERROR_NOTCL] %s' %
_("Could not generate punched hole Gerber because the punch hole size"
"is bigger than some of the apertures in the Gerber object."))
return 'fail'
elif round(float(grb_obj.apertures[apid]['width']), self.decimals) == \
round(float(grb_obj.apertures[apid]['height']), self.decimals) and \
self.square_cb.get_value():
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(punch_size / 2))
elif round(float(grb_obj.apertures[apid]['width']), self.decimals) != \
round(float(grb_obj.apertures[apid]['height']), self.decimals) and \
self.rectangular_cb.get_value():
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(punch_size / 2))
elif grb_obj.apertures[apid]['type'] == 'O' and self.oblong_cb.get_value():
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(punch_size / 2))
elif grb_obj.apertures[apid]['type'] not in ['C', 'R', 'O'] and self.other_cb.get_value():
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(punch_size / 2))
punching_geo = MultiPolygon(punching_geo)
if isinstance(grb_obj.solid_geometry, list):
temp_solid_geometry = MultiPolygon(grb_obj.solid_geometry)
else:
temp_solid_geometry = grb_obj.solid_geometry
punched_solid_geometry = temp_solid_geometry.difference(punching_geo)
if punched_solid_geometry == temp_solid_geometry:
self.app.inform.emit('[WARNING_NOTCL] %s' %
_("Could not generate punched hole Gerber because the newly created object "
"geometry is the same as the one in the source object geometry..."))
return 'fail'
# update the gerber apertures to include the clear geometry so it can be exported successfully
new_apertures = deepcopy(grb_obj.apertures)
new_apertures_items = new_apertures.items()
# find maximum aperture id
new_apid = max([int(x) for x, __ in new_apertures_items])
# store here the clear geometry, the key is the drill size
holes_apertures = {}
for apid, val in new_apertures_items:
for elem in val['geometry']:
# make it work only for Gerber Flashes who are Points in 'follow'
if 'solid' in elem and isinstance(elem['follow'], Point):
for geo in punching_geo:
clear_apid_size = punch_size
# since there may be drills that do not drill into a pad we test only for drills in a pad
if geo.within(elem['solid']):
geo_elem = {}
geo_elem['clear'] = geo.centroid
if clear_apid_size not in holes_apertures:
holes_apertures[clear_apid_size] = {}
holes_apertures[clear_apid_size]['type'] = 'C'
holes_apertures[clear_apid_size]['size'] = clear_apid_size
holes_apertures[clear_apid_size]['geometry'] = []
holes_apertures[clear_apid_size]['geometry'].append(deepcopy(geo_elem))
# add the clear geometry to new apertures; it's easier than to test if there are apertures with the same
# size and add there the clear geometry
for hole_size, ap_val in holes_apertures.items():
new_apid += 1
new_apertures[str(new_apid)] = deepcopy(ap_val)
def init_func(new_obj, app_obj):
new_obj.options.update(new_options)
new_obj.options['name'] = outname
new_obj.fill_color = deepcopy(grb_obj.fill_color)
new_obj.outline_color = deepcopy(grb_obj.outline_color)
new_obj.apertures = deepcopy(new_apertures)
new_obj.solid_geometry = deepcopy(punched_solid_geometry)
new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None,
local_use=new_obj, use_thread=False)
self.app.new_object('gerber', outname, init_func)
elif punch_method == 'ring':
circ_r_val = self.circular_ring_entry.get_value()
oblong_r_val = self.oblong_ring_entry.get_value()
square_r_val = self.square_ring_entry.get_value()
rect_r_val = self.rectangular_ring_entry.get_value()
other_r_val = self.other_ring_entry.get_value()
dia = None
if isinstance(grb_obj.solid_geometry, list):
temp_solid_geometry = MultiPolygon(grb_obj.solid_geometry)
else:
temp_solid_geometry = grb_obj.solid_geometry
punched_solid_geometry = temp_solid_geometry
new_apertures = deepcopy(grb_obj.apertures)
new_apertures_items = new_apertures.items()
# find maximum aperture id
new_apid = max([int(x) for x, __ in new_apertures_items])
# store here the clear geometry, the key is the new aperture size
holes_apertures = {}
for apid, apid_value in grb_obj.apertures.items():
ap_type = apid_value['type']
punching_geo = []
if ap_type == 'C' and self.circular_cb.get_value():
dia = float(apid_value['size']) - (2 * circ_r_val)
for elem in apid_value['geometry']:
if 'follow' in elem and isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif ap_type == 'O' and self.oblong_cb.get_value():
width = float(apid_value['width'])
height = float(apid_value['height'])
if width > height:
dia = float(apid_value['height']) - (2 * oblong_r_val)
else:
dia = float(apid_value['width']) - (2 * oblong_r_val)
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif ap_type == 'R':
width = float(apid_value['width'])
height = float(apid_value['height'])
# if the height == width (float numbers so the reason for the following)
if round(width, self.decimals) == round(height, self.decimals):
if self.square_cb.get_value():
dia = float(apid_value['height']) - (2 * square_r_val)
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif self.rectangular_cb.get_value():
if width > height:
dia = float(apid_value['height']) - (2 * rect_r_val)
else:
dia = float(apid_value['width']) - (2 * rect_r_val)
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif self.other_cb.get_value():
try:
dia = float(apid_value['size']) - (2 * other_r_val)
except KeyError:
if ap_type == 'AM':
pol = apid_value['geometry'][0]['solid']
x0, y0, x1, y1 = pol.bounds
dx = x1 - x0
dy = y1 - y0
if dx <= dy:
dia = dx - (2 * other_r_val)
else:
dia = dy - (2 * other_r_val)
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
# if dia is None then none of the above applied so we skip the following
if dia is None:
continue
punching_geo = MultiPolygon(punching_geo)
if punching_geo is None or punching_geo.is_empty:
continue
punched_solid_geometry = punched_solid_geometry.difference(punching_geo)
# update the gerber apertures to include the clear geometry so it can be exported successfully
for elem in apid_value['geometry']:
# make it work only for Gerber Flashes who are Points in 'follow'
if 'solid' in elem and isinstance(elem['follow'], Point):
clear_apid_size = dia
for geo in punching_geo:
# since there may be drills that do not drill into a pad we test only for geos in a pad
if geo.within(elem['solid']):
geo_elem = {}
geo_elem['clear'] = geo.centroid
if clear_apid_size not in holes_apertures:
holes_apertures[clear_apid_size] = {}
holes_apertures[clear_apid_size]['type'] = 'C'
holes_apertures[clear_apid_size]['size'] = clear_apid_size
holes_apertures[clear_apid_size]['geometry'] = []
holes_apertures[clear_apid_size]['geometry'].append(deepcopy(geo_elem))
# add the clear geometry to new apertures; it's easier than to test if there are apertures with the same
# size and add there the clear geometry
for hole_size, ap_val in holes_apertures.items():
new_apid += 1
new_apertures[str(new_apid)] = deepcopy(ap_val)
def init_func(new_obj, app_obj):
new_obj.options.update(new_options)
new_obj.options['name'] = outname
new_obj.fill_color = deepcopy(grb_obj.fill_color)
new_obj.outline_color = deepcopy(grb_obj.outline_color)
new_obj.apertures = deepcopy(new_apertures)
new_obj.solid_geometry = deepcopy(punched_solid_geometry)
new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None,
local_use=new_obj, use_thread=False)
self.app.new_object('gerber', outname, init_func)
elif punch_method == 'prop':
prop_factor = self.factor_entry.get_value() / 100.0
dia = None
if isinstance(grb_obj.solid_geometry, list):
temp_solid_geometry = MultiPolygon(grb_obj.solid_geometry)
else:
temp_solid_geometry = grb_obj.solid_geometry
punched_solid_geometry = temp_solid_geometry
new_apertures = deepcopy(grb_obj.apertures)
new_apertures_items = new_apertures.items()
# find maximum aperture id
new_apid = max([int(x) for x, __ in new_apertures_items])
# store here the clear geometry, the key is the new aperture size
holes_apertures = {}
for apid, apid_value in grb_obj.apertures.items():
ap_type = apid_value['type']
punching_geo = []
if ap_type == 'C' and self.circular_cb.get_value():
dia = float(apid_value['size']) * prop_factor
for elem in apid_value['geometry']:
if 'follow' in elem and isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif ap_type == 'O' and self.oblong_cb.get_value():
width = float(apid_value['width'])
height = float(apid_value['height'])
if width > height:
dia = float(apid_value['height']) * prop_factor
else:
dia = float(apid_value['width']) * prop_factor
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif ap_type == 'R':
width = float(apid_value['width'])
height = float(apid_value['height'])
# if the height == width (float numbers so the reason for the following)
if round(width, self.decimals) == round(height, self.decimals):
if self.square_cb.get_value():
dia = float(apid_value['height']) * prop_factor
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif self.rectangular_cb.get_value():
if width > height:
dia = float(apid_value['height']) * prop_factor
else:
dia = float(apid_value['width']) * prop_factor
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
elif self.other_cb.get_value():
try:
dia = float(apid_value['size']) * prop_factor
except KeyError:
if ap_type == 'AM':
pol = apid_value['geometry'][0]['solid']
x0, y0, x1, y1 = pol.bounds
dx = x1 - x0
dy = y1 - y0
if dx <= dy:
dia = dx * prop_factor
else:
dia = dy * prop_factor
for elem in grb_obj.apertures[apid]['geometry']:
if 'follow' in elem:
if isinstance(elem['follow'], Point):
punching_geo.append(elem['follow'].buffer(dia / 2))
# if dia is None then none of the above applied so we skip the following
if dia is None:
continue
punching_geo = MultiPolygon(punching_geo)
if punching_geo is None or punching_geo.is_empty:
continue
punched_solid_geometry = punched_solid_geometry.difference(punching_geo)
# update the gerber apertures to include the clear geometry so it can be exported successfully
for elem in apid_value['geometry']:
# make it work only for Gerber Flashes who are Points in 'follow'
if 'solid' in elem and isinstance(elem['follow'], Point):
clear_apid_size = dia
for geo in punching_geo:
# since there may be drills that do not drill into a pad we test only for geos in a pad
if geo.within(elem['solid']):
geo_elem = {}
geo_elem['clear'] = geo.centroid
if clear_apid_size not in holes_apertures:
holes_apertures[clear_apid_size] = {}
holes_apertures[clear_apid_size]['type'] = 'C'
holes_apertures[clear_apid_size]['size'] = clear_apid_size
holes_apertures[clear_apid_size]['geometry'] = []
holes_apertures[clear_apid_size]['geometry'].append(deepcopy(geo_elem))
# add the clear geometry to new apertures; it's easier than to test if there are apertures with the same
# size and add there the clear geometry
for hole_size, ap_val in holes_apertures.items():
new_apid += 1
new_apertures[str(new_apid)] = deepcopy(ap_val)
def init_func(new_obj, app_obj):
new_obj.options.update(new_options)
new_obj.options['name'] = outname
new_obj.fill_color = deepcopy(grb_obj.fill_color)
new_obj.outline_color = deepcopy(grb_obj.outline_color)
new_obj.apertures = deepcopy(new_apertures)
new_obj.solid_geometry = deepcopy(punched_solid_geometry)
new_obj.source_file = self.app.export_gerber(obj_name=outname, filename=None,
local_use=new_obj, use_thread=False)
self.app.new_object('gerber', outname, init_func)
def reset_fields(self):
self.gerber_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.exc_combo.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.ui_disconnect()

View File

@ -9,7 +9,7 @@ from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.QtCore import Qt
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox
from flatcamGUI.GUIElements import RadioSet, FCTextArea, FCSpinner, FCEntry, FCCheckBox, FCComboBox
from flatcamParsers.ParseSVG import *
from shapely.geometry.base import *
@ -69,12 +69,13 @@ class QRCode(FlatCAMTool):
i_grid_lay.setColumnStretch(0, 0)
i_grid_lay.setColumnStretch(1, 1)
self.grb_object_combo = QtWidgets.QComboBox()
self.grb_object_combo = FCComboBox()
self.grb_object_combo.setModel(self.app.collection)
self.grb_object_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.grb_object_combo.setCurrentIndex(1)
self.grb_object_combo.is_last = True
self.grb_object_combo.obj_type = "Gerber"
self.grbobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("GERBER"))
self.grbobj_label = QtWidgets.QLabel("<b>%s:</b>" % _("Object"))
self.grbobj_label.setToolTip(
_("Gerber Object to which the QRCode will be added.")
)
@ -101,7 +102,7 @@ class QRCode(FlatCAMTool):
_("QRCode version can have values from 1 (21x21 boxes)\n"
"to 40 (177x177 boxes).")
)
self.version_entry = FCSpinner()
self.version_entry = FCSpinner(callback=self.confirmation_message_int)
self.version_entry.set_range(1, 40)
self.version_entry.setWrapping(True)
@ -137,7 +138,7 @@ class QRCode(FlatCAMTool):
_("Box size control the overall size of the QRcode\n"
"by adjusting the size of each box in the code.")
)
self.bsize_entry = FCSpinner()
self.bsize_entry = FCSpinner(callback=self.confirmation_message_int)
self.bsize_entry.set_range(1, 9999)
self.bsize_entry.setWrapping(True)
@ -150,10 +151,9 @@ class QRCode(FlatCAMTool):
_("Size of the QRCode border. How many boxes thick is the border.\n"
"Default value is 4. The width of the clearance around the QRCode.")
)
self.border_size_entry = FCSpinner()
self.border_size_entry = FCSpinner(callback=self.confirmation_message_int)
self.border_size_entry.set_range(1, 9999)
self.border_size_entry.setWrapping(True)
self.border_size_entry.set_value(4)
grid_lay.addWidget(self.border_size_label, 4, 0)
grid_lay.addWidget(self.border_size_entry, 4, 1)
@ -386,6 +386,8 @@ class QRCode(FlatCAMTool):
def set_tool_ui(self):
self.units = self.app.defaults['units']
self.border_size_entry.set_value(4)
self.version_entry.set_value(int(self.app.defaults["tools_qrcode_version"]))
self.error_radio.set_value(self.app.defaults["tools_qrcode_error"])
self.bsize_entry.set_value(int(self.app.defaults["tools_qrcode_box_size"]))
@ -495,7 +497,7 @@ class QRCode(FlatCAMTool):
mask_geo = box(a, b, c, d).buffer(buff_val, join_style=2)
# update the solid geometry with the cutout (if it is the case)
new_solid_geometry = list()
new_solid_geometry = []
offset_mask_geo = translate(mask_geo, xoff=pos[0], yoff=pos[1])
for poly in geo_list:
if poly.contains(offset_mask_geo):
@ -522,7 +524,7 @@ class QRCode(FlatCAMTool):
box_size = float(self.bsize_entry.get_value()) / 10.0
sort_apid = list()
sort_apid = []
new_apid = '10'
if self.grb_object.apertures:
for k, v in list(self.grb_object.apertures.items()):
@ -536,8 +538,8 @@ class QRCode(FlatCAMTool):
# don't know if the condition is required since I already made sure above that the new_apid is a new one
if new_apid not in self.grb_object.apertures:
self.grb_object.apertures[new_apid] = dict()
self.grb_object.apertures[new_apid]['geometry'] = list()
self.grb_object.apertures[new_apid] = {}
self.grb_object.apertures[new_apid]['geometry'] = []
self.grb_object.apertures[new_apid]['type'] = 'R'
# TODO: HACK
# I've artificially added 1% to the height and width because otherwise after loading the
@ -548,14 +550,14 @@ class QRCode(FlatCAMTool):
self.grb_object.apertures[new_apid]['size'] = deepcopy(math.sqrt(box_size ** 2 + box_size ** 2))
if '0' not in self.grb_object.apertures:
self.grb_object.apertures['0'] = dict()
self.grb_object.apertures['0']['geometry'] = list()
self.grb_object.apertures['0'] = {}
self.grb_object.apertures['0']['geometry'] = []
self.grb_object.apertures['0']['type'] = 'REG'
self.grb_object.apertures['0']['size'] = 0.0
# in case that the QRCode geometry is dropped onto a copper region (found in the '0' aperture)
# make sure that I place a cutout there
zero_elem = dict()
zero_elem = {}
zero_elem['clear'] = offset_mask_geo
self.grb_object.apertures['0']['geometry'].append(deepcopy(zero_elem))
@ -570,12 +572,12 @@ class QRCode(FlatCAMTool):
try:
for geo in self.qrcode_geometry:
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = translate(geo, xoff=pos[0], yoff=pos[1])
geo_elem['follow'] = translate(geo.centroid, xoff=pos[0], yoff=pos[1])
self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
except TypeError:
geo_elem = dict()
geo_elem = {}
geo_elem['solid'] = self.qrcode_geometry
self.grb_object.apertures[new_apid]['geometry'].append(deepcopy(geo_elem))
@ -591,7 +593,7 @@ class QRCode(FlatCAMTool):
# face = '#0000FF' + str(hex(int(0.2 * 255)))[2:]
outline = '#0000FFAF'
offset_geo = list()
offset_geo = []
# 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 ...

View File

@ -8,7 +8,7 @@
from PyQt5 import QtWidgets
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection
from flatcamGUI.GUIElements import FCDoubleSpinner, FCCheckBox, OptionalInputSection, FCComboBox
from copy import deepcopy
from FlatCAMPool import *
@ -69,10 +69,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.all_obj_cb, 0, 2)
# Copper Top object
self.copper_t_object = QtWidgets.QComboBox()
self.copper_t_object = FCComboBox()
self.copper_t_object.setModel(self.app.collection)
self.copper_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.copper_t_object.setCurrentIndex(1)
self.copper_t_object.is_last = True
self.copper_t_object.obj_type = "Gerber"
self.copper_t_object_lbl = QtWidgets.QLabel('%s:' % _("Top"))
self.copper_t_object_lbl.setToolTip(
@ -86,10 +87,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.copper_t_cb, 1, 2)
# Copper Bottom object
self.copper_b_object = QtWidgets.QComboBox()
self.copper_b_object = FCComboBox()
self.copper_b_object.setModel(self.app.collection)
self.copper_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.copper_b_object.setCurrentIndex(1)
self.copper_b_object.is_last = True
self.copper_b_object.obj_type = "Gerber"
self.copper_b_object_lbl = QtWidgets.QLabel('%s:' % _("Bottom"))
self.copper_b_object_lbl.setToolTip(
@ -103,10 +105,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.copper_b_cb, 2, 2)
# SolderMask Top object
self.sm_t_object = QtWidgets.QComboBox()
self.sm_t_object = FCComboBox()
self.sm_t_object.setModel(self.app.collection)
self.sm_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sm_t_object.setCurrentIndex(1)
self.sm_t_object.is_last = True
self.sm_t_object.obj_type = "Gerber"
self.sm_t_object_lbl = QtWidgets.QLabel('%s:' % _("SM Top"))
self.sm_t_object_lbl.setToolTip(
@ -120,10 +123,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.sm_t_cb, 3, 2)
# SolderMask Bottom object
self.sm_b_object = QtWidgets.QComboBox()
self.sm_b_object = FCComboBox()
self.sm_b_object.setModel(self.app.collection)
self.sm_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sm_b_object.setCurrentIndex(1)
self.sm_b_object.is_last = True
self.sm_b_object.obj_type = "Gerber"
self.sm_b_object_lbl = QtWidgets.QLabel('%s:' % _("SM Bottom"))
self.sm_b_object_lbl.setToolTip(
@ -137,10 +141,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.sm_b_cb, 4, 2)
# SilkScreen Top object
self.ss_t_object = QtWidgets.QComboBox()
self.ss_t_object = FCComboBox()
self.ss_t_object.setModel(self.app.collection)
self.ss_t_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.ss_t_object.setCurrentIndex(1)
self.ss_t_object.is_last = True
self.ss_t_object.obj_type = "Gerber"
self.ss_t_object_lbl = QtWidgets.QLabel('%s:' % _("Silk Top"))
self.ss_t_object_lbl.setToolTip(
@ -154,10 +159,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.ss_t_cb, 5, 2)
# SilkScreen Bottom object
self.ss_b_object = QtWidgets.QComboBox()
self.ss_b_object = FCComboBox()
self.ss_b_object.setModel(self.app.collection)
self.ss_b_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.ss_b_object.setCurrentIndex(1)
self.ss_b_object.is_last = True
self.ss_b_object.obj_type = "Gerber"
self.ss_b_object_lbl = QtWidgets.QLabel('%s:' % _("Silk Bottom"))
self.ss_b_object_lbl.setToolTip(
@ -171,10 +177,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.ss_b_cb, 6, 2)
# Outline object
self.outline_object = QtWidgets.QComboBox()
self.outline_object = FCComboBox()
self.outline_object.setModel(self.app.collection)
self.outline_object.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.outline_object.setCurrentIndex(1)
self.outline_object.is_last = True
self.outline_object.obj_type = "Gerber"
self.outline_object_lbl = QtWidgets.QLabel('%s:' % _("Outline"))
self.outline_object_lbl.setToolTip(
@ -197,10 +204,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.excellon_title_lbl, 9, 0, 1, 3)
# Excellon 1 object
self.e1_object = QtWidgets.QComboBox()
self.e1_object = FCComboBox()
self.e1_object.setModel(self.app.collection)
self.e1_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.e1_object.setCurrentIndex(1)
self.e1_object.is_last = True
self.e1_object.obj_type = "Excellon"
self.e1_object_lbl = QtWidgets.QLabel('%s:' % _("Excellon 1"))
self.e1_object_lbl.setToolTip(
@ -215,10 +223,11 @@ class RulesCheck(FlatCAMTool):
self.grid_layout.addWidget(self.e1_cb, 10, 2)
# Excellon 2 object
self.e2_object = QtWidgets.QComboBox()
self.e2_object = FCComboBox()
self.e2_object.setModel(self.app.collection)
self.e2_object.setRootModelIndex(self.app.collection.index(1, 0, QtCore.QModelIndex()))
self.e2_object.setCurrentIndex(1)
self.e2_object.is_last = True
self.e2_object.obj_type = "Excellon"
self.e2_object_lbl = QtWidgets.QLabel('%s:' % _("Excellon 2"))
self.e2_object_lbl.setToolTip(
@ -260,7 +269,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.trace_size_cb)
# Trace size value
self.trace_size_entry = FCDoubleSpinner()
self.trace_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.trace_size_entry.set_range(0.00001, 999.99999)
self.trace_size_entry.set_precision(self.decimals)
self.trace_size_entry.setSingleStep(0.1)
@ -282,7 +291,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.clearance_copper2copper_cb)
# Copper2copper clearance value
self.clearance_copper2copper_entry = FCDoubleSpinner()
self.clearance_copper2copper_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_copper2copper_entry.set_range(0.00001, 999.99999)
self.clearance_copper2copper_entry.set_precision(self.decimals)
self.clearance_copper2copper_entry.setSingleStep(0.1)
@ -305,7 +314,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.clearance_copper2ol_cb)
# Copper2outline clearance value
self.clearance_copper2ol_entry = FCDoubleSpinner()
self.clearance_copper2ol_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_copper2ol_entry.set_range(0.00001, 999.99999)
self.clearance_copper2ol_entry.set_precision(self.decimals)
self.clearance_copper2ol_entry.setSingleStep(0.1)
@ -328,7 +337,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.clearance_silk2silk_cb)
# Copper2silkscreen clearance value
self.clearance_silk2silk_entry = FCDoubleSpinner()
self.clearance_silk2silk_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_silk2silk_entry.set_range(0.00001, 999.99999)
self.clearance_silk2silk_entry.set_precision(self.decimals)
self.clearance_silk2silk_entry.setSingleStep(0.1)
@ -351,7 +360,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.clearance_silk2sm_cb)
# Silkscreen2soldermask clearance value
self.clearance_silk2sm_entry = FCDoubleSpinner()
self.clearance_silk2sm_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_silk2sm_entry.set_range(0.00001, 999.99999)
self.clearance_silk2sm_entry.set_precision(self.decimals)
self.clearance_silk2sm_entry.setSingleStep(0.1)
@ -374,7 +383,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.clearance_silk2ol_cb)
# Silk2outline clearance value
self.clearance_silk2ol_entry = FCDoubleSpinner()
self.clearance_silk2ol_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_silk2ol_entry.set_range(0.00001, 999.99999)
self.clearance_silk2ol_entry.set_precision(self.decimals)
self.clearance_silk2ol_entry.setSingleStep(0.1)
@ -397,7 +406,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.clearance_sm2sm_cb)
# Soldermask2soldermask clearance value
self.clearance_sm2sm_entry = FCDoubleSpinner()
self.clearance_sm2sm_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_sm2sm_entry.set_range(0.00001, 999.99999)
self.clearance_sm2sm_entry.set_precision(self.decimals)
self.clearance_sm2sm_entry.setSingleStep(0.1)
@ -420,7 +429,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.ring_integrity_cb)
# Ring integrity value
self.ring_integrity_entry = FCDoubleSpinner()
self.ring_integrity_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.ring_integrity_entry.set_range(0.00001, 999.99999)
self.ring_integrity_entry.set_precision(self.decimals)
self.ring_integrity_entry.setSingleStep(0.1)
@ -445,7 +454,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.clearance_d2d_cb)
# Hole2Hole clearance value
self.clearance_d2d_entry = FCDoubleSpinner()
self.clearance_d2d_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.clearance_d2d_entry.set_range(0.00001, 999.99999)
self.clearance_d2d_entry.set_precision(self.decimals)
self.clearance_d2d_entry.setSingleStep(0.1)
@ -468,7 +477,7 @@ class RulesCheck(FlatCAMTool):
self.form_layout_1.addRow(self.drill_size_cb)
# Drile holes value
self.drill_size_entry = FCDoubleSpinner()
self.drill_size_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.drill_size_entry.set_range(0.00001, 999.99999)
self.drill_size_entry.set_precision(self.decimals)
self.drill_size_entry.setSingleStep(0.1)
@ -655,8 +664,8 @@ class RulesCheck(FlatCAMTool):
rule_title = rule
violations = list()
obj_violations = dict()
violations = []
obj_violations = {}
obj_violations.update({
'name': '',
'points': list()
@ -667,8 +676,8 @@ class RulesCheck(FlatCAMTool):
obj_violations['name'] = gerber_obj['name']
solid_geo = list()
clear_geo = list()
solid_geo = []
clear_geo = []
for apid in gerber_obj['apertures']:
if 'geometry' in gerber_obj['apertures'][apid]:
geometry = gerber_obj['apertures'][apid]['geometry']
@ -679,7 +688,7 @@ class RulesCheck(FlatCAMTool):
clear_geo.append(geo_el['clear'])
if clear_geo:
total_geo = list()
total_geo = []
for geo_c in clear_geo:
for geo_s in solid_geo:
if geo_c.within(geo_s):
@ -696,7 +705,7 @@ class RulesCheck(FlatCAMTool):
iterations = (iterations * (iterations - 1)) / 2
log.debug("RulesCheck.check_gerber_clearance(). Iterations: %s" % str(iterations))
min_dict = dict()
min_dict = {}
idx = 1
for geo in total_geo:
for s_geo in total_geo[idx:]:
@ -729,8 +738,8 @@ class RulesCheck(FlatCAMTool):
log.debug("RulesCheck.check_gerber_clearance()")
rule_title = rule
violations = list()
obj_violations = dict()
violations = []
obj_violations = {}
obj_violations.update({
'name': '',
'points': list()
@ -739,7 +748,7 @@ class RulesCheck(FlatCAMTool):
if len(gerber_list) == 2:
gerber_1 = gerber_list[0]
# added it so I won't have errors of using before declaring
gerber_2 = dict()
gerber_2 = {}
gerber_3 = gerber_list[1]
elif len(gerber_list) == 3:
@ -749,7 +758,7 @@ class RulesCheck(FlatCAMTool):
else:
return 'Fail. Not enough Gerber objects to check Gerber 2 Gerber clearance'
total_geo_grb_1 = list()
total_geo_grb_1 = []
for apid in gerber_1['apertures']:
if 'geometry' in gerber_1['apertures'][apid]:
geometry = gerber_1['apertures'][apid]['geometry']
@ -766,7 +775,7 @@ class RulesCheck(FlatCAMTool):
if 'solid' in geo_el and geo_el['solid'] is not None:
total_geo_grb_1.append(geo_el['solid'])
total_geo_grb_3 = list()
total_geo_grb_3 = []
for apid in gerber_3['apertures']:
if 'geometry' in gerber_3['apertures'][apid]:
geometry = gerber_3['apertures'][apid]['geometry']
@ -795,7 +804,7 @@ class RulesCheck(FlatCAMTool):
iterations = len_1 * len_3
log.debug("RulesCheck.check_gerber_clearance(). Iterations: %s" % str(iterations))
min_dict = dict()
min_dict = {}
for geo in total_geo_grb_1:
for s_geo in total_geo_grb_3:
# minimize the number of distances by not taking into considerations those that are too small
@ -817,7 +826,7 @@ class RulesCheck(FlatCAMTool):
for location in min_dict[dist]:
points_list.add(location)
name_list = list()
name_list = []
if gerber_1:
name_list.append(gerber_1['name'])
if gerber_2:
@ -837,8 +846,8 @@ class RulesCheck(FlatCAMTool):
rule = _("Hole Size")
violations = list()
obj_violations = dict()
violations = []
obj_violations = {}
obj_violations.update({
'name': '',
'dia': list()
@ -863,14 +872,14 @@ class RulesCheck(FlatCAMTool):
log.debug("RulesCheck.check_holes_clearance()")
rule = _("Hole to Hole Clearance")
violations = list()
obj_violations = dict()
violations = []
obj_violations = {}
obj_violations.update({
'name': '',
'points': list()
})
total_geo = list()
total_geo = []
for elem in elements:
for tool in elem['tools']:
if 'solid_geometry' in elem['tools'][tool]:
@ -878,7 +887,7 @@ class RulesCheck(FlatCAMTool):
for geo in geometry:
total_geo.append(geo)
min_dict = dict()
min_dict = {}
idx = 1
for geo in total_geo:
for s_geo in total_geo[idx:]:
@ -903,7 +912,7 @@ class RulesCheck(FlatCAMTool):
for location in min_dict[dist]:
points_list.add(location)
name_list = list()
name_list = []
for elem in elements:
name_list.append(elem['name'])
@ -919,8 +928,8 @@ class RulesCheck(FlatCAMTool):
rule = _("Trace Size")
violations = list()
obj_violations = dict()
violations = []
obj_violations = {}
obj_violations.update({
'name': '',
'size': list(),
@ -957,18 +966,18 @@ class RulesCheck(FlatCAMTool):
def check_gerber_annular_ring(obj_list, size, rule):
rule_title = rule
violations = list()
obj_violations = dict()
violations = []
obj_violations = {}
obj_violations.update({
'name': '',
'points': list()
})
# added it so I won't have errors of using before declaring
gerber_obj = dict()
gerber_extra_obj = dict()
exc_obj = dict()
exc_extra_obj = dict()
gerber_obj = {}
gerber_extra_obj = {}
exc_obj = {}
exc_extra_obj = {}
if len(obj_list) == 2:
gerber_obj = obj_list[0]
@ -997,7 +1006,7 @@ class RulesCheck(FlatCAMTool):
else:
return 'Fail. Not enough objects to check Minimum Annular Ring'
total_geo_grb = list()
total_geo_grb = []
for apid in gerber_obj['apertures']:
if 'geometry' in gerber_obj['apertures'][apid]:
geometry = gerber_obj['apertures'][apid]['geometry']
@ -1017,7 +1026,7 @@ class RulesCheck(FlatCAMTool):
total_geo_grb = MultiPolygon(total_geo_grb)
total_geo_grb = total_geo_grb.buffer(0)
total_geo_exc = list()
total_geo_exc = []
for tool in exc_obj['tools']:
if 'solid_geometry' in exc_obj['tools'][tool]:
geometry = exc_obj['tools'][tool]['solid_geometry']
@ -1047,7 +1056,7 @@ class RulesCheck(FlatCAMTool):
iterations = len_1 * len_2
log.debug("RulesCheck.check_gerber_annular_ring(). Iterations: %s" % str(iterations))
min_dict = dict()
min_dict = {}
dist = None
for geo in total_geo_grb:
for s_geo in total_geo_exc:
@ -1075,12 +1084,12 @@ class RulesCheck(FlatCAMTool):
else:
min_dict[dist] = [s_geo.representative_point()]
points_list = list()
points_list = []
for dist in min_dict.keys():
for location in min_dict[dist]:
points_list.append(location)
name_list = list()
name_list = []
try:
if gerber_obj:
name_list.append(gerber_obj['name'])
@ -1110,7 +1119,7 @@ class RulesCheck(FlatCAMTool):
return rule_title, violations
def execute(self):
self.results = list()
self.results = []
log.debug("RuleCheck() executing")
@ -1119,17 +1128,17 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Trace Size
if self.trace_size_cb.get_value():
copper_list = list()
copper_list = []
copper_name_1 = self.copper_t_object.currentText()
if copper_name_1 is not '' and self.copper_t_cb.get_value():
elem_dict = dict()
elem_dict = {}
elem_dict['name'] = deepcopy(copper_name_1)
elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_1).apertures)
copper_list.append(elem_dict)
copper_name_2 = self.copper_b_object.currentText()
if copper_name_2 is not '' and self.copper_b_cb.get_value():
elem_dict = dict()
elem_dict = {}
elem_dict['name'] = deepcopy(copper_name_2)
elem_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_name_2).apertures)
copper_list.append(elem_dict)
@ -1151,7 +1160,7 @@ class RulesCheck(FlatCAMTool):
if self.copper_t_cb.get_value():
copper_t_obj = self.copper_t_object.currentText()
copper_t_dict = dict()
copper_t_dict = {}
if copper_t_obj is not '':
copper_t_dict['name'] = deepcopy(copper_t_obj)
@ -1163,7 +1172,7 @@ class RulesCheck(FlatCAMTool):
_("TOP -> Copper to Copper clearance"))))
if self.copper_b_cb.get_value():
copper_b_obj = self.copper_b_object.currentText()
copper_b_dict = dict()
copper_b_dict = {}
if copper_b_obj is not '':
copper_b_dict['name'] = deepcopy(copper_b_obj)
copper_b_dict['apertures'] = deepcopy(self.app.collection.get_by_name(copper_b_obj).apertures)
@ -1181,9 +1190,9 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Copper to Outline Clearance
if self.clearance_copper2ol_cb.get_value() and self.out_cb.get_value():
top_dict = dict()
bottom_dict = dict()
outline_dict = dict()
top_dict = {}
bottom_dict = {}
outline_dict = {}
copper_top = self.copper_t_object.currentText()
if copper_top is not '' and self.copper_t_cb.get_value():
@ -1235,7 +1244,7 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Silk to Silk Clearance
if self.clearance_silk2silk_cb.get_value():
silk_dict = dict()
silk_dict = {}
try:
silk_silk_clearance = float(self.clearance_silk2silk_entry.get_value())
@ -1275,10 +1284,10 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Silk to Solder Mask Clearance
if self.clearance_silk2sm_cb.get_value():
silk_t_dict = dict()
sm_t_dict = dict()
silk_b_dict = dict()
sm_b_dict = dict()
silk_t_dict = {}
sm_t_dict = {}
silk_b_dict = {}
sm_b_dict = {}
top_ss = False
bottom_ss = False
@ -1344,9 +1353,9 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Silk to Outline Clearance
if self.clearance_silk2ol_cb.get_value():
top_dict = dict()
bottom_dict = dict()
outline_dict = dict()
top_dict = {}
bottom_dict = {}
outline_dict = {}
silk_top = self.ss_t_object.currentText()
if silk_top is not '' and self.ss_t_cb.get_value():
@ -1399,7 +1408,7 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Minimum Solder Mask Sliver
if self.clearance_silk2silk_cb.get_value():
sm_dict = dict()
sm_dict = {}
try:
sm_sm_clearance = float(self.clearance_sm2sm_entry.get_value())
@ -1439,10 +1448,10 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Minimum Annular Ring
if self.ring_integrity_cb.get_value():
top_dict = dict()
bottom_dict = dict()
exc_1_dict = dict()
exc_2_dict = dict()
top_dict = {}
bottom_dict = {}
exc_1_dict = {}
exc_2_dict = {}
copper_top = self.copper_t_object.currentText()
if copper_top is not '' and self.copper_t_cb.get_value():
@ -1504,17 +1513,17 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Hole to Hole Clearance
if self.clearance_d2d_cb.get_value():
exc_list = list()
exc_list = []
exc_name_1 = self.e1_object.currentText()
if exc_name_1 is not '' and self.e1_cb.get_value():
elem_dict = dict()
elem_dict = {}
elem_dict['name'] = deepcopy(exc_name_1)
elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
exc_list.append(elem_dict)
exc_name_2 = self.e2_object.currentText()
if exc_name_2 is not '' and self.e2_cb.get_value():
elem_dict = dict()
elem_dict = {}
elem_dict['name'] = deepcopy(exc_name_2)
elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
exc_list.append(elem_dict)
@ -1524,17 +1533,17 @@ class RulesCheck(FlatCAMTool):
# RULE: Check Holes Size
if self.drill_size_cb.get_value():
exc_list = list()
exc_list = []
exc_name_1 = self.e1_object.currentText()
if exc_name_1 is not '' and self.e1_cb.get_value():
elem_dict = dict()
elem_dict = {}
elem_dict['name'] = deepcopy(exc_name_1)
elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_1).tools)
exc_list.append(elem_dict)
exc_name_2 = self.e2_object.currentText()
if exc_name_2 is not '' and self.e2_cb.get_value():
elem_dict = dict()
elem_dict = {}
elem_dict['name'] = deepcopy(exc_name_2)
elem_dict['tools'] = deepcopy(self.app.collection.get_by_name(exc_name_2).tools)
exc_list.append(elem_dict)
@ -1542,7 +1551,7 @@ class RulesCheck(FlatCAMTool):
drill_size = float(self.drill_size_entry.get_value())
self.results.append(self.pool.apply_async(self.check_holes_size, args=(exc_list, drill_size)))
output = list()
output = []
for p in self.results:
output.append(p.get())

View File

@ -61,7 +61,8 @@ class SolderPaste(FlatCAMTool):
self.obj_combo = FCComboBox(callback=self.on_rmb_combo)
self.obj_combo.setModel(self.app.collection)
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.obj_combo.setCurrentIndex(1)
self.obj_combo.is_last = True
self.obj_combo.obj_type = "Gerber"
self.object_label = QtWidgets.QLabel("Gerber: ")
self.object_label.setToolTip(
@ -105,7 +106,7 @@ class SolderPaste(FlatCAMTool):
self.addtool_entry_lbl.setToolTip(
_("Diameter for the new Nozzle tool to add in the Tool Table")
)
self.addtool_entry = FCDoubleSpinner()
self.addtool_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.addtool_entry.set_range(0.0000001, 9999.9999)
self.addtool_entry.set_precision(self.decimals)
self.addtool_entry.setSingleStep(0.1)
@ -174,7 +175,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_box.addLayout(self.gcode_form_layout)
# Z dispense start
self.z_start_entry = FCDoubleSpinner()
self.z_start_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.z_start_entry.set_range(0.0000001, 9999.9999)
self.z_start_entry.set_precision(self.decimals)
self.z_start_entry.setSingleStep(0.1)
@ -186,7 +187,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.z_start_label, self.z_start_entry)
# Z dispense
self.z_dispense_entry = FCDoubleSpinner()
self.z_dispense_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.z_dispense_entry.set_range(0.0000001, 9999.9999)
self.z_dispense_entry.set_precision(self.decimals)
self.z_dispense_entry.setSingleStep(0.1)
@ -198,7 +199,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.z_dispense_label, self.z_dispense_entry)
# Z dispense stop
self.z_stop_entry = FCDoubleSpinner()
self.z_stop_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.z_stop_entry.set_range(0.0000001, 9999.9999)
self.z_stop_entry.set_precision(self.decimals)
self.z_stop_entry.setSingleStep(0.1)
@ -210,7 +211,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.z_stop_label, self.z_stop_entry)
# Z travel
self.z_travel_entry = FCDoubleSpinner()
self.z_travel_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.z_travel_entry.set_range(0.0000001, 9999.9999)
self.z_travel_entry.set_precision(self.decimals)
self.z_travel_entry.setSingleStep(0.1)
@ -223,7 +224,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.z_travel_label, self.z_travel_entry)
# Z toolchange location
self.z_toolchange_entry = FCDoubleSpinner()
self.z_toolchange_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.z_toolchange_entry.set_range(0.0000001, 9999.9999)
self.z_toolchange_entry.set_precision(self.decimals)
self.z_toolchange_entry.setSingleStep(0.1)
@ -244,8 +245,8 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.xy_toolchange_label, self.xy_toolchange_entry)
# Feedrate X-Y
self.frxy_entry = FCDoubleSpinner()
self.frxy_entry.set_range(0.0000001, 9999.9999)
self.frxy_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.frxy_entry.set_range(0.0000, 99999.9999)
self.frxy_entry.set_precision(self.decimals)
self.frxy_entry.setSingleStep(0.1)
@ -256,8 +257,8 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.frxy_label, self.frxy_entry)
# Feedrate Z
self.frz_entry = FCDoubleSpinner()
self.frz_entry.set_range(0.0000001, 9999.9999)
self.frz_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.frz_entry.set_range(0.0000, 99999.9999)
self.frz_entry.set_precision(self.decimals)
self.frz_entry.setSingleStep(0.1)
@ -269,8 +270,8 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.frz_label, self.frz_entry)
# Feedrate Z Dispense
self.frz_dispense_entry = FCDoubleSpinner()
self.frz_dispense_entry.set_range(0.0000001, 9999.9999)
self.frz_dispense_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.frz_dispense_entry.set_range(0.0000, 99999.9999)
self.frz_dispense_entry.set_precision(self.decimals)
self.frz_dispense_entry.setSingleStep(0.1)
@ -282,9 +283,9 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.frz_dispense_label, self.frz_dispense_entry)
# Spindle Speed Forward
self.speedfwd_entry = FCSpinner()
self.speedfwd_entry = FCSpinner(callback=self.confirmation_message_int)
self.speedfwd_entry.set_range(0, 999999)
self.speedfwd_entry.setSingleStep(1000)
self.speedfwd_entry.set_step(1000)
self.speedfwd_label = QtWidgets.QLabel('%s:' % _("Spindle Speed FWD"))
self.speedfwd_label.setToolTip(
@ -294,7 +295,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.speedfwd_label, self.speedfwd_entry)
# Dwell Forward
self.dwellfwd_entry = FCDoubleSpinner()
self.dwellfwd_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.dwellfwd_entry.set_range(0.0000001, 9999.9999)
self.dwellfwd_entry.set_precision(self.decimals)
self.dwellfwd_entry.setSingleStep(0.1)
@ -306,9 +307,9 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.dwellfwd_label, self.dwellfwd_entry)
# Spindle Speed Reverse
self.speedrev_entry = FCSpinner()
self.speedrev_entry = FCSpinner(callback=self.confirmation_message_int)
self.speedrev_entry.set_range(0, 999999)
self.speedrev_entry.setSingleStep(1000)
self.speedrev_entry.set_step(1000)
self.speedrev_label = QtWidgets.QLabel('%s:' % _("Spindle Speed REV"))
self.speedrev_label.setToolTip(
@ -318,7 +319,7 @@ class SolderPaste(FlatCAMTool):
self.gcode_form_layout.addRow(self.speedrev_label, self.speedrev_entry)
# Dwell Reverse
self.dwellrev_entry = FCDoubleSpinner()
self.dwellrev_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.dwellrev_entry.set_range(0.0000001, 9999.9999)
self.dwellrev_entry.set_precision(self.decimals)
self.dwellrev_entry.setSingleStep(0.1)
@ -383,7 +384,8 @@ class SolderPaste(FlatCAMTool):
self.geo_obj_combo = FCComboBox(callback=self.on_rmb_combo)
self.geo_obj_combo.setModel(self.app.collection)
self.geo_obj_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.geo_obj_combo.setCurrentIndex(1)
self.geo_obj_combo.is_last = True
self.geo_obj_combo.obj_type = "Geometry"
self.geo_object_label = QtWidgets.QLabel('%s:' % _("Geo Result"))
self.geo_object_label.setToolTip(
@ -416,7 +418,8 @@ class SolderPaste(FlatCAMTool):
self.cnc_obj_combo = FCComboBox(callback=self.on_rmb_combo)
self.cnc_obj_combo.setModel(self.app.collection)
self.cnc_obj_combo.setRootModelIndex(self.app.collection.index(3, 0, QtCore.QModelIndex()))
self.cnc_obj_combo.setCurrentIndex(1)
self.cnc_obj_combo.is_last = True
self.geo_obj_combo.obj_type = "CNCJob"
self.cnc_object_label = QtWidgets.QLabel('%s:' % _("CNC Result"))
self.cnc_object_label.setToolTip(
@ -491,6 +494,8 @@ class SolderPaste(FlatCAMTool):
self.units = ''
self.name = ""
self.obj = None
self.text_editor_tab = None
# this will be used in the combobox context menu, for delete entry
@ -652,10 +657,10 @@ class SolderPaste(FlatCAMTool):
for tooluid_key, tooluid_value in self.tooltable_tools.items():
if float('%.*f' % (self.decimals, tooluid_value['tooldia'])) == tool_sorted:
tool_id += 1
id = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
id.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
id_item = QtWidgets.QTableWidgetItem('%d' % int(tool_id))
id_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
row_no = tool_id - 1
self.tools_table.setItem(row_no, 0, id) # Tool name/id
self.tools_table.setItem(row_no, 0, id_item) # Tool name/id
# Make sure that the drill diameter when in MM is with no more than 2 decimals
# There are no drill bits in MM with more than 2 decimals diameter
@ -1295,7 +1300,7 @@ class SolderPaste(FlatCAMTool):
if obj.tools[tooluid_key]['solid_geometry'] is None:
a += 1
if a == len(obj.tools):
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry'))
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('Cancelled. Empty file, it has no geometry'))
return 'fail'
# use the name of the first tool selected in self.geo_tools_table which has the diameter passed as tool_dia
@ -1334,8 +1339,6 @@ class SolderPaste(FlatCAMTool):
assert isinstance(job_obj, FlatCAMCNCjob), \
"Initializer expected a FlatCAMCNCjob, got %s" % type(job_obj)
tool_cnc_dict = {}
# this turn on the FlatCAMCNCJob plot for multiple tools
job_obj.multitool = True
job_obj.multigeo = True

View File

@ -8,7 +8,7 @@
from PyQt5 import QtWidgets, QtCore
from FlatCAMTool import FlatCAMTool
from flatcamGUI.GUIElements import FCCheckBox, FCButton
from flatcamGUI.GUIElements import FCCheckBox, FCButton, FCComboBox
from shapely.geometry import Polygon, MultiPolygon, MultiLineString, LineString
from shapely.ops import cascaded_union
@ -66,10 +66,12 @@ class ToolSub(FlatCAMTool):
form_layout.addRow(self.gerber_title)
# Target Gerber Object
self.target_gerber_combo = QtWidgets.QComboBox()
self.target_gerber_combo = FCComboBox()
self.target_gerber_combo.setModel(self.app.collection)
self.target_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.target_gerber_combo.setCurrentIndex(1)
# self.target_gerber_combo.setCurrentIndex(1)
self.target_gerber_combo.is_last = True
self.target_gerber_combo.obj_type = "Gerber"
self.target_gerber_label = QtWidgets.QLabel('%s:' % _("Target"))
self.target_gerber_label.setToolTip(
@ -80,10 +82,11 @@ class ToolSub(FlatCAMTool):
form_layout.addRow(self.target_gerber_label, self.target_gerber_combo)
# Substractor Gerber Object
self.sub_gerber_combo = QtWidgets.QComboBox()
self.sub_gerber_combo = FCComboBox()
self.sub_gerber_combo.setModel(self.app.collection)
self.sub_gerber_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
self.sub_gerber_combo.setCurrentIndex(1)
self.sub_gerber_combo.is_last = True
self.sub_gerber_combo.obj_type = "Gerber"
self.sub_gerber_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
self.sub_gerber_label.setToolTip(
@ -118,10 +121,12 @@ class ToolSub(FlatCAMTool):
form_geo_layout.addRow(self.geo_title)
# Target Geometry Object
self.target_geo_combo = QtWidgets.QComboBox()
self.target_geo_combo = FCComboBox()
self.target_geo_combo.setModel(self.app.collection)
self.target_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.target_geo_combo.setCurrentIndex(1)
# self.target_geo_combo.setCurrentIndex(1)
self.target_geo_combo.is_last = True
self.target_geo_combo.obj_type = "Geometry"
self.target_geo_label = QtWidgets.QLabel('%s:' % _("Target"))
self.target_geo_label.setToolTip(
@ -132,10 +137,11 @@ class ToolSub(FlatCAMTool):
form_geo_layout.addRow(self.target_geo_label, self.target_geo_combo)
# Substractor Geometry Object
self.sub_geo_combo = QtWidgets.QComboBox()
self.sub_geo_combo = FCComboBox()
self.sub_geo_combo.setModel(self.app.collection)
self.sub_geo_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
self.sub_geo_combo.setCurrentIndex(1)
self.sub_geo_combo.is_last = True
self.sub_geo_combo.obj_type = "Geometry"
self.sub_geo_label = QtWidgets.QLabel('%s:' % _("Subtractor"))
self.sub_geo_label.setToolTip(
@ -254,14 +260,14 @@ class ToolSub(FlatCAMTool):
FlatCAMTool.run(self)
self.set_tool_ui()
self.app.ui.notebook.setTabText(2, _("Sub Tool"))
def set_tool_ui(self):
self.new_apertures.clear()
self.new_tools.clear()
self.new_solid_geometry = []
self.target_options.clear()
self.app.ui.notebook.setTabText(2, _("Sub Tool"))
def set_tool_ui(self):
self.tools_frame.show()
self.close_paths_cb.setChecked(self.app.defaults["tools_sub_close_paths"])
@ -303,14 +309,14 @@ class ToolSub(FlatCAMTool):
# crate the new_apertures dict structure
for apid in self.target_grb_obj.apertures:
self.new_apertures[apid] = dict()
self.new_apertures[apid] = {}
self.new_apertures[apid]['type'] = 'C'
self.new_apertures[apid]['size'] = self.target_grb_obj.apertures[apid]['size']
self.new_apertures[apid]['geometry'] = list()
self.new_apertures[apid]['geometry'] = []
geo_solid_union_list = list()
geo_follow_union_list = list()
geo_clear_union_list = list()
geo_solid_union_list = []
geo_follow_union_list = []
geo_clear_union_list = []
for apid1 in self.sub_grb_obj.apertures:
if 'geometry' in self.sub_grb_obj.apertures[apid1]:
@ -339,14 +345,14 @@ class ToolSub(FlatCAMTool):
self.app.worker_task.emit({'fcn': self.aperture_intersection, 'params': [apid, geo]})
def aperture_intersection(self, apid, geo):
new_geometry = list()
new_geometry = []
log.debug("Working on promise: %s" % str(apid))
with self.app.proc_container.new('%s: %s...' % (_("Parsing geometry for aperture"), str(apid))):
for geo_el in geo:
new_el = dict()
new_el = {}
if 'solid' in geo_el:
work_geo = geo_el['solid']
@ -513,14 +519,14 @@ class ToolSub(FlatCAMTool):
return
# create the target_options obj
# self.target_options = dict()
# self.target_options = {}
# for k, v in self.target_geo_obj.options.items():
# if k != 'name':
# self.target_options[k] = v
# crate the new_tools dict structure
for tool in self.target_geo_obj.tools:
self.new_tools[tool] = dict()
self.new_tools[tool] = {}
for key in self.target_geo_obj.tools[tool]:
if key == 'solid_geometry':
self.new_tools[tool][key] = []

View File

@ -68,7 +68,7 @@ class ToolTransform(FlatCAMTool):
"Negative numbers for CCW motion.")
)
self.rotate_entry = FCDoubleSpinner()
self.rotate_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.rotate_entry.set_precision(self.decimals)
self.rotate_entry.setSingleStep(45)
self.rotate_entry.setWrapping(True)
@ -77,7 +77,6 @@ class ToolTransform(FlatCAMTool):
# self.rotate_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.rotate_button = FCButton()
self.rotate_button.set_value(_("Rotate"))
self.rotate_button.setToolTip(
_("Rotate the selected object(s).\n"
"The point of reference is the middle of\n"
@ -103,13 +102,12 @@ class ToolTransform(FlatCAMTool):
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 360.")
)
self.skewx_entry = FCDoubleSpinner()
self.skewx_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.skewx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.skewx_entry.set_precision(self.decimals)
self.skewx_entry.set_range(-360, 360)
self.skewx_button = FCButton()
self.skewx_button.set_value(_("Skew X"))
self.skewx_button.setToolTip(
_("Skew/shear the selected object(s).\n"
"The point of reference is the middle of\n"
@ -125,13 +123,12 @@ class ToolTransform(FlatCAMTool):
_("Angle for Skew action, in degrees.\n"
"Float number between -360 and 360.")
)
self.skewy_entry = FCDoubleSpinner()
self.skewy_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.skewy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.skewy_entry.set_precision(self.decimals)
self.skewy_entry.set_range(-360, 360)
self.skewy_button = FCButton()
self.skewy_button.set_value(_("Skew Y"))
self.skewy_button.setToolTip(
_("Skew/shear the selected object(s).\n"
"The point of reference is the middle of\n"
@ -155,13 +152,12 @@ class ToolTransform(FlatCAMTool):
self.scalex_label.setToolTip(
_("Factor for scaling on X axis.")
)
self.scalex_entry = FCDoubleSpinner()
self.scalex_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.scalex_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.scalex_entry.set_precision(self.decimals)
self.scalex_entry.setMinimum(-1e6)
self.scalex_button = FCButton()
self.scalex_button.set_value(_("Scale X"))
self.scalex_button.setToolTip(
_("Scale the selected object(s).\n"
"The point of reference depends on \n"
@ -176,13 +172,12 @@ class ToolTransform(FlatCAMTool):
self.scaley_label.setToolTip(
_("Factor for scaling on Y axis.")
)
self.scaley_entry = FCDoubleSpinner()
self.scaley_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.scaley_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.scaley_entry.set_precision(self.decimals)
self.scaley_entry.setMinimum(-1e6)
self.scaley_button = FCButton()
self.scaley_button.set_value(_("Scale Y"))
self.scaley_button.setToolTip(
_("Scale the selected object(s).\n"
"The point of reference depends on \n"
@ -194,7 +189,6 @@ class ToolTransform(FlatCAMTool):
grid0.addWidget(self.scaley_button, 9, 2)
self.scale_link_cb = FCCheckBox()
self.scale_link_cb.set_value(True)
self.scale_link_cb.setText(_("Link"))
self.scale_link_cb.setToolTip(
_("Scale the selected object(s)\n"
@ -202,7 +196,6 @@ class ToolTransform(FlatCAMTool):
)
self.scale_zero_ref_cb = FCCheckBox()
self.scale_zero_ref_cb.set_value(True)
self.scale_zero_ref_cb.setText('%s' % _("Scale Reference"))
self.scale_zero_ref_cb.setToolTip(
_("Scale the selected object(s)\n"
@ -228,13 +221,12 @@ class ToolTransform(FlatCAMTool):
self.offx_label.setToolTip(
_("Distance to offset on X axis. In current units.")
)
self.offx_entry = FCDoubleSpinner()
self.offx_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.offx_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.offx_entry.set_precision(self.decimals)
self.offx_entry.setMinimum(-1e6)
self.offx_button = FCButton()
self.offx_button.set_value(_("Offset X"))
self.offx_button.setToolTip(
_("Offset the selected object(s).\n"
"The point of reference is the middle of\n"
@ -249,13 +241,12 @@ class ToolTransform(FlatCAMTool):
self.offy_label.setToolTip(
_("Distance to offset on Y axis. In current units.")
)
self.offy_entry = FCDoubleSpinner()
self.offy_entry = FCDoubleSpinner(callback=self.confirmation_message)
# self.offy_entry.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
self.offy_entry.set_precision(self.decimals)
self.offy_entry.setMinimum(-1e6)
self.offy_button = FCButton()
self.offy_button.set_value(_("Offset Y"))
self.offy_button.setToolTip(
_("Offset the selected object(s).\n"
"The point of reference is the middle of\n"
@ -276,13 +267,11 @@ class ToolTransform(FlatCAMTool):
grid0.addWidget(flip_title_label, 16, 0, 1, 3)
self.flipx_button = FCButton()
self.flipx_button.set_value(_("Flip on X"))
self.flipx_button.setToolTip(
_("Flip the selected object(s) over the X axis.")
)
self.flipy_button = FCButton()
self.flipy_button.set_value(_("Flip on Y"))
self.flipy_button.setToolTip(
_("Flip the selected object(s) over the X axis.")
)
@ -294,7 +283,6 @@ class ToolTransform(FlatCAMTool):
hlay0.addWidget(self.flipy_button)
self.flip_ref_cb = FCCheckBox()
self.flip_ref_cb.set_value(True)
self.flip_ref_cb.setText('%s' % _("Mirror Reference"))
self.flip_ref_cb.setToolTip(
_("Flip the selected object(s)\n"
@ -320,7 +308,6 @@ class ToolTransform(FlatCAMTool):
# self.flip_ref_entry.setFixedWidth(70)
self.flip_ref_button = FCButton()
self.flip_ref_button.set_value(_("Add"))
self.flip_ref_button.setToolTip(
_("The point coordinates can be captured by\n"
"left click on canvas together with pressing\n"
@ -353,14 +340,13 @@ class ToolTransform(FlatCAMTool):
"or decreased with the 'distance'.")
)
self.buffer_entry = FCDoubleSpinner()
self.buffer_entry = FCDoubleSpinner(callback=self.confirmation_message)
self.buffer_entry.set_precision(self.decimals)
self.buffer_entry.setSingleStep(0.1)
self.buffer_entry.setWrapping(True)
self.buffer_entry.set_range(-9999.9999, 9999.9999)
self.buffer_button = FCButton()
self.buffer_button.set_value(_("Buffer D"))
self.buffer_button.setToolTip(
_("Create the buffer effect on each geometry,\n"
"element from the selected object, using the distance.")
@ -380,14 +366,13 @@ class ToolTransform(FlatCAMTool):
"of the initial dimension.")
)
self.buffer_factor_entry = FCDoubleSpinner(suffix='%')
self.buffer_factor_entry = FCDoubleSpinner(callback=self.confirmation_message, suffix='%')
self.buffer_factor_entry.set_range(-100.0000, 1000.0000)
self.buffer_factor_entry.set_precision(self.decimals)
self.buffer_factor_entry.setWrapping(True)
self.buffer_factor_entry.setSingleStep(1)
self.buffer_factor_button = FCButton()
self.buffer_factor_button.set_value(_("Buffer F"))
self.buffer_factor_button.setToolTip(
_("Create the buffer effect on each geometry,\n"
"element from the selected object, using the factor.")
@ -412,6 +397,19 @@ class ToolTransform(FlatCAMTool):
self.transform_lay.addStretch()
# ## Reset Tool
self.reset_button = QtWidgets.QPushButton(_("Reset Tool"))
self.reset_button.setToolTip(
_("Will reset the tool parameters.")
)
self.reset_button.setStyleSheet("""
QPushButton
{
font-weight: bold;
}
""")
self.transform_lay.addWidget(self.reset_button)
# ## Signals
self.rotate_button.clicked.connect(self.on_rotate)
self.skewx_button.clicked.connect(self.on_skewx)
@ -426,6 +424,8 @@ class ToolTransform(FlatCAMTool):
self.buffer_button.clicked.connect(self.on_buffer_by_distance)
self.buffer_factor_button.clicked.connect(self.on_buffer_by_factor)
self.reset_button.clicked.connect(self.set_tool_ui)
# self.rotate_entry.returnPressed.connect(self.on_rotate)
# self.skewx_entry.returnPressed.connect(self.on_skewx)
# self.skewy_entry.returnPressed.connect(self.on_skewy)
@ -466,6 +466,22 @@ class ToolTransform(FlatCAMTool):
FlatCAMTool.install(self, icon, separator, shortcut='ALT+T', **kwargs)
def set_tool_ui(self):
self.rotate_button.set_value(_("Rotate"))
self.skewx_button.set_value(_("Skew X"))
self.skewy_button.set_value(_("Skew Y"))
self.scalex_button.set_value(_("Scale X"))
self.scaley_button.set_value(_("Scale Y"))
self.scale_link_cb.set_value(True)
self.scale_zero_ref_cb.set_value(True)
self.offx_button.set_value(_("Offset X"))
self.offy_button.set_value(_("Offset Y"))
self.flipx_button.set_value(_("Flip on X"))
self.flipy_button.set_value(_("Flip on Y"))
self.flip_ref_cb.set_value(True)
self.flip_ref_button.set_value(_("Add"))
self.buffer_button.set_value(_("Buffer D"))
self.buffer_factor_button.set_value(_("Buffer F"))
# ## Initialize form
if self.app.defaults["tools_transform_rotate"]:
self.rotate_entry.set_value(self.app.defaults["tools_transform_rotate"])

View File

@ -17,7 +17,7 @@ from flatcamTools.ToolDistanceMin import DistanceMin
from flatcamTools.ToolMove import ToolMove
from flatcamTools.ToolNonCopperClear import NonCopperClear
from flatcamTools.ToolNCC import NonCopperClear
from flatcamTools.ToolPaint import ToolPaint
from flatcamTools.ToolOptimal import ToolOptimal
@ -38,3 +38,6 @@ from flatcamTools.ToolSolderPaste import SolderPaste
from flatcamTools.ToolSub import ToolSub
from flatcamTools.ToolTransform import ToolTransform
from flatcamTools.ToolPunchGerber import ToolPunchGerber
from flatcamTools.ToolInvertGerber import ToolInvertGerber

View File

@ -22,7 +22,7 @@ class Berta_CNC(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ''
gcode = '(This preprocessor is used with a BERTA CNC router.)\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -35,24 +35,57 @@ class Berta_CNC(FlatCAMPostProc):
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
@ -80,7 +113,7 @@ class Berta_CNC(FlatCAMPostProc):
gcode += 'G54\n'
gcode += 'G0\n'
gcode += '(Berta)\n'
gcode += 'G94\n'
gcode += 'G94'
return gcode
@ -194,10 +227,10 @@ M0""".format(z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolcha
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
gcode += '(Berta)\n'

View File

@ -12,7 +12,7 @@ from FlatCAMPostProc import *
# is compatible with almost any version of Grbl.
class grbl_laser(FlatCAMPostProc):
class GRBL_laser(FlatCAMPostProc):
include_header = True
coordinate_format = "%.*f"
@ -20,7 +20,8 @@ class grbl_laser(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
gcode = ''
gcode = '(This preprocessor is used with a motion controller loaded with GRBL firmware. )\n'
gcode += '(It is for the case when it is used together with a LASER connected on the SPINDLE connector.)\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -28,7 +29,9 @@ class grbl_laser(FlatCAMPostProc):
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Feedrate rapids: ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z Focus: ' + str(p['z_move']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
@ -40,10 +43,12 @@ class grbl_laser(FlatCAMPostProc):
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Laser Power (Spindle Speed): ' + str(p['spindlespeed']) + ')\n\n'
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += 'G90\n'
gcode += 'G17\n'
gcode += 'G94\n'
gcode += 'G94'
return gcode
@ -51,19 +56,20 @@ class grbl_laser(FlatCAMPostProc):
return ''
def lift_code(self, p):
return 'M05 S0'
return 'M5'
def down_code(self, p):
sdir = {'CW': 'M03', 'CCW': 'M04'}[p.spindledir]
if p.spindlespeed:
return 'M03 S%d' % p.spindlespeed
return '%s S%s' % (sdir, str(p.spindlespeed))
else:
return 'M03'
return sdir
def toolchange_code(self, p):
return ''
def up_to_zero_code(self, p):
return 'M05'
return 'M5'
def position_code(self, p):
return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \
@ -77,10 +83,10 @@ class grbl_laser(FlatCAMPostProc):
' F' + str(self.feedrate_format % (p.fr_decimals, p.feedrate))
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode
@ -101,4 +107,4 @@ class grbl_laser(FlatCAMPostProc):
return ''
def spindle_stop_code(self, p):
return 'M05'
return 'M5'

View File

@ -17,7 +17,7 @@ class ISEL_CNC(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ''
gcode = '(This preprocessor is used with a ISEL CNC router.)\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -26,28 +26,56 @@ class ISEL_CNC(FlatCAMPostProc):
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
@ -129,10 +157,10 @@ M01""".format(tool=int(p.tool), toolC=toolC_formatted)
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode

View File

@ -15,7 +15,7 @@ class ISEL_ICP_CNC(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ''
gcode = '; This preprocessor is used with a ISEL ICP CNC router.\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -25,36 +25,71 @@ class ISEL_ICP_CNC(FlatCAMPostProc):
gcode += 'IMF_PBL flatcam\n\n'
if str(p['options']['type']) == 'Geometry':
gcode += '; TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n'
gcode += '; Spindle Speed: %s RPM\n' % str(p['spindlespeed'])
gcode += '; Feedrate: ' + str(p['feedrate']) + units + '/min' + '\n'
if str(p['options']['type']) == 'Geometry':
gcode += '; Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + '\n'
gcode += '\n'
gcode += '; Z_Cut: ' + str(p['z_cut']) + units + '\n'
if str(p['options']['type']) == 'Geometry':
gcode += ';TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n'
gcode += ';Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n'
if p['multidepth'] is True:
gcode += '; DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
gcode += ';DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + '\n'
gcode += '; Z_Move: ' + str(p['z_move']) + units + '\n'
gcode += '; Z Toolchange: ' + str(p['z_toolchange']) + units + '\n'
if coords_xy is not None:
gcode += '; X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + '\n'
else:
gcode += '; X,Y Toolchange: ' + "None" + units + '\n'
gcode += '; Z Start: ' + str(p['startz']) + units + '\n'
gcode += '; Z End: ' + str(p['z_end']) + units + '\n'
gcode += '; Steps per circle: ' + str(p['steps_per_circle']) + '\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '; Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n'
else:
gcode += '; Preprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n'
gcode += '\n'
gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n'
gcode += '; X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n'
gcode += '; Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n'
gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + '\n'
gcode += '\n;FEEDRATE RAPIDS: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + '\n'
gcode += '\n;Z_CUT: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + '\n'
gcode += '\n;Tools Offset: \n'
for tool, val in p['exc_cnc_tools'].items():
gcode += ';Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + '\n'
if p['multidepth'] is True:
gcode += '\n;DEPTH_PER_CUT: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + '\n'
gcode += '\n;Z_MOVE: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + '\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += ';Z Toolchange: ' + str(p['z_toolchange']) + units + '\n'
if coords_xy is not None:
gcode += ';X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + '\n'
else:
gcode += ';X,Y Toolchange: ' + "None" + units + '\n'
gcode += ';Z Start: ' + str(p['startz']) + units + '\n'
gcode += ';Z End: ' + str(p['z_end']) + units + '\n'
gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n' + '\n'
else:
gcode += ';Preprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n' + '\n'
gcode += ';X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n'
gcode += ';Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n\n'
gcode += ';Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
return gcode

View File

@ -9,7 +9,7 @@
from FlatCAMPostProc import *
class marlin(FlatCAMPostProc):
class Marlin(FlatCAMPostProc):
include_header = True
coordinate_format = "%.*f"
@ -27,47 +27,74 @@ class marlin(FlatCAMPostProc):
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += ';TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n' + '\n'
gcode += ';Feedrate: ' + str(p['feedrate']) + units + '/min' + '\n'
if str(p['options']['type']) == 'Geometry':
gcode += ';TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n'
gcode += ';Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n'
if str(p['options']['type']) == 'Geometry':
gcode += ';Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n'
if p['multidepth'] is True:
gcode += ';DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + '\n'
gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n'
gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n'
gcode += ';Z Toolchange: ' + str(p['z_toolchange']) + units + '\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n'
if coords_xy is not None:
gcode += ';X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + '\n'
else:
gcode += ';X,Y Toolchange: ' + "None" + units + '\n'
gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + '\n'
gcode += '\n;FEEDRATE RAPIDS: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + '\n'
gcode += '\n;Z_CUT: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + '\n'
gcode += '\n;Tools Offset: \n'
for tool, val in p['exc_cnc_tools'].items():
gcode += ';Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + '\n'
if p['multidepth'] is True:
gcode += '\n;DEPTH_PER_CUT: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + '\n'
gcode += '\n;Z_MOVE: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + '\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += ';Z Toolchange: ' + str(p['z_toolchange']) + units + '\n'
if coords_xy is not None:
gcode += ';X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + '\n'
else:
gcode += ';X,Y Toolchange: ' + "None" + units + '\n'
gcode += ';Z Start: ' + str(p['startz']) + units + '\n'
gcode += ';Z End: ' + str(p['z_end']) + units + '\n'
gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n'
gcode += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n' + '\n'
else:
gcode += ';Preprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n' + '\n'
gcode += ';X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n'
gcode += ';Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n\n'
gcode += ';Spindle Speed: ' + str(p['spindlespeed']) + ' RPM' + '\n' + '\n'
gcode += ';Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += 'G90\n'
gcode += 'G94\n'
gcode += 'G90'
return gcode
@ -188,10 +215,10 @@ G0 Z{z_toolchange}
return ('G1 ' + self.position_code(p)).format(**p) + " " + self.inline_feedrate_code(p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G0 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + " " + self.feedrate_rapid_code(p) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n"
return gcode
@ -219,8 +246,11 @@ G0 Z{z_toolchange}
return sdir
def dwell_code(self, p):
gcode = 'G4 P' + str(p.dwelltime)
if p.dwelltime:
return 'G4 P' + str(p.dwelltime)
return gcode
def spindle_stop_code(self, p):
return 'M5'
gcode = 'M400\n'
gcode += 'M5'
return gcode

View File

@ -0,0 +1,120 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# Website: http://flatcam.org #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8-Feb-2020 #
# License: MIT Licence #
# ##########################################################
from FlatCAMPostProc import *
class Marlin_laser_FAN_pin(FlatCAMPostProc):
include_header = True
coordinate_format = "%.*f"
feedrate_format = '%.*f'
feedrate_rapid_format = feedrate_format
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ';This preprocessor is used with a motion controller loaded with MARLIN firmware.\n'
gcode += ';It is for the case when it is used together with a LASER connected on one of the FAN pins.\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
gcode += ';Feedrate: ' + str(p['feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate rapids: ' + str(p['feedrate_rapid']) + units + '/min' + '\n\n'
gcode += ';Z Focus: ' + str(p['z_move']) + units + '\n'
gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n'
else:
gcode += ';Preprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n' + '\n'
gcode += ';X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n'
gcode += ';Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n\n'
gcode += ';Laser Power (Spindle Speed): ' + str(p['spindlespeed']) + '\n' + '\n'
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += 'G90'
return gcode
def startz_code(self, p):
if p.startz is not None:
return 'G0 Z' + self.coordinate_format % (p.coords_decimals, p.z_move)
else:
return ''
def lift_code(self, p):
gcode = 'M400\n'
gcode += 'M107'
return gcode
def down_code(self, p):
if p.spindlespeed:
return '%s S%s' % ('M106', str(p.spindlespeed))
else:
return 'M106'
def toolchange_code(self, p):
return ''
def up_to_zero_code(self, p):
gcode = 'M400\n'
gcode += 'M107'
return gcode
def position_code(self, p):
return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \
(p.coords_decimals, p.x, p.coords_decimals, p.y)
def rapid_code(self, p):
return ('G0 ' + self.position_code(p)).format(**p) + " " + self.feedrate_rapid_code(p)
def linear_code(self, p):
return ('G1 ' + self.position_code(p)).format(**p) + " " + self.inline_feedrate_code(p)
def end_code(self, p):
coords_xy = p['xy_end']
gcode = ('G0 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + " " + self.feedrate_rapid_code(p) + "\n")
if coords_xy and coords_xy != '':
gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n"
return gcode
def feedrate_code(self, p):
return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, p.feedrate))
def z_feedrate_code(self, p):
return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, p.z_feedrate))
def inline_feedrate_code(self, p):
return 'F' + self.feedrate_format % (p.fr_decimals, p.feedrate)
def feedrate_rapid_code(self, p):
return 'F' + self.feedrate_rapid_format % (p.fr_decimals, p.feedrate_rapid)
def spindle_code(self, p):
if p.spindlespeed:
return 'M106 S%s' % str(p.spindlespeed)
else:
return 'M106'
def dwell_code(self, p):
return ''
def spindle_stop_code(self, p):
gcode = 'M400\n'
gcode += 'M106 S0'
return gcode

View File

@ -0,0 +1,122 @@
# ##########################################################
# FlatCAM: 2D Post-processing for Manufacturing #
# Website: http://flatcam.org #
# File Author: Marius Adrian Stanciu (c) #
# Date: 8-Feb-2020 #
# License: MIT Licence #
# ##########################################################
from FlatCAMPostProc import *
class Marlin_laser_Spindle_pin(FlatCAMPostProc):
include_header = True
coordinate_format = "%.*f"
feedrate_format = '%.*f'
feedrate_rapid_format = feedrate_format
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ';This preprocessor is used with a motion controller loaded with MARLIN firmware.\n'
gcode += ';It is for the case when it is used together with a LASER connected on the SPINDLE connector.\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
ymin = '%.*f' % (p.coords_decimals, p['options']['ymin'])
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
gcode += ';Feedrate: ' + str(p['feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate rapids: ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
gcode += ';Z Focus: ' + str(p['z_move']) + units + '\n'
gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n'
else:
gcode += ';Preprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n' + '\n'
gcode += ';X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n'
gcode += ';Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n\n'
gcode += ';Laser Power (Spindle Speed): ' + str(p['spindlespeed']) + '\n' + '\n'
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += 'G90'
return gcode
def startz_code(self, p):
if p.startz is not None:
return 'G0 Z' + self.coordinate_format % (p.coords_decimals, p.z_move)
else:
return ''
def lift_code(self, p):
gcode = 'M400\n'
gcode += 'M5'
return gcode
def down_code(self, p):
sdir = {'CW': 'M3', 'CCW': 'M4'}[p.spindledir]
if p.spindlespeed:
return '%s S%s' % (sdir, str(p.spindlespeed))
else:
return sdir
def toolchange_code(self, p):
return ''
def up_to_zero_code(self, p):
gcode = 'M400\n'
gcode += 'M5'
return gcode
def position_code(self, p):
return ('X' + self.coordinate_format + ' Y' + self.coordinate_format) % \
(p.coords_decimals, p.x, p.coords_decimals, p.y)
def rapid_code(self, p):
return ('G0 ' + self.position_code(p)).format(**p) + " " + self.feedrate_rapid_code(p)
def linear_code(self, p):
return ('G1 ' + self.position_code(p)).format(**p) + " " + self.inline_feedrate_code(p)
def end_code(self, p):
coords_xy = p['xy_end']
gcode = ('G0 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + " " + self.feedrate_rapid_code(p) + "\n")
if coords_xy and coords_xy != '':
gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n"
return gcode
def feedrate_code(self, p):
return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, p.feedrate))
def z_feedrate_code(self, p):
return 'G1 F' + str(self.feedrate_format % (p.fr_decimals, p.z_feedrate))
def inline_feedrate_code(self, p):
return 'F' + self.feedrate_format % (p.fr_decimals, p.feedrate)
def feedrate_rapid_code(self, p):
return 'F' + self.feedrate_rapid_format % (p.fr_decimals, p.feedrate_rapid)
def spindle_code(self, p):
sdir = {'CW': 'M3', 'CCW': 'M4'}[p.spindledir]
if p.spindlespeed:
return '%s S%s' % (sdir, str(p.spindlespeed))
else:
return sdir
def dwell_code(self, p):
return ''
def spindle_stop_code(self, p):
gcode = 'M400\n'
gcode += 'M5'
return gcode

View File

@ -122,10 +122,10 @@ G00 Z{z_toolchange}
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = [float(eval(a)) for a in p['xy_toolchange'].split(",") if a != '']
coords_xy = [float(eval(a)) for a in p['xy_end'].split(",") if a != '']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, float(p['z_toolchange'])) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode

View File

@ -19,7 +19,7 @@ class Repetier(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ''
gcode = ';This preprocessor is used with a motion controller loaded with REPETIER firmware.\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -27,43 +27,71 @@ class Repetier(FlatCAMPostProc):
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += ';TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n' + '\n'
gcode += ';Feedrate: ' + str(p['feedrate']) + units + '/min' + '\n'
if str(p['options']['type']) == 'Geometry':
gcode += ';TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + '\n'
gcode += ';Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + '\n'
gcode += ';Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n'
if str(p['options']['type']) == 'Geometry':
gcode += ';Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + '\n' + '\n'
gcode += ';Z_Cut: ' + str(p['z_cut']) + units + '\n'
if p['multidepth'] is True:
gcode += ';DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + '\n'
gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n'
gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n'
gcode += ';Z Toolchange: ' + str(p['z_toolchange']) + units + '\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n;TOOLS DIAMETER: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + '\n'
if coords_xy is not None:
gcode += ';X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + '\n'
else:
gcode += ';X,Y Toolchange: ' + "None" + units + '\n'
gcode += '\n;FEEDRATE Z: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + '\n'
gcode += '\n;FEEDRATE RAPIDS: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + '\n'
gcode += '\n;Z_CUT: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + '\n'
gcode += '\n;Tools Offset: \n'
for tool, val in p['exc_cnc_tools'].items():
gcode += ';Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + '\n'
if p['multidepth'] is True:
gcode += '\n;DEPTH_PER_CUT: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + '\n'
gcode += '\n;Z_MOVE: \n'
for tool, val in p['exc_tools'].items():
gcode += ';Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + '\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += ';Z Toolchange: ' + str(p['z_toolchange']) + units + '\n'
if coords_xy is not None:
gcode += ';X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + '\n'
else:
gcode += ';X,Y Toolchange: ' + "None" + units + '\n'
gcode += ';Z Start: ' + str(p['startz']) + units + '\n'
gcode += ';Z End: ' + str(p['z_end']) + units + '\n'
gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n'
gcode += ';Preprocessor Excellon: ' + str(p['pp_excellon_name']) + '\n' + '\n'
else:
gcode += ';Preprocessor Geometry: ' + str(p['pp_geometry_name']) + '\n' + '\n'
gcode += ';X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + '\n'
gcode += ';Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + '\n\n'
gcode += ';Spindle Speed: ' + str(p['spindlespeed']) + ' RPM' + '\n' + '\n'
gcode += ';Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += 'G90\n'
@ -178,10 +206,10 @@ G0 Z{z_toolchange}
return ('G1 ' + self.position_code(p)).format(**p) + " " + self.inline_feedrate_code(p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G0 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + " " + self.feedrate_rapid_code(p) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n"
return gcode

View File

@ -27,35 +27,63 @@ class Toolchange_Custom(FlatCAMPostProc):
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n'
else:
gcode += '(Preprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
@ -145,10 +173,10 @@ M6
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode

View File

@ -9,7 +9,7 @@
from FlatCAMPostProc import *
class Toolchange_manual(FlatCAMPostProc):
class Toolchange_Manual(FlatCAMPostProc):
include_header = True
coordinate_format = "%.*f"
@ -27,27 +27,57 @@ class Toolchange_manual(FlatCAMPostProc):
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
@ -119,6 +149,7 @@ M0
G00 Z{z_toolchange}
(MSG, Now the tool can be tightened more securely.)
M0
(MSG, Drilling with Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
""".format(x_toolchange=self.coordinate_format % (p.coords_decimals, x_toolchange),
y_toolchange=self.coordinate_format % (p.coords_decimals, y_toolchange),
z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange),
@ -139,6 +170,7 @@ M0
G00 Z{z_toolchange}
(MSG, Now the tool can be tightened more securely.)
M0
(MSG, Milling with Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
""".format(
z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange),
tool=int(p.tool),
@ -203,12 +235,11 @@ M0
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
else:
gcode += 'G00 X0 Y0' + "\n"
return gcode
def feedrate_code(self, p):

View File

@ -18,7 +18,7 @@ class Toolchange_Probe_MACH3(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ''
gcode = '(This preprocessor is used with MACH3 with probing height.)\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -27,37 +27,63 @@ class Toolchange_Probe_MACH3(FlatCAMPostProc):
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Feedrate Probe ' + str(p['feedrate_probe']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
gcode += '(Z Probe Depth: ' + str(p['z_pdepth']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n'
else:
gcode += '(Preprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
@ -246,10 +272,10 @@ M0
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode

View File

@ -18,7 +18,8 @@ class default(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ''
gcode = '(This preprocessor is the default preprocessor used by FlatCAM.)\n'
gcode += '(It is made to work with MACH3 compatible motion controllers.)\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -27,28 +28,56 @@ class default(FlatCAMPostProc):
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
@ -66,7 +95,7 @@ class default(FlatCAMPostProc):
gcode += ('G20\n' if p.units.upper() == 'IN' else 'G21\n')
gcode += 'G90\n'
gcode += 'G94\n'
gcode += 'G94'
return gcode
@ -117,11 +146,11 @@ M6
M0
G00 Z{z_toolchange}
""".format(x_toolchange=self.coordinate_format % (p.coords_decimals, x_toolchange),
y_toolchange=self.coordinate_format % (p.coords_decimals, y_toolchange),
z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
y_toolchange=self.coordinate_format % (p.coords_decimals, y_toolchange),
z_toolchange=self.coordinate_format % (p.coords_decimals, z_toolchange),
tool=int(p.tool),
t_drills=no_drills,
toolC=toolC_formatted)
else:
gcode = """
M5
@ -188,11 +217,11 @@ G00 Z{z_toolchange}
return ('G01 ' + self.position_code(p)).format(**p)
def end_code(self, p):
coords_xy = p['xy_toolchange']
end_coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
if end_coords_xy and end_coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=end_coords_xy[0], y=end_coords_xy[1]) + "\n"
return gcode
def feedrate_code(self, p):

View File

@ -18,7 +18,8 @@ class grbl_11(FlatCAMPostProc):
def start_code(self, p):
units = ' ' + str(p['units']).lower()
coords_xy = p['xy_toolchange']
gcode = ''
gcode = '(This preprocessor is used with a motion controller loaded with GRBL firmware.)\n'
gcode += '(It is configured to be compatible with almost any version of GRBL firmware.)\n\n'
xmin = '%.*f' % (p.coords_decimals, p['options']['xmin'])
xmax = '%.*f' % (p.coords_decimals, p['options']['xmax'])
@ -26,41 +27,71 @@ class grbl_11(FlatCAMPostProc):
ymax = '%.*f' % (p.coords_decimals, p['options']['ymax'])
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n' + '\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n'
else:
gcode += '(Preprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
gcode += '(X range: ' + '{: >9s}'.format(xmin) + ' ... ' + '{: >9s}'.format(xmax) + ' ' + units + ')\n'
gcode += '(Y range: ' + '{: >9s}'.format(ymin) + ' ... ' + '{: >9s}'.format(ymax) + ' ' + units + ')\n\n'
gcode += '(Spindle Speed: ' + str(p['spindlespeed']) + ' RPM' + ')\n' + '\n'
gcode += '(Spindle Speed: %s RPM)\n' % str(p['spindlespeed'])
gcode += ('G20' if p.units.upper() == 'IN' else 'G21') + "\n"
gcode += 'G90\n'
@ -187,10 +218,10 @@ G00 Z{z_toolchange}
' F' + str(self.feedrate_format % (p.fr_decimals, p.feedrate))
def end_code(self, p):
coords_xy = p['xy_toolchange']
coords_xy = p['xy_end']
gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.z_end) + "\n")
if coords_xy is not None:
if coords_xy and coords_xy != '':
gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
return gcode

View File

@ -27,33 +27,63 @@ class line_xyz(FlatCAMPostProc):
if str(p['options']['type']) == 'Geometry':
gcode += '(TOOL DIAMETER: ' + str(p['options']['tool_dia']) + units + ')\n'
gcode += '(Feedrate: ' + str(p['feedrate']) + units + '/min' + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate_XY: ' + str(p['feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate_Z: ' + str(p['z_feedrate']) + units + '/min' + ')\n'
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if str(p['options']['type']) == 'Geometry':
gcode += '(Feedrate rapids ' + str(p['feedrate_rapid']) + units + '/min' + ')\n' + '\n'
gcode += '(Z_Cut: ' + str(p['z_cut']) + units + ')\n'
if p['multidepth'] is True:
gcode += '(DepthPerCut: ' + str(p['z_depthpercut']) + units + ' <=>' + \
str(math.ceil(abs(p['z_cut']) / p['z_depthpercut'])) + ' passes' + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
elif str(p['options']['type']) == 'Excellon' and p['use_ui'] is True:
gcode += '\n(TOOLS DIAMETER: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Dia: %s' % str(val["C"]) + ')\n'
gcode += '\n(FEEDRATE Z: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate: %s' % str(val['data']["feedrate_z"]) + ')\n'
gcode += '\n(FEEDRATE RAPIDS: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Feedrate Rapids: %s' % \
str(val['data']["feedrate_rapid"]) + ')\n'
gcode += '\n(Z_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Cut: %s' % str(val['data']["cutz"]) + ')\n'
gcode += '\n(Tools Offset: )\n'
for tool, val in p['exc_cnc_tools'].items():
gcode += '(Tool: %s -> ' % str(val['tool']) + 'Offset Z: %s' % str(val['offset_z']) + ')\n'
if p['multidepth'] is True:
gcode += '\n(DEPTH_PER_CUT: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'DeptPerCut: %s' % \
str(val['data']["depthperpass"]) + ')\n'
gcode += '\n(Z_MOVE: )\n'
for tool, val in p['exc_tools'].items():
gcode += '(Tool: %s -> ' % str(tool) + 'Z_Move: %s' % str(val['data']["travelz"]) + ')\n'
gcode += '\n'
if p['toolchange'] is True:
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
gcode += '(Z Toolchange: ' + str(p['z_toolchange']) + units + ')\n'
if coords_xy is not None:
gcode += '(X,Y Toolchange: ' + "%.*f, %.*f" % (p.decimals, coords_xy[0],
p.decimals, coords_xy[1]) + units + ')\n'
else:
gcode += '(X,Y Toolchange: ' + "None" + units + ')\n'
gcode += '(Z Start: ' + str(p['startz']) + units + ')\n'
gcode += '(Z End: ' + str(p['z_end']) + units + ')\n'
gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n'
if str(p['options']['type']) == 'Excellon' or str(p['options']['type']) == 'Excellon Geometry':
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n'
gcode += '(Preprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n'
else:
gcode += '(Preprocessor Geometry: ' + str(p['pp_geometry_name']) + ')\n' + '\n'
@ -176,8 +206,8 @@ M0""".format(x_toolchange=self.coordinate_format % (p.coords_decimals, x_toolcha
return g
def end_code(self, p):
coords_xy = p['xy_toolchange']
if coords_xy is not None:
coords_xy = p['xy_end']
if coords_xy and coords_xy != '':
g = 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
else:
g = ('G00 ' + self.position_code(p)).format(**p)

View File

@ -1,7 +1,8 @@
# This file contains python only requirements to be installed with pip
# Python packages that cannot be installed with pip (e.g. PyQt5, GDAL) are not included.
# Usage: pip3 install -r requirements.txt
numpy >=1.16
pyqt5==5.12.1
numpy>=1.16
matplotlib>=3.1
cycler>=0.10
python-dateutil>=2.1

View File

@ -3,6 +3,7 @@ sudo apt install --reinstall libpng-dev libfreetype6 libfreetype6-dev libgeos-de
sudo apt install --reinstall python3-dev python3-pyqt5 python3-pyqt5.qtopengl python3-gdal python3-simplejson
sudo apt install --reinstall python3-pip python3-tk python3-imaging
sudo python3 -m pip install --upgrade pyqt5==5.12
sudo python3 -m pip install --upgrade pip numpy scipy shapely rtree tk lxml cycler python-dateutil kiwisolver dill
sudo python3 -m pip install --upgrade vispy pyopengl setuptools svg.path ortools freetype-py fontTools rasterio ezdxf
sudo python3 -m pip install --upgrade matplotlib qrcode reportlab svglib

BIN
share/black32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

BIN
share/invert16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

BIN
share/invert32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 523 B

BIN
share/origin2_16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

BIN
share/origin2_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 808 B

BIN
share/punch16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

BIN
share/punch32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 B

BIN
share/white32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 B

View File

@ -78,7 +78,7 @@ class TclCommand(object):
:return: current command
"""
command_string = list()
command_string = []
command_string.append(self.aliases[0])
if self.original_args is not None:

View File

@ -52,7 +52,7 @@ class TclCommandBounds(TclCommand):
:return:
"""
obj_list = list()
obj_list = []
if 'objects' in args:
try:
obj_list = [str(obj_name) for obj_name in str(args['objects']).split(",") if obj_name != '']
@ -68,7 +68,7 @@ class TclCommandBounds(TclCommand):
_("Expected a list of objects names separated by comma. Got"), str(args['objects'])))
return 'fail'
result_list = list()
result_list = []
for name in obj_list:
obj = self.app.collection.get_by_name(name)

View File

@ -213,7 +213,12 @@ class TclCommandCncjob(TclCommandSignaled):
local_tools_dict[tool_uid]['data']['feedrate_rapid'] = args["feedrate_rapid"]
local_tools_dict[tool_uid]['data']['multidepth'] = args["multidepth"]
local_tools_dict[tool_uid]['data']['extracut'] = args["extracut"]
local_tools_dict[tool_uid]['data']['extracut_length'] = args["extracut_length"]
if args["extracut"] is True:
local_tools_dict[tool_uid]['data']['extracut_length'] = args["extracut_length"]
else:
local_tools_dict[tool_uid]['data']['extracut_length'] = None
local_tools_dict[tool_uid]['data']['depthperpass'] = args["depthperpass"]
local_tools_dict[tool_uid]['data']['toolchange'] = args["toolchange"]
local_tools_dict[tool_uid]['data']['toolchangez'] = args["toolchangez"]

View File

@ -175,6 +175,7 @@ class TclCommandCopperClear(TclCommand):
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangez": self.app.defaults["geometry_toolchangez"],
"endz": self.app.defaults["geometry_endz"],
"endxy": self.app.defaults["geometry_endxy"],
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"startz": self.app.defaults["geometry_startz"],
@ -187,7 +188,7 @@ class TclCommandCopperClear(TclCommand):
"paintcontour": self.app.defaults["tools_paintcontour"],
"paintoverlap": self.app.defaults["tools_paintoverlap"]
})
ncc_tools = dict()
ncc_tools = {}
tooluid = 0
for tool in tools:

View File

@ -169,7 +169,7 @@ class TclCommandDrillcncjob(TclCommandSignaled):
else:
return "fail"
drillz = args["drillz"] if "drillz" in args and args["drillz"] is not None else obj.options["drillz"]
drillz = args["drillz"] if "drillz" in args and args["drillz"] is not None else obj.options["cutz"]
if "toolchangez" in args:
toolchange = True
@ -185,7 +185,10 @@ class TclCommandDrillcncjob(TclCommandSignaled):
opt_type = args["opt_type"] if "opt_type" in args and args["opt_type"] else 'B'
job_obj.z_move = args["travelz"] if "travelz" in args and args["travelz"] else obj.options["travelz"]
job_obj.feedrate = args["feedrate"] if "feedrate" in args and args["feedrate"] else obj.options["feedrate"]
job_obj.z_feedrate = args["feedrate"] if "feedrate" in args and args["feedrate"] else \
obj.options["feedrate"]
job_obj.feedrate_rapid = args["feedrate_rapid"] \
if "feedrate_rapid" in args and args["feedrate_rapid"] else obj.options["feedrate_rapid"]
@ -226,9 +229,6 @@ class TclCommandDrillcncjob(TclCommandSignaled):
float(job_obj.exc_cnc_tools[t_item]['offset_z']) + float(drillz)
job_obj.exc_cnc_tools[t_item]['data']['ppname_e'] = obj.options['ppname_e']
# for now there is no tool offset support in this Tcl Command so we write the 0.0 value here
job_obj.tool_offset[t_item] = 0.0
job_obj.origin_kind = 'excellon'
job_obj.gcode_parse()

View File

@ -164,6 +164,8 @@ class TclCommandPaint(TclCommand):
"toolchange": self.app.defaults["geometry_toolchange"],
"toolchangez": self.app.defaults["geometry_toolchangez"],
"endz": self.app.defaults["geometry_endz"],
"endxy": self.app.defaults["geometry_endxy"],
"spindlespeed": self.app.defaults["geometry_spindlespeed"],
"toolchangexy": self.app.defaults["geometry_toolchangexy"],
"startz": self.app.defaults["geometry_startz"],
@ -176,7 +178,7 @@ class TclCommandPaint(TclCommand):
"paintcontour": self.app.defaults["tools_paintcontour"],
"paintoverlap": self.app.defaults["tools_paintoverlap"]
})
paint_tools = dict()
paint_tools = {}
tooluid = 0
for tool in tools:

View File

@ -228,7 +228,7 @@ class TclCommandPanelize(TclCommand):
def translate_recursion(geom):
if type(geom) == list:
geoms = list()
geoms = []
for local_geom in geom:
geoms.append(translate_recursion(local_geom))
return geoms

View File

@ -66,7 +66,7 @@ class TclCommandSetOrigin(TclCommand):
:return:
"""
loc = list()
loc = []
if 'auto' in args:
if bool(args['auto']) is True:
objs = self.app.collection.get_list()