diff --git a/FlatCAMApp.py b/FlatCAMApp.py
index 1f9f81b6..47116142 100644
--- a/FlatCAMApp.py
+++ b/FlatCAMApp.py
@@ -481,17 +481,44 @@ class App(QtCore.QObject):
"tools_transform_offset_x": self.tools_defaults_form.tools_transform_group.offx_entry,
"tools_transform_offset_y": self.tools_defaults_form.tools_transform_group.offy_entry,
"tools_transform_mirror_reference": self.tools_defaults_form.tools_transform_group.mirror_reference_cb,
- "tools_transform_mirror_point": self.tools_defaults_form.tools_transform_group.flip_ref_entry
+ "tools_transform_mirror_point": self.tools_defaults_form.tools_transform_group.flip_ref_entry,
+
+ "tools_solderpaste_tools": self.tools_defaults_form.tools_solderpaste_group.nozzle_tool_dia_entry,
+ "tools_solderpaste_new": self.tools_defaults_form.tools_solderpaste_group.addtool_entry,
+ "tools_solderpaste_z_start": self.tools_defaults_form.tools_solderpaste_group.z_start_entry,
+ "tools_solderpaste_z_dispense": self.tools_defaults_form.tools_solderpaste_group.z_dispense_entry,
+ "tools_solderpaste_z_stop": self.tools_defaults_form.tools_solderpaste_group.z_stop_entry,
+ "tools_solderpaste_z_travel": self.tools_defaults_form.tools_solderpaste_group.z_travel_entry,
+ "tools_solderpaste_frxy": self.tools_defaults_form.tools_solderpaste_group.frxy_entry,
+ "tools_solderpaste_frz": self.tools_defaults_form.tools_solderpaste_group.frz_entry,
+ "tools_solderpaste_speedfwd": self.tools_defaults_form.tools_solderpaste_group.speedfwd_entry,
+ "tools_solderpaste_dwellfwd": self.tools_defaults_form.tools_solderpaste_group.dwellfwd_entry,
+ "tools_solderpaste_speedrev": self.tools_defaults_form.tools_solderpaste_group.speedrev_entry,
+ "tools_solderpaste_dwellrev": self.tools_defaults_form.tools_solderpaste_group.dwellrev_entry,
+ "tools_solderpaste_pp": self.tools_defaults_form.tools_solderpaste_group.pp_combo
}
- # loads postprocessors
+
+
+ #############################
+ #### LOAD POSTPROCESSORS ####
+ #############################
+
+
self.postprocessors = load_postprocessors(self)
for name in list(self.postprocessors.keys()):
+
+ # 'Paste' postprocessors are to be used only in the Solder Paste Dispensing Tool
+ if name.partition('_')[0] == 'Paste':
+ self.tools_defaults_form.tools_solderpaste_group.pp_combo.addItem(name)
+ continue
+
self.geometry_defaults_form.geometry_opt_group.pp_geometry_name_cb.addItem(name)
# HPGL postprocessor is only for Geometry objects therefore it should not be in the Excellon Preferences
if name == 'hpgl':
continue
+
self.excellon_defaults_form.excellon_opt_group.pp_excellon_name_cb.addItem(name)
self.defaults = LoudDict()
@@ -711,6 +738,17 @@ class App(QtCore.QObject):
"tools_transform_mirror_point": (0, 0),
"tools_solderpaste_tools": "1.0, 0.3",
+ "tools_solderpaste_new": 0.3,
+ "tools_solderpaste_z_start": 0.005,
+ "tools_solderpaste_z_dispense": 0.01,
+ "tools_solderpaste_z_stop": 0.005,
+ "tools_solderpaste_z_travel": 0.1,
+ "tools_solderpaste_frxy": 3.0,
+ "tools_solderpaste_frz": 3.0,
+ "tools_solderpaste_speedfwd": 20,
+ "tools_solderpaste_dwellfwd": 1,
+ "tools_solderpaste_speedrev": 10,
+ "tools_solderpaste_dwellrev": 1
})
###############################
@@ -3667,14 +3705,15 @@ class App(QtCore.QObject):
if notebook_widget_name == 'tool_tab':
tool_widget = self.ui.tool_scroll_area.widget().objectName()
+ tool_add_popup = FCInputDialog(title="New Tool ...",
+ text='Enter a Tool Diameter:',
+ min=0.0000, max=99.9999, decimals=4)
+ tool_add_popup.setWindowIcon(QtGui.QIcon('share/letter_t_32.png'))
+
+ val, ok = tool_add_popup.get_value()
+
# and only if the tool is NCC Tool
if tool_widget == self.ncclear_tool.toolName:
- tool_add_popup = FCInputDialog(title="New Tool ...",
- text='Enter a Tool Diameter:',
- min=0.0000, max=99.9999, decimals=4)
- tool_add_popup.setWindowIcon(QtGui.QIcon('share/letter_t_32.png'))
-
- val, ok = tool_add_popup.get_value()
if ok:
if float(val) == 0:
self.inform.emit(
@@ -3686,12 +3725,6 @@ class App(QtCore.QObject):
"[WARNING_NOTCL] Adding Tool cancelled ...")
# and only if the tool is Paint Area Tool
elif tool_widget == self.paint_tool.toolName:
- tool_add_popup = FCInputDialog(title="New Tool ...",
- text='Enter a Tool Diameter:',
- min=0.0000, max=99.9999, decimals=4)
- tool_add_popup.setWindowIcon(QtGui.QIcon('share/letter_t_32.png'))
-
- val, ok = tool_add_popup.get_value()
if ok:
if float(val) == 0:
self.inform.emit(
@@ -3701,6 +3734,18 @@ class App(QtCore.QObject):
else:
self.inform.emit(
"[WARNING_NOTCL] Adding Tool cancelled ...")
+ # and only if the tool is Solder Paste Dispensing Tool
+ elif tool_widget == self.paste_tool.toolName:
+ if ok:
+ if float(val) == 0:
+ self.inform.emit(
+ "[WARNING_NOTCL] Please enter a tool diameter with non-zero value, in Float format.")
+ return
+ self.paste_tool.on_tool_add(dia=float(val))
+ else:
+ self.inform.emit(
+ "[WARNING_NOTCL] Adding Tool cancelled ...")
+
# It's meant to delete tools in tool tables via a 'Delete' shortcut key but only if certain conditions are met
# See description bellow.
@@ -3724,6 +3769,9 @@ class App(QtCore.QObject):
elif tool_widget == self.paint_tool.toolName:
self.paint_tool.on_tool_delete()
+ # and only if the tool is Solder Paste Dispensing Tool
+ elif tool_widget == self.paste_tool.toolName:
+ self.paste_tool.on_tool_delete()
else:
self.on_delete()
diff --git a/FlatCAMGUI.py b/FlatCAMGUI.py
index aaf9ec8e..64b535d0 100644
--- a/FlatCAMGUI.py
+++ b/FlatCAMGUI.py
@@ -981,6 +981,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
ALT+D |
2-Sided PCB Tool |
+
+ ALT+K |
+ Solder Paste Dispensing Tool |
+
ALT+L |
Film PCB Tool |
@@ -1733,6 +1737,11 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
self.app.dblsidedtool.run()
return
+ # Solder Paste Dispensing Tool
+ if key == QtCore.Qt.Key_K:
+ self.app.paste_tool.run()
+ return
+
# Film Tool
if key == QtCore.Qt.Key_L:
self.app.film_tool.run()
@@ -2556,21 +2565,25 @@ class ToolsPreferencesUI(QtWidgets.QWidget):
self.tools_transform_group = ToolsTransformPrefGroupUI()
self.tools_transform_group.setMinimumWidth(200)
+ self.tools_solderpaste_group = ToolsSolderpastePrefGroupUI()
+ self.tools_solderpaste_group.setMinimumWidth(200)
+
self.vlay = QtWidgets.QVBoxLayout()
self.vlay.addWidget(self.tools_ncc_group)
self.vlay.addWidget(self.tools_paint_group)
+ self.vlay.addWidget(self.tools_film_group)
self.vlay1 = QtWidgets.QVBoxLayout()
self.vlay1.addWidget(self.tools_cutout_group)
+ self.vlay1.addWidget(self.tools_transform_group)
self.vlay1.addWidget(self.tools_2sided_group)
- self.vlay1.addWidget(self.tools_film_group)
self.vlay2 = QtWidgets.QVBoxLayout()
self.vlay2.addWidget(self.tools_panelize_group)
self.vlay2.addWidget(self.tools_calculators_group)
self.vlay3 = QtWidgets.QVBoxLayout()
- self.vlay3.addWidget(self.tools_transform_group)
+ self.vlay3.addWidget(self.tools_solderpaste_group)
self.layout.addLayout(self.vlay)
self.layout.addLayout(self.vlay1)
@@ -5137,6 +5150,150 @@ class ToolsTransformPrefGroupUI(OptionsGroupUI):
self.layout.addStretch()
+class ToolsSolderpastePrefGroupUI(OptionsGroupUI):
+ def __init__(self, parent=None):
+
+ super(ToolsSolderpastePrefGroupUI, self).__init__(self)
+
+ self.setTitle(str("SolderPaste Tool Options"))
+
+ ## Solder Paste Dispensing
+ self.solderpastelabel = QtWidgets.QLabel("Parameters:")
+ self.solderpastelabel.setToolTip(
+ "A tool to create GCode for dispensing\n"
+ "solder paste onto a PCB."
+ )
+ self.layout.addWidget(self.solderpastelabel)
+
+ grid0 = QtWidgets.QGridLayout()
+ self.layout.addLayout(grid0)
+
+ # Nozzle Tool Diameters
+ nozzletdlabel = QtWidgets.QLabel('Tools dia:')
+ nozzletdlabel.setToolTip(
+ "Diameters of nozzle tools, separated by ','"
+ )
+ self.nozzle_tool_dia_entry = FCEntry()
+ grid0.addWidget(nozzletdlabel, 0, 0)
+ grid0.addWidget(self.nozzle_tool_dia_entry, 0, 1)
+
+ # New Nozzle Tool Dia
+ self.addtool_entry_lbl = QtWidgets.QLabel('New Nozzle Dia:')
+ self.addtool_entry_lbl.setToolTip(
+ "Diameter for the new Nozzle tool to add in the Tool Table"
+ )
+ self.addtool_entry = FCEntry()
+ grid0.addWidget(self.addtool_entry_lbl, 1, 0)
+ grid0.addWidget(self.addtool_entry, 1, 1)
+
+ # Z dispense start
+ self.z_start_entry = FCEntry()
+ self.z_start_label = QtWidgets.QLabel("Z Dispense Start:")
+ self.z_start_label.setToolTip(
+ "The height (Z) when solder paste dispensing starts."
+ )
+ grid0.addWidget(self.z_start_label, 2, 0)
+ grid0.addWidget(self.z_start_entry, 2, 1)
+
+ # Z dispense
+ self.z_dispense_entry = FCEntry()
+ self.z_dispense_label = QtWidgets.QLabel("Z Dispense:")
+ self.z_dispense_label.setToolTip(
+ "The height (Z) when doing solder paste dispensing."
+ )
+ grid0.addWidget(self.z_dispense_label, 3, 0)
+ grid0.addWidget(self.z_dispense_entry, 3, 1)
+
+ # Z dispense stop
+ self.z_stop_entry = FCEntry()
+ self.z_stop_label = QtWidgets.QLabel("Z Dispense Stop:")
+ self.z_stop_label.setToolTip(
+ "The height (Z) when solder paste dispensing stops."
+ )
+ grid0.addWidget(self.z_stop_label, 4, 0)
+ grid0.addWidget(self.z_stop_entry, 4, 1)
+
+ # Z travel
+ self.z_travel_entry = FCEntry()
+ self.z_travel_label = QtWidgets.QLabel("Z Travel:")
+ self.z_travel_label.setToolTip(
+ "The height (Z) for travel between pads\n"
+ "(without dispensing solder paste)."
+ )
+ grid0.addWidget(self.z_travel_label, 5, 0)
+ grid0.addWidget(self.z_travel_entry, 5, 1)
+
+ # Feedrate X-Y
+ self.frxy_entry = FCEntry()
+ self.frxy_label = QtWidgets.QLabel("Feedrate X-Y:")
+ self.frxy_label.setToolTip(
+ "Feedrate (speed) while moving on the X-Y plane."
+ )
+ grid0.addWidget(self.frxy_label, 6, 0)
+ grid0.addWidget(self.frxy_entry, 6, 1)
+
+ # Feedrate Z
+ self.frz_entry = FCEntry()
+ self.frz_label = QtWidgets.QLabel("Feedrate Z:")
+ self.frz_label.setToolTip(
+ "Feedrate (speed) while moving vertically\n"
+ "(on Z plane)."
+ )
+ grid0.addWidget(self.frz_label, 7, 0)
+ grid0.addWidget(self.frz_entry, 7, 1)
+
+ # Spindle Speed Forward
+ self.speedfwd_entry = FCEntry()
+ self.speedfwd_label = QtWidgets.QLabel("Spindle Speed FWD:")
+ self.speedfwd_label.setToolTip(
+ "The dispenser speed while pushing solder paste\n"
+ "through the dispenser nozzle."
+ )
+ grid0.addWidget(self.speedfwd_label, 8, 0)
+ grid0.addWidget(self.speedfwd_entry, 8, 1)
+
+ # Dwell Forward
+ self.dwellfwd_entry = FCEntry()
+ self.dwellfwd_label = QtWidgets.QLabel("Dwell FWD:")
+ self.dwellfwd_label.setToolTip(
+ "Pause after solder dispensing."
+ )
+ grid0.addWidget(self.dwellfwd_label, 9, 0)
+ grid0.addWidget(self.dwellfwd_entry, 9, 1)
+
+ # Spindle Speed Reverse
+ self.speedrev_entry = FCEntry()
+ self.speedrev_label = QtWidgets.QLabel("Spindle Speed REV:")
+ self.speedrev_label.setToolTip(
+ "The dispenser speed while retracting solder paste\n"
+ "through the dispenser nozzle."
+ )
+ grid0.addWidget(self.speedrev_label, 10, 0)
+ grid0.addWidget(self.speedrev_entry, 10, 1)
+
+ # Dwell Reverse
+ self.dwellrev_entry = FCEntry()
+ self.dwellrev_label = QtWidgets.QLabel("Dwell REV:")
+ self.dwellrev_label.setToolTip(
+ "Pause after solder paste dispenser retracted,\n"
+ "to allow pressure equilibrium."
+ )
+ grid0.addWidget(self.dwellrev_label, 11, 0)
+ grid0.addWidget(self.dwellrev_entry, 11, 1)
+
+ # Postprocessors
+ pp_label = QtWidgets.QLabel('PostProcessors:')
+ pp_label.setToolTip(
+ "Files that control the GCode generation."
+ )
+
+ self.pp_combo = FCComboBox()
+ grid0.addWidget(pp_label, 12, 0)
+ grid0.addWidget(self.pp_combo, 12, 1)
+
+ self.layout.addStretch()
+
+
class FlatCAMActivityView(QtWidgets.QWidget):
def __init__(self, parent=None):
diff --git a/README.md b/README.md
index 81df3261..a712e936 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,10 @@ CAD program, and create G-Code for Isolation routing.
20.02.2019
- finished added a Tool Table for Tool SolderPaste
-- working on multi tool soder paste dispensing
+- working on multi tool solder paste dispensing
+- finished the Edit -> Preferences defaults section
+- finished the UI, created the postprocessor 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
diff --git a/flatcamTools/ToolSolderPaste.py b/flatcamTools/ToolSolderPaste.py
index 5030ea7e..09e2150a 100644
--- a/flatcamTools/ToolSolderPaste.py
+++ b/flatcamTools/ToolSolderPaste.py
@@ -77,7 +77,7 @@ class ToolSolderPaste(FlatCAMTool):
hlay_tools = QtWidgets.QHBoxLayout()
self.layout.addLayout(hlay_tools)
- self.addtool_entry_lbl = QtWidgets.QLabel('Nozzle Dia:')
+ self.addtool_entry_lbl = QtWidgets.QLabel('New Nozzle Tool:')
self.addtool_entry_lbl.setToolTip(
"Diameter for the new Nozzle tool to add in the Tool Table"
)
@@ -108,16 +108,25 @@ class ToolSolderPaste(FlatCAMTool):
"Generate solder paste dispensing geometry."
)
+ step1_lbl = QtWidgets.QLabel("STEP 1:")
+ step1_lbl.setToolTip(
+ "First step is to select a number of nozzle tools for usage\n"
+ "and then create a solder paste dispensing geometry out of an\n"
+ "Solder Paste Mask Gerber file."
+ )
+
grid0.addWidget(self.addtool_btn, 0, 0)
# grid2.addWidget(self.copytool_btn, 0, 1)
grid0.addWidget(self.deltool_btn, 0, 2)
+
+ grid0.addWidget(step1_lbl, 2, 0)
grid0.addWidget(self.soldergeo_btn, 2, 2)
## Form Layout
geo_form_layout = QtWidgets.QFormLayout()
self.layout.addLayout(geo_form_layout)
- ## Gerber Object to be used for solderpaste dispensing
+ ## Geometry Object to be used for solderpaste dispensing
self.geo_obj_combo = QtWidgets.QComboBox()
self.geo_obj_combo.setModel(self.app.collection)
self.geo_obj_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
@@ -147,10 +156,7 @@ class ToolSolderPaste(FlatCAMTool):
self.z_start_entry = FCEntry()
self.z_start_label = QtWidgets.QLabel("Z Dispense Start:")
self.z_start_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "The height (Z) when solder paste dispensing starts."
)
form_layout.addRow(self.z_start_label, self.z_start_entry)
@@ -158,9 +164,8 @@ class ToolSolderPaste(FlatCAMTool):
self.z_dispense_entry = FCEntry()
self.z_dispense_label = QtWidgets.QLabel("Z Dispense:")
self.z_dispense_label.setToolTip(
- "Margin over bounds. A positive value here\n"
- "will make the cutout of the PCB further from\n"
- "the actual PCB border"
+ "The height (Z) when doing solder paste dispensing."
+
)
form_layout.addRow(self.z_dispense_label, self.z_dispense_entry)
@@ -168,10 +173,7 @@ class ToolSolderPaste(FlatCAMTool):
self.z_stop_entry = FCEntry()
self.z_stop_label = QtWidgets.QLabel("Z Dispense Stop:")
self.z_stop_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "The height (Z) when solder paste dispensing stops."
)
form_layout.addRow(self.z_stop_label, self.z_stop_entry)
@@ -179,10 +181,8 @@ class ToolSolderPaste(FlatCAMTool):
self.z_travel_entry = FCEntry()
self.z_travel_label = QtWidgets.QLabel("Z Travel:")
self.z_travel_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "The height (Z) for travel between pads\n"
+ "(without dispensing solder paste)."
)
form_layout.addRow(self.z_travel_label, self.z_travel_entry)
@@ -190,10 +190,7 @@ class ToolSolderPaste(FlatCAMTool):
self.frxy_entry = FCEntry()
self.frxy_label = QtWidgets.QLabel("Feedrate X-Y:")
self.frxy_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "Feedrate (speed) while moving on the X-Y plane."
)
form_layout.addRow(self.frxy_label, self.frxy_entry)
@@ -201,10 +198,8 @@ class ToolSolderPaste(FlatCAMTool):
self.frz_entry = FCEntry()
self.frz_label = QtWidgets.QLabel("Feedrate Z:")
self.frz_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "Feedrate (speed) while moving vertically\n"
+ "(on Z plane)."
)
form_layout.addRow(self.frz_label, self.frz_entry)
@@ -212,10 +207,8 @@ class ToolSolderPaste(FlatCAMTool):
self.speedfwd_entry = FCEntry()
self.speedfwd_label = QtWidgets.QLabel("Spindle Speed FWD:")
self.speedfwd_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "The dispenser speed while pushing solder paste\n"
+ "through the dispenser nozzle."
)
form_layout.addRow(self.speedfwd_label, self.speedfwd_entry)
@@ -223,10 +216,7 @@ class ToolSolderPaste(FlatCAMTool):
self.dwellfwd_entry = FCEntry()
self.dwellfwd_label = QtWidgets.QLabel("Dwell FWD:")
self.dwellfwd_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "Pause after solder dispensing."
)
form_layout.addRow(self.dwellfwd_label, self.dwellfwd_entry)
@@ -234,10 +224,8 @@ class ToolSolderPaste(FlatCAMTool):
self.speedrev_entry = FCEntry()
self.speedrev_label = QtWidgets.QLabel("Spindle Speed REV:")
self.speedrev_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "The dispenser speed while retracting solder paste\n"
+ "through the dispenser nozzle."
)
form_layout.addRow(self.speedrev_label, self.speedrev_entry)
@@ -245,42 +233,98 @@ class ToolSolderPaste(FlatCAMTool):
self.dwellrev_entry = FCEntry()
self.dwellrev_label = QtWidgets.QLabel("Dwell REV:")
self.dwellrev_label.setToolTip(
- "The size of the gaps in the cutout\n"
- "used to keep the board connected to\n"
- "the surrounding material (the one \n"
- "from which the PCB is cutout)."
+ "Pause after solder paste dispenser retracted,\n"
+ "to allow pressure equilibrium."
)
form_layout.addRow(self.dwellrev_label, self.dwellrev_entry)
# Postprocessors
pp_label = QtWidgets.QLabel('PostProcessors:')
pp_label.setToolTip(
- "Files that control the GCoe generation."
+ "Files that control the GCode generation."
)
self.pp_combo = FCComboBox()
- pp_items = [1, 2, 3, 4, 5]
- for it in pp_items:
- self.pp_combo.addItem(str(it))
- self.pp_combo.setStyleSheet('background-color: rgb(255,255,255)')
+ self.pp_combo.setStyleSheet('background-color: rgb(255,255,255)')
form_layout.addRow(pp_label, self.pp_combo)
## Buttons
- hlay = QtWidgets.QHBoxLayout()
- self.gcode_box.addLayout(hlay)
+ grid1 = QtWidgets.QGridLayout()
+ self.gcode_box.addLayout(grid1)
- hlay.addStretch()
-
- self.solder_gcode = QtWidgets.QPushButton("Generate GCode")
- self.solder_gcode.setToolTip(
- "Generate GCode to dispense Solder Paste\n"
+ self.solder_gcode_btn = QtWidgets.QPushButton("Generate GCode")
+ self.solder_gcode_btn.setToolTip(
+ "Generate GCode for Solder Paste dispensing\n"
"on PCB pads."
)
- hlay.addWidget(self.solder_gcode)
+
+ step2_lbl = QtWidgets.QLabel("STEP 2:")
+ step2_lbl.setToolTip(
+ "Second step is to select a solder paste dispensing geometry,\n"
+ "set the CAM parameters and then generate a CNCJob object which\n"
+ "will pe painted on canvas in blue color."
+ )
+
+ grid1.addWidget(step2_lbl, 0, 0)
+ grid1.addWidget(self.solder_gcode_btn, 0, 2)
+
+ ## Form Layout
+ cnc_form_layout = QtWidgets.QFormLayout()
+ self.gcode_box.addLayout(cnc_form_layout)
+
+ ## Gerber Object to be used for solderpaste dispensing
+ self.cnc_obj_combo = QtWidgets.QComboBox()
+ 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_object_label = QtWidgets.QLabel("CNCJob: ")
+ self.cnc_object_label.setToolTip(
+ "CNCJob Solder paste object.\n"
+ "In order to enable the GCode save section,\n"
+ "the name of the object has to end in:\n"
+ "'_solderpaste' as a protection."
+ )
+ cnc_form_layout.addRow(self.cnc_object_label, self.cnc_obj_combo)
+
+ self.save_gcode_frame = QtWidgets.QFrame()
+ self.save_gcode_frame.setContentsMargins(0, 0, 0, 0)
+ self.layout.addWidget(self.save_gcode_frame)
+ self.save_gcode_box = QtWidgets.QVBoxLayout()
+ self.save_gcode_box.setContentsMargins(0, 0, 0, 0)
+ self.save_gcode_frame.setLayout(self.save_gcode_box)
+
+
+ ## Buttons
+ grid2 = QtWidgets.QGridLayout()
+ self.save_gcode_box.addLayout(grid2)
+
+ self.solder_gcode_view_btn = QtWidgets.QPushButton("View GCode")
+ self.solder_gcode_view_btn.setToolTip(
+ "View the generated GCode for Solder Paste dispensing\n"
+ "on PCB pads."
+ )
+
+ self.solder_gcode_save_btn = QtWidgets.QPushButton("Save GCode")
+ self.solder_gcode_save_btn.setToolTip(
+ "Save the generated GCode for Solder Paste dispensing\n"
+ "on PCB pads, to a file."
+ )
+
+ step3_lbl = QtWidgets.QLabel("STEP 3:")
+ step3_lbl.setToolTip(
+ "Third step (and last) is to select a CNCJob made from \n"
+ "a solder paste dispensing geometry, and then view/save it's GCode."
+ )
+
+ grid2.addWidget(step3_lbl, 0, 0)
+ grid2.addWidget(self.solder_gcode_view_btn, 0, 2)
+ grid2.addWidget(self.solder_gcode_save_btn, 1, 2)
self.layout.addStretch()
self.gcode_frame.setDisabled(True)
+ self.save_gcode_frame.setDisabled(True)
self.tools = {}
self.tooluid = 0
@@ -289,9 +333,14 @@ class ToolSolderPaste(FlatCAMTool):
self.addtool_btn.clicked.connect(self.on_tool_add)
self.deltool_btn.clicked.connect(self.on_tool_delete)
self.soldergeo_btn.clicked.connect(self.on_create_geo)
- self.solder_gcode.clicked.connect(self.on_create_gcode)
+ self.solder_gcode_btn.clicked.connect(self.on_create_gcode)
+ self.solder_gcode_view_btn.clicked.connect(self.on_view_gcode)
+ self.solder_gcode_save_btn.clicked.connect(self.on_save_gcode)
+
self.geo_obj_combo.currentIndexChanged.connect(self.on_geo_select)
+ self.cnc_obj_combo.currentIndexChanged.connect(self.on_cncjob_select)
+
def run(self):
self.app.report_usage("ToolSolderPaste()")
@@ -310,12 +359,65 @@ class ToolSolderPaste(FlatCAMTool):
def set_tool_ui(self):
- # self.ncc_overlap_entry.set_value(self.app.defaults["tools_nccoverlap"])
- # self.ncc_margin_entry.set_value(self.app.defaults["tools_nccmargin"])
- # self.ncc_method_radio.set_value(self.app.defaults["tools_nccmethod"])
- # self.ncc_connect_cb.set_value(self.app.defaults["tools_nccconnect"])
- # self.ncc_contour_cb.set_value(self.app.defaults["tools_ncccontour"])
- # self.ncc_rest_cb.set_value(self.app.defaults["tools_nccrest"])
+ if self.app.defaults["tools_solderpaste_new"]:
+ self.addtool_entry.set_value(self.app.defaults["tools_solderpaste_new"])
+ else:
+ self.addtool_entry.set_value(0.0)
+
+ if self.app.defaults["tools_solderpaste_z_start"]:
+ self.z_start_entry.set_value(self.app.defaults["tools_solderpaste_z_start"])
+ else:
+ self.z_start_entry.set_value(0.0)
+
+ if self.app.defaults["tools_solderpaste_z_dispense"]:
+ self.z_dispense_entry.set_value(self.app.defaults["tools_solderpaste_z_dispense"])
+ else:
+ self.z_dispense_entry.set_value(0.0)
+
+ if self.app.defaults["tools_solderpaste_z_stop"]:
+ self.z_stop_entry.set_value(self.app.defaults["tools_solderpaste_z_stop"])
+ else:
+ self.z_stop_entry.set_value(1.0)
+
+ if self.app.defaults["tools_solderpaste_z_travel"]:
+ self.z_travel_entry.set_value(self.app.defaults["tools_solderpaste_z_travel"])
+ else:
+ self.z_travel_entry.set_value(1.0)
+
+ if self.app.defaults["tools_solderpaste_frxy"]:
+ self.frxy_entry.set_value(self.app.defaults["tools_solderpaste_frxy"])
+ else:
+ self.frxy_entry.set_value(True)
+
+ if self.app.defaults["tools_solderpaste_frz"]:
+ self.frz_entry.set_value(self.app.defaults["tools_solderpaste_frz"])
+ else:
+ self.frz_entry.set_value(True)
+
+ if self.app.defaults["tools_solderpaste_speedfwd"]:
+ self.speedfwd_entry.set_value(self.app.defaults["tools_solderpaste_speedfwd"])
+ else:
+ self.speedfwd_entry.set_value(0.0)
+
+ if self.app.defaults["tools_solderpaste_dwellfwd"]:
+ self.dwellfwd_entry.set_value(self.app.defaults["tools_solderpaste_dwellfwd"])
+ else:
+ self.dwellfwd_entry.set_value(0.0)
+
+ if self.app.defaults["tools_solderpaste_speedrev"]:
+ self.speedrev_entry.set_value(self.app.defaults["tools_solderpaste_speedrev"])
+ else:
+ self.speedrev_entry.set_value(False)
+
+ if self.app.defaults["tools_solderpaste_dwellrev"]:
+ self.dwellrev_entry.set_value(self.app.defaults["tools_solderpaste_dwellrev"])
+ else:
+ self.dwellrev_entry.set_value((0, 0))
+
+ if self.app.defaults["tools_solderpaste_pp"]:
+ self.pp_combo.set_value(self.app.defaults["tools_solderpaste_pp"])
+ else:
+ self.pp_combo.set_value('Paste_1')
self.tools_table.setupContextMenu()
self.tools_table.addContextMenu(
@@ -347,6 +449,13 @@ class ToolSolderPaste(FlatCAMTool):
self.obj = None
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
+
+ for name in list(self.app.postprocessors.keys()):
+ # populate only with postprocessor files that start with 'Paste_'
+ if name.partition('_')[0] != 'Paste':
+ continue
+ self.pp_combo.addItem(name)
+
self.reset_fields()
def build_ui(self):
@@ -355,11 +464,6 @@ class ToolSolderPaste(FlatCAMTool):
# updated units
self.units = self.app.general_options_form.general_app_group.units_radio.get_value().upper()
- if self.units == "IN":
- self.addtool_entry.set_value(0.039)
- else:
- self.addtool_entry.set_value(1)
-
sorted_tools = []
for k, v in self.tools.items():
sorted_tools.append(float('%.4f' % float(v['tooldia'])))
@@ -573,7 +677,7 @@ class ToolSolderPaste(FlatCAMTool):
self.tools.pop(t, None)
except AttributeError:
- self.app.inform.emit("[WARNING_NOTCL]Delete failed. Select a Nozzle tool to delete.")
+ self.app.inform.emit("[WARNING_NOTCL] Delete failed. Select a Nozzle tool to delete.")
return
except Exception as e:
log.debug(str(e))
@@ -587,6 +691,12 @@ class ToolSolderPaste(FlatCAMTool):
else:
self.gcode_frame.setDisabled(True)
+ def on_cncjob_select(self):
+ if self.cnc_obj_combo.currentText().rpartition('_')[2] == 'solderpaste':
+ self.save_gcode_frame.setDisabled(False)
+ else:
+ self.save_gcode_frame.setDisabled(True)
+
@staticmethod
def distance(pt1, pt2):
return sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2)
@@ -595,15 +705,21 @@ class ToolSolderPaste(FlatCAMTool):
proc = self.app.proc_container.new("Creating Solder Paste dispensing geometry.")
name = self.obj_combo.currentText()
+ if name == '':
+ self.app.inform.emit("[WARNING_NOTCL] No SolderPaste mask Gerber object loaded.")
+ return
+
obj = self.app.collection.get_by_name(name)
- if type(obj.solid_geometry) is not list:
+ if type(obj.solid_geometry) is not list and type(obj.solid_geometry) is not MultiPolygon:
obj.solid_geometry = [obj.solid_geometry]
# Sort tools in descending order
sorted_tools = []
for k, v in self.tools.items():
- sorted_tools.append(float('%.4f' % float(v['tooldia'])))
+ # make sure that the tools diameter is more than zero and not zero
+ if float(v['tooldia']) > 0:
+ sorted_tools.append(float('%.4f' % float(v['tooldia'])))
sorted_tools.sort(reverse=True)
def geo_init(geo_obj, app_obj):
@@ -624,8 +740,8 @@ class ToolSolderPaste(FlatCAMTool):
diagonal_1 = LineString([min, max])
diagonal_2 = LineString([min_r, max_r])
- round_diag_1 = round(diagonal_1.intersection(p).length, 4)
- round_diag_2 = round(diagonal_2.intersection(p).length, 4)
+ round_diag_1 = round(diagonal_1.intersection(p).length, 2)
+ round_diag_2 = round(diagonal_2.intersection(p).length, 2)
if round_diag_1 == round_diag_2:
l = distance((xmin, ymin), (xmax, ymin))
@@ -654,7 +770,12 @@ class ToolSolderPaste(FlatCAMTool):
rest_geo = []
tooluid = 1
+ if not sorted_tools:
+ self.app.inform.emit("[WARNING_NOTCL] No Nozzle tools in the tool table.")
+ return 'fail'
+
for tool in sorted_tools:
+
offset = tool / 2
for uid, v in self.tools.items():
@@ -699,7 +820,9 @@ class ToolSolderPaste(FlatCAMTool):
else:
rest_geo.append(g)
- work_geo = rest_geo
+ work_geo = deepcopy(rest_geo)
+ rest_geo[:] = []
+
if not work_geo:
app_obj.inform.emit("[success] Solder Paste geometry generated successfully...")
return
@@ -728,6 +851,26 @@ class ToolSolderPaste(FlatCAMTool):
self.app.worker_task.emit({'fcn': job_thread, 'params': [self.app]})
# self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
+ def on_view_gcode(self):
+ name = self.obj_combo.currentText()
+
+ def geo_init(geo_obj, app_obj):
+ pass
+
+ # self.app.new_object("geometry", name + "_cutout", geo_init)
+ # self.app.inform.emit("[success] Rectangular CutOut operation finished.")
+ # self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
+
+ def on_save_gcode(self):
+ name = self.obj_combo.currentText()
+
+ def geo_init(geo_obj, app_obj):
+ pass
+
+ # self.app.new_object("geometry", name + "_cutout", geo_init)
+ # self.app.inform.emit("[success] Rectangular CutOut operation finished.")
+ # self.app.ui.notebook.setCurrentWidget(self.app.ui.project_tab)
+
def on_create_gcode(self):
name = self.obj_combo.currentText()
@@ -740,3 +883,5 @@ class ToolSolderPaste(FlatCAMTool):
def reset_fields(self):
self.obj_combo.setRootModelIndex(self.app.collection.index(0, 0, QtCore.QModelIndex()))
+ self.geo_obj_combo.setRootModelIndex(self.app.collection.index(2, 0, QtCore.QModelIndex()))
+ self.cnc_obj_combo.setRootModelIndex(self.app.collection.index(3, 0, QtCore.QModelIndex()))
diff --git a/postprocessors/Paste_1.py b/postprocessors/Paste_1.py
new file mode 100644
index 00000000..66e994b5
--- /dev/null
+++ b/postprocessors/Paste_1.py
@@ -0,0 +1,196 @@
+from FlatCAMPostProc import *
+
+
+class Paste_1(FlatCAMPostProc):
+
+ coordinate_format = "%.*f"
+ feedrate_format = '%.*f'
+
+ def start_code(self, p):
+ units = ' ' + str(p['units']).lower()
+ coords_xy = p['toolchange_xy']
+ gcode = ''
+
+ 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'])
+
+ 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_Z: ' + str(p['feedrate_z']) + 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':
+ if p['multidepth'] is True:
+ gcode += '(DepthPerCut: ' + str(p['depthpercut']) + units + ' <=>' + \
+ str(math.ceil(abs(p['z_cut']) / p['depthpercut'])) + ' passes' + ')\n'
+
+ gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n'
+ gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n'
+
+ if coords_xy is not None:
+ gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], 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['endz']) + 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 += '(Postprocessor Excellon: ' + str(p['pp_excellon_name']) + ')\n' + '\n'
+ else:
+ gcode += '(Postprocessor 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'])
+
+ gcode += ('G20\n' if p.units.upper() == 'IN' else 'G21\n')
+ gcode += 'G90\n'
+ gcode += 'G94\n'
+
+ return gcode
+
+ def startz_code(self, p):
+ if p.startz is not None:
+ return 'G00 Z' + self.coordinate_format%(p.coords_decimals, p.startz)
+ else:
+ return ''
+
+ def lift_code(self, p):
+ return 'G00 Z' + self.coordinate_format%(p.coords_decimals, p.z_move)
+
+ def down_code(self, p):
+ return 'G01 Z' + self.coordinate_format%(p.coords_decimals, p.z_cut)
+
+ def toolchange_code(self, p):
+ toolchangez = p.toolchangez
+ toolchangexy = p.toolchange_xy
+ f_plunge = p.f_plunge
+ gcode = ''
+
+ if toolchangexy is not None:
+ toolchangex = toolchangexy[0]
+ toolchangey = toolchangexy[1]
+
+ no_drills = 1
+
+ if int(p.tool) == 1 and p.startz is not None:
+ toolchangez = p.startz
+
+ if p.units.upper() == 'MM':
+ toolC_formatted = format(p.toolC, '.2f')
+ else:
+ toolC_formatted = format(p.toolC, '.4f')
+
+ if str(p['options']['type']) == 'Excellon':
+ for i in p['options']['Tools_in_use']:
+ if i[0] == p.tool:
+ no_drills = i[2]
+
+ if toolchangexy is not None:
+ gcode = """
+M5
+G00 Z{toolchangez}
+G00 X{toolchangex} Y{toolchangey}
+T{tool}
+M6
+(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
+M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
+ toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
+ toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
+ tool=int(p.tool),
+ t_drills=no_drills,
+ toolC=toolC_formatted)
+ else:
+ gcode = """
+M5
+G00 Z{toolchangez}
+T{tool}
+M6
+(MSG, Change to Tool Dia = {toolC} ||| Total drills for tool T{tool} = {t_drills})
+M0""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
+ tool=int(p.tool),
+ t_drills=no_drills,
+ toolC=toolC_formatted)
+ if f_plunge is True:
+ gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
+ return gcode
+
+ else:
+ if toolchangexy is not None:
+ gcode = """
+M5
+G00 Z{toolchangez}
+G00 X{toolchangex} Y{toolchangey}
+T{tool}
+M6
+(MSG, Change to Tool Dia = {toolC})
+M0""".format(toolchangex=self.coordinate_format % (p.coords_decimals, toolchangex),
+ toolchangey=self.coordinate_format % (p.coords_decimals, toolchangey),
+ toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez),
+ tool=int(p.tool),
+ toolC=toolC_formatted)
+ else:
+ gcode = """
+M5
+G00 Z{toolchangez}
+T{tool}
+M6
+(MSG, Change to Tool Dia = {toolC})
+M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez),
+ tool=int(p.tool),
+ toolC=toolC_formatted)
+
+ if f_plunge is True:
+ gcode += '\nG00 Z%.*f' % (p.coords_decimals, p.z_move)
+ return gcode
+
+ def up_to_zero_code(self, p):
+ return 'G01 Z0'
+
+ 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 ('G00 ' + self.position_code(p)).format(**p)
+
+ def linear_code(self, p):
+ return ('G01 ' + self.position_code(p)).format(**p)
+
+ def end_code(self, p):
+ coords_xy = p['toolchange_xy']
+ gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n")
+
+ if coords_xy is not None:
+ gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n"
+ return gcode
+
+ def feedrate_code(self, p):
+ return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate))
+
+ def feedrate_z_code(self, p):
+ return 'G01 F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate_z))
+
+ def spindle_code(self, p):
+ if p.spindlespeed:
+ return 'M03 S' + str(p.spindlespeed)
+ else:
+ return 'M03'
+
+ def dwell_code(self, p):
+ if p.dwelltime:
+ return 'G4 P' + str(p.dwelltime)
+
+ def spindle_stop_code(self,p):
+ return 'M05'