- 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.
This commit is contained in:
Marius Stanciu 2019-02-20 22:46:46 +02:00 committed by Marius S
parent bd1293a774
commit f62e7e51fd
5 changed files with 638 additions and 89 deletions

View File

@ -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()

View File

@ -981,6 +981,10 @@ class FlatCAMGUI(QtWidgets.QMainWindow):
<td height="20"><strong>ALT+D</strong></td>
<td>&nbsp;2-Sided PCB Tool</td>
</tr>
<tr height="20">
<td height="20"><strong>ALT+K</strong></td>
<td>&nbsp;Solder Paste Dispensing Tool</td>
</tr>
<tr height="20">
<td height="20"><strong>ALT+L</strong></td>
<td>&nbsp;Film PCB Tool</td>
@ -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("<b>Parameters:</b>")
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('<b>New Nozzle Dia:</b>')
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):

View File

@ -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

View File

@ -77,7 +77,7 @@ class ToolSolderPaste(FlatCAMTool):
hlay_tools = QtWidgets.QHBoxLayout()
self.layout.addLayout(hlay_tools)
self.addtool_entry_lbl = QtWidgets.QLabel('<b>Nozzle Dia:</b>')
self.addtool_entry_lbl = QtWidgets.QLabel('<b>New Nozzle Tool:</b>')
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("<b>STEP 1:</b>")
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("<b>STEP 2:</b>")
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("<b>STEP 3:</b>")
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()))

196
postprocessors/Paste_1.py Normal file
View File

@ -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'